mirror of
				https://github.com/MHSanaei/3x-ui.git
				synced 2025-10-31 12:22:51 +00:00 
			
		
		
		
	Compare commits
	
		
			No commits in common. "f7a3ebf2f3c28d40c1ae126f73ac6a8c9e22c2c6" and "bacbcc61a436112c720f6c2920b844ab0eebb987" have entirely different histories.
		
	
	
		
			f7a3ebf2f3
			...
			bacbcc61a4
		
	
		
					 15 changed files with 2537 additions and 2780 deletions
				
			
		|  | @ -2150,7 +2150,7 @@ Inbound.TrojanSettings.Fallback = class extends XrayCommonClass { | ||||||
| Inbound.ShadowsocksSettings = class extends Inbound.Settings { | Inbound.ShadowsocksSettings = class extends Inbound.Settings { | ||||||
|     constructor(protocol, |     constructor(protocol, | ||||||
|         method = SSMethods.BLAKE3_AES_256_GCM, |         method = SSMethods.BLAKE3_AES_256_GCM, | ||||||
|         password = '', |         password = RandomUtil.randomShadowsocksPassword(), | ||||||
|         network = 'tcp,udp', |         network = 'tcp,udp', | ||||||
|         shadowsockses = [new Inbound.ShadowsocksSettings.Shadowsocks()], |         shadowsockses = [new Inbound.ShadowsocksSettings.Shadowsocks()], | ||||||
|         ivCheck = false, |         ivCheck = false, | ||||||
|  | @ -2188,7 +2188,7 @@ Inbound.ShadowsocksSettings = class extends Inbound.Settings { | ||||||
| Inbound.ShadowsocksSettings.Shadowsocks = class extends XrayCommonClass { | Inbound.ShadowsocksSettings.Shadowsocks = class extends XrayCommonClass { | ||||||
|     constructor( |     constructor( | ||||||
|         method = '', |         method = '', | ||||||
|         password = '', |         password = RandomUtil.randomShadowsocksPassword(), | ||||||
|         email = RandomUtil.randomLowerAndNum(8), |         email = RandomUtil.randomLowerAndNum(8), | ||||||
|         limitIp = 0, |         limitIp = 0, | ||||||
|         totalGB = 0, |         totalGB = 0, | ||||||
|  |  | ||||||
|  | @ -138,14 +138,8 @@ class RandomUtil { | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     static randomShadowsocksPassword(method = SSMethods.BLAKE3_AES_256_GCM) { |     static randomShadowsocksPassword() { | ||||||
|         let length = 32; |         const array = new Uint8Array(32); | ||||||
| 
 |  | ||||||
|         if ([SSMethods.BLAKE3_AES_128_GCM].includes(method)) { |  | ||||||
|             length = 16;  |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         const array = new Uint8Array(length); |  | ||||||
| 
 | 
 | ||||||
|         window.crypto.getRandomValues(array); |         window.crypto.getRandomValues(array); | ||||||
| 
 | 
 | ||||||
|  | @ -795,25 +789,6 @@ class LanguageManager { | ||||||
|             if (window.navigator) { |             if (window.navigator) { | ||||||
|                 lang = window.navigator.language || window.navigator.userLanguage; |                 lang = window.navigator.language || window.navigator.userLanguage; | ||||||
| 
 | 
 | ||||||
|                 const simularLangs = [ |  | ||||||
|                     ["ar", this.supportedLanguages[0].value], |  | ||||||
|                     ["fa", this.supportedLanguages[2].value], |  | ||||||
|                     ["ja", this.supportedLanguages[5].value], |  | ||||||
|                     ["ru", this.supportedLanguages[6].value], |  | ||||||
|                     ["vi", this.supportedLanguages[7].value], |  | ||||||
|                     ["es", this.supportedLanguages[8].value], |  | ||||||
|                     ["id", this.supportedLanguages[9].value], |  | ||||||
|                     ["uk", this.supportedLanguages[10].value], |  | ||||||
|                     ["tr", this.supportedLanguages[11].value], |  | ||||||
|                     ["pt", this.supportedLanguages[12].value], |  | ||||||
|                 ] |  | ||||||
| 
 |  | ||||||
|                 simularLangs.forEach((pair) => { |  | ||||||
|                     if (lang === pair[0]) { |  | ||||||
|                         lang = pair[1]; |  | ||||||
|                     } |  | ||||||
|                 }); |  | ||||||
| 
 |  | ||||||
|                 if (LanguageManager.isSupportLanguage(lang)) { |                 if (LanguageManager.isSupportLanguage(lang)) { | ||||||
|                     CookieManager.setCookie("lang", lang, 150); |                     CookieManager.setCookie("lang", lang, 150); | ||||||
|                 } else { |                 } else { | ||||||
|  |  | ||||||
							
								
								
									
										31
									
								
								web/html/common/head.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								web/html/common/head.html
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,31 @@ | ||||||
|  | {{define "head"}} | ||||||
|  | <head> | ||||||
|  |   <meta charset="UTF-8"> | ||||||
|  |   <meta name="renderer" content="webkit"> | ||||||
|  |   <meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||||||
|  |   <meta name="robots" content="noindex,nofollow"> | ||||||
|  |   <link rel="stylesheet" href="{{ .base_path }}assets/ant-design-vue/antd.min.css"> | ||||||
|  |   <link rel="stylesheet" href="{{ .base_path }}assets/css/custom.min.css?{{ .cur_ver }}"> | ||||||
|  |   <style> | ||||||
|  |     [v-cloak] { | ||||||
|  |       display: none; | ||||||
|  |     } | ||||||
|  |     /* vazirmatn-regular - arabic_latin_latin-ext */ | ||||||
|  |     @font-face { | ||||||
|  |       font-display: swap; | ||||||
|  |       font-family: 'Vazirmatn'; | ||||||
|  |       font-style: normal; | ||||||
|  |       font-weight: 400; | ||||||
|  |       src: url('{{ .base_path }}assets/Vazirmatn-UI-NL-Regular.woff2') format('woff2'); | ||||||
|  |       unicode-range: U+0600-06FF, U+200C-200E, U+2010-2011, U+204F, U+2E41, U+FB50-FDFF, U+FE80-FEFC, U+0030-0039; | ||||||
|  |     } | ||||||
|  |     body { | ||||||
|  |       font-family: -apple-system, BlinkMacSystemFont, 'Vazirmatn', 'Segoe UI', 'PingFang SC', 'Hiragino Sans GB', | ||||||
|  |         'Microsoft YaHei', 'Helvetica Neue', Helvetica, Arial, sans-serif, 'Apple Color Emoji', | ||||||
|  |         'Segoe UI Emoji', 'Segoe UI Symbol'; | ||||||
|  |     } | ||||||
|  |   </style> | ||||||
|  |   <title>{{ .host }} – {{ i18n .title}}</title> | ||||||
|  | </head> | ||||||
|  | <div id="message"></div> | ||||||
|  | {{end}} | ||||||
							
								
								
									
										14
									
								
								web/html/common/js.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								web/html/common/js.html
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,14 @@ | ||||||
|  | {{define "js"}} | ||||||
|  | <script src="{{ .base_path }}assets/vue/vue.min.js?{{ .cur_ver }}"></script> | ||||||
|  | <script src="{{ .base_path }}assets/moment/moment.min.js"></script> | ||||||
|  | <script src="{{ .base_path }}assets/ant-design-vue/antd.min.js"></script> | ||||||
|  | <script src="{{ .base_path }}assets/axios/axios.min.js?{{ .cur_ver }}"></script> | ||||||
|  | <script src="{{ .base_path }}assets/qs/qs.min.js"></script> | ||||||
|  | <script src="{{ .base_path }}assets/js/axios-init.js?{{ .cur_ver }}"></script> | ||||||
|  | <script src="{{ .base_path }}assets/js/util/date-util.js?{{ .cur_ver }}"></script> | ||||||
|  | <script src="{{ .base_path }}assets/js/util/index.js?{{ .cur_ver }}"></script> | ||||||
|  | <script> | ||||||
|  |     const basePath = '{{ .base_path }}'; | ||||||
|  |     axios.defaults.baseURL = basePath; | ||||||
|  | </script> | ||||||
|  | {{end}} | ||||||
|  | @ -1,58 +0,0 @@ | ||||||
| {{ define "page/head_start" }} |  | ||||||
| <!DOCTYPE html> |  | ||||||
| <html> |  | ||||||
| <head> |  | ||||||
|   <meta charset="UTF-8"> |  | ||||||
|   <meta name="renderer" content="webkit"> |  | ||||||
|   <meta name="viewport" content="width=device-width, initial-scale=1.0"> |  | ||||||
|   <meta name="robots" content="noindex,nofollow"> |  | ||||||
|   <link rel="stylesheet" href="{{ .base_path }}assets/ant-design-vue/antd.min.css"> |  | ||||||
|   <link rel="stylesheet" href="{{ .base_path }}assets/css/custom.min.css?{{ .cur_ver }}"> |  | ||||||
|   <style> |  | ||||||
|     [v-cloak] { |  | ||||||
|       display: none; |  | ||||||
|     } |  | ||||||
|     /* vazirmatn-regular - arabic_latin_latin-ext */ |  | ||||||
|     @font-face { |  | ||||||
|       font-display: swap; |  | ||||||
|       font-family: 'Vazirmatn'; |  | ||||||
|       font-style: normal; |  | ||||||
|       font-weight: 400; |  | ||||||
|       src: url('{{ .base_path }}assets/Vazirmatn-UI-NL-Regular.woff2') format('woff2'); |  | ||||||
|       unicode-range: U+0600-06FF, U+200C-200E, U+2010-2011, U+204F, U+2E41, U+FB50-FDFF, U+FE80-FEFC, U+0030-0039; |  | ||||||
|     } |  | ||||||
|     body { |  | ||||||
|       font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Vazirmatn', 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; |  | ||||||
|     } |  | ||||||
|   </style> |  | ||||||
|   <title>{{ .host }} – {{ i18n .title}}</title> |  | ||||||
| {{ end }} |  | ||||||
| 
 |  | ||||||
| {{ define "page/head_end" }} |  | ||||||
| </head> |  | ||||||
| {{ end }} |  | ||||||
| 
 |  | ||||||
| {{ define "page/body_start" }} |  | ||||||
| <body> |  | ||||||
|   <div id="message"></div> |  | ||||||
| {{ end }} |  | ||||||
| 
 |  | ||||||
| {{ define "page/body_scripts" }} |  | ||||||
| <script src="{{ .base_path }}assets/vue/vue.min.js?{{ .cur_ver }}"></script> |  | ||||||
| <script src="{{ .base_path }}assets/moment/moment.min.js"></script> |  | ||||||
| <script src="{{ .base_path }}assets/ant-design-vue/antd.min.js"></script> |  | ||||||
| <script src="{{ .base_path }}assets/axios/axios.min.js?{{ .cur_ver }}"></script> |  | ||||||
| <script src="{{ .base_path }}assets/qs/qs.min.js"></script> |  | ||||||
| <script src="{{ .base_path }}assets/js/axios-init.js?{{ .cur_ver }}"></script> |  | ||||||
| <script src="{{ .base_path }}assets/js/util/date-util.js?{{ .cur_ver }}"></script> |  | ||||||
| <script src="{{ .base_path }}assets/js/util/index.js?{{ .cur_ver }}"></script> |  | ||||||
| <script> |  | ||||||
|   const basePath = '{{ .base_path }}'; |  | ||||||
|   axios.defaults.baseURL = basePath; |  | ||||||
| </script> |  | ||||||
| {{ end }} |  | ||||||
|    |  | ||||||
| {{ define "page/body_end" }} |  | ||||||
| </body> |  | ||||||
| </html> |  | ||||||
| {{ end }} |  | ||||||
|  | @ -41,28 +41,14 @@ | ||||||
|   </template> |   </template> | ||||||
| </template> | </template> | ||||||
| <template slot="client" slot-scope="text, client"> | <template slot="client" slot-scope="text, client"> | ||||||
|   <a-space direction="horizontal" :size="2"> |   <a-tooltip> | ||||||
|     <a-tooltip> |     <template slot="title"> | ||||||
|       <template slot="title"> |       <template v-if="!isClientEnabled(record, client.email)">{{ i18n "depleted" }}</template> | ||||||
|         <template v-if="!isClientEnabled(record, client.email)">{{ i18n "depleted" }}</template> |       <template v-else-if="!client.enable">{{ i18n "disabled" }}</template> | ||||||
|         <template v-else-if="!client.enable">{{ i18n "disabled" }}</template> |       <template v-else-if="client.enable && isClientOnline(client.email)">{{ i18n "online" }}</template> | ||||||
|         <template v-else-if="client.enable && isClientOnline(client.email)">{{ i18n "online" }}</template> |     </template> | ||||||
|       </template> |     <a-badge :class="isClientOnline(client.email)? 'online-animation' : ''" :color="client.enable ? statsExpColor(record, client.email) : themeSwitcher.isDarkTheme ? '#2c3950' : '#bcbcbc'"></a-badge> | ||||||
|       <a-badge :class="isClientOnline(client.email)? 'online-animation' : ''" :color="client.enable ? statsExpColor(record, client.email) : themeSwitcher.isDarkTheme ? '#2c3950' : '#bcbcbc'"></a-badge> |   </a-tooltip> [[ client.email ]] | ||||||
|     </a-tooltip> |  | ||||||
|     <a-space direction="vertical" :size="2"> |  | ||||||
|       <span class="client-email">[[ client.email ]]</span> |  | ||||||
|       <template v-if="client.comment && client.comment.trim()"> |  | ||||||
|         <a-tooltip v-if="client.comment.length > 50" :overlay-class-name="themeSwitcher.currentTheme"> |  | ||||||
|           <template slot="title"> |  | ||||||
|             [[ client.comment ]] |  | ||||||
|           </template> |  | ||||||
|           <span class="client-comment">[[ client.comment.substring(0, 47) + '...' ]]</span> |  | ||||||
|         </a-tooltip> |  | ||||||
|         <span v-else class="client-comment">[[ client.comment ]]</span> |  | ||||||
|       </template> |  | ||||||
|     </a-space> |  | ||||||
|   </a-space> |  | ||||||
| </template> | </template> | ||||||
| <template slot="traffic" slot-scope="text, client"> | <template slot="traffic" slot-scope="text, client"> | ||||||
|   <a-popover :overlay-class-name="themeSwitcher.currentTheme"> |   <a-popover :overlay-class-name="themeSwitcher.currentTheme"> | ||||||
|  |  | ||||||
|  | @ -22,7 +22,7 @@ | ||||||
|                     <span>{{ i18n "reset" }}</span> |                     <span>{{ i18n "reset" }}</span> | ||||||
|                 </template> |                 </template> | ||||||
|                 {{ i18n "password" }} |                 {{ i18n "password" }} | ||||||
|                 <a-icon v-if="inbound.protocol === Protocols.SHADOWSOCKS" @click="client.password = RandomUtil.randomShadowsocksPassword(inbound.settings.method)" type="sync"></a-icon> |                 <a-icon v-if="inbound.protocol === Protocols.SHADOWSOCKS"@click="client.password = RandomUtil.randomShadowsocksPassword()" type="sync"></a-icon> | ||||||
|                 <a-icon v-if="inbound.protocol === Protocols.TROJAN" @click="client.password = RandomUtil.randomSeq(10)"type="sync"> </a-icon> |                 <a-icon v-if="inbound.protocol === Protocols.TROJAN" @click="client.password = RandomUtil.randomSeq(10)"type="sync"> </a-icon> | ||||||
|             </a-tooltip> |             </a-tooltip> | ||||||
|         </template> |         </template> | ||||||
|  |  | ||||||
|  | @ -31,7 +31,7 @@ | ||||||
|             <a-tooltip> |             <a-tooltip> | ||||||
|                 <template slot="title"> |                 <template slot="title"> | ||||||
|                     <span>{{ i18n "reset" }}</span> |                     <span>{{ i18n "reset" }}</span> | ||||||
|                 </template> Password <a-icon @click="inbound.settings.password = RandomUtil.randomShadowsocksPassword(inbound.settings.method)" type="sync"></a-icon> |                 </template> Password <a-icon @click="inbound.settings.password = RandomUtil.randomShadowsocksPassword()" type="sync"></a-icon> | ||||||
|             </a-tooltip> |             </a-tooltip> | ||||||
|         </template> |         </template> | ||||||
|         <a-input v-model.trim="inbound.settings.password"></a-input> |         <a-input v-model.trim="inbound.settings.password"></a-input> | ||||||
|  |  | ||||||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							|  | @ -1,9 +1,20 @@ | ||||||
| {{ template "page/head_start" .}} | <!DOCTYPE html> | ||||||
|  | <html lang="en"> | ||||||
|  | {{template "head" .}} | ||||||
| <style> | <style> | ||||||
|   @media (min-width: 769px) { |   @media (min-width: 769px) { | ||||||
|     .ant-layout-content { |     .ant-layout-content { | ||||||
|       margin: 24px 16px; |       margin: 24px 16px; | ||||||
|     } |     } | ||||||
|  |     .ant-card-hoverable { | ||||||
|  |       margin-inline: 0.3rem; | ||||||
|  |     } | ||||||
|  |     .ant-alert-error { | ||||||
|  |       margin-inline: 0.3rem; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   .ant-col-sm-24 { | ||||||
|  |     margin-top: 10px; | ||||||
|   } |   } | ||||||
|   .ant-card-dark h2 { |   .ant-card-dark h2 { | ||||||
|     color: var(--dark-color-text-primary); |     color: var(--dark-color-text-primary); | ||||||
|  | @ -68,385 +79,384 @@ | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| </style> | </style> | ||||||
| {{ template "page/head_end" .}} |  | ||||||
| 
 | 
 | ||||||
| {{ template "page/body_start" .}} | <body> | ||||||
| <a-layout id="app" v-cloak :class="themeSwitcher.currentTheme"> |   <a-layout id="app" v-cloak :class="themeSwitcher.currentTheme"> | ||||||
|   <a-sidebar></a-sidebar> |     <a-sidebar></a-sidebar> | ||||||
|   <a-layout id="content-layout"> |     <a-layout id="content-layout"> | ||||||
|     <a-layout-content> |       <a-layout-content> | ||||||
|       <a-spin :spinning="loadingStates.spinning" :delay="200" :tip="loadingTip"> |         <a-spin :spinning="spinning" :delay="200" :tip="loadingTip"> | ||||||
|         <transition name="list" appear> |           <transition name="list" appear> | ||||||
|           <a-alert type="error" v-if="showAlert && loadingStates.fetched" :style="{ marginBottom: '10px' }" |             <a-alert type="error" v-if="showAlert" :style="{ marginBottom: '10px' }" | ||||||
|             message='{{ i18n "secAlertTitle" }}' |               message='{{ i18n "secAlertTitle" }}' | ||||||
|             color="red" |               color="red" | ||||||
|             description='{{ i18n "secAlertSsl" }}' |               description='{{ i18n "secAlertSsl" }}' | ||||||
|             show-icon closable> |               show-icon closable> | ||||||
|           </a-alert> |             </a-alert> | ||||||
|         </transition> |           </transition> | ||||||
|         <transition name="list" appear> |           <transition name="list" appear> | ||||||
|           <template> |             <template> | ||||||
|             <a-row v-if="!loadingStates.fetched"> |               <a-row v-if="!status.isLoaded"> | ||||||
|               <a-card :style="{ textAlign: 'center', padding: '30px 0', marginTop: '10px', background: 'transparent', border: 'none' }"> |                 <a-card :style="{ textAlign: 'center', padding: '30px 0', marginTop: '10px', background: 'transparent', border: 'none' }"> | ||||||
|                 <a-spin tip='{{ i18n "loading" }}'></a-spin> |                   <a-spin tip='{{ i18n "loading" }}'></a-spin> | ||||||
|               </a-card> |  | ||||||
|             </a-row> |  | ||||||
|             <a-row :gutter="[isMobile ? 8 : 16, isMobile ? 0 : 12]" v-else> |  | ||||||
|               <a-col> |  | ||||||
|                 <a-card hoverable> |  | ||||||
|                   <a-row :gutter="[0, isMobile ? 16 : 0]"> |  | ||||||
|                     <a-col :sm="24" :md="12"> |  | ||||||
|                       <a-row> |  | ||||||
|                         <a-col :span="12" :style="{ textAlign: 'center' }"> |  | ||||||
|                           <a-progress type="dashboard" status="normal" |  | ||||||
|                             :stroke-color="status.cpu.color" |  | ||||||
|                             :percent="status.cpu.percent"></a-progress> |  | ||||||
|                           <div> |  | ||||||
|                             <b>{{ i18n "pages.index.cpu" }}:</b> [[ CPUFormatter.cpuCoreFormat(status.cpuCores) ]]  |  | ||||||
|                             <a-tooltip> |  | ||||||
|                               <a-icon type="area-chart"></a-icon>  |  | ||||||
|                               <template slot="title"> |  | ||||||
|                                 <div><b>{{ i18n "pages.index.logicalProcessors" }}:</b> [[ (status.logicalPro) ]]</div> |  | ||||||
|                                 <div><b>{{ i18n "pages.index.frequency" }}:</b> [[ CPUFormatter.cpuSpeedFormat(status.cpuSpeedMhz) ]]</div> |  | ||||||
|                               </template> |  | ||||||
|                             </a-tooltip> |  | ||||||
|                           </div> |  | ||||||
|                         </a-col> |  | ||||||
|                         <a-col :span="12" :style="{ textAlign: 'center' }"> |  | ||||||
|                           <a-progress type="dashboard" status="normal" |  | ||||||
|                             :stroke-color="status.mem.color" |  | ||||||
|                             :percent="status.mem.percent"></a-progress> |  | ||||||
|                           <div> |  | ||||||
|                             <b>{{ i18n "pages.index.memory"}}:</b> [[ SizeFormatter.sizeFormat(status.mem.current) ]] / [[ SizeFormatter.sizeFormat(status.mem.total) ]] |  | ||||||
|                           </div> |  | ||||||
|                         </a-col> |  | ||||||
|                       </a-row> |  | ||||||
|                     </a-col> |  | ||||||
|                     <a-col :sm="24" :md="12"> |  | ||||||
|                       <a-row> |  | ||||||
|                         <a-col :span="12" :style="{ textAlign: 'center' }"> |  | ||||||
|                           <a-progress type="dashboard" status="normal" |  | ||||||
|                             :stroke-color="status.swap.color" |  | ||||||
|                             :percent="status.swap.percent"></a-progress> |  | ||||||
|                           <div> |  | ||||||
|                             <b>{{ i18n "pages.index.swap" }}:</b> [[ SizeFormatter.sizeFormat(status.swap.current) ]] / [[ SizeFormatter.sizeFormat(status.swap.total) ]] |  | ||||||
|                           </div> |  | ||||||
|                         </a-col> |  | ||||||
|                         <a-col :span="12" :style="{ textAlign: 'center' }"> |  | ||||||
|                           <a-progress type="dashboard" status="normal" |  | ||||||
|                             :stroke-color="status.disk.color" |  | ||||||
|                             :percent="status.disk.percent"></a-progress> |  | ||||||
|                           <div> |  | ||||||
|                             <b>{{ i18n "pages.index.storage"}}:</b> [[ SizeFormatter.sizeFormat(status.disk.current) ]] / [[ SizeFormatter.sizeFormat(status.disk.total) ]] |  | ||||||
|                           </div> |  | ||||||
|                         </a-col> |  | ||||||
|                       </a-row> |  | ||||||
|                     </a-col> |  | ||||||
|                   </a-row> |  | ||||||
|                 </a-card> |                 </a-card> | ||||||
|               </a-col> |               </a-row> | ||||||
|               <a-col :sm="24" :lg="12"> |               <a-row v-else> | ||||||
|                 <a-card hoverable> |                 <a-row> | ||||||
|                   <template #title> |                   <a-card hoverable> | ||||||
|                     <a-space direction="horizontal"> |                     <a-row> | ||||||
|                       <span>{{ i18n "pages.index.xrayStatus" }}</span> |                       <a-col :sm="24" :md="12"> | ||||||
|                       <a-tag v-if="isMobile && status.xray.version != 'Unknown'" color="green"> |                         <a-row> | ||||||
|                         v[[ status.xray.version ]] |                           <a-col :span="12" :style="{ textAlign: 'center' }"> | ||||||
|                       </a-tag> |                             <a-progress type="dashboard" status="normal" | ||||||
|                     </a-space> |                               :stroke-color="status.cpu.color" | ||||||
|                   </template> |                               :percent="status.cpu.percent"></a-progress> | ||||||
|                   <template #extra> |                             <div> | ||||||
|                     <template v-if="status.xray.state != 'error'"> |                               <b>{{ i18n "pages.index.cpu" }}:</b> [[ CPUFormatter.cpuCoreFormat(status.cpuCores) ]]  | ||||||
|                       <a-badge status="processing" class="running-animation" :text="status.xray.stateMsg" :color="status.xray.color"/> |                               <a-tooltip> | ||||||
|  |                                 <a-icon type="area-chart"></a-icon>  | ||||||
|  |                                 <template slot="title"> | ||||||
|  |                                   <div><b>{{ i18n "pages.index.logicalProcessors" }}:</b> [[ (status.logicalPro) ]]</div> | ||||||
|  |                                   <div><b>{{ i18n "pages.index.frequency" }}:</b> [[ CPUFormatter.cpuSpeedFormat(status.cpuSpeedMhz) ]]</div> | ||||||
|  |                                 </template> | ||||||
|  |                               </a-tooltip> | ||||||
|  |                             </div> | ||||||
|  |                           </a-col> | ||||||
|  |                           <a-col :span="12" :style="{ textAlign: 'center' }"> | ||||||
|  |                             <a-progress type="dashboard" status="normal" | ||||||
|  |                               :stroke-color="status.mem.color" | ||||||
|  |                               :percent="status.mem.percent"></a-progress> | ||||||
|  |                             <div> | ||||||
|  |                               <b>{{ i18n "pages.index.memory"}}:</b> [[ SizeFormatter.sizeFormat(status.mem.current) ]] / [[ SizeFormatter.sizeFormat(status.mem.total) ]] | ||||||
|  |                             </div> | ||||||
|  |                           </a-col> | ||||||
|  |                         </a-row> | ||||||
|  |                       </a-col> | ||||||
|  |                       <a-col :sm="24" :md="12"> | ||||||
|  |                         <a-row> | ||||||
|  |                           <a-col :span="12" :style="{ textAlign: 'center' }"> | ||||||
|  |                             <a-progress type="dashboard" status="normal" | ||||||
|  |                               :stroke-color="status.swap.color" | ||||||
|  |                               :percent="status.swap.percent"></a-progress> | ||||||
|  |                             <div> | ||||||
|  |                               <b>{{ i18n "pages.index.swap" }}:</b> [[ SizeFormatter.sizeFormat(status.swap.current) ]] / [[ SizeFormatter.sizeFormat(status.swap.total) ]] | ||||||
|  |                             </div> | ||||||
|  |                           </a-col> | ||||||
|  |                           <a-col :span="12" :style="{ textAlign: 'center' }"> | ||||||
|  |                             <a-progress type="dashboard" status="normal" | ||||||
|  |                               :stroke-color="status.disk.color" | ||||||
|  |                               :percent="status.disk.percent"></a-progress> | ||||||
|  |                             <div> | ||||||
|  |                               <b>{{ i18n "pages.index.storage"}}:</b> [[ SizeFormatter.sizeFormat(status.disk.current) ]] / [[ SizeFormatter.sizeFormat(status.disk.total) ]] | ||||||
|  |                             </div> | ||||||
|  |                           </a-col> | ||||||
|  |                         </a-row> | ||||||
|  |                       </a-col> | ||||||
|  |                     </a-row> | ||||||
|  |                   </a-card> | ||||||
|  |                 </a-row> | ||||||
|  |                 <a-col :sm="24" :lg="12"> | ||||||
|  |                   <a-card hoverable> | ||||||
|  |                     <template #title> | ||||||
|  |                       <a-space direction="horizontal"> | ||||||
|  |                         <span>{{ i18n "pages.index.xrayStatus" }}</span> | ||||||
|  |                         <a-tag v-if="isMobile && status.xray.version != 'Unknown'" color="green"> | ||||||
|  |                           v[[ status.xray.version ]] | ||||||
|  |                         </a-tag> | ||||||
|  |                       </a-space> | ||||||
|                     </template> |                     </template> | ||||||
|                     <template v-else> |                     <template #extra> | ||||||
|                       <a-popover :overlay-class-name="themeSwitcher.currentTheme"> |                       <template v-if="status.xray.state != 'error'"> | ||||||
|                         <span slot="title"> |                         <a-badge status="processing" class="running-animation" :text="status.xray.stateMsg" :color="status.xray.color"/> | ||||||
|                           <a-row type="flex" align="middle" justify="space-between"> |                       </template> | ||||||
|                             <a-col> |                       <template v-else> | ||||||
|                               <span>{{ i18n "pages.index.xrayErrorPopoverTitle" }}</span> |                         <a-popover :overlay-class-name="themeSwitcher.currentTheme"> | ||||||
|                             </a-col> |                           <span slot="title"> | ||||||
|                             <a-col> |                             <a-row type="flex" align="middle" justify="space-between"> | ||||||
|                               <a-icon type="bars" :style="{ cursor: 'pointer', float: 'right' }" @click="openLogs()"></a-tag> |                               <a-col> | ||||||
|                             </a-col> |                                 <span>{{ i18n "pages.index.xrayErrorPopoverTitle" }}</span> | ||||||
|                           </a-row> |                               </a-col> | ||||||
|  |                               <a-col> | ||||||
|  |                                 <a-icon type="bars" :style="{ cursor: 'pointer', float: 'right' }" @click="openLogs()"></a-tag> | ||||||
|  |                               </a-col> | ||||||
|  |                             </a-row> | ||||||
|  |                           </span> | ||||||
|  |                           <template slot="content"> | ||||||
|  |                             <span :style="{ maxWidth: '400px' }" v-for="line in status.xray.errorMsg.split('\n')">[[ line ]]</span> | ||||||
|  |                           </template> | ||||||
|  |                           <a-badge :text="status.xray.stateMsg" :color="status.xray.color"/> | ||||||
|  |                         </a-popover> | ||||||
|  |                       </template> | ||||||
|  |                     </template> | ||||||
|  |                     <template #actions> | ||||||
|  |                       <a-space direction="horizontal" @click="stopXrayService" :style="{ justifyContent: 'center' }"> | ||||||
|  |                         <a-icon type="poweroff"></a-icon> | ||||||
|  |                         <span v-if="!isMobile">{{ i18n "pages.index.stopXray" }}</span> | ||||||
|  |                       </a-space> | ||||||
|  |                       <a-space direction="horizontal" @click="restartXrayService" :style="{ justifyContent: 'center' }"> | ||||||
|  |                         <a-icon type="reload"></a-icon> | ||||||
|  |                         <span v-if="!isMobile">{{ i18n "pages.index.restartXray" }}</span> | ||||||
|  |                       </a-space> | ||||||
|  |                       <a-space direction="horizontal" @click="openSelectV2rayVersion" :style="{ justifyContent: 'center' }"> | ||||||
|  |                         <a-icon type="tool"></a-icon> | ||||||
|  |                         <span v-if="!isMobile"> | ||||||
|  |                           [[ status.xray.version != 'Unknown' ? `v${status.xray.version}` : '{{ i18n "pages.index.xraySwitch" }}' ]] | ||||||
|                         </span> |                         </span> | ||||||
|                         <template slot="content"> |                       </a-space> | ||||||
|                           <span :style="{ maxWidth: '400px' }" v-for="line in status.xray.errorMsg.split('\n')">[[ line ]]</span> |  | ||||||
|                         </template> |  | ||||||
|                         <a-badge :text="status.xray.stateMsg" :color="status.xray.color"/> |  | ||||||
|                       </a-popover> |  | ||||||
|                     </template> |                     </template> | ||||||
|                   </template> |                   </a-card> | ||||||
|                   <template #actions> |                 </a-col> | ||||||
|                     <a-space direction="horizontal" @click="stopXrayService" :style="{ justifyContent: 'center' }"> |                 <a-col :sm="24" :lg="12"> | ||||||
|                       <a-icon type="poweroff"></a-icon> |                   <a-card title='{{ i18n "menu.link" }}' hoverable> | ||||||
|                       <span v-if="!isMobile">{{ i18n "pages.index.stopXray" }}</span> |                     <template #actions> | ||||||
|                     </a-space> |                       <a-space direction="horizontal" @click="openLogs()" :style="{ justifyContent: 'center' }"> | ||||||
|                     <a-space direction="horizontal" @click="restartXrayService" :style="{ justifyContent: 'center' }"> |                         <a-icon type="bars"></a-icon> | ||||||
|                       <a-icon type="reload"></a-icon> |                         <span v-if="!isMobile">{{ i18n "pages.index.logs" }}</span> | ||||||
|                       <span v-if="!isMobile">{{ i18n "pages.index.restartXray" }}</span> |                       </a-space> | ||||||
|                     </a-space> |                       <a-space direction="horizontal" @click="openConfig" :style="{ justifyContent: 'center' }"> | ||||||
|                     <a-space direction="horizontal" @click="openSelectV2rayVersion" :style="{ justifyContent: 'center' }"> |                         <a-icon type="control"></a-icon> | ||||||
|                       <a-icon type="tool"></a-icon> |                         <span v-if="!isMobile">{{ i18n "pages.index.config" }}</span> | ||||||
|                       <span v-if="!isMobile"> |                       </a-space> | ||||||
|                         [[ status.xray.version != 'Unknown' ? `v${status.xray.version}` : '{{ i18n "pages.index.xraySwitch" }}' ]] |                       <a-space direction="horizontal" @click="openBackup" :style="{ justifyContent: 'center' }"> | ||||||
|                       </span> |                         <a-icon type="cloud-server"></a-icon> | ||||||
|                     </a-space> |                         <span v-if="!isMobile">{{ i18n "pages.index.backup" }}</span> | ||||||
|                   </template> |                       </a-space> | ||||||
|                 </a-card> |                     </template> | ||||||
|               </a-col> |                   </a-card> | ||||||
|               <a-col :sm="24" :lg="12"> |                 </a-col> | ||||||
|                 <a-card title='{{ i18n "menu.link" }}' hoverable> |                 <a-col :sm="24" :lg="12"> | ||||||
|                   <template #actions> |                   <a-card title='3X-UI' hoverable> | ||||||
|                     <a-space direction="horizontal" @click="openLogs()" :style="{ justifyContent: 'center' }"> |                     <a rel="noopener" href="https://github.com/MHSanaei/3x-ui/releases" target="_blank"> | ||||||
|                       <a-icon type="bars"></a-icon> |                       <a-tag color="green"> | ||||||
|                       <span v-if="!isMobile">{{ i18n "pages.index.logs" }}</span> |                         <span>v{{ .cur_ver }}</span> | ||||||
|                     </a-space> |                       </a-tag> | ||||||
|                     <a-space direction="horizontal" @click="openConfig" :style="{ justifyContent: 'center' }"> |                     </a> | ||||||
|                       <a-icon type="control"></a-icon> |                     <a rel="noopener" href="https://t.me/XrayUI" target="_blank"> | ||||||
|                       <span v-if="!isMobile">{{ i18n "pages.index.config" }}</span> |                       <a-tag color="green"> | ||||||
|                     </a-space> |                         <span>@XrayUI</span> | ||||||
|                     <a-space direction="horizontal" @click="openBackup" :style="{ justifyContent: 'center' }"> |                       </a-tag> | ||||||
|                       <a-icon type="cloud-server"></a-icon> |                     </a> | ||||||
|                       <span v-if="!isMobile">{{ i18n "pages.index.backup" }}</span> |                     <a rel="noopener" href="https://github.com/MHSanaei/3x-ui/wiki" target="_blank"> | ||||||
|                     </a-space> |                       <a-tag> | ||||||
|                   </template> |                         <span>{{ i18n "pages.index.documentation" }}</span> | ||||||
|                 </a-card> |                       </a-tag> | ||||||
|               </a-col> |                     </a> | ||||||
|               <a-col :sm="24" :lg="12"> |                   </a-card> | ||||||
|                 <a-card title='3X-UI' hoverable> |                 </a-col> | ||||||
|                   <a rel="noopener" href="https://github.com/MHSanaei/3x-ui/releases" target="_blank"> |                 <a-col :sm="24" :lg="12"> | ||||||
|  |                   <a-card title='{{ i18n "pages.index.operationHours" }}' hoverable> | ||||||
|  |                     <a-tag :color="status.xray.color">Xray: [[ TimeFormatter.formatSecond(status.appStats.uptime) ]]</a-tag> | ||||||
|  |                     <a-tag color="green">OS: [[ TimeFormatter.formatSecond(status.uptime) ]]</a-tag> | ||||||
|  |                   </a-card> | ||||||
|  |                 </a-col> | ||||||
|  |                 <a-col :sm="24" :lg="12"> | ||||||
|  |                   <a-card title='{{ i18n "pages.index.systemLoad" }}' hoverable> | ||||||
|                     <a-tag color="green"> |                     <a-tag color="green"> | ||||||
|                       <span>v{{ .cur_ver }}</span> |                       <a-tooltip> | ||||||
|  |                         [[ status.loads[0] ]] | [[ status.loads[1] ]] | [[ status.loads[2] ]] | ||||||
|  |                         <template slot="title"> | ||||||
|  |                           {{ i18n "pages.index.systemLoadDesc" }} | ||||||
|  |                         </template> | ||||||
|  |                       </a-tooltip> | ||||||
|                     </a-tag> |                     </a-tag> | ||||||
|                   </a> |                   </a-card> | ||||||
|                   <a rel="noopener" href="https://t.me/XrayUI" target="_blank"> |                 </a-col> | ||||||
|                     <a-tag color="green"> |                 <a-col :sm="24" :lg="12"> | ||||||
|                       <span>@XrayUI</span> |                   <a-card title='{{ i18n "usage"}}' hoverable> | ||||||
|                     </a-tag> |                     <a-tag color="green"> {{ i18n "pages.index.memory" }}: [[ SizeFormatter.sizeFormat(status.appStats.mem) ]] </a-tag> | ||||||
|                   </a> |                     <a-tag color="green"> {{ i18n "pages.index.threads" }}: [[ status.appStats.threads ]] </a-tag> | ||||||
|                   <a rel="noopener" href="https://github.com/MHSanaei/3x-ui/wiki" target="_blank"> |                   </a-card> | ||||||
|                     <a-tag color="purple"> |                 </a-col> | ||||||
|                       <span>{{ i18n "pages.index.documentation" }}</span> |                 <a-col :sm="24" :lg="12"> | ||||||
|                     </a-tag> |                   <a-card title='{{ i18n "pages.index.overallSpeed" }}' hoverable> | ||||||
|                   </a> |                     <a-row :gutter="isMobile ? [8,8] : 0"> | ||||||
|                 </a-card> |                       <a-col :span="12"> | ||||||
|               </a-col> |                         <a-custom-statistic title='{{ i18n "pages.index.upload" }}' :value="SizeFormatter.sizeFormat(status.netIO.up)"> | ||||||
|               <a-col :sm="24" :lg="12"> |                           <template #prefix> | ||||||
|                 <a-card title='{{ i18n "pages.index.operationHours" }}' hoverable> |                             <a-icon type="arrow-up" /> | ||||||
|                   <a-tag :color="status.xray.color">Xray: [[ TimeFormatter.formatSecond(status.appStats.uptime) ]]</a-tag> |                           </template> | ||||||
|                   <a-tag color="green">OS: [[ TimeFormatter.formatSecond(status.uptime) ]]</a-tag> |                           <template #suffix> | ||||||
|                 </a-card> |                             /s | ||||||
|               </a-col> |                           </template> | ||||||
|               <a-col :sm="24" :lg="12"> |                         </a-custom-statistic> | ||||||
|                 <a-card title='{{ i18n "pages.index.systemLoad" }}' hoverable> |                       </a-col> | ||||||
|                   <a-tag color="green"> |                       <a-col :span="12"> | ||||||
|                     <a-tooltip> |                         <a-custom-statistic title='{{ i18n "pages.index.download" }}' :value="SizeFormatter.sizeFormat(status.netIO.down)"> | ||||||
|                       [[ status.loads[0] ]] | [[ status.loads[1] ]] | [[ status.loads[2] ]] |                           <template #prefix> | ||||||
|                       <template slot="title"> |                             <a-icon type="arrow-down" /> | ||||||
|                         {{ i18n "pages.index.systemLoadDesc" }} |                           </template> | ||||||
|                       </template> |                           <template #suffix> | ||||||
|                     </a-tooltip> |                             /s | ||||||
|                   </a-tag> |                           </template> | ||||||
|                 </a-card> |                         </a-custom-statistic> | ||||||
|               </a-col> |                       </a-col> | ||||||
|               <a-col :sm="24" :lg="12"> |                     </a-row> | ||||||
|                 <a-card title='{{ i18n "usage"}}' hoverable> |                   </a-card> | ||||||
|                   <a-tag color="green"> {{ i18n "pages.index.memory" }}: [[ SizeFormatter.sizeFormat(status.appStats.mem) ]] </a-tag> |                 </a-col> | ||||||
|                   <a-tag color="green"> {{ i18n "pages.index.threads" }}: [[ status.appStats.threads ]] </a-tag> |                 <a-col :sm="24" :lg="12"> | ||||||
|                 </a-card> |                   <a-card title='{{ i18n "pages.index.totalData" }}' hoverable> | ||||||
|               </a-col> |                     <a-row :gutter="isMobile ? [8,8] : 0"> | ||||||
|               <a-col :sm="24" :lg="12"> |                       <a-col :span="12"> | ||||||
|                 <a-card title='{{ i18n "pages.index.overallSpeed" }}' hoverable> |                         <a-custom-statistic title='{{ i18n "pages.index.sent" }}' :value="SizeFormatter.sizeFormat(status.netTraffic.sent)"> | ||||||
|                   <a-row :gutter="isMobile ? [8,8] : 0"> |                           <template #prefix> | ||||||
|                     <a-col :span="12"> |                             <a-icon type="cloud-upload" /> | ||||||
|                       <a-custom-statistic title='{{ i18n "pages.index.upload" }}' :value="SizeFormatter.sizeFormat(status.netIO.up)"> |                           </template> | ||||||
|                         <template #prefix> |                         </a-custom-statistic> | ||||||
|                           <a-icon type="arrow-up" /> |                       </a-col> | ||||||
|  |                       <a-col :span="12"> | ||||||
|  |                         <a-custom-statistic title='{{ i18n "pages.index.received" }}' :value="SizeFormatter.sizeFormat(status.netTraffic.recv)"> | ||||||
|  |                           <template #prefix> | ||||||
|  |                             <a-icon type="cloud-download" /> | ||||||
|  |                           </template> | ||||||
|  |                         </a-custom-statistic> | ||||||
|  |                       </a-col> | ||||||
|  |                     </a-row> | ||||||
|  |                   </a-card> | ||||||
|  |                 </a-col> | ||||||
|  |                 <a-col :sm="24" :lg="12"> | ||||||
|  |                   <a-card title='{{ i18n "pages.index.ipAddresses" }}' hoverable> | ||||||
|  |                     <template #extra> | ||||||
|  |                       <a-tooltip :placement="isMobile ? 'topRight' : 'top'"> | ||||||
|  |                         <template #title> | ||||||
|  |                           {{ i18n "pages.index.toggleIpVisibility" }} | ||||||
|                         </template> |                         </template> | ||||||
|                         <template #suffix> |                         <a-icon :type="showIp ? 'eye' : 'eye-invisible'" :style="{ fontSize: '1rem' }" @click="showIp = !showIp"></a-icon> | ||||||
|                           /s |                       </a-tooltip> | ||||||
|                         </template> |                     </template> | ||||||
|                       </a-custom-statistic> |                     <a-row :class="showIp ? 'ip-visible' : 'ip-hidden'" :gutter="isMobile ? [8,8] : 0"> | ||||||
|                     </a-col> |                       <a-col :span="isMobile ? 24 : 12"> | ||||||
|                     <a-col :span="12"> |                         <a-custom-statistic title="IPv4" :value="status.publicIP.ipv4"> | ||||||
|                       <a-custom-statistic title='{{ i18n "pages.index.download" }}' :value="SizeFormatter.sizeFormat(status.netIO.down)"> |                           <template #prefix> | ||||||
|                         <template #prefix> |                             <a-icon type="global" /> | ||||||
|                           <a-icon type="arrow-down" /> |                           </template> | ||||||
|                         </template> |                         </a-custom-statistic> | ||||||
|                         <template #suffix> |                       </a-col> | ||||||
|                           /s |                       <a-col :span="isMobile ? 24 : 12"> | ||||||
|                         </template> |                         <a-custom-statistic title="IPv6" :value="status.publicIP.ipv6"> | ||||||
|                       </a-custom-statistic> |                           <template #prefix> | ||||||
|                     </a-col> |                             <a-icon type="global" /> | ||||||
|                   </a-row> |                           </template> | ||||||
|                 </a-card> |                         </a-custom-statistic> | ||||||
|               </a-col> |                       </a-col> | ||||||
|               <a-col :sm="24" :lg="12"> |                     </a-row> | ||||||
|                 <a-card title='{{ i18n "pages.index.totalData" }}' hoverable> |                   </a-card> | ||||||
|                   <a-row :gutter="isMobile ? [8,8] : 0"> |                 </a-col> | ||||||
|                     <a-col :span="12"> |                 <a-col :sm="24" :lg="12"> | ||||||
|                       <a-custom-statistic title='{{ i18n "pages.index.sent" }}' :value="SizeFormatter.sizeFormat(status.netTraffic.sent)"> |                   <a-card title='{{ i18n "pages.index.connectionCount" }}' hoverable> | ||||||
|                         <template #prefix> |                     <a-row :gutter="isMobile ? [8,8] : 0"> | ||||||
|                           <a-icon type="cloud-upload" /> |                       <a-col :span="12"> | ||||||
|                         </template> |                         <a-custom-statistic title="TCP" :value="status.tcpCount"> | ||||||
|                       </a-custom-statistic> |                           <template #prefix> | ||||||
|                     </a-col> |                             <a-icon type="swap" /> | ||||||
|                     <a-col :span="12"> |                           </template> | ||||||
|                       <a-custom-statistic title='{{ i18n "pages.index.received" }}' :value="SizeFormatter.sizeFormat(status.netTraffic.recv)"> |                         </a-custom-statistic> | ||||||
|                         <template #prefix> |                       </a-col> | ||||||
|                           <a-icon type="cloud-download" /> |                       <a-col :span="12"> | ||||||
|                         </template> |                         <a-custom-statistic title="UDP" :value="status.udpCount"> | ||||||
|                       </a-custom-statistic> |                           <template #prefix> | ||||||
|                     </a-col> |                             <a-icon type="swap" /> | ||||||
|                   </a-row> |                           </template> | ||||||
|                 </a-card> |                         </a-custom-statistic> | ||||||
|               </a-col> |                       </a-col> | ||||||
|               <a-col :sm="24" :lg="12"> |                     </a-row> | ||||||
|                 <a-card title='{{ i18n "pages.index.ipAddresses" }}' hoverable> |                   </a-card> | ||||||
|                   <template #extra> |                 </a-col> | ||||||
|                     <a-tooltip :placement="isMobile ? 'topRight' : 'top'"> |               </a-row> | ||||||
|                       <template #title> |             </template> | ||||||
|                         {{ i18n "pages.index.toggleIpVisibility" }} |           </transition> | ||||||
|                       </template> |         </a-spin> | ||||||
|                       <a-icon :type="showIp ? 'eye' : 'eye-invisible'" :style="{ fontSize: '1rem' }" @click="showIp = !showIp"></a-icon> |       </a-layout-content> | ||||||
|                     </a-tooltip> |     </a-layout> | ||||||
|                   </template> |     <a-modal id="version-modal" v-model="versionModal.visible" title='{{ i18n "pages.index.xraySwitch" }}' :closable="true" | ||||||
|                   <a-row :class="showIp ? 'ip-visible' : 'ip-hidden'" :gutter="isMobile ? [8,8] : 0"> |         @ok="() => versionModal.visible = false" :class="themeSwitcher.currentTheme" footer=""> | ||||||
|                     <a-col :span="isMobile ? 24 : 12"> |       <a-collapse default-active-key="1"> | ||||||
|                       <a-custom-statistic title="IPv4" :value="status.publicIP.ipv4"> |         <a-collapse-panel key="1" header='Xray'> | ||||||
|                         <template #prefix> |           <a-alert type="warning" :style="{ marginBottom: '12px', width: '100%' }" message='{{ i18n "pages.index.xraySwitchClickDesk" }}' show-icon></a-alert> | ||||||
|                           <a-icon type="global" /> |           <a-list class="ant-version-list" bordered :style="{ width: '100%' }"> | ||||||
|                         </template> |             <a-list-item class="ant-version-list-item" v-for="version, index in versionModal.versions"> | ||||||
|                       </a-custom-statistic> |               <a-tag :color="index % 2 == 0 ? 'purple' : 'green'">[[ version ]]</a-tag> | ||||||
|                     </a-col> |               <a-radio :class="themeSwitcher.currentTheme" :checked="version === `v${status.xray.version}`" @click="switchV2rayVersion(version)"></a-radio> | ||||||
|                     <a-col :span="isMobile ? 24 : 12"> |             </a-list-item> | ||||||
|                       <a-custom-statistic title="IPv6" :value="status.publicIP.ipv6"> |           </a-list> | ||||||
|                         <template #prefix> |         </a-collapse-panel> | ||||||
|                           <a-icon type="global" /> |         <a-collapse-panel key="2" header='Geofiles'> | ||||||
|                         </template> |           <a-list class="ant-version-list" bordered :style="{ width: '100%' }"> | ||||||
|                       </a-custom-statistic> |             <a-list-item class="ant-version-list-item" v-for="file, index in ['geosite.dat', 'geoip.dat', 'geosite_IR.dat', 'geoip_IR.dat', 'geosite_RU.dat', 'geoip_RU.dat']"> | ||||||
|                     </a-col> |               <a-tag :color="index % 2 == 0 ? 'purple' : 'green'">[[ file ]]</a-tag> | ||||||
|                   </a-row> |               <a-icon type="reload" @click="updateGeofile(file)" :style="{ marginRight: '8px' }"/> | ||||||
|                 </a-card> |             </a-list-item> | ||||||
|               </a-col> |           </a-list> | ||||||
|               <a-col :sm="24" :lg="12"> |         </a-collapse-panel> | ||||||
|                 <a-card title='{{ i18n "pages.index.connectionCount" }}' hoverable> |       </a-collapse> | ||||||
|                   <a-row :gutter="isMobile ? [8,8] : 0"> |     </a-modal> | ||||||
|                     <a-col :span="12"> |     <a-modal id="log-modal" v-model="logModal.visible" | ||||||
|                       <a-custom-statistic title="TCP" :value="status.tcpCount"> |         :closable="true" @cancel="() => logModal.visible = false" | ||||||
|                         <template #prefix> |         :class="themeSwitcher.currentTheme" | ||||||
|                           <a-icon type="swap" /> |         width="800px" footer=""> | ||||||
|                         </template> |       <template slot="title"> | ||||||
|                       </a-custom-statistic> |         {{ i18n "pages.index.logs" }} | ||||||
|                     </a-col> |         <a-icon :spin="logModal.loading" | ||||||
|                     <a-col :span="12"> |           type="sync" | ||||||
|                       <a-custom-statistic title="UDP" :value="status.udpCount"> |           :style="{ verticalAlign: 'middle', marginLeft: '10px' }" | ||||||
|                         <template #prefix> |           :disabled="logModal.loading" | ||||||
|                           <a-icon type="swap" /> |           @click="openLogs()"> | ||||||
|                         </template> |         </a-icon> | ||||||
|                       </a-custom-statistic> |       </template> | ||||||
|                     </a-col> |       <a-form layout="inline"> | ||||||
|                   </a-row> |         <a-form-item :style="{ marginRight: '0.5rem' }"> | ||||||
|                 </a-card> |           <a-input-group compact> | ||||||
|               </a-col> |             <a-select size="small" v-model="logModal.rows" :style="{ width: '70px' }" | ||||||
|             </a-row> |                 @change="openLogs()" :dropdown-class-name="themeSwitcher.currentTheme"> | ||||||
|           </template> |               <a-select-option value="10">10</a-select-option> | ||||||
|         </transition> |               <a-select-option value="20">20</a-select-option> | ||||||
|       </a-spin> |               <a-select-option value="50">50</a-select-option> | ||||||
|     </a-layout-content> |               <a-select-option value="100">100</a-select-option> | ||||||
|  |               <a-select-option value="500">500</a-select-option> | ||||||
|  |             </a-select> | ||||||
|  |             <a-select size="small" v-model="logModal.level" :style="{ width: '95px' }" | ||||||
|  |                 @change="openLogs()" :dropdown-class-name="themeSwitcher.currentTheme"> | ||||||
|  |               <a-select-option value="debug">Debug</a-select-option> | ||||||
|  |               <a-select-option value="info">Info</a-select-option> | ||||||
|  |               <a-select-option value="notice">Notice</a-select-option> | ||||||
|  |               <a-select-option value="warning">Warning</a-select-option> | ||||||
|  |               <a-select-option value="err">Error</a-select-option> | ||||||
|  |             </a-select> | ||||||
|  |           </a-input-group> | ||||||
|  |         </a-form-item> | ||||||
|  |         <a-form-item> | ||||||
|  |           <a-checkbox v-model="logModal.syslog" @change="openLogs()">SysLog</a-checkbox> | ||||||
|  |         </a-form-item> | ||||||
|  |         <a-form-item :style="{ float: 'right' }"> | ||||||
|  |           <a-button type="primary" icon="download" @click="FileManager.downloadTextFile(logModal.logs?.join('\n'), 'x-ui.log')"></a-button> | ||||||
|  |         </a-form-item> | ||||||
|  |       </a-form> | ||||||
|  |       <div class="ant-input" :style="{ height: 'auto', maxHeight: '500px', overflow: 'auto', marginTop: '0.5rem' }" v-html="logModal.formattedLogs"></div> | ||||||
|  |     </a-modal> | ||||||
|  |     <a-modal id="backup-modal"  | ||||||
|  |         v-model="backupModal.visible"  | ||||||
|  |         title='{{ i18n "pages.index.backupTitle" }}' | ||||||
|  |         :closable="true" | ||||||
|  |         footer="" | ||||||
|  |         :class="themeSwitcher.currentTheme"> | ||||||
|  |       <a-list class="ant-backup-list" bordered :style="{ width: '100%' }"> | ||||||
|  |         <a-list-item class="ant-backup-list-item"> | ||||||
|  |           <a-list-item-meta> | ||||||
|  |             <template #title>{{ i18n "pages.index.exportDatabase" }}</template> | ||||||
|  |             <template #description>{{ i18n "pages.index.exportDatabaseDesc" }}</template> | ||||||
|  |           </a-list-item-meta> | ||||||
|  |           <a-button @click="exportDatabase()" type="primary" icon="download"/> | ||||||
|  |         </a-list-item> | ||||||
|  |         <a-list-item class="ant-backup-list-item"> | ||||||
|  |           <a-list-item-meta> | ||||||
|  |             <template #title>{{ i18n "pages.index.importDatabase" }}</template> | ||||||
|  |             <template #description>{{ i18n "pages.index.importDatabaseDesc" }}</template> | ||||||
|  |           </a-list-item-meta> | ||||||
|  |           <a-button @click="importDatabase()" type="primary" icon="upload" /> | ||||||
|  |         </a-list-item> | ||||||
|  |       </a-list> | ||||||
|  |     </a-modal> | ||||||
|   </a-layout> |   </a-layout> | ||||||
|   <a-modal id="version-modal" v-model="versionModal.visible" title='{{ i18n "pages.index.xraySwitch" }}' :closable="true" | {{template "js" .}} | ||||||
|       @ok="() => versionModal.visible = false" :class="themeSwitcher.currentTheme" footer=""> |  | ||||||
|     <a-collapse default-active-key="1"> |  | ||||||
|       <a-collapse-panel key="1" header='Xray'> |  | ||||||
|         <a-alert type="warning" :style="{ marginBottom: '12px', width: '100%' }" message='{{ i18n "pages.index.xraySwitchClickDesk" }}' show-icon></a-alert> |  | ||||||
|         <a-list class="ant-version-list" bordered :style="{ width: '100%' }"> |  | ||||||
|           <a-list-item class="ant-version-list-item" v-for="version, index in versionModal.versions"> |  | ||||||
|             <a-tag :color="index % 2 == 0 ? 'purple' : 'green'">[[ version ]]</a-tag> |  | ||||||
|             <a-radio :class="themeSwitcher.currentTheme" :checked="version === `v${status.xray.version}`" @click="switchV2rayVersion(version)"></a-radio> |  | ||||||
|           </a-list-item> |  | ||||||
|         </a-list> |  | ||||||
|       </a-collapse-panel> |  | ||||||
|       <a-collapse-panel key="2" header='Geofiles'> |  | ||||||
|         <a-list class="ant-version-list" bordered :style="{ width: '100%' }"> |  | ||||||
|           <a-list-item class="ant-version-list-item" v-for="file, index in ['geosite.dat', 'geoip.dat', 'geosite_IR.dat', 'geoip_IR.dat', 'geosite_RU.dat', 'geoip_RU.dat']"> |  | ||||||
|             <a-tag :color="index % 2 == 0 ? 'purple' : 'green'">[[ file ]]</a-tag> |  | ||||||
|             <a-icon type="reload" @click="updateGeofile(file)" :style="{ marginRight: '8px' }"/> |  | ||||||
|           </a-list-item> |  | ||||||
|         </a-list> |  | ||||||
|       </a-collapse-panel> |  | ||||||
|     </a-collapse> |  | ||||||
|   </a-modal> |  | ||||||
|   <a-modal id="log-modal" v-model="logModal.visible" |  | ||||||
|       :closable="true" @cancel="() => logModal.visible = false" |  | ||||||
|       :class="themeSwitcher.currentTheme" |  | ||||||
|       width="800px" footer=""> |  | ||||||
|     <template slot="title"> |  | ||||||
|       {{ i18n "pages.index.logs" }} |  | ||||||
|       <a-icon :spin="logModal.loading" |  | ||||||
|         type="sync" |  | ||||||
|         :style="{ verticalAlign: 'middle', marginLeft: '10px' }" |  | ||||||
|         :disabled="logModal.loading" |  | ||||||
|         @click="openLogs()"> |  | ||||||
|       </a-icon> |  | ||||||
|     </template> |  | ||||||
|     <a-form layout="inline"> |  | ||||||
|       <a-form-item :style="{ marginRight: '0.5rem' }"> |  | ||||||
|         <a-input-group compact> |  | ||||||
|           <a-select size="small" v-model="logModal.rows" :style="{ width: '70px' }" |  | ||||||
|               @change="openLogs()" :dropdown-class-name="themeSwitcher.currentTheme"> |  | ||||||
|             <a-select-option value="10">10</a-select-option> |  | ||||||
|             <a-select-option value="20">20</a-select-option> |  | ||||||
|             <a-select-option value="50">50</a-select-option> |  | ||||||
|             <a-select-option value="100">100</a-select-option> |  | ||||||
|             <a-select-option value="500">500</a-select-option> |  | ||||||
|           </a-select> |  | ||||||
|           <a-select size="small" v-model="logModal.level" :style="{ width: '95px' }" |  | ||||||
|               @change="openLogs()" :dropdown-class-name="themeSwitcher.currentTheme"> |  | ||||||
|             <a-select-option value="debug">Debug</a-select-option> |  | ||||||
|             <a-select-option value="info">Info</a-select-option> |  | ||||||
|             <a-select-option value="notice">Notice</a-select-option> |  | ||||||
|             <a-select-option value="warning">Warning</a-select-option> |  | ||||||
|             <a-select-option value="err">Error</a-select-option> |  | ||||||
|           </a-select> |  | ||||||
|         </a-input-group> |  | ||||||
|       </a-form-item> |  | ||||||
|       <a-form-item> |  | ||||||
|         <a-checkbox v-model="logModal.syslog" @change="openLogs()">SysLog</a-checkbox> |  | ||||||
|       </a-form-item> |  | ||||||
|       <a-form-item :style="{ float: 'right' }"> |  | ||||||
|         <a-button type="primary" icon="download" @click="FileManager.downloadTextFile(logModal.logs?.join('\n'), 'x-ui.log')"></a-button> |  | ||||||
|       </a-form-item> |  | ||||||
|     </a-form> |  | ||||||
|     <div class="ant-input" :style="{ height: 'auto', maxHeight: '500px', overflow: 'auto', marginTop: '0.5rem' }" v-html="logModal.formattedLogs"></div> |  | ||||||
|   </a-modal> |  | ||||||
|   <a-modal id="backup-modal"  |  | ||||||
|       v-model="backupModal.visible"  |  | ||||||
|       title='{{ i18n "pages.index.backupTitle" }}' |  | ||||||
|       :closable="true" |  | ||||||
|       footer="" |  | ||||||
|       :class="themeSwitcher.currentTheme"> |  | ||||||
|     <a-list class="ant-backup-list" bordered :style="{ width: '100%' }"> |  | ||||||
|       <a-list-item class="ant-backup-list-item"> |  | ||||||
|         <a-list-item-meta> |  | ||||||
|           <template #title>{{ i18n "pages.index.exportDatabase" }}</template> |  | ||||||
|           <template #description>{{ i18n "pages.index.exportDatabaseDesc" }}</template> |  | ||||||
|         </a-list-item-meta> |  | ||||||
|         <a-button @click="exportDatabase()" type="primary" icon="download"/> |  | ||||||
|       </a-list-item> |  | ||||||
|       <a-list-item class="ant-backup-list-item"> |  | ||||||
|         <a-list-item-meta> |  | ||||||
|           <template #title>{{ i18n "pages.index.importDatabase" }}</template> |  | ||||||
|           <template #description>{{ i18n "pages.index.importDatabaseDesc" }}</template> |  | ||||||
|         </a-list-item-meta> |  | ||||||
|         <a-button @click="importDatabase()" type="primary" icon="upload" /> |  | ||||||
|       </a-list-item> |  | ||||||
|     </a-list> |  | ||||||
|   </a-modal> |  | ||||||
| </a-layout> |  | ||||||
| {{template "page/body_scripts" .}} |  | ||||||
| {{template "component/aSidebar" .}} | {{template "component/aSidebar" .}} | ||||||
| {{template "component/aThemeSwitch" .}} | {{template "component/aThemeSwitch" .}} | ||||||
| {{template "component/aCustomStatistic" .}} | {{template "component/aCustomStatistic" .}} | ||||||
|  | @ -479,7 +489,7 @@ | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     class Status { |     class Status { | ||||||
|         constructor(data) { |         constructor(data, isLoaded = false) { | ||||||
|             this.cpu = new CurTotal(0, 0); |             this.cpu = new CurTotal(0, 0); | ||||||
|             this.cpuCores = 0; |             this.cpuCores = 0; | ||||||
|             this.logicalPro = 0; |             this.logicalPro = 0; | ||||||
|  | @ -503,6 +513,7 @@ | ||||||
|               return; |               return; | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|  |             this.isLoaded = isLoaded; | ||||||
|             this.cpu = new CurTotal(data.cpu, 100); |             this.cpu = new CurTotal(data.cpu, 100); | ||||||
|             this.cpuCores = data.cpuCores; |             this.cpuCores = data.cpuCores; | ||||||
|             this.logicalPro = data.logicalPro; |             this.logicalPro = data.logicalPro; | ||||||
|  | @ -622,39 +633,32 @@ | ||||||
|         mixins: [MediaQueryMixin], |         mixins: [MediaQueryMixin], | ||||||
|         data: { |         data: { | ||||||
|             themeSwitcher, |             themeSwitcher, | ||||||
|             loadingStates: { |  | ||||||
|               fetched: false, |  | ||||||
|               spinning: false |  | ||||||
|             }, |  | ||||||
|             status: new Status(), |             status: new Status(), | ||||||
|             versionModal, |             versionModal, | ||||||
|             logModal, |             logModal, | ||||||
|             backupModal, |             backupModal, | ||||||
|  |             spinning: false, | ||||||
|             loadingTip: '{{ i18n "loading"}}', |             loadingTip: '{{ i18n "loading"}}', | ||||||
|             showAlert: false, |             showAlert: false, | ||||||
|             showIp: false |             showIp: false | ||||||
|         }, |         }, | ||||||
|         methods: { |         methods: { | ||||||
|             loading(spinning, tip = '{{ i18n "loading"}}') { |             loading(spinning, tip = '{{ i18n "loading"}}') { | ||||||
|                 this.loadingStates.spinning = spinning; |                 this.spinning = spinning; | ||||||
|                 this.loadingTip = tip; |                 this.loadingTip = tip; | ||||||
|             }, |             }, | ||||||
|             async getStatus() { |             async getStatus() { | ||||||
|                 try { |                 try { | ||||||
|                     const msg = await HttpUtil.post('/server/status'); |                     const msg = await HttpUtil.post('/server/status'); | ||||||
|                     if (msg.success) { |                     if (msg.success) { | ||||||
|                         if (!this.loadingStates.fetched) { |  | ||||||
|                             this.loadingStates.fetched = true; |  | ||||||
|                         } |  | ||||||
| 
 |  | ||||||
|                         this.setStatus(msg.obj, true); |                         this.setStatus(msg.obj, true); | ||||||
|                     } |                     } | ||||||
|                 } catch (e) { |                 } catch (e) { | ||||||
|                     console.error("Failed to get status:", e); |                     console.error("Failed to get status:", e); | ||||||
|                 } |                 } | ||||||
|             }, |             }, | ||||||
|             setStatus(data) { |             setStatus(data, isLoaded = false) { | ||||||
|                 this.status = new Status(data); |               this.status = new Status(data, isLoaded); | ||||||
|             }, |             }, | ||||||
|             async openSelectV2rayVersion() { |             async openSelectV2rayVersion() { | ||||||
|                 this.loading(true); |                 this.loading(true); | ||||||
|  | @ -784,4 +788,5 @@ | ||||||
|         }, |         }, | ||||||
|     }); |     }); | ||||||
| </script> | </script> | ||||||
| {{ template "page/body_end" .}} | </body> | ||||||
|  | </html> | ||||||
|  |  | ||||||
|  | @ -1,4 +1,6 @@ | ||||||
| {{ template "page/head_start" .}} | <!DOCTYPE html> | ||||||
|  | <html lang="en"> | ||||||
|  | {{template "head" .}} | ||||||
| <style> | <style> | ||||||
|   html * { |   html * { | ||||||
|     -webkit-font-smoothing: antialiased; |     -webkit-font-smoothing: antialiased; | ||||||
|  | @ -451,174 +453,174 @@ | ||||||
|     margin: 2px 0 4px; |     margin: 2px 0 4px; | ||||||
|   } |   } | ||||||
| </style> | </style> | ||||||
| {{ template "page/head_end" .}} |  | ||||||
| 
 | 
 | ||||||
| {{ template "page/body_start" .}} | <body> | ||||||
| <a-layout id="app" v-cloak :class="themeSwitcher.currentTheme"> |   <a-layout id="app" v-cloak :class="themeSwitcher.currentTheme"> | ||||||
|   <transition name="list" appear> |     <transition name="list" appear> | ||||||
|     <a-layout-content class="under" :style="{ minHeight: '0' }"> |       <a-layout-content class="under" :style="{ minHeight: '0' }"> | ||||||
|       <div class="waves-header"> |         <div class="waves-header"> | ||||||
|         <div class="waves-inner-header"></div> |           <div class="waves-inner-header"></div> | ||||||
|         <svg class="waves" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" |           <svg class="waves" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" | ||||||
|           viewBox="0 24 150 28" preserveAspectRatio="none" shape-rendering="auto"> |             viewBox="0 24 150 28" preserveAspectRatio="none" shape-rendering="auto"> | ||||||
|           <defs> |             <defs> | ||||||
|             <path id="gentle-wave" d="M-160 44c30 0 58-18 88-18s 58 18 88 18 58-18 88-18 58 18 88 18 v44h-352z" /> |               <path id="gentle-wave" d="M-160 44c30 0 58-18 88-18s 58 18 88 18 58-18 88-18 58 18 88 18 v44h-352z" /> | ||||||
|           </defs> |             </defs> | ||||||
|           <g class="parallax"> |             <g class="parallax"> | ||||||
|             <use xlink:href="#gentle-wave" x="48" y="0" fill="rgba(0, 135, 113, 0.08)" /> |               <use xlink:href="#gentle-wave" x="48" y="0" fill="rgba(0, 135, 113, 0.08)" /> | ||||||
|             <use xlink:href="#gentle-wave" x="48" y="3" fill="rgba(0, 135, 113, 0.08)" /> |               <use xlink:href="#gentle-wave" x="48" y="3" fill="rgba(0, 135, 113, 0.08)" /> | ||||||
|             <use xlink:href="#gentle-wave" x="48" y="5" fill="rgba(0, 135, 113, 0.08)" /> |               <use xlink:href="#gentle-wave" x="48" y="5" fill="rgba(0, 135, 113, 0.08)" /> | ||||||
|             <use xlink:href="#gentle-wave" x="48" y="7" fill="#c7ebe2" /> |               <use xlink:href="#gentle-wave" x="48" y="7" fill="#c7ebe2" /> | ||||||
|           </g> |             </g> | ||||||
|         </svg> |           </svg> | ||||||
|       </div> |         </div> | ||||||
|       <a-row type="flex" justify="center" align="middle" :style="{ height: '100%', overflow: 'auto', overflowX: 'hidden' }"> |         <a-row type="flex" justify="center" align="middle" :style="{ height: '100%', overflow: 'auto', overflowX: 'hidden' }"> | ||||||
|         <a-col :xs="22" :sm="12" :md="10" :lg="8" :xl="6" :xxl="5" id="login" :style="{ margin: '3rem 0' }"> |           <a-col :xs="22" :sm="12" :md="10" :lg="8" :xl="6" :xxl="5" id="login" :style="{ margin: '3rem 0' }"> | ||||||
|           <div class="setting-section"> |             <div class="setting-section"> | ||||||
|             <a-popover :overlay-class-name="themeSwitcher.currentTheme" title='{{ i18n "menu.settings" }}' placement="bottomRight" trigger="click"> |               <a-popover :overlay-class-name="themeSwitcher.currentTheme" title='{{ i18n "menu.settings" }}' placement="bottomRight" trigger="click"> | ||||||
|               <template slot="content"> |                 <template slot="content"> | ||||||
|                 <a-space direction="vertical" :size="10"> |                   <a-space direction="vertical" :size="10"> | ||||||
|                   <a-theme-switch-login></a-theme-switch-login> |                     <a-theme-switch-login></a-theme-switch-login> | ||||||
|                   <span>{{ i18n "pages.settings.language" }}</span> |                     <span>{{ i18n "pages.settings.language" }}</span> | ||||||
|                   <a-select ref="selectLang" :style="{ width: '100%' }" v-model="lang" @change="LanguageManager.setLanguage(lang)" :dropdown-class-name="themeSwitcher.currentTheme"> |                     <a-select ref="selectLang" :style="{ width: '100%' }" v-model="lang" @change="LanguageManager.setLanguage(lang)" :dropdown-class-name="themeSwitcher.currentTheme"> | ||||||
|                     <a-select-option :value="l.value" label="English" v-for="l in LanguageManager.supportedLanguages"> |                       <a-select-option :value="l.value" label="English" v-for="l in LanguageManager.supportedLanguages"> | ||||||
|                       <span role="img" aria-label="l.name" v-text="l.icon"></span> |                         <span role="img" aria-label="l.name" v-text="l.icon"></span> | ||||||
|                         <span v-text="l.name"></span> |                           <span v-text="l.name"></span> | ||||||
|                     </a-select-option> |                       </a-select-option> | ||||||
|                   </a-select> |                     </a-select> | ||||||
|                 </a-space> |                   </a-space> | ||||||
|               </template> |                 </template> | ||||||
|               <a-button shape="circle" icon="setting"></a-button> |                 <a-button shape="circle" icon="setting"></a-button> | ||||||
|             </a-popover> |               </a-popover> | ||||||
|           </div> |             </div> | ||||||
|           <a-row type="flex" justify="center"> |             <a-row type="flex" justify="center"> | ||||||
|             <a-col :style="{ width: '100%' }"> |               <a-col :style="{ width: '100%' }"> | ||||||
|               <h2 class="title headline zoom"> |                 <h2 class="title headline zoom"> | ||||||
|                 <span class="words-wrapper"> |                   <span class="words-wrapper"> | ||||||
|                   <b class="is-visible">{{ i18n "pages.login.hello" }}</b> |                     <b class="is-visible">{{ i18n "pages.login.hello" }}</b> | ||||||
|                   <b>{{ i18n "pages.login.title" }}</b> |                     <b>{{ i18n "pages.login.title" }}</b> | ||||||
|                 </span> |                   </span> | ||||||
|               </h2> |                 </h2> | ||||||
|             </a-col> |               </a-col> | ||||||
|           </a-row> |             </a-row> | ||||||
|           <a-row type="flex" justify="center"> |             <a-row type="flex" justify="center"> | ||||||
|             <a-col span="24"> |               <a-col span="24"> | ||||||
|               <a-form> |                 <a-form> | ||||||
|                 <a-space direction="vertical" size="middle"> |                   <a-space direction="vertical" size="middle"> | ||||||
|                   <a-form-item> |                     <a-form-item> | ||||||
|                     <a-input autocomplete="username" name="username" v-model.trim="user.username" |                       <a-input autocomplete="username" name="username" v-model.trim="user.username" | ||||||
|                       placeholder='{{ i18n "username" }}' @keydown.enter.native="login" autofocus> |                         placeholder='{{ i18n "username" }}' @keydown.enter.native="login" autofocus> | ||||||
|                       <a-icon slot="prefix" type="user" :style="{ fontSize: '1rem' }"></a-icon> |                         <a-icon slot="prefix" type="user" :style="{ fontSize: '1rem' }"></a-icon> | ||||||
|                     </a-input> |                       </a-input> | ||||||
|                   </a-form-item> |                     </a-form-item> | ||||||
|                   <a-form-item> |                     <a-form-item> | ||||||
|                     <a-input-password autocomplete="password" name="password" v-model.trim="user.password" |                       <a-input-password autocomplete="password" name="password" v-model.trim="user.password" | ||||||
|                       placeholder='{{ i18n "password" }}' @keydown.enter.native="login"> |                         placeholder='{{ i18n "password" }}' @keydown.enter.native="login"> | ||||||
|                       <a-icon slot="prefix" type="lock" :style="{ fontSize: '1rem' }"></a-icon> |                         <a-icon slot="prefix" type="lock" :style="{ fontSize: '1rem' }"></a-icon> | ||||||
|                     </a-input-password> |                       </a-input-password> | ||||||
|                   </a-form-item> |                     </a-form-item> | ||||||
|                   <a-form-item v-if="twoFactorEnable"> |                     <a-form-item v-if="twoFactorEnable"> | ||||||
|                     <a-input autocomplete="totp" name="twoFactorCode" v-model.trim="user.twoFactorCode" |                       <a-input autocomplete="totp" name="twoFactorCode" v-model.trim="user.twoFactorCode" | ||||||
|                       placeholder='{{ i18n "twoFactorCode" }}' @keydown.enter.native="login"> |                         placeholder='{{ i18n "twoFactorCode" }}' @keydown.enter.native="login"> | ||||||
|                       <a-icon slot="prefix" type="key" :style="{ fontSize: '1rem' }"></a-icon> |                         <a-icon slot="prefix" type="key" :style="{ fontSize: '1rem' }"></a-icon> | ||||||
|                     </a-input> |                       </a-input> | ||||||
|                   </a-form-item> |                     </a-form-item> | ||||||
|                   <a-form-item> |                     <a-form-item> | ||||||
|                     <a-row justify="center" class="centered"> |                       <a-row justify="center" class="centered"> | ||||||
|                       <div :style="{ height: '50px', marginTop: '1rem', ...loading ? { width: '52px' } : { display: 'inline-block' } }" class="wave-btn-bg wave-btn-bg-cl"> |                         <div :style="{ height: '50px', marginTop: '1rem', ...loading ? { width: '52px' } : { display: 'inline-block' } }" class="wave-btn-bg wave-btn-bg-cl"> | ||||||
|                         <a-button class="ant-btn-primary-login" type="primary" :loading="loading" @click="login" |                           <a-button class="ant-btn-primary-login" type="primary" :loading="loading" @click="login" | ||||||
|                           :icon="loading ? 'poweroff' : undefined"> |                             :icon="loading ? 'poweroff' : undefined"> | ||||||
|                           [[ loading ? '' : '{{ i18n "login" }}' ]] |                             [[ loading ? '' : '{{ i18n "login" }}' ]] | ||||||
|                         </a-button> |                           </a-button> | ||||||
|                       </div> |                         </div> | ||||||
|                     </a-row> |                       </a-row> | ||||||
|                   </a-form-item> |                     </a-form-item> | ||||||
|                 </a-space> |                   </a-space> | ||||||
|               </a-form> |                 </a-form> | ||||||
|             </a-col> |               </a-col> | ||||||
|           </a-row> |             </a-row> | ||||||
|         </a-col> |           </a-col> | ||||||
|       </a-row> |         </a-row> | ||||||
|     </a-layout-content> |       </a-layout-content> | ||||||
|   </transition> |     </transition> | ||||||
| </a-layout> |   </a-layout> | ||||||
| {{template "page/body_scripts" .}} |   {{template "js" .}} | ||||||
| {{template "component/aThemeSwitch" .}} |   {{template "component/aThemeSwitch" .}} | ||||||
| <script> |   <script> | ||||||
|   const app = new Vue({ |     const app = new Vue({ | ||||||
|     delimiters: ['[[', ']]'], |       delimiters: ['[[', ']]'], | ||||||
|     el: '#app', |       el: '#app', | ||||||
|     data: { |       data: { | ||||||
|       themeSwitcher, |         themeSwitcher, | ||||||
|       loading: false, |         loading: false, | ||||||
|       user: { |         user: { | ||||||
|         username: "", |           username: "", | ||||||
|         password: "", |           password: "", | ||||||
|         twoFactorCode: "" |           twoFactorCode: "" | ||||||
|  |         }, | ||||||
|  |         twoFactorEnable: false, | ||||||
|  |         lang: "" | ||||||
|       }, |       }, | ||||||
|       twoFactorEnable: false, |       async mounted() { | ||||||
|       lang: "" |         this.lang = LanguageManager.getLanguage(); | ||||||
|     }, |         this.twoFactorEnable = await this.getTwoFactorEnable(); | ||||||
|     async mounted() { |  | ||||||
|       this.lang = LanguageManager.getLanguage(); |  | ||||||
|       this.twoFactorEnable = await this.getTwoFactorEnable(); |  | ||||||
|     }, |  | ||||||
|     methods: { |  | ||||||
|       async login() { |  | ||||||
|         this.loading = true; |  | ||||||
|         const msg = await HttpUtil.post('/login', this.user); |  | ||||||
|         this.loading = false; |  | ||||||
|         if (msg.success) { |  | ||||||
|           location.href = basePath + 'panel/'; |  | ||||||
|         } |  | ||||||
|       }, |       }, | ||||||
|       async getTwoFactorEnable() { |       methods: { | ||||||
|         this.loading = true; |         async login() { | ||||||
|         const msg = await HttpUtil.post('/getTwoFactorEnable'); |           this.loading = true; | ||||||
|         this.loading = false; |           const msg = await HttpUtil.post('/login', this.user); | ||||||
|         if (msg.success) { |           this.loading = false; | ||||||
|           this.twoFactorEnable = msg.obj; |           if (msg.success) { | ||||||
|           return msg.obj; |             location.href = basePath + 'panel/'; | ||||||
|         } |           } | ||||||
|  |         }, | ||||||
|  |         async getTwoFactorEnable() { | ||||||
|  |           this.loading = true; | ||||||
|  |           const msg = await HttpUtil.post('/getTwoFactorEnable'); | ||||||
|  |           this.loading = false; | ||||||
|  |           if (msg.success) { | ||||||
|  |             this.twoFactorEnable = msg.obj; | ||||||
|  |             return msg.obj; | ||||||
|  |           } | ||||||
|  |         }, | ||||||
|       }, |       }, | ||||||
|     }, |     }); | ||||||
|   }); |  | ||||||
| 
 | 
 | ||||||
|   document.addEventListener("DOMContentLoaded", function () { |     document.addEventListener("DOMContentLoaded", function () { | ||||||
|     var animationDelay = 2000; |       var animationDelay = 2000; | ||||||
|     initHeadline(); |       initHeadline(); | ||||||
| 
 | 
 | ||||||
|     function initHeadline() { |       function initHeadline() { | ||||||
|       animateHeadline(document.querySelectorAll('.headline')); |         animateHeadline(document.querySelectorAll('.headline')); | ||||||
|     } |       } | ||||||
| 
 | 
 | ||||||
|     function animateHeadline(headlines) { |       function animateHeadline(headlines) { | ||||||
|       var duration = animationDelay; |         var duration = animationDelay; | ||||||
|       headlines.forEach(function (headline) { |         headlines.forEach(function (headline) { | ||||||
|  |           setTimeout(function () { | ||||||
|  |             hideWord(headline.querySelector('.is-visible')); | ||||||
|  |           }, duration); | ||||||
|  |         }); | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       function hideWord(word) { | ||||||
|  |         var nextWord = takeNext(word); | ||||||
|  |         switchWord(word, nextWord); | ||||||
|         setTimeout(function () { |         setTimeout(function () { | ||||||
|           hideWord(headline.querySelector('.is-visible')); |           hideWord(nextWord); | ||||||
|         }, duration); |         }, animationDelay); | ||||||
|       }); |       } | ||||||
|     } |  | ||||||
| 
 | 
 | ||||||
|     function hideWord(word) { |       function takeNext(word) { | ||||||
|       var nextWord = takeNext(word); |         return word.nextElementSibling ? word.nextElementSibling : word.parentElement.firstElementChild; | ||||||
|       switchWord(word, nextWord); |       } | ||||||
|       setTimeout(function () { |  | ||||||
|         hideWord(nextWord); |  | ||||||
|       }, animationDelay); |  | ||||||
|     } |  | ||||||
| 
 | 
 | ||||||
|     function takeNext(word) { |       function switchWord(oldWord, newWord) { | ||||||
|       return word.nextElementSibling ? word.nextElementSibling : word.parentElement.firstElementChild; |         oldWord.classList.remove('is-visible'); | ||||||
|     } |         oldWord.classList.add('is-hidden'); | ||||||
| 
 |         newWord.classList.remove('is-hidden'); | ||||||
|     function switchWord(oldWord, newWord) { |         newWord.classList.add('is-visible'); | ||||||
|       oldWord.classList.remove('is-visible'); |       } | ||||||
|       oldWord.classList.add('is-hidden'); |     }); | ||||||
|       newWord.classList.remove('is-hidden'); |   </script> | ||||||
|       newWord.classList.add('is-visible'); | </body> | ||||||
|     } | </html> | ||||||
|   }); |  | ||||||
| </script> |  | ||||||
| {{ template "page/body_end" .}} |  | ||||||
|  |  | ||||||
|  | @ -1,4 +1,6 @@ | ||||||
| {{ template "page/head_start" .}} | <!DOCTYPE html> | ||||||
|  | <html lang="en"> | ||||||
|  | {{template "head" .}} | ||||||
| <style> | <style> | ||||||
|   @media (min-width: 769px) { |   @media (min-width: 769px) { | ||||||
|     .ant-layout-content { |     .ant-layout-content { | ||||||
|  | @ -58,103 +60,68 @@ | ||||||
|     margin-block-end: 12px; |     margin-block-end: 12px; | ||||||
|   } |   } | ||||||
| </style> | </style> | ||||||
| {{ template "page/head_end" .}} | <body> | ||||||
| 
 |   <a-layout id="app" v-cloak :class="themeSwitcher.currentTheme"> | ||||||
| {{ template "page/body_start" .}} |     <a-sidebar></a-sidebar> | ||||||
| <a-layout id="app" v-cloak :class="themeSwitcher.currentTheme"> |     <a-layout id="content-layout"> | ||||||
|   <a-sidebar></a-sidebar> |       <a-layout-content> | ||||||
|   <a-layout id="content-layout"> |         <a-spin :spinning="spinning" :delay="500" tip='{{ i18n "loading"}}'> | ||||||
|     <a-layout-content> |           <transition name="list" appear> | ||||||
|       <a-spin :spinning="loadingStates.spinning" :delay="500" tip='{{ i18n "loading"}}'> |             <a-alert type="error" v-if="confAlerts.length>0" :style="{ marginBottom: '10px' }" | ||||||
|         <transition name="list" appear> |                 message='{{ i18n "secAlertTitle" }}' | ||||||
|           <a-alert type="error" v-if="confAlerts.length>0 && loadingStates.fetched" :style="{ marginBottom: '10px' }" |                 color="red" | ||||||
|               message='{{ i18n "secAlertTitle" }}' |                 show-icon closable> | ||||||
|               color="red" |               <template slot="description"> | ||||||
|               show-icon closable> |                 <b>{{ i18n "secAlertConf" }}</b> | ||||||
|             <template slot="description"> |                 <ul><li v-for="a in confAlerts">[[ a ]]</li></ul> | ||||||
|               <b>{{ i18n "secAlertConf" }}</b> |               </template> | ||||||
|               <ul><li v-for="a in confAlerts">[[ a ]]</li></ul> |             </a-alert> | ||||||
|             </template> |           </transition> | ||||||
|           </a-alert> |           <a-space direction="vertical"> | ||||||
|         </transition> |             <a-card hoverable :style="{ marginBottom: '.5rem', overflowX: 'hidden' }"> | ||||||
|         <transition name="list" appear> |               <a-row :style="{ display: 'flex', flexWrap: 'wrap', alignItems: 'center' }"> | ||||||
|           <template> |                 <a-col :xs="24" :sm="10" :style="{ padding: '4px' }"> | ||||||
|             <a-row v-if="!loadingStates.fetched"> |                   <a-space direction="horizontal"> | ||||||
|               <a-card :style="{ textAlign: 'center', padding: '30px 0', marginTop: '10px', background: 'transparent', border: 'none' }"> |                     <a-button type="primary" :disabled="saveBtnDisable" @click="updateAllSetting">{{ i18n "pages.settings.save" }}</a-button> | ||||||
|                 <a-spin tip='{{ i18n "loading" }}'></a-spin> |                     <a-button type="danger" :disabled="!saveBtnDisable" @click="restartPanel">{{ i18n "pages.settings.restartPanel" }}</a-button> | ||||||
|               </a-card> |                   </a-space> | ||||||
|             </a-row> |                 </a-col> | ||||||
|             <a-row :gutter="[isMobile ? 8 : 16, isMobile ? 0 : 12]" v-else> |                 <a-col :xs="24" :sm="14"> | ||||||
|               <a-col> |                   <template> | ||||||
|                 <a-card hoverable> |                     <div> | ||||||
|                   <a-row :style="{ display: 'flex', flexWrap: 'wrap', alignItems: 'center' }"> |                       <a-back-top :target="() => document.getElementById('content-layout')" visibility-height="200"></a-back-top> | ||||||
|                     <a-col :xs="24" :sm="10" :style="{ padding: '4px' }"> |                       <a-alert type="warning" :style="{ float: 'right', width: 'fit-content' }" | ||||||
|                       <a-space direction="horizontal"> |                         message='{{ i18n "pages.settings.infoDesc" }}' | ||||||
|                         <a-button type="primary" :disabled="saveBtnDisable" @click="updateAllSetting">{{ i18n "pages.settings.save" }}</a-button> |                         show-icon> | ||||||
|                         <a-button type="danger" :disabled="!saveBtnDisable" @click="restartPanel">{{ i18n "pages.settings.restartPanel" }}</a-button> |                       </a-alert> | ||||||
|                       </a-space> |                     </div> | ||||||
|                     </a-col> |                   </template> | ||||||
|                     <a-col :xs="24" :sm="14"> |                 </a-col> | ||||||
|                       <template> |               </a-row> | ||||||
|                         <div> |             </a-card> | ||||||
|                           <a-back-top :target="() => document.getElementById('content-layout')" visibility-height="200"></a-back-top> |             <a-tabs default-active-key="1"> | ||||||
|                           <a-alert type="warning" :style="{ float: 'right', width: 'fit-content' }" |               <a-tab-pane key="1" tab='{{ i18n "pages.settings.panelSettings" }}' :style="{ paddingTop: '20px' }"> | ||||||
|                             message='{{ i18n "pages.settings.infoDesc" }}' |                 {{ template "settings/panel/general" . }} | ||||||
|                             show-icon> |               </a-tab-pane> | ||||||
|                           </a-alert> |               <a-tab-pane key="2" tab='{{ i18n "pages.settings.securitySettings" }}' :style="{ paddingTop: '20px' }"> | ||||||
|                         </div> |                 {{ template "settings/panel/security" . }} | ||||||
|                       </template> |               </a-tab-pane> | ||||||
|                     </a-col> |               <a-tab-pane key="3" tab='{{ i18n "pages.settings.TGBotSettings" }}' :style="{ paddingTop: '20px' }"> | ||||||
|                   </a-row> |                 {{ template "settings/panel/telegram" . }} | ||||||
|                 </a-card> |               </a-tab-pane> | ||||||
|               </a-col> |               <a-tab-pane key="4" tab='{{ i18n "pages.settings.subSettings" }}' :style="{ paddingTop: '20px' }"> | ||||||
|               <a-col> |                 {{ template "settings/panel/subscription/general" . }} | ||||||
|                 <a-tabs default-active-key="1"> |               </a-tab-pane> | ||||||
|                   <a-tab-pane key="1" :style="{ paddingTop: '20px' }"> |               <a-tab-pane key="5" tab='{{ i18n "pages.settings.subSettings" }} Json' v-if="allSetting.subEnable" :style="{ paddingTop: '20px' }"> | ||||||
|                     <template #tab> |                 {{ template "settings/panel/subscription/json" . }} | ||||||
|                       <a-icon type="setting"></a-icon> |               </a-tab-pane> | ||||||
|                       <span>{{ i18n "pages.settings.panelSettings" }}</span> |             </a-tabs> | ||||||
|                     </template> |           </a-space> | ||||||
|                     {{ template "settings/panel/general" . }} |         </a-spin> | ||||||
|                   </a-tab-pane> |       </a-layout-content> | ||||||
|                   <a-tab-pane key="2" :style="{ paddingTop: '20px' }"> |     </a-layout> | ||||||
|                     <template #tab> |  | ||||||
|                       <a-icon type="safety"></a-icon> |  | ||||||
|                       <span>{{ i18n "pages.settings.securitySettings" }}</span> |  | ||||||
|                     </template> |  | ||||||
|                     {{ template "settings/panel/security" . }} |  | ||||||
|                   </a-tab-pane> |  | ||||||
|                   <a-tab-pane key="3" :style="{ paddingTop: '20px' }"> |  | ||||||
|                     <template #tab> |  | ||||||
|                       <a-icon type="message"></a-icon> |  | ||||||
|                       <span>{{ i18n "pages.settings.TGBotSettings" }}</span> |  | ||||||
|                     </template> |  | ||||||
|                     {{ template "settings/panel/telegram" . }} |  | ||||||
|                   </a-tab-pane> |  | ||||||
|                   <a-tab-pane key="4" :style="{ paddingTop: '20px' }"> |  | ||||||
|                     <template #tab> |  | ||||||
|                       <a-icon type="cloud-server"></a-icon> |  | ||||||
|                       <span>{{ i18n "pages.settings.subSettings" }}</span> |  | ||||||
|                     </template> |  | ||||||
|                     {{ template "settings/panel/subscription/general" . }} |  | ||||||
|                   </a-tab-pane> |  | ||||||
|                   <a-tab-pane key="5" v-if="allSetting.subEnable" :style="{ paddingTop: '20px' }"> |  | ||||||
|                     <template #tab> |  | ||||||
|                       <a-icon type="code"></a-icon> |  | ||||||
|                       <span>{{ i18n "pages.settings.subSettings" }} (JSON)</span> |  | ||||||
|                     </template> |  | ||||||
|                     {{ template "settings/panel/subscription/json" . }} |  | ||||||
|                   </a-tab-pane> |  | ||||||
|                 </a-tabs> |  | ||||||
|               </a-col> |  | ||||||
|             </a-row> |  | ||||||
|           </template> |  | ||||||
|         </transition> |  | ||||||
|       </a-spin> |  | ||||||
|     </a-layout-content> |  | ||||||
|   </a-layout> |   </a-layout> | ||||||
| </a-layout> | {{template "js" .}} | ||||||
| {{template "page/body_scripts" .}} |  | ||||||
| <script src="{{ .base_path }}assets/qrcode/qrious2.min.js?{{ .cur_ver }}"></script> | <script src="{{ .base_path }}assets/qrcode/qrious2.min.js?{{ .cur_ver }}"></script> | ||||||
| <script src="{{ .base_path }}assets/otpauth/otpauth.umd.min.js?{{ .cur_ver }}"></script> | <script src="{{ .base_path }}assets/otpauth/otpauth.umd.min.js?{{ .cur_ver }}"></script> | ||||||
| <script src="{{ .base_path }}assets/js/model/setting.js?{{ .cur_ver }}"></script> | <script src="{{ .base_path }}assets/js/model/setting.js?{{ .cur_ver }}"></script> | ||||||
|  | @ -165,14 +132,10 @@ | ||||||
| <script> | <script> | ||||||
|   const app = new Vue({ |   const app = new Vue({ | ||||||
|     delimiters: ['[[', ']]'], |     delimiters: ['[[', ']]'], | ||||||
|     mixins: [MediaQueryMixin], |  | ||||||
|     el: '#app', |     el: '#app', | ||||||
|     data: { |     data: { | ||||||
|       themeSwitcher, |       themeSwitcher, | ||||||
|       loadingStates: { |       spinning: false, | ||||||
|         fetched: false, |  | ||||||
|         spinning: false |  | ||||||
|       }, |  | ||||||
|       oldAllSetting: new AllSetting(), |       oldAllSetting: new AllSetting(), | ||||||
|       allSetting: new AllSetting(), |       allSetting: new AllSetting(), | ||||||
|       saveBtnDisable: true, |       saveBtnDisable: true, | ||||||
|  | @ -285,16 +248,13 @@ | ||||||
|     }, |     }, | ||||||
|     methods: { |     methods: { | ||||||
|       loading(spinning = true) { |       loading(spinning = true) { | ||||||
|         this.loadingStates.spinning = spinning; |         this.spinning = spinning; | ||||||
|       }, |       }, | ||||||
|       async getAllSetting() { |       async getAllSetting() { | ||||||
|  |         this.loading(true); | ||||||
|         const msg = await HttpUtil.post("/panel/setting/all"); |         const msg = await HttpUtil.post("/panel/setting/all"); | ||||||
| 
 |         this.loading(false); | ||||||
|         if (msg.success) { |         if (msg.success) { | ||||||
|           if (!this.loadingStates.fetched) { |  | ||||||
|             this.loadingStates.fetched = true |  | ||||||
|           } |  | ||||||
| 
 |  | ||||||
|           this.oldAllSetting = new AllSetting(msg.obj); |           this.oldAllSetting = new AllSetting(msg.obj); | ||||||
|           this.allSetting = new AllSetting(msg.obj); |           this.allSetting = new AllSetting(msg.obj); | ||||||
|           app.changeRemarkSample(); |           app.changeRemarkSample(); | ||||||
|  | @ -548,7 +508,7 @@ | ||||||
|           if (!this.allSetting) return []; |           if (!this.allSetting) return []; | ||||||
|           var alerts = [] |           var alerts = [] | ||||||
|           if (window.location.protocol !== "https:") alerts.push('{{ i18n "secAlertSSL" }}'); |           if (window.location.protocol !== "https:") alerts.push('{{ i18n "secAlertSSL" }}'); | ||||||
|           if (this.allSetting.webPort === 2053) alerts.push('{{ i18n "secAlertPanelPort" }}'); |           if (this.allSetting.webPort == 54321) alerts.push('{{ i18n "secAlertPanelPort" }}'); | ||||||
|           panelPath = window.location.pathname.split('/').length < 4 |           panelPath = window.location.pathname.split('/').length < 4 | ||||||
|           if (panelPath && this.allSetting.webBasePath == '/') alerts.push('{{ i18n "secAlertPanelURI" }}'); |           if (panelPath && this.allSetting.webBasePath == '/') alerts.push('{{ i18n "secAlertPanelURI" }}'); | ||||||
|           if (this.allSetting.subEnable) { |           if (this.allSetting.subEnable) { | ||||||
|  | @ -571,4 +531,5 @@ | ||||||
|     } |     } | ||||||
|   }); |   }); | ||||||
| </script> | </script> | ||||||
| {{ template "page/body_end" .}} | </body> | ||||||
|  | </html> | ||||||
|  | @ -1,5 +1,5 @@ | ||||||
| {{define "settings/xray/advanced"}} | {{define "settings/xray/advanced"}} | ||||||
| <a-space direction="vertical" size="small" :style="{ marginTop: '20px' }"> | <a-space direction="vertical" size="small"> | ||||||
|     <a-list-item-meta title='{{ i18n "pages.xray.Template"}}' |     <a-list-item-meta title='{{ i18n "pages.xray.Template"}}' | ||||||
|         description='{{ i18n "pages.xray.TemplateDesc"}}'></a-list-item-meta> |         description='{{ i18n "pages.xray.TemplateDesc"}}'></a-list-item-meta> | ||||||
|     <a-radio-group v-model="advSettings" @change="changeCode" button-style="solid" :style="{ margin: '10px 0' }" |     <a-radio-group v-model="advSettings" @change="changeCode" button-style="solid" :style="{ margin: '10px 0' }" | ||||||
|  |  | ||||||
							
								
								
									
										2788
									
								
								web/html/xray.html
									
									
									
									
									
								
							
							
						
						
									
										2788
									
								
								web/html/xray.html
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							|  | @ -33,7 +33,7 @@ | ||||||
| "sure" = "Да" | "sure" = "Да" | ||||||
| "encryption" = "Шифрование" | "encryption" = "Шифрование" | ||||||
| "useIPv4ForHost" = "Использовать IPv4 для хоста" | "useIPv4ForHost" = "Использовать IPv4 для хоста" | ||||||
| "transmission" = "Транспорт" | "transmission" = "Протокол" | ||||||
| "host" = "Хост" | "host" = "Хост" | ||||||
| "path" = "Путь" | "path" = "Путь" | ||||||
| "camouflage" = "Маскировка" | "camouflage" = "Маскировка" | ||||||
|  | @ -59,10 +59,10 @@ | ||||||
| "security" = "Безопасность" | "security" = "Безопасность" | ||||||
| "secAlertTitle" = "Предупреждение системы безопасности" | "secAlertTitle" = "Предупреждение системы безопасности" | ||||||
| "secAlertSsl" = "Это соединение не защищено. Пожалуйста, не вводите конфиденциальную информацию, пока не установите SSL сертификат для защиты соединения" | "secAlertSsl" = "Это соединение не защищено. Пожалуйста, не вводите конфиденциальную информацию, пока не установите SSL сертификат для защиты соединения" | ||||||
| "secAlertConf" = "Некоторые настройки уязвимы для атак. Чтобы в будущем не было проблем, нужно усилить защиту." | "secAlertConf" = "Некоторые настройки уязвимы для атак. Рекомендуем усилить протоколы безопасности, чтобы предотвратить проблемы в будущем." | ||||||
| "secAlertSSL" = "Ваше подключение к панели не защищено. Установите SSL сертификат для защиты данных." | "secAlertSSL" = "Ваше подключение к панели небезопасно. Пожалуйста, установите SSL сертификат для защиты данных." | ||||||
| "secAlertPanelPort" = "Порт панели по умолчанию небезопасен. Установите случайный или просто другой порт." | "secAlertPanelPort" = "Порт, на котором работает панель небезопасен. Пожалуйста, установите случайный или просто другой порт." | ||||||
| "secAlertPanelURI" = "Адрес панели по умолчанию небезопасен. Сделайте адрес сложным." | "secAlertPanelURI" = "URI-адрес панели по умолчанию небезопасен. Пожалуйста, настройте сложный URI-адрес." | ||||||
| "secAlertSubURI" = "URI-адрес подписки по умолчанию небезопасен. Пожалуйста, настройте сложный URI-адрес." | "secAlertSubURI" = "URI-адрес подписки по умолчанию небезопасен. Пожалуйста, настройте сложный URI-адрес." | ||||||
| "secAlertSubJsonURI" = "URI-адрес по умолчанию для JSON подписки небезопасен. Пожалуйста, настройте сложный URI-адрес." | "secAlertSubJsonURI" = "URI-адрес по умолчанию для JSON подписки небезопасен. Пожалуйста, настройте сложный URI-адрес." | ||||||
| "emptyDnsDesc" = "Нет добавленных DNS-серверов." | "emptyDnsDesc" = "Нет добавленных DNS-серверов." | ||||||
|  | @ -75,8 +75,8 @@ | ||||||
| "theme" = "Тема" | "theme" = "Тема" | ||||||
| "dark" = "Темная" | "dark" = "Темная" | ||||||
| "ultraDark" = "Очень темная" | "ultraDark" = "Очень темная" | ||||||
| "dashboard" = "Дашборд" | "dashboard" = "Статус системы" | ||||||
| "inbounds" = "Инбаунды" | "inbounds" = "Входящие подключения" | ||||||
| "settings" = "Настройки" | "settings" = "Настройки" | ||||||
| "xray" = "Настройки Xray" | "xray" = "Настройки Xray" | ||||||
| "logout" = "Выход" | "logout" = "Выход" | ||||||
|  | @ -91,15 +91,15 @@ | ||||||
| "invalidFormData" = "Недопустимый формат данных" | "invalidFormData" = "Недопустимый формат данных" | ||||||
| "emptyUsername" = "Введите имя пользователя" | "emptyUsername" = "Введите имя пользователя" | ||||||
| "emptyPassword" = "Введите пароль" | "emptyPassword" = "Введите пароль" | ||||||
| "wrongUsernameOrPassword" = "Неверные данные учетной записи." | "wrongUsernameOrPassword" = "Неверное имя пользователя, пароль или код двухфакторной аутентификации." | ||||||
| "successLogin" = "Вы успешно вошли в аккаунт" | "successLogin" = "Успешный вход" | ||||||
| 
 | 
 | ||||||
| [pages.index] | [pages.index] | ||||||
| "title" = "Дашборд" | "title" = "Статус системы" | ||||||
| "cpu" = "ЦП" | "cpu" = "ЦП" | ||||||
| "logicalProcessors" = "Логические процессоры" | "logicalProcessors" = "Логические процессоры" | ||||||
| "frequency" = "Частота" | "frequency" = "Частота" | ||||||
| "swap" = "Файл подкачки" | "swap" = "Swap" | ||||||
| "storage" = "Диск" | "storage" = "Диск" | ||||||
| "memory" = "ОЗУ" | "memory" = "ОЗУ" | ||||||
| "threads" = "Потоки" | "threads" = "Потоки" | ||||||
|  | @ -136,7 +136,7 @@ | ||||||
| "geofileUpdateDialogDesc" = "Это обновит файл #filename#." | "geofileUpdateDialogDesc" = "Это обновит файл #filename#." | ||||||
| "geofileUpdatePopover" = "Геофайл успешно обновлён" | "geofileUpdatePopover" = "Геофайл успешно обновлён" | ||||||
| "dontRefresh" = "Установка в процессе. Не обновляйте страницу" | "dontRefresh" = "Установка в процессе. Не обновляйте страницу" | ||||||
| "logs" = "Журнал" | "logs" = "Логи" | ||||||
| "config" = "Конфигурация" | "config" = "Конфигурация" | ||||||
| "backup" = "Резервная копия" | "backup" = "Резервная копия" | ||||||
| "backupTitle" = "Резервная копия базы данных" | "backupTitle" = "Резервная копия базы данных" | ||||||
|  | @ -151,10 +151,10 @@ | ||||||
| "getConfigError" = "Произошла ошибка при получении конфигурационного файла" | "getConfigError" = "Произошла ошибка при получении конфигурационного файла" | ||||||
| 
 | 
 | ||||||
| [pages.inbounds] | [pages.inbounds] | ||||||
| "title" = "Инбаунды" | "title" = "Входящие подключения" | ||||||
| "totalDownUp" = "Объем отправленного/полученного трафика" | "totalDownUp" = "Объем отправленного/полученного трафика" | ||||||
| "totalUsage" = "Всего трафика" | "totalUsage" = "Всего трафика" | ||||||
| "inboundCount" = "Всего инбаундов" | "inboundCount" = "Всего подключений" | ||||||
| "operate" = "Меню" | "operate" = "Меню" | ||||||
| "enable" = "Включить" | "enable" = "Включить" | ||||||
| "remark" = "Примечание" | "remark" = "Примечание" | ||||||
|  | @ -165,18 +165,18 @@ | ||||||
| "transportConfig" = "Транспорт" | "transportConfig" = "Транспорт" | ||||||
| "expireDate" = "Дата окончания" | "expireDate" = "Дата окончания" | ||||||
| "resetTraffic" = "Сброс трафика" | "resetTraffic" = "Сброс трафика" | ||||||
| "addInbound" = "Создать инбаунд" | "addInbound" = "Создать новое подключение" | ||||||
| "generalActions" = "Общие действия" | "generalActions" = "Общие действия" | ||||||
| "autoRefresh" = "Автообновление" | "autoRefresh" = "Автообновление" | ||||||
| "autoRefreshInterval" = "Интервал" | "autoRefreshInterval" = "Интервал" | ||||||
| "modifyInbound" = "Изменить инбаунд" | "modifyInbound" = "Изменить входящее подключение" | ||||||
| "deleteInbound" = "Удалить инбаунд" | "deleteInbound" = "Удалить входящее подключение" | ||||||
| "deleteInboundContent" = "Вы уверены, что хотите удалить инбаунд?" | "deleteInboundContent" = "Вы уверены, что хотите удалить входящее подключение?" | ||||||
| "deleteClient" = "Удалить клиента" | "deleteClient" = "Удалить клиента" | ||||||
| "deleteClientContent" = "Вы уверены, что хотите удалить клиента?" | "deleteClientContent" = "Вы уверены, что хотите удалить клиента?" | ||||||
| "resetTrafficContent" = "Вы уверены, что хотите сбросить трафик?" | "resetTrafficContent" = "Вы уверены, что хотите сбросить трафик?" | ||||||
| "inboundUpdateSuccess" = "Инбаунд успешно обновлен." | "inboundUpdateSuccess" = "Входящее подключение успешно обновлено." | ||||||
| "inboundCreateSuccess" = "Инбаунд успешно создан." | "inboundCreateSuccess" = "Входящее подключение успешно создано." | ||||||
| "copyLink" = "Копировать ссылку" | "copyLink" = "Копировать ссылку" | ||||||
| "address" = "Адрес" | "address" = "Адрес" | ||||||
| "network" = "Сеть" | "network" = "Сеть" | ||||||
|  | @ -196,11 +196,11 @@ | ||||||
| "export" = "Экспорт ссылок" | "export" = "Экспорт ссылок" | ||||||
| "clone" = "Клонировать" | "clone" = "Клонировать" | ||||||
| "cloneInbound" = "Клонировать" | "cloneInbound" = "Клонировать" | ||||||
| "cloneInboundContent" = "Будут клонированы все настройки инбаундов, кроме списка клиентов, порта и IP-адреса прослушивания" | "cloneInboundContent" = "Будут клонированы все настройки входящих подключений, кроме списка клиентов, порта и IP-адреса прослушивания" | ||||||
| "cloneInboundOk" = "Клонировано" | "cloneInboundOk" = "Клонировано" | ||||||
| "resetAllTraffic" = "Сброс трафика всех инбаундов" | "resetAllTraffic" = "Сброс трафика всех подключений" | ||||||
| "resetAllTrafficTitle" = "Сброс трафика всех инбаундов" | "resetAllTrafficTitle" = "Сброс трафика всех подключений" | ||||||
| "resetAllTrafficContent" = "Вы уверены, что хотите сбросить трафик всех инбаундов?" | "resetAllTrafficContent" = "Вы уверены, что хотите сбросить трафик всех подключений?" | ||||||
| "resetInboundClientTraffics" = "Сброс трафика клиента" | "resetInboundClientTraffics" = "Сброс трафика клиента" | ||||||
| "resetInboundClientTrafficTitle" = "Сброс трафика клиентов" | "resetInboundClientTrafficTitle" = "Сброс трафика клиентов" | ||||||
| "resetInboundClientTrafficContent" = "Вы уверены, что хотите сбросить трафик для этих клиентов?" | "resetInboundClientTrafficContent" = "Вы уверены, что хотите сбросить трафик для этих клиентов?" | ||||||
|  | @ -222,10 +222,10 @@ | ||||||
| "subscriptionDesc" = "Вы можете найти свою ссылку подписки в разделе 'Подробнее'" | "subscriptionDesc" = "Вы можете найти свою ссылку подписки в разделе 'Подробнее'" | ||||||
| "info" = "Информация" | "info" = "Информация" | ||||||
| "same" = "Тот же" | "same" = "Тот же" | ||||||
| "inboundData" = "Данные инбаундов" | "inboundData" = "Данные подключений" | ||||||
| "exportInbound" = "Экспорт инбаундов" | "exportInbound" = "Экспорт входящих подключений" | ||||||
| "import" = "Импортировать" | "import" = "Импортировать" | ||||||
| "importInbound" = "Импорт инбаундов" | "importInbound" = "Импорт входящих подключений" | ||||||
| 
 | 
 | ||||||
| [pages.client] | [pages.client] | ||||||
| "add" = "Создать клиента" | "add" = "Создать клиента" | ||||||
|  | @ -249,13 +249,13 @@ | ||||||
| "obtain" = "Получить" | "obtain" = "Получить" | ||||||
| "updateSuccess" = "Обновление прошло успешно" | "updateSuccess" = "Обновление прошло успешно" | ||||||
| "logCleanSuccess" = "Лог был очищен" | "logCleanSuccess" = "Лог был очищен" | ||||||
| "inboundsUpdateSuccess" = "Инбаунды успешно обновлены" | "inboundsUpdateSuccess" = "Входящие подключения успешно обновлены" | ||||||
| "inboundUpdateSuccess" = "Инбаунд успешно обновлено" | "inboundUpdateSuccess" = "Входящее подключение успешно обновлено" | ||||||
| "inboundCreateSuccess" = "Инбаунд успешно создано" | "inboundCreateSuccess" = "Входящее подключение успешно создано" | ||||||
| "inboundDeleteSuccess" = "Инбаунд успешно удалено" | "inboundDeleteSuccess" = "Входящее подключение успешно удалено" | ||||||
| "inboundClientAddSuccess" = "Клиент(ы) инбаунда добавлен(ы)" | "inboundClientAddSuccess" = "Клиент(ы) входящего подключения добавлен(ы)" | ||||||
| "inboundClientDeleteSuccess" = "Клиент инбаунда удалён" | "inboundClientDeleteSuccess" = "Клиент входящего подключения удалён" | ||||||
| "inboundClientUpdateSuccess" = "Клиент инбаунда обновлён" | "inboundClientUpdateSuccess" = "Клиент входящего подключения обновлён" | ||||||
| "delDepletedClientsSuccess" = "Все исчерпанные клиенты удалены" | "delDepletedClientsSuccess" = "Все исчерпанные клиенты удалены" | ||||||
| "resetAllClientTrafficSuccess" = "Весь трафик клиента сброшен" | "resetAllClientTrafficSuccess" = "Весь трафик клиента сброшен" | ||||||
| "resetAllTrafficSuccess" = "Весь трафик сброшен" | "resetAllTrafficSuccess" = "Весь трафик сброшен" | ||||||
|  | @ -281,15 +281,15 @@ | ||||||
| [pages.settings] | [pages.settings] | ||||||
| "title" = "Настройки" | "title" = "Настройки" | ||||||
| "save" = "Сохранить" | "save" = "Сохранить" | ||||||
| "infoDesc" = "Каждое внесённое изменение должно быть сохранено. Пожалуйста, перезапустите панель, чтобы изменения вступили в силу." | "infoDesc" = "Каждое выполненное изменение необходимо сохранить. Пожалуйста, перезапустите панель, чтобы изменения вступили в силу" | ||||||
| "restartPanel" = "Перезапуск панели" | "restartPanel" = "Перезапуск панели" | ||||||
| "restartPanelDesc" = "Вы уверены, что хотите перезапустить панель? Подтвердите, и перезапуск произойдёт через 3 секунды. Если панель будет недоступна, проверьте лог сервера" | "restartPanelDesc" = "Вы уверены, что хотите перезапустить панель? Подтвердите, и перезапуск произойдёт через 3 секунды. Если панель будет недоступна, проверьте лог сервера" | ||||||
| "restartPanelSuccess" = "Панель успешно перезапущена" | "restartPanelSuccess" = "Панель успешно перезапущена" | ||||||
| "actions" = "Действия" | "actions" = "Действия" | ||||||
| "resetDefaultConfig" = "Восстановить настройки по умолчанию" | "resetDefaultConfig" = "Восстановить настройки по умолчанию" | ||||||
| "panelSettings" = "Панель" | "panelSettings" = "Настройки панели" | ||||||
| "securitySettings" = "Учетная запись" | "securitySettings" = "Настройки безопасности" | ||||||
| "TGBotSettings" = "Telegram" | "TGBotSettings" = "Настройки Telegram бота" | ||||||
| "panelListeningIP" = "IP-адрес для управления панелью" | "panelListeningIP" = "IP-адрес для управления панелью" | ||||||
| "panelListeningIPDesc" = "Оставьте пустым для подключения с любого IP" | "panelListeningIPDesc" = "Оставьте пустым для подключения с любого IP" | ||||||
| "panelListeningDomain" = "Домен панели" | "panelListeningDomain" = "Домен панели" | ||||||
|  | @ -303,7 +303,7 @@ | ||||||
| "panelUrlPath" = "Корневой путь URL адреса панели" | "panelUrlPath" = "Корневой путь URL адреса панели" | ||||||
| "panelUrlPathDesc" = "Должен начинаться с '/' и заканчиваться '/'" | "panelUrlPathDesc" = "Должен начинаться с '/' и заканчиваться '/'" | ||||||
| "pageSize" = "Размер нумерации страниц" | "pageSize" = "Размер нумерации страниц" | ||||||
| "pageSizeDesc" = "Определить размер страницы для таблицы инбаундов. Установите 0, чтобы отключить" | "pageSizeDesc" = "Определить размер страницы для таблицы входящих подключений. Установите 0, чтобы отключить" | ||||||
| "remarkModel" = "Модель примечания и символ разделения" | "remarkModel" = "Модель примечания и символ разделения" | ||||||
| "datepicker" = "Выбор даты" | "datepicker" = "Выбор даты" | ||||||
| "datepickerPlaceholder" = "Выберите дату" | "datepickerPlaceholder" = "Выберите дату" | ||||||
|  | @ -391,7 +391,7 @@ | ||||||
| [pages.xray] | [pages.xray] | ||||||
| "title" = "Настройки Xray" | "title" = "Настройки Xray" | ||||||
| "save" = "Сохранить" | "save" = "Сохранить" | ||||||
| "restart" = "Перезапуск Xray" | "restart" = "Перезапустить Xray" | ||||||
| "restartSuccess" = "Xray успешно перезапущен" | "restartSuccess" = "Xray успешно перезапущен" | ||||||
| "stopSuccess" = "Xray успешно остановлен" | "stopSuccess" = "Xray успешно остановлен" | ||||||
| "restartError" = "Произошла ошибка при перезапуске Xray." | "restartError" = "Произошла ошибка при перезапуске Xray." | ||||||
|  | @ -402,7 +402,7 @@ | ||||||
| "generalConfigsDesc" = "Эти параметры описывают общие настройки" | "generalConfigsDesc" = "Эти параметры описывают общие настройки" | ||||||
| "logConfigs" = "Логи" | "logConfigs" = "Логи" | ||||||
| "logConfigsDesc" = "Логи могут замедлять работу сервера. Включайте только нужные вам виды логов при необходимости!" | "logConfigsDesc" = "Логи могут замедлять работу сервера. Включайте только нужные вам виды логов при необходимости!" | ||||||
| "blockConfigsDesc" = "Настройте, чтобы клиенты не имели доступа к определенным протоколам" | "blockConfigsDesc" = "Настройте, чтобы клиенты не имели доступа к определенным протоколам и веб-сайтами" | ||||||
| "basicRouting" = "Базовые соединения" | "basicRouting" = "Базовые соединения" | ||||||
| "blockConnectionsConfigsDesc" = "Эти параметры будут блокировать трафик в зависимости от страны назначения." | "blockConnectionsConfigsDesc" = "Эти параметры будут блокировать трафик в зависимости от страны назначения." | ||||||
| "directConnectionsConfigsDesc" = "Прямое соединение означает, что определенный трафик не будет перенаправлен через другой сервер." | "directConnectionsConfigsDesc" = "Прямое соединение означает, что определенный трафик не будет перенаправлен через другой сервер." | ||||||
|  | @ -413,19 +413,19 @@ | ||||||
| "ipv4Routing" = "Правила IPv4" | "ipv4Routing" = "Правила IPv4" | ||||||
| "ipv4RoutingDesc" = "Эти параметры позволят клиентам маршрутизироваться к целевым доменам только через IPv4" | "ipv4RoutingDesc" = "Эти параметры позволят клиентам маршрутизироваться к целевым доменам только через IPv4" | ||||||
| "warpRouting" = "Правила WARP" | "warpRouting" = "Правила WARP" | ||||||
| "warpRoutingDesc" = " Эти опции будут направлять трафик в зависимости от конкретного пункта назначения через WARP." | "warpRoutingDesc" = "Внимание: перед использованием этих параметров установите WARP в режиме прокси-сервера socks5 на свой сервер, следуя инструкциям на GitHub панели. WARP будет направлять трафик на веб-сайты через серверы Cloudflare" | ||||||
| "Template" = "Шаблон конфигурации Xray" | "Template" = "Шаблон конфигурации Xray" | ||||||
| "TemplateDesc" = "На основе шаблона создаётся конфигурационный файл Xray." | "TemplateDesc" = "Создание файла конфигурации Xray на основе этого шаблона" | ||||||
| "FreedomStrategy" = "Настройка стратегии протокола Freedom" | "FreedomStrategy" = "Настройка стратегии протокола Freedom" | ||||||
| "FreedomStrategyDesc" = "Установка стратегии вывода сети в протоколе Freedom" | "FreedomStrategyDesc" = "Установка стратегии вывода сети в протоколе Freedom" | ||||||
| "RoutingStrategy" = "Настройка маршрутизации доменов" | "RoutingStrategy" = "Настройка маршрутизации доменов" | ||||||
| "RoutingStrategyDesc" = "Установка общей стратегии маршрутизации разрешения DNS" | "RoutingStrategyDesc" = "Установка общей стратегии маршрутизации разрешения DNS" | ||||||
| "Torrent" = "Заблокировать BitTorrent" | "Torrent" = "Заблокировать BitTorrent" | ||||||
| "Inbounds" = "Инбаунды" | "Inbounds" = "Входящее соединение" | ||||||
| "InboundsDesc" = "Изменение шаблона конфигурации для подключения определенных клиентов" | "InboundsDesc" = "Изменение шаблона конфигурации для подключения определенных клиентов" | ||||||
| "Outbounds" = "Аутбаунды" | "Outbounds" = "Исходящее соединение" | ||||||
| "Balancers" = "Балансировщик" | "Balancers" = "Балансировщик" | ||||||
| "OutboundsDesc" = "Изменение шаблона конфигурации, чтобы определить аутбаунды для этого сервера" | "OutboundsDesc" = "Изменение шаблона конфигурации, чтобы определить исходящие соединения для этого сервера" | ||||||
| "Routings" = "Маршрутизация" | "Routings" = "Маршрутизация" | ||||||
| "RoutingsDesc" = "Важен приоритет каждого правила!" | "RoutingsDesc" = "Важен приоритет каждого правила!" | ||||||
| "completeTemplate" = "Все" | "completeTemplate" = "Все" | ||||||
|  | @ -456,8 +456,8 @@ | ||||||
| "down" = "Опустить вниз" | "down" = "Опустить вниз" | ||||||
| "source" = "Источник" | "source" = "Источник" | ||||||
| "dest" = "Пункт назначения" | "dest" = "Пункт назначения" | ||||||
| "inbound" = "Инбаунд" | "inbound" = "Входящее соединение" | ||||||
| "outbound" = "Аутбаунд" | "outbound" = "Исходящее соединение" | ||||||
| "balancer" = "Балансировщик" | "balancer" = "Балансировщик" | ||||||
| "info" = "Информация" | "info" = "Информация" | ||||||
| "add" = "Создать правило" | "add" = "Создать правило" | ||||||
|  | @ -465,9 +465,9 @@ | ||||||
| "useComma" = "Элементы, разделённые запятыми" | "useComma" = "Элементы, разделённые запятыми" | ||||||
| 
 | 
 | ||||||
| [pages.xray.outbound] | [pages.xray.outbound] | ||||||
| "addOutbound" = "Создать аутбаунд" | "addOutbound" = "Создать исходящее соединение" | ||||||
| "addReverse" = "Создать реверс-прокси" | "addReverse" = "Создать реверс-прокси" | ||||||
| "editOutbound" = "Изменить аутбаунд" | "editOutbound" = "Изменить исходящее соединение" | ||||||
| "editReverse" = "Редактировать реверс-прокси" | "editReverse" = "Редактировать реверс-прокси" | ||||||
| "tag" = "Тег" | "tag" = "Тег" | ||||||
| "tagDesc" = "Уникальный тег" | "tagDesc" = "Уникальный тег" | ||||||
|  | @ -481,7 +481,7 @@ | ||||||
| "intercon" = "Соединение" | "intercon" = "Соединение" | ||||||
| "settings" = "Настройки" | "settings" = "Настройки" | ||||||
| "accountInfo" = "Информация об учетной записи" | "accountInfo" = "Информация об учетной записи" | ||||||
| "outboundStatus" = "Статус аутбаунда" | "outboundStatus" = "Исходящий статус" | ||||||
| "sendThrough" = "Отправить через" | "sendThrough" = "Отправить через" | ||||||
| 
 | 
 | ||||||
| [pages.xray.balancer] | [pages.xray.balancer] | ||||||
|  | @ -494,7 +494,7 @@ | ||||||
| "balancerDesc" = "Невозможно одновременно использовать balancerTag и outboundTag. При одновременном использовании будет работать только outboundTag." | "balancerDesc" = "Невозможно одновременно использовать balancerTag и outboundTag. При одновременном использовании будет работать только outboundTag." | ||||||
| 
 | 
 | ||||||
| [pages.xray.wireguard] | [pages.xray.wireguard] | ||||||
| "secretKey" = "Секретный ключ" | "secretKey" = "Приватный ключ" | ||||||
| "publicKey" = "Публичный ключ" | "publicKey" = "Публичный ключ" | ||||||
| "allowedIPs" = "Разрешенные IP-адреса" | "allowedIPs" = "Разрешенные IP-адреса" | ||||||
| "endpoint" = "Конечная точка" | "endpoint" = "Конечная точка" | ||||||
|  | @ -555,8 +555,8 @@ | ||||||
| "modifyUser" = "Вы успешно изменили учетные данные администратора." | "modifyUser" = "Вы успешно изменили учетные данные администратора." | ||||||
| "originalUserPassIncorrect" = "Неверное имя пользователя или пароль" | "originalUserPassIncorrect" = "Неверное имя пользователя или пароль" | ||||||
| "userPassMustBeNotEmpty" = "Новое имя пользователя и новый пароль должны быть заполнены" | "userPassMustBeNotEmpty" = "Новое имя пользователя и новый пароль должны быть заполнены" | ||||||
| "getOutboundTrafficError" = "Ошибка получения трафика аутбаунда" | "getOutboundTrafficError" = "Ошибка получения исходящего трафика" | ||||||
| "resetOutboundTrafficError" = "Ошибка сброса трафика аутбаунда" | "resetOutboundTrafficError" = "Ошибка сброса исходящего трафика" | ||||||
| 
 | 
 | ||||||
| [tgbot] | [tgbot] | ||||||
| "keyboardClosed" = "❌ Клавиатура закрыта." | "keyboardClosed" = "❌ Клавиатура закрыта." | ||||||
|  | @ -564,7 +564,7 @@ | ||||||
| "noQuery" = "❌ Запрос не найден. Пожалуйста, повторите команду." | "noQuery" = "❌ Запрос не найден. Пожалуйста, повторите команду." | ||||||
| "wentWrong" = "❌ Что-то пошло не так..." | "wentWrong" = "❌ Что-то пошло не так..." | ||||||
| "noIpRecord" = "❗ Нет записей об IP-адресе." | "noIpRecord" = "❗ Нет записей об IP-адресе." | ||||||
| "noInbounds" = "❗ У вас не настроено ни одного инбаунда." | "noInbounds" = "❗ У вас не настроено ни одного подключения." | ||||||
| "unlimited" = "♾ Безлимит" | "unlimited" = "♾ Безлимит" | ||||||
| "add" = "Добавить" | "add" = "Добавить" | ||||||
| "month" = "Месяц" | "month" = "Месяц" | ||||||
|  | @ -573,7 +573,7 @@ | ||||||
| "days" = "Дней" | "days" = "Дней" | ||||||
| "hours" = "Часов" | "hours" = "Часов" | ||||||
| "unknown" = "Неизвестно" | "unknown" = "Неизвестно" | ||||||
| "inbounds" = "Инбаунды" | "inbounds" = "Подключения" | ||||||
| "clients" = "Клиенты" | "clients" = "Клиенты" | ||||||
| "offline" = "🔴 Офлайн" | "offline" = "🔴 Офлайн" | ||||||
| "online" = "🟢 Онлайн" | "online" = "🟢 Онлайн" | ||||||
|  | @ -587,7 +587,7 @@ | ||||||
| "status" = "✅ Бот функционирует нормально." | "status" = "✅ Бот функционирует нормально." | ||||||
| "usage" = "❗ Пожалуйста, укажите email для поиска." | "usage" = "❗ Пожалуйста, укажите email для поиска." | ||||||
| "getID" = "🆔 Ваш User ID: <code>{{ .ID }}</code>" | "getID" = "🆔 Ваш User ID: <code>{{ .ID }}</code>" | ||||||
| "helpAdminCommands" = "🔃 Для перезапуска Xray Core:\r\n<code>/restart</code>\r\n\r\n🔎 Для поиска клиента по email:\r\n<code>/usage [Email]</code>\r\n\r\n📊 Для поиска инбаундов (со статистикой клиентов):\r\n<code>/inbound [имя подключения]</code>\r\n\r\n🆔 Ваш Telegram User ID:\r\n<code>/id</code>" | "helpAdminCommands" = "🔃 Для перезапуска Xray Core:\r\n<code>/restart</code>\r\n\r\n🔎 Для поиска клиента по email:\r\n<code>/usage [Email]</code>\r\n\r\n📊 Для поиска подключений (со статистикой клиентов):\r\n<code>/inbound [имя подключения]</code>\r\n\r\n🆔 Ваш Telegram User ID:\r\n<code>/id</code>" | ||||||
| "helpClientCommands" = "💲 Для просмотра информации о вашей подписке используйте команду:\r\n<code>/usage [Email]</code>\r\n\r\n🆔 Ваш Telegram User ID:\r\n<code>/id</code>" | "helpClientCommands" = "💲 Для просмотра информации о вашей подписке используйте команду:\r\n<code>/usage [Email]</code>\r\n\r\n🆔 Ваш Telegram User ID:\r\n<code>/id</code>" | ||||||
| "restartUsage" = "\r\n\r\n<code>/restart</code>" | "restartUsage" = "\r\n\r\n<code>/restart</code>" | ||||||
| "restartSuccess" = "✅ Ядро Xray успешно перезапущено." | "restartSuccess" = "✅ Ядро Xray успешно перезапущено." | ||||||
|  | @ -653,8 +653,8 @@ | ||||||
| "pass_prompt" = "🔑 Стандартный пароль: {{ .ClientPassword }}\n\nВведите ваш пароль." | "pass_prompt" = "🔑 Стандартный пароль: {{ .ClientPassword }}\n\nВведите ваш пароль." | ||||||
| "email_prompt" = "📧 Стандартный email: {{ .ClientEmail }}\n\nВведите ваш email." | "email_prompt" = "📧 Стандартный email: {{ .ClientEmail }}\n\nВведите ваш email." | ||||||
| "comment_prompt" = "💬 Стандартный комментарий: {{ .ClientComment }}\n\nВведите ваш комментарий." | "comment_prompt" = "💬 Стандартный комментарий: {{ .ClientComment }}\n\nВведите ваш комментарий." | ||||||
| "inbound_client_data_id" = "🔄 Инбаунды: {{ .InboundRemark }}\n\n🔑 ID: {{ .ClientId }}\n📧 Email: {{ .ClientEmail }}\n📊 Трафик: {{ .ClientTraffic }}\n📅 Дата исчерпания: {{ .ClientExp }}\n💬 Комментарий: {{ .ClientComment }}\n\nТеперь вы можете добавить клиента в инбаунд!" | "inbound_client_data_id" = "🔄 Подключения: {{ .InboundRemark }}\n\n🔑 ID: {{ .ClientId }}\n📧 Email: {{ .ClientEmail }}\n📊 Трафик: {{ .ClientTraffic }}\n📅 Дата исчерпания: {{ .ClientExp }}\n💬 Комментарий: {{ .ClientComment }}\n\nТеперь вы можете добавить клиента в подключение!" | ||||||
| "inbound_client_data_pass" = "🔄 Инбаунды: {{ .InboundRemark }}\n\n🔑 Пароль: {{ .ClientPass }}\n📧 Email: {{ .ClientEmail }}\n📊 Трафик: {{ .ClientTraffic }}\n📅 Дата исчерпания: {{ .ClientExp }}\n💬 Комментарий: {{ .ClientComment }}\n\nТеперь вы можете добавить клиента в инбаунд!" | "inbound_client_data_pass" = "🔄 Подключения: {{ .InboundRemark }}\n\n🔑 Пароль: {{ .ClientPass }}\n📧 Email: {{ .ClientEmail }}\n📊 Трафик: {{ .ClientTraffic }}\n📅 Дата исчерпания: {{ .ClientExp }}\n💬 Комментарий: {{ .ClientComment }}\n\nТеперь вы можете добавить клиента в подключение!" | ||||||
| "cancel" = "❌ Процесс отменён! \n\nВы можете снова начать с /start в любое время. 🔄" | "cancel" = "❌ Процесс отменён! \n\nВы можете снова начать с /start в любое время. 🔄" | ||||||
| "error_add_client"  = "⚠️ Ошибка:\n\n {{ .error }}" | "error_add_client"  = "⚠️ Ошибка:\n\n {{ .error }}" | ||||||
| "using_default_value"  = "Используется значение по умолчанию👌" | "using_default_value"  = "Используется значение по умолчанию👌" | ||||||
|  | @ -676,7 +676,7 @@ | ||||||
| "confirmToggle" = "✅ Подтвердить вкл/выкл пользователя?" | "confirmToggle" = "✅ Подтвердить вкл/выкл пользователя?" | ||||||
| "dbBackup" = "📂 Бэкап БД" | "dbBackup" = "📂 Бэкап БД" | ||||||
| "serverUsage" = "💻 Состояние сервера" | "serverUsage" = "💻 Состояние сервера" | ||||||
| "getInbounds" = "🔌 Инбаунды" | "getInbounds" = "🔌 Подключения" | ||||||
| "depleteSoon" = "⚠️ Скоро конец" | "depleteSoon" = "⚠️ Скоро конец" | ||||||
| "clientUsage" = "Статистика клиента" | "clientUsage" = "Статистика клиента" | ||||||
| "onlines" = "🟢 Онлайн" | "onlines" = "🟢 Онлайн" | ||||||
|  | @ -714,7 +714,7 @@ | ||||||
| [tgbot.answers] | [tgbot.answers] | ||||||
| "successfulOperation" = "✅ Успешно!" | "successfulOperation" = "✅ Успешно!" | ||||||
| "errorOperation" = "❗ Ошибка в операции." | "errorOperation" = "❗ Ошибка в операции." | ||||||
| "getInboundsFailed" = "❌ Не удалось получить инбаунды." | "getInboundsFailed" = "❌ Не удалось получить подключения." | ||||||
| "getClientsFailed" = "❌ Не удалось получить клиентов." | "getClientsFailed" = "❌ Не удалось получить клиентов." | ||||||
| "canceled" = "❌ {{ .Email }}: Операция отменена." | "canceled" = "❌ {{ .Email }}: Операция отменена." | ||||||
| "clientRefreshSuccess" = "✅ {{ .Email }}: Клиент успешно обновлен." | "clientRefreshSuccess" = "✅ {{ .Email }}: Клиент успешно обновлен." | ||||||
|  | @ -731,5 +731,5 @@ | ||||||
| "enableSuccess" = "✅ {{ .Email }}: Включено успешно." | "enableSuccess" = "✅ {{ .Email }}: Включено успешно." | ||||||
| "disableSuccess" = "✅ {{ .Email }}: Отключено успешно." | "disableSuccess" = "✅ {{ .Email }}: Отключено успешно." | ||||||
| "askToAddUserId" = "❌ Ваша конфигурация не найдена!\r\n💭 Пожалуйста, попросите администратора использовать ваш Telegram User ID в конфигурации.\r\n\r\n🆔 Ваш User ID: <code>{{ .TgUserID }}</code>" | "askToAddUserId" = "❌ Ваша конфигурация не найдена!\r\n💭 Пожалуйста, попросите администратора использовать ваш Telegram User ID в конфигурации.\r\n\r\n🆔 Ваш User ID: <code>{{ .TgUserID }}</code>" | ||||||
| "chooseClient" = "Выберите клиента для инбаунда {{ .Inbound }}" | "chooseClient" = "Выберите клиента для подключения {{ .Inbound }}" | ||||||
| "chooseInbound" = "Выберите инбаунд" | "chooseInbound" = "Выберите подключение" | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue