From 95416cd553e32adbaf02058460866c3ba48a9acd Mon Sep 17 00:00:00 2001 From: MHSanaei Date: Fri, 1 Mar 2024 23:31:07 +0330 Subject: [PATCH 01/55] translation - direct connection --- web/translation/translate.en_US.toml | 2 +- web/translation/translate.es_ES.toml | 2 +- web/translation/translate.fa_IR.toml | 2 +- web/translation/translate.id_ID.toml | 2 +- web/translation/translate.ru_RU.toml | 2 +- web/translation/translate.uk_UA.toml | 2 +- web/translation/translate.vi_VN.toml | 2 +- web/translation/translate.zh_Hans.toml | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/web/translation/translate.en_US.toml b/web/translation/translate.en_US.toml index 64032cb7..20278b6b 100644 --- a/web/translation/translate.en_US.toml +++ b/web/translation/translate.en_US.toml @@ -327,7 +327,7 @@ "blockCountryConfigs" = "Block Country" "blockCountryConfigsDesc" = "These options will block traffic based on the specific requested country." "directCountryConfigs" = "Direct Country" -"directCountryConfigsDesc" = "These options will directly forward traffic based on the specific requested country." +"directCountryConfigsDesc" = "A direct connection ensures that specific traffic is not routed through another server." "ipv4Configs" = "IPv4 Routing" "ipv4ConfigsDesc" = "These options will route traffic based on a specific destination via IPv4." "warpConfigs" = "WARP Routing" diff --git a/web/translation/translate.es_ES.toml b/web/translation/translate.es_ES.toml index 48f5740b..25cd7737 100644 --- a/web/translation/translate.es_ES.toml +++ b/web/translation/translate.es_ES.toml @@ -327,7 +327,7 @@ "blockCountryConfigs" = "Configuraciones de Bloqueo por País" "blockCountryConfigsDesc" = "Estas opciones evitarán que los usuarios se conecten a dominios de países específicos." "directCountryConfigs" = "Configuraciones de Conexión Directa por País" -"directCountryConfigsDesc" = "Estas opciones conectarán a los usuarios directamente a dominios de países específicos." +"directCountryConfigsDesc" = "Una conexión directa asegura que el tráfico específico no se enrutará a través de otro servidor." "ipv4Configs" = "Configuraciones IPv4" "ipv4ConfigsDesc" = "Estas opciones solo enrutarán a los dominios objetivo a través de IPv4." "warpConfigs" = "Configuraciones de WARP" diff --git a/web/translation/translate.fa_IR.toml b/web/translation/translate.fa_IR.toml index 45abcc23..5e10a9b5 100644 --- a/web/translation/translate.fa_IR.toml +++ b/web/translation/translate.fa_IR.toml @@ -327,7 +327,7 @@ "blockCountryConfigs" = "مسدودسازی کشور" "blockCountryConfigsDesc" = "این گزینه‌ها ترافیک را بر اساس کشور درخواستی خاص مسدود می‌کند" "directCountryConfigs" = "اتصال مستقیم کشور" -"directCountryConfigsDesc" = "این گزینه‌ها ترافیک را بر اساس کشور درخواستی خاص بصورت مستقیم ارسال می‌کند" +"directCountryConfigsDesc" = "اتصال مستقیم اطمینان حاصل می‌کند که ترافیک خاص از طریق یک سرور دیگر هدایت نمی‌شود." "ipv4Configs" = "IPv4 مسیریابی" "ipv4ConfigsDesc" = "این گزینه‌ها ترافیک‌ را از طریق آی‌پی‌نسخه4 به مقصد هدایت می‌کند" "warpConfigs" = "WARP مسیریابی" diff --git a/web/translation/translate.id_ID.toml b/web/translation/translate.id_ID.toml index fc71a866..152fb9a3 100644 --- a/web/translation/translate.id_ID.toml +++ b/web/translation/translate.id_ID.toml @@ -327,7 +327,7 @@ "blockCountryConfigs" = "Blokir Negara" "blockCountryConfigsDesc" = "Opsi ini akan memblokir lalu lintas berdasarkan negara yang diminta." "directCountryConfigs" = "Langsung ke Negara" -"directCountryConfigsDesc" = "Opsi ini akan langsung meneruskan lalu lintas berdasarkan negara yang diminta." +"directCountryConfigsDesc" = "Koneksi langsung memastikan bahwa lalu lintas tertentu tidak diarahkan melalui server lain." "ipv4Configs" = "Pengalihan IPv4" "ipv4ConfigsDesc" = "Opsi ini akan mengalihkan lalu lintas berdasarkan tujuan tertentu melalui IPv4." "warpConfigs" = "Pengalihan WARP" diff --git a/web/translation/translate.ru_RU.toml b/web/translation/translate.ru_RU.toml index e163cb3a..5ff597e1 100644 --- a/web/translation/translate.ru_RU.toml +++ b/web/translation/translate.ru_RU.toml @@ -327,7 +327,7 @@ "blockCountryConfigs" = "Конфигурации блокировки страны" "blockCountryConfigsDesc" = "Эти параметры не позволят пользователям подключаться к доменам определенной страны" "directCountryConfigs" = "Настройки прямого подключения для страны" -"directCountryConfigsDesc" = "Эти параметры позволят пользователям подключаться напрямую к доменам определенной страны" +"directCountryConfigsDesc" = "Прямое подключение обеспечивает, что конкретный трафик не направляется через другой сервер." "ipv4Configs" = "Настройки IPv4" "ipv4ConfigsDesc" = "Эти параметры позволят пользователям маршрутизироваться к целевым доменам только через IPv4" "warpConfigs" = "Настройки WARP" diff --git a/web/translation/translate.uk_UA.toml b/web/translation/translate.uk_UA.toml index e86ed425..6d04f0a7 100644 --- a/web/translation/translate.uk_UA.toml +++ b/web/translation/translate.uk_UA.toml @@ -327,7 +327,7 @@ "blockCountryConfigs" = "Заблокувати країну" "blockCountryConfigsDesc" = "Ці параметри блокуватимуть трафік на основі конкретної запитуваної країни." "directCountryConfigs" = "Пряма країна" -"directCountryConfigsDesc" = "Ці параметри безпосередньо перенаправлятимуть трафік на основі конкретної запитуваної країни." +"directCountryConfigsDesc" = "Пряме підключення забезпечує, що конкретний трафік не маршрутизується через інший сервер." "ipv4Configs" = "Маршрутизація IPv4" "ipv4ConfigsDesc" = "Ці параметри спрямовуватимуть трафік на основі певного призначення через IPv4." "warpConfigs" = "WARP маршрутизація" diff --git a/web/translation/translate.vi_VN.toml b/web/translation/translate.vi_VN.toml index b4e952b2..1d5f5841 100644 --- a/web/translation/translate.vi_VN.toml +++ b/web/translation/translate.vi_VN.toml @@ -327,7 +327,7 @@ "blockCountryConfigs" = "Cấu hình Chặn Quốc gia" "blockCountryConfigsDesc" = "Những tùy chọn này sẽ ngăn người dùng kết nối đến các tên miền quốc gia cụ thể." "directCountryConfigs" = "Cấu hình Kết nối Trực tiếp Quốc gia" -"directCountryConfigsDesc" = "Những tùy chọn này sẽ kết nối người dùng trực tiếp đến các tên miền quốc gia cụ thể." +"directCountryConfigsDesc" = "Một kết nối trực tiếp đảm bảo rằng lưu lượng cụ thể không được định tuyến qua một máy chủ khác." "ipv4Configs" = "Cấu hình IPv4" "ipv4ConfigsDesc" = "Những tùy chọn này sẽ chỉ định kết nối đến các tên miền mục tiêu qua IPv4." "warpConfigs" = "Cấu hình WARP" diff --git a/web/translation/translate.zh_Hans.toml b/web/translation/translate.zh_Hans.toml index eeaf222f..c6ff4254 100644 --- a/web/translation/translate.zh_Hans.toml +++ b/web/translation/translate.zh_Hans.toml @@ -327,7 +327,7 @@ "blockCountryConfigs" = "阻止国家配置" "blockCountryConfigsDesc" = "这些选项将阻止用户连接到特定国家/地区的域。" "directCountryConfigs" = "直接国家配置" -"directCountryConfigsDesc" = "这些选项会将用户直接连接到特定国家/地区的域。" +"directCountryConfigsDesc" = "直接连接确保特定流量不通过另一台服务器路由。" "ipv4Configs" = "IPv4 配置" "ipv4ConfigsDesc" = "此选项将仅通过 IPv4 路由到目标域" "warpConfigs" = "WARP 配置" From 1c97593360082b36086684d6622023636649f572 Mon Sep 17 00:00:00 2001 From: LOVECHEN Date: Sat, 2 Mar 2024 06:12:52 +0800 Subject: [PATCH 02/55] Update translate.zh_Hans.toml (#1955) * Fix Dockerfile Fix wrong image * Update translate.zh_Hans.toml Fixed wrong translation, although I think system load / usage should be merged into the project --- web/translation/translate.zh_Hans.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/translation/translate.zh_Hans.toml b/web/translation/translate.zh_Hans.toml index c6ff4254..feb8a6ee 100644 --- a/web/translation/translate.zh_Hans.toml +++ b/web/translation/translate.zh_Hans.toml @@ -48,7 +48,7 @@ "getVersion" = "获取版本" "install" = "安装" "clients" = "客户端" -"usage" = "用法" +"usage" = "使用情况" "secretToken" = "安全密钥" "remained" = "剩余" "security" = "安全" From 3a46c3302be4d44d4d7f742683b6f88d10b1ddbd Mon Sep 17 00:00:00 2001 From: Tara Rostami <132676256+TaraRostami@users.noreply.github.com> Date: Sat, 2 Mar 2024 01:43:04 +0330 Subject: [PATCH 03/55] UI improvements (#1963) * Update sortableTable.html * Update custom.css --- web/assets/css/custom.css | 10 +++- web/html/xui/component/sortableTable.html | 59 ++++++++++++++--------- 2 files changed, 45 insertions(+), 24 deletions(-) diff --git a/web/assets/css/custom.css b/web/assets/css/custom.css index 5f957cce..4ab6c5a8 100644 --- a/web/assets/css/custom.css +++ b/web/assets/css/custom.css @@ -793,7 +793,6 @@ style attribute { .ant-dropdown-menu-dark .ant-dropdown-menu-item:hover, .dark .ant-select-dropdown-menu-item-selected, -.dark .ant-select-dropdown-menu-item:hover, .dark .ant-calendar-time-picker-select-option-selected { background-color: var(--dark-color-surface-600); } @@ -1249,3 +1248,12 @@ b, strong { .dark .ant-alert-close-icon .anticon-close:hover { color: rgb(255 255 255); } + +.ant-empty-small { + margin: 4px 0; + background-color: transparent !important; +} + +.ant-empty-small .ant-empty-image { + height: 20px; +} diff --git a/web/html/xui/component/sortableTable.html b/web/html/xui/component/sortableTable.html index 4e9af06d..8d14e091 100644 --- a/web/html/xui/component/sortableTable.html +++ b/web/html/xui/component/sortableTable.html @@ -32,7 +32,7 @@ }, props: ['data-source', 'customRow'], inheritAttrs: false, - provide() { + provide() { const sortable = {} Object.defineProperty(sortable, "setSortableIndex", { @@ -50,25 +50,21 @@ } }, render: function (createElement) { - return createElement( - 'a-table', - { - class: { - 'ant-table-is-sorting': this.isDragging(), - }, - props: { - ...this.$attrs, - 'data-source': this.records, - customRow: (record, index) => this.customRowRender(record, index), - }, - on: this.$listeners, - nativeOn: { - drop: (e) => this.dropHandler(e), - }, - scopedSlots: this.$scopedSlots, + return createElement('a-table', { + class: { + 'ant-table-is-sorting': this.isDragging(), }, - this.$slots.default, - ) + props: { + ...this.$attrs, + 'data-source': this.records, + customRow: (record, index) => this.customRowRender(record, index), + }, + on: this.$listeners, + nativeOn: { + drop: (e) => this.dropHandler(e), + }, + scopedSlots: this.$scopedSlots, + }, this.$slots.default, ) }, created() { this.$memoSort = {}; @@ -91,8 +87,15 @@ e.preventDefault(); return; } + const hideDragImage = this.$el.cloneNode(true); + hideDragImage.id = "hideDragImage-hide"; + hideDragImage.style.opacity = 0; + document.body.appendChild(hideDragImage); + e.dataTransfer.setDragImage(hideDragImage, 0, 0); }, dragStopHandler(e, index) { + const hideDragImage = document.getElementById('hideDragImage-hide'); + if (hideDragImage) hideDragImage.remove(); this.resetSortableIndex(e, index); }, dragOverHandler(e, index) { @@ -209,16 +212,26 @@ } } .ant-table-is-sorting .draggable-row td { - background-color: white !important; + background-color: #ffffff !important; } .dark .ant-table-is-sorting .draggable-row td { background-color: var(--dark-color-surface-100) !important; } + .ant-table-is-sorting .dragging td { + background-color: rgb(232 244 242) !important; + color: rgba(0, 0, 0, 0.3); + } + .dark .ant-table-is-sorting .dragging td { + background-color: var(--dark-color-table-hover) !important; + color: rgba(255, 255, 255, 0.3); + } .ant-table-is-sorting .dragging { - opacity: 0.5; + opacity: 1; + box-shadow: 1px -2px 2px #008771; + transition: all 0.2s; } .ant-table-is-sorting .dragging .ant-table-row-index { - opacity: 0; + opacity: 0.3; } -{{end}} \ No newline at end of file +{{end}} From 6563d23f382526b901947f5166aab14f5e9cfcbe Mon Sep 17 00:00:00 2001 From: MHSanaei Date: Sat, 2 Mar 2024 21:10:12 +0330 Subject: [PATCH 04/55] Enhance CheckClientIpJob #1964 --- web/job/check_client_ip_job.go | 74 ++++++++++++++++++++-------------- web/service/tgbot.go | 2 - 2 files changed, 44 insertions(+), 32 deletions(-) diff --git a/web/job/check_client_ip_job.go b/web/job/check_client_ip_job.go index 0d46249d..c38112f6 100644 --- a/web/job/check_client_ip_job.go +++ b/web/job/check_client_ip_job.go @@ -10,11 +10,12 @@ import ( "regexp" "sort" "strings" + "sync" "time" + "x-ui/config" "x-ui/database" "x-ui/database/model" - "x-ui/config" "x-ui/logger" "x-ui/xray" ) @@ -38,30 +39,43 @@ func NewCheckClientIpJob() *CheckClientIpJob { } func (j *CheckClientIpJob) Run() { + var wg sync.WaitGroup - // create files and dirs required for iplimit if not exists - for i := 0; i < len(ipFiles); i++ { - err := os.MkdirAll(config.GetLogFolder(), 0770) - j.checkError(err) - file, err := os.OpenFile(ipFiles[i], os.O_CREATE|os.O_APPEND|os.O_RDWR, 0644) - j.checkError(err) - defer file.Close() + if j.checkFail2BanInstalled() { + j.openLogFiles(ipFiles) } - // check for limit ip if j.hasLimitIp() { - j.checkFail2BanInstalled() - j.processLogFile() + if j.checkFail2BanInstalled() && xray.GetAccessLogPath() == "./access.log" { + j.processLogFile() + } else { + if !j.checkFail2BanInstalled() { + logger.Warning("fail2ban is not installed. IP limiting may not work properly.") + } + switch xray.GetAccessLogPath() { + case "none": + logger.Warning("Access log is set to 'none', check your Xray Configs") + case "": + logger.Warning("Access log doesn't exist in your Xray Configs") + } + } } - if !j.hasLimitIp() && xray.GetAccessLogPath() == "./access.log" { - go j.clearLogTime() + if !j.checkFail2BanInstalled() && xray.GetAccessLogPath() == "./access.log" { + wg.Add(1) + go func() { + defer wg.Done() + j.clearLogTime() + }() + wg.Wait() } } func (j *CheckClientIpJob) clearLogTime() { - for { - time.Sleep(time.Hour) + ticker := time.NewTicker(time.Hour) + defer ticker.Stop() + + for range ticker.C { j.clearAccessLog() } } @@ -75,15 +89,18 @@ func (j *CheckClientIpJob) clearAccessLog() { // reopen the access log file for reading file, err := os.Open(accessLogPath) j.checkError(err) - defer file.Close() // copy access log content to persistent file _, err = io.Copy(logAccessP, file) j.checkError(err) + // close the file after copying content + file.Close() + // clean access log err = os.Truncate(accessLogPath, 0) j.checkError(err) + } func (j *CheckClientIpJob) hasLimitIp() bool { @@ -115,29 +132,26 @@ func (j *CheckClientIpJob) hasLimitIp() bool { return false } -func (j *CheckClientIpJob) checkFail2BanInstalled() { +func (j *CheckClientIpJob) checkFail2BanInstalled() bool { cmd := "fail2ban-client" args := []string{"-h"} - err := exec.Command(cmd, args...).Run() - if err != nil { - logger.Warning("fail2ban is not installed. IP limiting may not work properly.") + return err == nil +} + +func (j *CheckClientIpJob) openLogFiles(ipFiles []string) { + for i := 0; i < len(ipFiles); i++ { + err := os.MkdirAll(config.GetLogFolder(), 0770) + j.checkError(err) + file, err := os.OpenFile(ipFiles[i], os.O_CREATE|os.O_APPEND|os.O_RDWR, 0644) + j.checkError(err) + defer file.Close() } } func (j *CheckClientIpJob) processLogFile() { accessLogPath := xray.GetAccessLogPath() - if accessLogPath == "none" { - logger.Warning("Access log is set to 'none' check your Xray Configs") - return - } - - if accessLogPath == "" { - logger.Warning("Access log doesn't exist in your Xray Configs") - return - } - file, err := os.Open(accessLogPath) j.checkError(err) defer file.Close() diff --git a/web/service/tgbot.go b/web/service/tgbot.go index 87445a9a..30d19dba 100644 --- a/web/service/tgbot.go +++ b/web/service/tgbot.go @@ -1560,8 +1560,6 @@ func (t *Tgbot) sendBackup(chatId int64) { } else { logger.Error("Error in opening config.json file for backup: ", err) } - - t.sendBanLogs(chatId, false) } func (t *Tgbot) sendBanLogs(chatId int64, dt bool) { From c47a67975f5fd4de81c6fc9e9dac12a3e3cdbbaf Mon Sep 17 00:00:00 2001 From: emirjorge <117597846+emirjorge@users.noreply.github.com> Date: Tue, 5 Mar 2024 05:21:43 -0500 Subject: [PATCH 05/55] Improved Spanish translation (#1980) --- web/translation/translate.en_US.toml | 2 +- web/translation/translate.es_ES.toml | 1250 +++++++++++++------------- 2 files changed, 626 insertions(+), 626 deletions(-) diff --git a/web/translation/translate.en_US.toml b/web/translation/translate.en_US.toml index 20278b6b..5c3a4db4 100644 --- a/web/translation/translate.en_US.toml +++ b/web/translation/translate.en_US.toml @@ -201,7 +201,7 @@ "last" = "Last" "prefix" = "Prefix" "postfix" = "Postfix" -"delayedStart" = "Start on Initial Use" +"delayedStart" = "Start After First Use" "expireDays" = "Duration" "days" = "Day(s)" "renew" = "Auto Renew" diff --git a/web/translation/translate.es_ES.toml b/web/translation/translate.es_ES.toml index 25cd7737..ad007860 100644 --- a/web/translation/translate.es_ES.toml +++ b/web/translation/translate.es_ES.toml @@ -1,625 +1,625 @@ -"username" = "Nombre de Usuario" -"password" = "Contraseña" -"login" = "Acceder" -"confirm" = "Confirmar" -"cancel" = "Cancelar" -"close" = "Cerrar" -"copy" = "Copiar" -"copied" = "Copiado" -"download" = "Descargar" -"remark" = "Nota" -"enable" = "Habilitar" -"protocol" = "Protocolo" -"search" = "Buscar" -"filter" = "Filtrar" -"loading" = "Cargando..." -"second" = "Segundo" -"minute" = "Minuto" -"hour" = "Hora" -"day" = "Día" -"check" = "Verificar" -"indefinite" = "Indefinido" -"unlimited" = "Ilimitado" -"none" = "None" -"qrCode" = "Código QR" -"info" = "Más Información" -"edit" = "Editar" -"delete" = "Eliminar" -"reset" = "Restablecer" -"copySuccess" = "Copiado exitosamente" -"sure" = "Seguro" -"encryption" = "Encriptación" -"transmission" = "Transmisión" -"host" = "Anfitrión" -"path" = "Ruta" -"camouflage" = "Camuflaje" -"status" = "Estado" -"enabled" = "Habilitado" -"disabled" = "Deshabilitado" -"depleted" = "Agotado" -"depletingSoon" = "Agotándose" -"offline" = "fuera de línea" -"online" = "en línea" -"domainName" = "Nombre de dominio" -"monitor" = "Listening IP" -"certificate" = "Certificado Digital" -"fail" = "Falló" -"success" = "Éxito" -"getVersion" = "Obtener versión" -"install" = "Instalar" -"clients" = "Clientes" -"usage" = "Uso" -"secretToken" = "Token Secreto" -"remained" = "Restante" -"security" = "Seguridad" -"secAlertTitle" = "Alerta de seguridad" -"secAlertSsl" = "Esta conexión no es segura. Evite ingresar información confidencial hasta que TLS esté activado para la protección de datos." -"secAlertConf" = "Certae occasus vulnerabiles sunt impetus. Commendatur ad securitatem protocolla roboranda ne interrupta potentiale." -"secAlertSSL" = "La panel carece de conexión segura. Por favor, instale un certificado TLS para la protección de datos." -"secAlertPanelPort" = "La puerto predeterminado del panel es vulnerable. Por favor, configure un puerto aleatorio o específico." -"secAlertPanelURI" = "La ruta URI predeterminada del panel no es segura. Por favor, configure una ruta URI compleja." -"secAlertSubURI" = "La ruta URI predeterminada de la suscripción no es segura. Por favor, configure una ruta URI compleja." -"secAlertSubJsonURI" = "La ruta URI predeterminada de la suscripción JSON no es segura. Por favor, configure una ruta URI compleja." - -[menu] -"dashboard" = "Estado del Sistema" -"inbounds" = "Entradas" -"settings" = "Configuraciones" -"xray" = "Ajustes Xray" -"logout" = "Cerrar Sesión" -"link" = "Gestionar" - -[pages.login] -"hello" = "Hola" -"title" = "Bienvenido" -"loginAgain" = "El límite de tiempo de inicio de sesión ha expirado. Por favor, inicia sesión nuevamente." - -[pages.login.toasts] -"invalidFormData" = "El formato de los datos de entrada es inválido." -"emptyUsername" = "Por favor ingresa el nombre de usuario." -"emptyPassword" = "Por favor ingresa la contraseña." -"wrongUsernameOrPassword" = "Nombre de usuario o contraseña inválidos." -"successLogin" = "Inicio de Sesión Exitoso" - -[pages.index] -"title" = "Estado del Sistema" -"memory" = "Memoria" -"hard" = "Disco Duro" -"xrayStatus" = "Xray" -"stopXray" = "Detener" -"restartXray" = "Reiniciar" -"xraySwitch" = "Versión" -"xraySwitchClick" = "Elige la versión a la que deseas cambiar." -"xraySwitchClickDesk" = "Elige sabiamente, ya que las versiones anteriores pueden no ser compatibles con las configuraciones actuales." -"operationHours" = "Tiempo de Funcionamiento" -"systemLoad" = "Carga del Sistema" -"systemLoadDesc" = "promedio de carga del sistema en los últimos 1, 5 y 15 minutos" -"connectionTcpCountDesc" = "Conexiones TCP totales en todas las tarjetas de red." -"connectionUdpCountDesc" = "Conexiones UDP totales en todas las tarjetas de red." -"connectionCount" = "Número de Conexiones" -"upSpeed" = "Velocidad de Subida Total para Todas las Tarjetas de Red." -"downSpeed" = "Velocidad de Bajada Total para Todas las Tarjetas de Red." -"totalSent" = "Tráfico Total de Subida de Todas las Tarjetas de Red desde el inicio del sistema." -"totalReceive" = "Datos Descargados Totales en Todas las Tarjetas de Red desde el inicio del sistema." -"xraySwitchVersionDialog" = "Cambiar Versión de Xray" -"xraySwitchVersionDialogDesc" = "¿Estás seguro de que deseas cambiar la versión de Xray a" -"dontRefresh" = "La instalación está en progreso, por favor no actualices esta página." -"logs" = "Registros" -"config" = "Configuración" -"backup" = "Copia de Seguridad y Restauración" -"backupTitle" = "Copia de Seguridad y Restauración de la Base de Datos" -"backupDescription" = "Recuerda hacer una copia de seguridad antes de importar una nueva base de datos." -"exportDatabase" = "Descargar Base de Datos" -"importDatabase" = "Cargar Base de Datos" - -[pages.inbounds] -"title" = "Entradas" -"totalDownUp" = "Subidas/Descargas Totales" -"totalUsage" = "Uso Total" -"inboundCount" = "Número de Entradas" -"operate" = "Menú" -"enable" = "Habilitar" -"remark" = "Notas" -"protocol" = "Protocolo" -"port" = "Puerto" -"traffic" = "Tráfico" -"details" = "Detalles" -"transportConfig" = "Transporte" -"expireDate" = "Fecha de Expiración" -"resetTraffic" = "Restablecer Tráfico" -"addInbound" = "Agregar Entrada" -"generalActions" = "Acciones Generales" -"create" = "Crear" -"update" = "Actualizar" -"modifyInbound" = "Modificar Entrada" -"deleteInbound" = "Eliminar Entrada" -"deleteInboundContent" = "¿Confirmar eliminación de entrada?" -"deleteClient" = "Eliminar cliente" -"deleteClientContent" = "¿Está seguro de que desea eliminar el cliente?" -"resetTrafficContent" = "¿Confirmar restablecimiento de tráfico?" -"copyLink" = "Copiar Enlace" -"address" = "Dirección" -"network" = "Red" -"destinationPort" = "Puerto de Destino" -"targetAddress" = "Dirección de Destino" -"monitorDesc" = "Dejar en blanco por defecto" -"meansNoLimit" = " = illimitata. (unidad: GB)" -"totalFlow" = "Flujo Total" -"leaveBlankToNeverExpire" = "Dejar en Blanco para Nunca Expirar" -"noRecommendKeepDefault" = "No hay requisitos especiales para mantener la configuración predeterminada" -"certificatePath" = "Ruta del Archivo" -"certificateContent" = "Contenido del Archivo" -"publicKey" = "llave Pública" -"privatekey" = "llave Privada" -"clickOnQRcode" = "Haz clic en el Código QR para Copiar" -"client" = "Cliente" -"export" = "Exportar Enlaces" -"clone" = "Clonar" -"cloneInbound" = "Clonar Entradas" -"cloneInboundContent" = "Se aplicarán todas las configuraciones de esta entrada, excepto el Puerto, la IP de Escucha y los Clientes, al clon." -"cloneInboundOk" = "Clonar" -"resetAllTraffic" = "Restablecer Tráfico de Todas las Entradas" -"resetAllTrafficTitle" = "Restablecer tráfico de todas las entradas" -"resetAllTrafficContent" = "¿Estás seguro de que deseas restablecer el tráfico de todas las entradas?" -"resetInboundClientTraffics" = "Restablecer Tráfico de Clientes" -"resetInboundClientTrafficTitle" = "Restablecer todo el tráfico de clientes" -"resetInboundClientTrafficContent" = "¿Estás seguro de que deseas restablecer todo el tráfico para los clientes de esta entrada?" -"resetAllClientTraffics" = "Restablecer Tráfico de Todos los Clientes" -"resetAllClientTrafficTitle" = "Restablecer todo el tráfico de clientes" -"resetAllClientTrafficContent" = "¿Estás seguro de que deseas restablecer todo el tráfico para todos los clientes?" -"delDepletedClients" = "Eliminar Clientes Agotados" -"delDepletedClientsTitle" = "Eliminar clientes agotados" -"delDepletedClientsContent" = "¿Estás seguro de que deseas eliminar todos los clientes agotados?" -"email" = "Email" -"emailDesc" = "Por favor proporciona una dirección de correo electrónico única." -"IPLimit" = "Límite de IP" -"IPLimitDesc" = "Desactiva la entrada si la cantidad supera el valor ingresado (ingresa 0 para desactivar el límite de IP)." -"IPLimitlog" = "Registro de IP" -"IPLimitlogDesc" = "Registro de historial de IPs (antes de habilitar la entrada después de que haya sido desactivada por el límite de IP, debes borrar el registro)." -"IPLimitlogclear" = "Limpiar el Registro" -"setDefaultCert" = "Establecer certificado desde el panel" -"xtlsDesc" = "La versión del núcleo de Xray debe ser 1.7.5" -"realityDesc" = "La versión del núcleo de Xray debe ser 1.8.0 o superior." -"telegramDesc" = "Utiliza únicamente IDs de chat (puedes obtenerlo aquí @userinfobot o usando el comando '/id' en el bot)." -"subscriptionDesc" = "Puedes encontrar tu enlace de suscripción en Detalles, también puedes usar el mismo nombre para varias configuraciones." -"info" = "Info" -"same" = "misma" -"inboundData" = "Datos de entrada" -"exportInbound" = "Exportación entrante" -"import" = "Importar" -"importInbound" = "Importar un entrante" - -[pages.client] -"add" = "Agregar Cliente" -"edit" = "Editar Cliente" -"submitAdd" = "Agregar Cliente" -"submitEdit" = "Guardar Cambios" -"clientCount" = "Número de Clientes" -"bulk" = "Agregar en Lote" -"method" = "Método" -"first" = "Primero" -"last" = "Último" -"prefix" = "Prefijo" -"postfix" = "Sufijo" -"delayedStart" = "Inicio Inicial" -"expireDays" = "Duratio" -"days" = "día(s)" -"renew" = "Renovación automática" -"renewDesc" = "Auto-renovatio post tutelam receptam. (0 = disable) (unitas: dies)" - -[pages.inbounds.toasts] -"obtain" = "Recibir" - -[pages.inbounds.stream.general] -"request" = "Pedido" -"response" = "Respuesta" -"name" = "Nombre" -"value" = "Valor" - -[pages.inbounds.stream.tcp] -"version" = "Versión" -"method" = "Método" -"path" = "Camino" -"status" = "Estado" -"statusDescription" = "Descripción de la Situación" -"requestHeader" = "Encabezado de solicitud" -"responseHeader" = "Encabezado de respuesta" - -[pages.inbounds.stream.quic] -"encryption" = "Cifrado" - -[pages.settings] -"title" = "Configuraciones" -"save" = "Guardar" -"infoDesc" = "Cada cambio realizado aquí debe ser guardado. Por favor, reinicie el panel para aplicar los cambios." -"restartPanel" = "Reiniciar Panel" -"restartPanelDesc" = "¿Está seguro de que desea reiniciar el panel? Haga clic en Aceptar para reiniciar después de 3 segundos. Si no puede acceder al panel después de reiniciar, por favor, consulte la información de registro del panel en el servidor." -"actions" = "Acciones" -"resetDefaultConfig" = "Restablecer a Configuración Predeterminada" -"panelSettings" = "Configuraciones del Panel" -"securitySettings" = "Configuraciones de Seguridad" -"TGBotSettings" = "Configuraciones de Bot de Telegram" -"panelListeningIP" = "IP de Escucha del Panel" -"panelListeningIPDesc" = "Dejar en blanco por defecto para monitorear todas las IPs." -"panelListeningDomain" = "Dominio de Escucha del Panel" -"panelListeningDomainDesc" = "Dejar en blanco por defecto para monitorear todos los dominios e IPs." -"panelPort" = "Puerto del Panel" -"panelPortDesc" = "El puerto utilizado para mostrar este panel." -"publicKeyPath" = "Ruta del Archivo de Clave Pública del Certificado del Panel" -"publicKeyPathDesc" = "Complete con una ruta absoluta que comience con." -"privateKeyPath" = "Ruta del Archivo de Clave Privada del Certificado del Panel" -"privateKeyPathDesc" = "Complete con una ruta absoluta que comience con." -"panelUrlPath" = "Ruta Raíz de la URL del Panel" -"panelUrlPathDesc" = "Debe empezar con '/' y terminar con." -"pageSize" = "Tamaño de paginación" -"pageSizeDesc" = "Defina el tamaño de página para la tabla de entradas. Establezca 0 para desactivar" -"remarkModel" = "Modelo de observación y carácter de separación" -"datepicker" = "selector de fechas" -"datepickerPlaceholder" = "Seleccionar fecha" -"datepickerDescription" = "El tipo de calendario selector especifica la fecha de vencimiento" -"sampleRemark" = "Observación de muestra" -"oldUsername" = "Nombre de Usuario Actual" -"currentPassword" = "Contraseña Actual" -"newUsername" = "Nuevo Nombre de Usuario" -"newPassword" = "Nueva Contraseña" -"telegramBotEnable" = "Habilitar bot de Telegram" -"telegramBotEnableDesc" = "Conéctese a las funciones de este panel a través del bot de Telegram." -"telegramToken" = "Token de Telegram" -"telegramTokenDesc" = "Debe obtener el token del administrador de bots de Telegram @botfather." -"telegramProxy" = "Socks5 Proxy" -"telegramProxyDesc" = "Si necesita el proxy Socks5 para conectarse a Telegram. Ajuste su configuración según la guía." -"telegramChatId" = "IDs de Chat de Telegram para Administradores" -"telegramChatIdDesc" = "IDs de Chat múltiples separados por comas. Use @userinfobot o use el comando '/id' en el bot para obtener sus IDs de Chat." -"telegramNotifyTime" = "Hora de Notificación del Bot de Telegram" -"telegramNotifyTimeDesc" = "Usar el formato de tiempo de Crontab." -"tgNotifyBackup" = "Respaldo de Base de Datos" -"tgNotifyBackupDesc" = "Incluir archivo de respaldo de base de datos con notificación de informe." -"tgNotifyLogin" = "Notificación de Inicio de Sesión" -"tgNotifyLoginDesc" = "Muestra el nombre de usuario, dirección IP y hora cuando alguien intenta iniciar sesión en su panel." -"sessionMaxAge" = "Edad Máxima de Sesión" -"sessionMaxAgeDesc" = "La duración de una sesión de inicio de sesión (unidad: minutos)." -"expireTimeDiff" = "Umbral de Expiración para Notificación" -"expireTimeDiffDesc" = "Reciba notificaciones sobre la expiración de la cuenta antes del umbral (unidad: días)." -"trafficDiff" = "Umbral de Tráfico para Notificación" -"trafficDiffDesc" = "Reciba notificaciones sobre el agotamiento del tráfico antes de alcanzar el umbral (unidad: GB)." -"tgNotifyCpu" = "Umbral de Alerta de Porcentaje de CPU" -"tgNotifyCpuDesc" = "Reciba notificaciones si el uso de la CPU supera este umbral (unidad: %)." -"timeZone" = "Zona Horaria" -"timeZoneDesc" = "Las tareas programadas se ejecutan de acuerdo con la hora en esta zona horaria." -"subSettings" = "Suscripción" -"subEnable" = "Habilitar Servicio" -"subEnableDesc" = "Función de suscripción con configuración separada." -"subListen" = "Listening IP" -"subListenDesc" = "Dejar en blanco por defecto para monitorear todas las IPs." -"subPort" = "Puerto de Suscripción" -"subPortDesc" = "El número de puerto para el servicio de suscripción debe estar sin usar en el servidor." -"subCertPath" = "Ruta del Archivo de Clave Pública del Certificado de Suscripción" -"subCertPathDesc" = "Complete con una ruta absoluta que comience con '/'" -"subKeyPath" = "Ruta del Archivo de Clave Privada del Certificado de Suscripción" -"subKeyPathDesc" = "Complete con una ruta absoluta que comience con '/'" -"subPath" = "Ruta Raíz de la URL de Suscripción" -"subPathDesc" = "Debe empezar con '/' y terminar con '/'" -"subDomain" = "Dominio de Escucha" -"subDomainDesc" = "Dejar en blanco por defecto para monitorear todos los dominios e IPs." -"subUpdates" = "Intervalos de Actualización de Suscripción" -"subUpdatesDesc" = "Horas de intervalo entre actualizaciones en la aplicación del cliente." -"subEncrypt" = "Encriptar configuraciones" -"subEncryptDesc" = "Encriptar las configuraciones devueltas en la suscripción." -"subShowInfo" = "Mostrar información de uso" -"subShowInfoDesc" = "Mostrar tráfico restante y fecha después del nombre de configuración." -"subURI" = "URI de proxy inverso" -"subURIDesc" = "Cambiar el URI base de la URL de suscripción para usar detrás de los servidores proxy" -"fragment" = "Fragmentación" -"fragmentDesc" = "Habilitar la fragmentación para el paquete de saludo TLS" - -[pages.xray] -"title" = "Xray Configuración" -"save" = "Guardar configuración" -"restart" = "Reiniciar Xray" -"basicTemplate" = "Plantilla Básica" -"advancedTemplate" = "Plantilla Avanzada" -"generalConfigs" = "Configuraciones Generales" -"generalConfigsDesc" = "Estas opciones proporcionarán ajustes generales." -"logConfigs" = "Registro" -"logConfigsDesc" = "Los registros pueden afectar la eficiencia de su servidor. Se recomienda habilitarlo sabiamente solo en caso de sus necesidades." -"blockConfigs" = "Configuraciones de Bloqueo" -"blockConfigsDesc" = "Estas opciones evitarán que los usuarios se conecten a protocolos y sitios web específicos." -"blockCountryConfigs" = "Configuraciones de Bloqueo por País" -"blockCountryConfigsDesc" = "Estas opciones evitarán que los usuarios se conecten a dominios de países específicos." -"directCountryConfigs" = "Configuraciones de Conexión Directa por País" -"directCountryConfigsDesc" = "Una conexión directa asegura que el tráfico específico no se enrutará a través de otro servidor." -"ipv4Configs" = "Configuraciones IPv4" -"ipv4ConfigsDesc" = "Estas opciones solo enrutarán a los dominios objetivo a través de IPv4." -"warpConfigs" = "Configuraciones de WARP" -"warpConfigsDesc" = "Precaución: Antes de usar estas opciones, instale WARP en modo de proxy socks5 en su servidor siguiendo los pasos en el GitHub del panel. WARP enrutará el tráfico a los sitios web a través de los servidores de Cloudflare." -"Template" = "Plantilla de Configuración de Xray" -"TemplateDesc" = "Genera el archivo de configuración final de Xray basado en esta plantilla." -"FreedomStrategy" = "Configurar Estrategia para el Protocolo Freedom" -"FreedomStrategyDesc" = "Establece la estrategia de salida de la red en el Protocolo Freedom." -"RoutingStrategy" = "Configurar Estrategia de Enrutamiento de Dominios" -"RoutingStrategyDesc" = "Establece la estrategia general de enrutamiento para la resolución de DNS." -"Torrent" = "Prohibir Uso de BitTorrent" -"TorrentDesc" = "Cambia la plantilla de configuración para evitar el uso de BitTorrent por parte de los usuarios." -"PrivateIp" = "Prohibir Conexiones a Rangos de IP Privadas" -"PrivateIpDesc" = "Cambia la plantilla de configuración para evitar la conexión a rangos de IP privadas." -"Ads" = "Bloquear Anuncios" -"AdsDesc" = "Cambia la plantilla de configuración para bloquear anuncios." -"Family" = "Bloquee malware y contenido para adultos" -"FamilyDesc" = "Resolutores de DNS de Cloudflare para bloquear malware y contenido para adultos para protección familiar." -"Security" = "Bloquee sitios web de malware, phishing y criptomineros" -"SecurityDesc" = "Cambiar la plantilla de configuración para la protección de seguridad." -"Speedtest" = "Bloquear Sitios Web de Pruebas de Velocidad" -"SpeedtestDesc" = "Cambia la plantilla de configuración para evitar la conexión a sitios web de pruebas de velocidad." -"IRIp" = "Desactivar Conexión a Rangos de IP de Irán" -"IRIpDesc" = "Cambia la plantilla de configuración para evitar la conexión a rangos de IP de Irán." -"IRDomain" = "Desactivar Conexión a Dominios de Irán" -"IRDomainDesc" = "Cambia la plantilla de configuración para evitar la conexión a dominios de Irán." -"ChinaIp" = "Desactivar Conexión a Rangos de IP de China" -"ChinaIpDesc" = "Cambia la plantilla de configuración para evitar la conexión a rangos de IP de China." -"ChinaDomain" = "Desactivar Conexión a Dominios de China" -"ChinaDomainDesc" = "Cambia la plantilla de configuración para evitar la conexión a dominios de China." -"RussiaIp" = "Desactivar Conexión a Rangos de IP de Rusia" -"RussiaIpDesc" = "Cambia la plantilla de configuración para evitar la conexión a rangos de IP de Rusia." -"RussiaDomain" = "Desactivar Conexión a Dominios de Rusia" -"RussiaDomainDesc" = "Cambia la plantilla de configuración para evitar la conexión a dominios de Rusia." -"VNIp" = "Deshabilitar la conexión a las IP de Vietnam" -"VNIpDesc" = "Cambie la plantilla de configuración para evitar conectarse a rangos de IP de Vietnam." -"VNDomain" = "Deshabilitar la conexión a dominios de Vietnam" -"VNDomainDesc" = "Cambie la plantilla de configuración para evitar conectarse a dominios de Vietnam." -"DirectIRIp" = "Conexión Directa a Rangos de IP de Irán" -"DirectIRIpDesc" = "Cambia la plantilla de configuración para conectarse directamente a rangos de IP de Irán." -"DirectIRDomain" = "Conexión Directa a Dominios de Irán" -"DirectIRDomainDesc" = "Cambia la plantilla de configuración para conectarse directamente a dominios de Irán." -"DirectChinaIp" = "Conexión Directa a Rangos de IP de China" -"DirectChinaIpDesc" = "Cambia la plantilla de configuración para conectarse directamente a rangos de IP de China." -"DirectChinaDomain" = "Conexión Directa a Dominios de China" -"DirectChinaDomainDesc" = "Cambia la plantilla de configuración para conectarse directamente a dominios de China." -"DirectRussiaIp" = "Conexión Directa a Rangos de IP de Rusia" -"DirectRussiaIpDesc" = "Cambia la plantilla de configuración para conectarse directamente a rangos de IP de Rusia." -"DirectRussiaDomain" = "Conexión Directa a Dominios de Rusia" -"DirectRussiaDomainDesc" = "Cambia la plantilla de configuración para conectarse directamente a dominios de Rusia." -"DirectVNIp" = "Conexión directa a IP de Vietnam" -"DirectVNIpDesc" = "Cambie la plantilla de configuración para la conexión directa a rangos de IP de Vietnam." -"DirectVNDomain" = "Conexión directa a dominios de Vietnam" -"DirectVNDomainDesc" = "Cambie la plantilla de configuración para la conexión directa a dominios de Vietnam." -"GoogleIPv4" = "Usar IPv4 para Google" -"GoogleIPv4Desc" = "Agregar enrutamiento para que Google se conecte con IPv4." -"NetflixIPv4" = "Usar IPv4 para Netflix" -"NetflixIPv4Desc" = "Agregar enrutamiento para que Netflix se conecte con IPv4." -"GoogleWARP" = "Google" -"GoogleWARPDesc" = "Enruta el tráfico a Apple a través de WARP." -"OpenAIWARP" = "OpenAI (ChatGPT)" -"OpenAIWARPDesc" = "Enruta el tráfico a OpenAI (ChatGPT) a través de WARP." -"NetflixWARP" = "Netflix" -"NetflixWARPDesc" = "Enruta el tráfico a Netflix a través de WARP." -"MetaWARP" = "Meta" -"MetaWARPDesc" = "Enruta el tráfico a Meta (Instagram, Facebook, WhatsApp, Threads,...) a través de WARP." -"AppleWARP" = "Apple" -"AppleWARPDesc" = "Enruta el tráfico a Apple a través de WARP." -"RedditWARP" = "Reddit" -"RedditWARPDesc" = "Enruta el tráfico a Reddit a través de WARP." -"SpotifyWARP" = "Spotify" -"SpotifyWARPDesc" = "Enruta el tráfico a Spotify a través de WARP." -"IRWARP" = "Rutear dominios de Irán a través de WARP." -"IRWARPDesc" = "Agregar enrutamiento para dominios de Irán a través de WARP." -"Inbounds" = "Entrante" -"InboundsDesc" = "Cambia la plantilla de configuración para aceptar clientes específicos." -"Outbounds" = "Salidas" -"Balancers" = "Equilibradores" -"OutboundsDesc" = "Cambia la plantilla de configuración para definir formas de salida para este servidor." -"Routings" = "Reglas de enrutamiento" -"RoutingsDesc" = "¡La prioridad de cada regla es importante!" -"completeTemplate" = "Todos" -"logLevel" = "Nivel de registro" -"logLevelDesc" = "El nivel de registro para registros de errores, que indica la información que debe registrarse." -"accessLog" = "Registro de acceso" -"accessLogDesc" = "La ruta del archivo para el registro de acceso. El valor especial 'ninguno' deshabilita los registros de acceso" -"errorLog" = "Registro de errores" -"errorLogDesc" = "La ruta del archivo para el registro de errores. El valor especial 'ninguno' deshabilitó los registros de errores" - -[pages.xray.rules] -"first" = "Primero" -"last" = "Último" -"up" = "arriba" -"down" = "abajo" -"source" = "Fuente" -"dest" = "Destino" -"inbound" = "Entrante" -"outbound" = "saliente" -"balancer" = "Balancín" -"info" = "Información" -"add" = "Agregar regla" -"edit" = "Editar regla" -"useComma" = "Elementos separados por comas" - -[pages.xray.outbound] -"addOutbound" = "Agregar salida" -"addReverse" = "Agregar reverso" -"editOutbound" = "Editar salida" -"editReverse" = "Editar reverso" -"tag" = "Etiqueta" -"tagDesc" = "etiqueta única" -"address" = "Dirección" -"reverse" = "Reverso" -"domain" = "Dominio" -"type" = "Tipo" -"bridge" = "puente" -"portal" = "portal" -"intercon" = "Interconexión" - -[pages.xray.balancer] -"addBalancer" = "Agregar equilibrador" -"editBalancer" = "Editar balanceador" -"balancerStrategy" = "Estrategia" -"balancerSelectors" = "Selectores" -"tag" = "Etiqueta" -"tagDesc" = "etiqueta única" -"balancerDesc" = "No es posible utilizar balancerTag y outboundTag al mismo tiempo. Si se utilizan al mismo tiempo, sólo funcionará outboundTag." - -[pages.xray.wireguard] -"secretKey" = "Llave secreta" -"publicKey" = "Llave pública" -"allowedIPs" = "IP permitidas" -"endpoint" = "Punto final" -"psk" = "Clave precompartida" -"domainStrategy" = "Estrategia de dominio" - -[pages.xray.dns] -"enable" = "Habilitar DNS" -"enableDesc" = "Habilitar servidor DNS integrado" -"strategy" = "Estrategia de consulta" -"strategyDesc" = "Estrategia general para resolver nombres de dominio" -"add" = "Agregar servidor" -"edit" = "Editar servidor" -"domains" = "Dominios" - -[pages.xray.fakedns] -"add" = "Agregar DNS falso" -"edit" = "Editar DNS falso" -"ipPool" = "Subred del grupo de IP" -"poolSize" = "Tamaño del grupo" - -[pages.settings.security] -"admin" = "Administrador" -"secret" = "Token Secreto" -"loginSecurity" = "Seguridad de Inicio de Sesión" -"loginSecurityDesc" = "Habilitar un paso adicional de seguridad para el inicio de sesión de usuarios." -"secretToken" = "Token Secreto" -"secretTokenDesc" = "Por favor, copia y guarda este token de forma segura en un lugar seguro. Este token es necesario para iniciar sesión y no se puede recuperar con la herramienta de comando x-ui." - -[pages.settings.toasts] -"modifySettings" = "Modificar Configuraciones " -"getSettings" = "Obtener Configuraciones " -"modifyUser" = "Modificar Usuario " -"originalUserPassIncorrect" = "Nombre de usuario o contraseña original incorrectos" -"userPassMustBeNotEmpty" = "El nuevo nombre de usuario y la nueva contraseña no pueden estar vacíos" - -[tgbot] -"keyboardClosed" = "❌ ¡Teclado personalizado cerrado!" -"noResult" = "❗ ¡Sin resultados!" -"noQuery" = "❌ ¡Consulta no encontrada! ¡Por favor utiliza el comando nuevamente!" -"wentWrong" = "❌ ¡Algo salió mal!" -"noIpRecord" = "❗ ¡Sin Registro de IP!" -"noInbounds" = "❗ ¡No se encontraron entradas!" -"unlimited" = "♾ Ilimitado" -"add" = "Agregar" -"month" = "Mes" -"months" = "Meses" -"day" = "Día" -"days" = "Días" -"hours" = "Horas" -"unknown" = "Desconocido" -"inbounds" = "Entradas" -"clients" = "Clientes" -"offline" = "🔴 Sin conexión" -"online" = "🟢 En línea" - -[tgbot.commands] -"unknown" = "❗ Comando desconocido" -"pleaseChoose" = "👇 Por favor elige:\r\n" -"help" = "🤖 ¡Bienvenido a este bot! Está diseñado para ofrecerte datos específicos del servidor y te permite hacer modificaciones según sea necesario.\r\n\r\n" -"start" = "👋 Hola {{ .Firstname }}.\r\n" -"welcome" = "🤖 Bienvenido al bot de gestión de {{ .Hostname }}.\r\n" -"status" = "✅ ¡El bot está bien!" -"usage" = "❗ ¡Por favor proporciona un texto para buscar!" -"getID" = "🆔 Tu ID: {{ .ID }}" -"helpAdminCommands" = "Buscar un correo electrónico de cliente:\r\n/usage [Email]\r\n\r\nBuscar entradas (con estadísticas de cliente):\r\n/inbound [Nota]" -"helpClientCommands" = "Para buscar estadísticas, simplemente usa el siguiente comando:\r\n\r\n/usage [UUID|Contraseña]" - -[tgbot.messages] -"cpuThreshold" = "🔴 El uso de CPU {{ .Percent }}% es mayor que el umbral {{ .Threshold }}%" -"selectUserFailed" = "❌ ¡Error al seleccionar usuario!" -"userSaved" = "✅ Usuario de Telegram guardado." -"loginSuccess" = "✅ Has iniciado sesión en el panel con éxito.\r\n" -"loginFailed" = "❗️ Falló el inicio de sesión en el panel.\r\n" -"report" = "🕰 Informes programados: {{ .RunTime }}\r\n" -"datetime" = "⏰ Fecha y Hora: {{ .DateTime }}\r\n" -"hostname" = "💻 Nombre del Host: {{ .Hostname }}\r\n" -"version" = "🚀 Versión de X-UI: {{ .Version }}\r\n" -"ipv6" = "🌐 IPv6: {{ .IPv6 }}\r\n" -"ipv4" = "🌐 IPv4: {{ .IPv4 }}\r\n" -"ip" = "🌐 IP: {{ .IP }}\r\n" -"ips" = "🔢 IPs:\r\n{{ .IPs }}\r\n" -"serverUpTime" = "⏳ Tiempo de actividad del servidor: {{ .UpTime }} {{ .Unit }}\r\n" -"serverLoad" = "📈 Carga del servidor: {{ .Load1 }}, {{ .Load2 }}, {{ .Load3 }}\r\n" -"serverMemory" = "📋 Memoria del servidor: {{ .Current }}/{{ .Total }}\r\n" -"tcpCount" = "🔹 Conteo de TCP: {{ .Count }}\r\n" -"udpCount" = "🔸 Conteo de UDP: {{ .Count }}\r\n" -"traffic" = "🚦 Tráfico: {{ .Total }} (↑{{ .Upload }},↓{{ .Download }})\r\n" -"xrayStatus" = "ℹ️ Estado de Xray: {{ .State }}\r\n" -"username" = "👤 Nombre de usuario: {{ .Username }}\r\n" -"time" = "⏰ Hora: {{ .Time }}\r\n" -"inbound" = "📍 Inbound: {{ .Remark }}\r\n" -"port" = "🔌 Puerto: {{ .Port }}\r\n" -"expire" = "📅 Fecha de Vencimiento: {{ .Time }}\r\n" -"expireIn" = "📅 Vence en: {{ .Time }}\r\n" -"active" = "💡 Activo: {{ .Enable }}\r\n" -"enabled" = "🚨 Habilitado: {{ .Enable }}\r\n" -"online" = "🌐 Estado de conexión: {{ .Status }}\r\n" -"email" = "📧 Email: {{ .Email }}\r\n" -"upload" = "🔼 Subida: ↑{{ .Upload }}\r\n" -"download" = "🔽 Bajada: ↓{{ .Download }}\r\n" -"total" = "📊 Total: ↑↓{{ .UpDown }} / {{ .Total }}\r\n" -"TGUser" = "👤 Usuario de Telegram: {{ .TelegramID }}\r\n" -"exhaustedMsg" = "🚨 Agotado {{ .Type }}:\r\n" -"exhaustedCount" = "🚨 Cantidad de Agotados {{ .Type }}:\r\n" -"onlinesCount" = "🌐 Clientes en línea: {{ .Count }}\r\n" -"disabled" = "🛑 Desactivado: {{ .Disabled }}\r\n" -"depleteSoon" = "🔜 Se agotará pronto: {{ .Deplete }}\r\n\r\n" -"backupTime" = "🗄 Hora de la Copia de Seguridad: {{ .Time }}\r\n" -"refreshedOn" = "\r\n📋🔄 Actualizado en: {{ .Time }}\r\n\r\n" -"yes" = "✅ Sí" -"no" = "❌ No" - -[tgbot.buttons] -"closeKeyboard" = "❌ Cerrar Teclado" -"cancel" = "❌ Cancelar" -"cancelReset" = "❌ Cancelar Reinicio" -"cancelIpLimit" = "❌ Cancelar Límite de IP" -"confirmResetTraffic" = "✅ ¿Confirmar Reinicio de Tráfico?" -"confirmClearIps" = "✅ ¿Confirmar Limpiar IPs?" -"confirmRemoveTGUser" = "✅ ¿Confirmar Eliminar Usuario de Telegram?" -"confirmToggle" = " ✅ ¿Confirmar habilitar/deshabilitar usuario?" -"dbBackup" = "Obtener Copia de Seguridad de BD" -"serverUsage" = "Uso del Servidor" -"getInbounds" = "Obtener Entradas" -"depleteSoon" = "Pronto se Agotará" -"clientUsage" = "Obtener Uso" -"onlines" = "Clientes en línea" -"commands" = "Comandos" -"refresh" = "🔄 Actualizar" -"clearIPs" = "❌ Limpiar IPs" -"removeTGUser" = "❌ Eliminar Usuario de Telegram" -"selectTGUser" = "👤 Seleccionar Usuario de Telegram" -"selectOneTGUser" = "👤 Selecciona un usuario de telegram:" -"resetTraffic" = "📈 Reiniciar Tráfico" -"resetExpire" = "📅 Cambiar fecha de Vencimiento" -"ipLog" = "🔢 Registro de IP" -"ipLimit" = "🔢 Límite de IP" -"setTGUser" = "👤 Establecer Usuario de Telegram" -"toggle" = "🔘 Habilitar / Deshabilitar" -"custom" = "🔢 Costumbre" -"confirmNumber" = "✅ Confirmar: {{ .Num }}" -"confirmNumberAdd" = "✅ Confirmar agregando: {{ .Num }}" -"limitTraffic" = "🚧 Límite de tráfico" -"getBanLogs" = "Registros de prohibición" - -[tgbot.answers] -"successfulOperation" = "✅ ¡Exitosa!" -"errorOperation" = "❗ Error en la Operación." -"getInboundsFailed" = "❌ Error al obtener las entradas" -"canceled" = "❌ {{ .Email }} : Operación cancelada." -"clientRefreshSuccess" = "✅ {{ .Email }} : Cliente actualizado exitosamente." -"IpRefreshSuccess" = "✅ {{ .Email }} : IPs actualizadas exitosamente." -"TGIdRefreshSuccess" = "✅ {{ .Email }} : Usuario de Telegram del cliente actualizado exitosamente." -"resetTrafficSuccess" = "✅ {{ .Email }} : Tráfico reiniciado exitosamente." -"setTrafficLimitSuccess" = "✅ {{ .Email }} : Límite de Tráfico guardado exitosamente." -"expireResetSuccess" = "✅ {{ .Email }} : Días de vencimiento reiniciados exitosamente." -"resetIpSuccess" = "✅ {{ .Email }} : Límite de IP {{ .Count }} guardado exitosamente." -"clearIpSuccess" = "✅ {{ .Email }} : IPs limpiadas exitosamente." -"getIpLog" = "✅ {{ .Email }} : Obtener Registro de IP." -"getUserInfo" = "✅ {{ .Email }} : Obtener Información de Usuario de Telegram." -"removedTGUserSuccess" = "✅ {{ .Email }} : Usuario de Telegram eliminado exitosamente." -"enableSuccess" = "✅ {{ .Email }} : Habilitado exitosamente." -"disableSuccess" = "✅ {{ .Email }} : Deshabilitado exitosamente." -"askToAddUserId" = "¡No se encuentra su configuración!\r\nPor favor, pídale a su administrador que use su ID de usuario de Telegram en su(s) configuración(es).\r\n\r\nSu ID de usuario: {{ .TgUserID }}" +"username" = "Nombre de Usuario" +"password" = "Contraseña" +"login" = "Acceder" +"confirm" = "Confirmar" +"cancel" = "Cancelar" +"close" = "Cerrar" +"copy" = "Copiar" +"copied" = "Copiado" +"download" = "Descargar" +"remark" = "Nota" +"enable" = "Habilitar" +"protocol" = "Protocolo" +"search" = "Buscar" +"filter" = "Filtrar" +"loading" = "Cargando..." +"second" = "Segundo" +"minute" = "Minuto" +"hour" = "Hora" +"day" = "Día" +"check" = "Verificar" +"indefinite" = "Indefinido" +"unlimited" = "Ilimitado" +"none" = "None" +"qrCode" = "Código QR" +"info" = "Más Información" +"edit" = "Editar" +"delete" = "Eliminar" +"reset" = "Restablecer" +"copySuccess" = "Copiado exitosamente" +"sure" = "Seguro" +"encryption" = "Encriptación" +"transmission" = "Transmisión" +"host" = "Anfitrión" +"path" = "Ruta" +"camouflage" = "Camuflaje" +"status" = "Estado" +"enabled" = "Habilitado" +"disabled" = "Deshabilitado" +"depleted" = "Agotado" +"depletingSoon" = "Agotándose" +"offline" = "fuera de línea" +"online" = "en línea" +"domainName" = "Nombre de dominio" +"monitor" = "Listening IP" +"certificate" = "Certificado Digital" +"fail" = "Falló" +"success" = "Éxito" +"getVersion" = "Obtener versión" +"install" = "Instalar" +"clients" = "Clientes" +"usage" = "Uso" +"secretToken" = "Token Secreto" +"remained" = "Restante" +"security" = "Seguridad" +"secAlertTitle" = "Alerta de Seguridad" +"secAlertSsl" = "Esta conexión no es segura. Por favor, evite ingresar información sensible hasta que se active TLS para la protección de datos." +"secAlertConf" = "Ciertas configuraciones son vulnerables a ataques. Se recomienda reforzar los protocolos de seguridad para prevenir posibles violaciones." +"secAlertSSL" = "El panel carece de una conexión segura. Por favor, instale un certificado TLS para la protección de datos." +"secAlertPanelPort" = "El puerto predeterminado del panel es vulnerable. Por favor, configure un puerto aleatorio o específico." +"secAlertPanelURI" = "La ruta URI predeterminada del panel no es segura. Por favor, configure una ruta URI compleja." +"secAlertSubURI" = "La ruta URI predeterminada de la suscripción no es segura. Por favor, configure una ruta URI compleja." +"secAlertSubJsonURI" = "La ruta URI JSON predeterminada de la suscripción no es segura. Por favor, configure una ruta URI compleja." + +[menu] +"dashboard" = "Estado del Sistema" +"inbounds" = "Entradas" +"settings" = "Configuraciones" +"xray" = "Ajustes Xray" +"logout" = "Cerrar Sesión" +"link" = "Gestionar" + +[pages.login] +"hello" = "Hola" +"title" = "Bienvenido" +"loginAgain" = "El límite de tiempo de inicio de sesión ha expirado. Por favor, inicia sesión nuevamente." + +[pages.login.toasts] +"invalidFormData" = "El formato de los datos de entrada es inválido." +"emptyUsername" = "Por favor ingresa el nombre de usuario." +"emptyPassword" = "Por favor ingresa la contraseña." +"wrongUsernameOrPassword" = "Nombre de usuario o contraseña inválidos." +"successLogin" = "Inicio de Sesión Exitoso" + +[pages.index] +"title" = "Estado del Sistema" +"memory" = "Memoria" +"hard" = "Disco Duro" +"xrayStatus" = "Xray" +"stopXray" = "Detener" +"restartXray" = "Reiniciar" +"xraySwitch" = "Versión" +"xraySwitchClick" = "Elige la versión a la que deseas cambiar." +"xraySwitchClickDesk" = "Elige sabiamente, ya que las versiones anteriores pueden no ser compatibles con las configuraciones actuales." +"operationHours" = "Tiempo de Funcionamiento" +"systemLoad" = "Carga del Sistema" +"systemLoadDesc" = "promedio de carga del sistema en los últimos 1, 5 y 15 minutos" +"connectionTcpCountDesc" = "Conexiones TCP totales en todas las tarjetas de red." +"connectionUdpCountDesc" = "Conexiones UDP totales en todas las tarjetas de red." +"connectionCount" = "Número de Conexiones" +"upSpeed" = "Velocidad de Subida Total para Todas las Tarjetas de Red." +"downSpeed" = "Velocidad de Bajada Total para Todas las Tarjetas de Red." +"totalSent" = "Tráfico Total de Subida de Todas las Tarjetas de Red desde el inicio del sistema." +"totalReceive" = "Datos Descargados Totales en Todas las Tarjetas de Red desde el inicio del sistema." +"xraySwitchVersionDialog" = "Cambiar Versión de Xray" +"xraySwitchVersionDialogDesc" = "¿Estás seguro de que deseas cambiar la versión de Xray a" +"dontRefresh" = "La instalación está en progreso, por favor no actualices esta página." +"logs" = "Registros" +"config" = "Configuración" +"backup" = "Copia de Seguridad y Restauración" +"backupTitle" = "Copia de Seguridad y Restauración de la Base de Datos" +"backupDescription" = "Recuerda hacer una copia de seguridad antes de importar una nueva base de datos." +"exportDatabase" = "Descargar Base de Datos" +"importDatabase" = "Cargar Base de Datos" + +[pages.inbounds] +"title" = "Entradas" +"totalDownUp" = "Subidas/Descargas Totales" +"totalUsage" = "Uso Total" +"inboundCount" = "Número de Entradas" +"operate" = "Menú" +"enable" = "Habilitar" +"remark" = "Notas" +"protocol" = "Protocolo" +"port" = "Puerto" +"traffic" = "Tráfico" +"details" = "Detalles" +"transportConfig" = "Transporte" +"expireDate" = "Fecha de Expiración" +"resetTraffic" = "Restablecer Tráfico" +"addInbound" = "Agregar Entrada" +"generalActions" = "Acciones Generales" +"create" = "Crear" +"update" = "Actualizar" +"modifyInbound" = "Modificar Entrada" +"deleteInbound" = "Eliminar Entrada" +"deleteInboundContent" = "¿Confirmar eliminación de entrada?" +"deleteClient" = "Eliminar cliente" +"deleteClientContent" = "¿Está seguro de que desea eliminar el cliente?" +"resetTrafficContent" = "¿Confirmar restablecimiento de tráfico?" +"copyLink" = "Copiar Enlace" +"address" = "Dirección" +"network" = "Red" +"destinationPort" = "Puerto de Destino" +"targetAddress" = "Dirección de Destino" +"monitorDesc" = "Dejar en blanco por defecto" +"meansNoLimit" = " = illimitata. (unidad: GB)" +"totalFlow" = "Flujo Total" +"leaveBlankToNeverExpire" = "Dejar en Blanco para Nunca Expirar" +"noRecommendKeepDefault" = "No hay requisitos especiales para mantener la configuración predeterminada" +"certificatePath" = "Camino" +"certificateContent" = "Contenido" +"publicKey" = "Clave Pública" +"privatekey" = "Clave Privada" +"clickOnQRcode" = "Haz clic en el Código QR para Copiar" +"client" = "Cliente" +"export" = "Exportar Enlaces" +"clone" = "Clonar" +"cloneInbound" = "Clonar Entradas" +"cloneInboundContent" = "Se aplicarán todas las configuraciones de esta entrada, excepto el Puerto, la IP de Escucha y los Clientes, al clon." +"cloneInboundOk" = "Clonar" +"resetAllTraffic" = "Restablecer Tráfico de Todas las Entradas" +"resetAllTrafficTitle" = "Restablecer tráfico de todas las entradas" +"resetAllTrafficContent" = "¿Estás seguro de que deseas restablecer el tráfico de todas las entradas?" +"resetInboundClientTraffics" = "Restablecer Tráfico de Clientes" +"resetInboundClientTrafficTitle" = "Restablecer todo el tráfico de clientes" +"resetInboundClientTrafficContent" = "¿Estás seguro de que deseas restablecer todo el tráfico para los clientes de esta entrada?" +"resetAllClientTraffics" = "Restablecer Tráfico de Todos los Clientes" +"resetAllClientTrafficTitle" = "Restablecer todo el tráfico de clientes" +"resetAllClientTrafficContent" = "¿Estás seguro de que deseas restablecer todo el tráfico para todos los clientes?" +"delDepletedClients" = "Eliminar Clientes Agotados" +"delDepletedClientsTitle" = "Eliminar clientes agotados" +"delDepletedClientsContent" = "¿Estás seguro de que deseas eliminar todos los clientes agotados?" +"email" = "Email" +"emailDesc" = "Por favor proporciona una dirección de correo electrónico única." +"IPLimit" = "Límite de IP" +"IPLimitDesc" = "Desactiva la entrada si la cantidad supera el valor ingresado (ingresa 0 para desactivar el límite de IP)." +"IPLimitlog" = "Registro de IP" +"IPLimitlogDesc" = "Registro de historial de IPs (antes de habilitar la entrada después de que haya sido desactivada por el límite de IP, debes borrar el registro)." +"IPLimitlogclear" = "Limpiar el Registro" +"setDefaultCert" = "Establecer certificado desde el panel" +"xtlsDesc" = "La versión del núcleo de Xray debe ser 1.7.5" +"realityDesc" = "La versión del núcleo de Xray debe ser 1.8.0 o superior." +"telegramDesc" = "Utiliza únicamente IDs de chat (puedes obtenerlo aquí @userinfobot o usando el comando '/id' en el bot)." +"subscriptionDesc" = "Puedes encontrar tu enlace de suscripción en Detalles, también puedes usar el mismo nombre para varias configuraciones." +"info" = "Info" +"same" = "misma" +"inboundData" = "Datos de entrada" +"exportInbound" = "Exportación entrante" +"import" = "Importar" +"importInbound" = "Importar un entrante" + +[pages.client] +"add" = "Agregar Cliente" +"edit" = "Editar Cliente" +"submitAdd" = "Agregar Cliente" +"submitEdit" = "Guardar Cambios" +"clientCount" = "Número de Clientes" +"bulk" = "Agregar en Lote" +"method" = "Método" +"first" = "Primero" +"last" = "Último" +"prefix" = "Prefijo" +"postfix" = "Sufijo" +"delayedStart" = "Iniciar el primer uso" +"expireDays" = "Duración" +"days" = "Día(s)" +"renew" = "Renovación automática" +"renewDesc" = "Renovación automática después de la expiración. (0 = desactivar) (unidad: día)" + +[pages.inbounds.toasts] +"obtain" = "Recibir" + +[pages.inbounds.stream.general] +"request" = "Pedido" +"response" = "Respuesta" +"name" = "Nombre" +"value" = "Valor" + +[pages.inbounds.stream.tcp] +"version" = "Versión" +"method" = "Método" +"path" = "Camino" +"status" = "Estado" +"statusDescription" = "Descripción de la Situación" +"requestHeader" = "Encabezado de solicitud" +"responseHeader" = "Encabezado de respuesta" + +[pages.inbounds.stream.quic] +"encryption" = "Cifrado" + +[pages.settings] +"title" = "Configuraciones" +"save" = "Guardar" +"infoDesc" = "Cada cambio realizado aquí debe ser guardado. Por favor, reinicie el panel para aplicar los cambios." +"restartPanel" = "Reiniciar Panel" +"restartPanelDesc" = "¿Está seguro de que desea reiniciar el panel? Haga clic en Aceptar para reiniciar después de 3 segundos. Si no puede acceder al panel después de reiniciar, por favor, consulte la información de registro del panel en el servidor." +"actions" = "Acciones" +"resetDefaultConfig" = "Restablecer a Configuración Predeterminada" +"panelSettings" = "Configuraciones del Panel" +"securitySettings" = "Configuraciones de Seguridad" +"TGBotSettings" = "Configuraciones de Bot de Telegram" +"panelListeningIP" = "IP de Escucha del Panel" +"panelListeningIPDesc" = "Dejar en blanco por defecto para monitorear todas las IPs." +"panelListeningDomain" = "Dominio de Escucha del Panel" +"panelListeningDomainDesc" = "Dejar en blanco por defecto para monitorear todos los dominios e IPs." +"panelPort" = "Puerto del Panel" +"panelPortDesc" = "El puerto utilizado para mostrar este panel." +"publicKeyPath" = "Ruta del Archivo de Clave Pública del Certificado del Panel" +"publicKeyPathDesc" = "Complete con una ruta absoluta que comience con." +"privateKeyPath" = "Ruta del Archivo de Clave Privada del Certificado del Panel" +"privateKeyPathDesc" = "Complete con una ruta absoluta que comience con." +"panelUrlPath" = "Ruta Raíz de la URL del Panel" +"panelUrlPathDesc" = "Debe empezar con '/' y terminar con." +"pageSize" = "Tamaño de paginación" +"pageSizeDesc" = "Defina el tamaño de página para la tabla de entradas. Establezca 0 para desactivar" +"remarkModel" = "Modelo de observación y carácter de separación" +"datepicker" = "selector de fechas" +"datepickerPlaceholder" = "Seleccionar fecha" +"datepickerDescription" = "El tipo de calendario selector especifica la fecha de vencimiento" +"sampleRemark" = "Observación de muestra" +"oldUsername" = "Nombre de Usuario Actual" +"currentPassword" = "Contraseña Actual" +"newUsername" = "Nuevo Nombre de Usuario" +"newPassword" = "Nueva Contraseña" +"telegramBotEnable" = "Habilitar bot de Telegram" +"telegramBotEnableDesc" = "Conéctese a las funciones de este panel a través del bot de Telegram." +"telegramToken" = "Token de Telegram" +"telegramTokenDesc" = "Debe obtener el token del administrador de bots de Telegram @botfather." +"telegramProxy" = "Socks5 Proxy" +"telegramProxyDesc" = "Si necesita el proxy Socks5 para conectarse a Telegram. Ajuste su configuración según la guía." +"telegramChatId" = "IDs de Chat de Telegram para Administradores" +"telegramChatIdDesc" = "IDs de Chat múltiples separados por comas. Use @userinfobot o use el comando '/id' en el bot para obtener sus IDs de Chat." +"telegramNotifyTime" = "Hora de Notificación del Bot de Telegram" +"telegramNotifyTimeDesc" = "Usar el formato de tiempo de Crontab." +"tgNotifyBackup" = "Respaldo de Base de Datos" +"tgNotifyBackupDesc" = "Incluir archivo de respaldo de base de datos con notificación de informe." +"tgNotifyLogin" = "Notificación de Inicio de Sesión" +"tgNotifyLoginDesc" = "Muestra el nombre de usuario, dirección IP y hora cuando alguien intenta iniciar sesión en su panel." +"sessionMaxAge" = "Edad Máxima de Sesión" +"sessionMaxAgeDesc" = "La duración de una sesión de inicio de sesión (unidad: minutos)." +"expireTimeDiff" = "Umbral de Expiración para Notificación" +"expireTimeDiffDesc" = "Reciba notificaciones sobre la expiración de la cuenta antes del umbral (unidad: días)." +"trafficDiff" = "Umbral de Tráfico para Notificación" +"trafficDiffDesc" = "Reciba notificaciones sobre el agotamiento del tráfico antes de alcanzar el umbral (unidad: GB)." +"tgNotifyCpu" = "Umbral de Alerta de Porcentaje de CPU" +"tgNotifyCpuDesc" = "Reciba notificaciones si el uso de la CPU supera este umbral (unidad: %)." +"timeZone" = "Zona Horaria" +"timeZoneDesc" = "Las tareas programadas se ejecutan de acuerdo con la hora en esta zona horaria." +"subSettings" = "Suscripción" +"subEnable" = "Habilitar Servicio" +"subEnableDesc" = "Función de suscripción con configuración separada." +"subListen" = "Listening IP" +"subListenDesc" = "Dejar en blanco por defecto para monitorear todas las IPs." +"subPort" = "Puerto de Suscripción" +"subPortDesc" = "El número de puerto para el servicio de suscripción debe estar sin usar en el servidor." +"subCertPath" = "Ruta del Archivo de Clave Pública del Certificado de Suscripción" +"subCertPathDesc" = "Complete con una ruta absoluta que comience con '/'" +"subKeyPath" = "Ruta del Archivo de Clave Privada del Certificado de Suscripción" +"subKeyPathDesc" = "Complete con una ruta absoluta que comience con '/'" +"subPath" = "Ruta Raíz de la URL de Suscripción" +"subPathDesc" = "Debe empezar con '/' y terminar con '/'" +"subDomain" = "Dominio de Escucha" +"subDomainDesc" = "Dejar en blanco por defecto para monitorear todos los dominios e IPs." +"subUpdates" = "Intervalos de Actualización de Suscripción" +"subUpdatesDesc" = "Horas de intervalo entre actualizaciones en la aplicación del cliente." +"subEncrypt" = "Encriptar configuraciones" +"subEncryptDesc" = "Encriptar las configuraciones devueltas en la suscripción." +"subShowInfo" = "Mostrar información de uso" +"subShowInfoDesc" = "Mostrar tráfico restante y fecha después del nombre de configuración." +"subURI" = "URI de proxy inverso" +"subURIDesc" = "Cambiar el URI base de la URL de suscripción para usar detrás de los servidores proxy" +"fragment" = "Fragmentación" +"fragmentDesc" = "Habilitar la fragmentación para el paquete de saludo de TLS" + +[pages.xray] +"title" = "Xray Configuración" +"save" = "Guardar configuración" +"restart" = "Reiniciar Xray" +"basicTemplate" = "Plantilla Básica" +"advancedTemplate" = "Plantilla Avanzada" +"generalConfigs" = "Configuraciones Generales" +"generalConfigsDesc" = "Estas opciones proporcionarán ajustes generales." +"logConfigs" = "Registro" +"logConfigsDesc" = "Los registros pueden afectar la eficiencia de su servidor. Se recomienda habilitarlos sabiamente solo en caso de sus necesidades." +"blockConfigs" = "Configuraciones de Bloqueo" +"blockConfigsDesc" = "Estas opciones evitarán que los usuarios se conecten a protocolos y sitios web específicos." +"blockCountryConfigs" = "Configuraciones de Bloqueo por País" +"blockCountryConfigsDesc" = "Estas opciones evitarán que los usuarios se conecten a dominios de países específicos." +"directCountryConfigs" = "Configuraciones de Conexión Directa por País" +"directCountryConfigsDesc" = "Una conexión directa asegura que el tráfico específico no se enrutará a través de otro servidor." +"ipv4Configs" = "Configuraciones IPv4" +"ipv4ConfigsDesc" = "Estas opciones solo enrutarán a los dominios objetivo a través de IPv4." +"warpConfigs" = "Configuraciones de WARP" +"warpConfigsDesc" = "Precaución: Antes de usar estas opciones, instale WARP en modo de proxy socks5 en su servidor siguiendo los pasos en el GitHub del panel. WARP enrutará el tráfico a los sitios web a través de los servidores de Cloudflare." +"Template" = "Plantilla de Configuración de Xray" +"TemplateDesc" = "Genera el archivo de configuración final de Xray basado en esta plantilla." +"FreedomStrategy" = "Configurar Estrategia para el Protocolo Freedom" +"FreedomStrategyDesc" = "Establece la estrategia de salida de la red en el Protocolo Freedom." +"RoutingStrategy" = "Configurar Estrategia de Enrutamiento de Dominios" +"RoutingStrategyDesc" = "Establece la estrategia general de enrutamiento para la resolución de DNS." +"Torrent" = "Prohibir Uso de BitTorrent" +"TorrentDesc" = "Cambia la plantilla de configuración para evitar el uso de BitTorrent por parte de los usuarios." +"PrivateIp" = "Prohibir Conexiones a Rangos de IP Privadas" +"PrivateIpDesc" = "Cambia la plantilla de configuración para evitar la conexión a rangos de IP privadas." +"Ads" = "Bloquear Anuncios" +"AdsDesc" = "Cambia la plantilla de configuración para bloquear anuncios." +"Family" = "Bloquee malware y contenido para adultos" +"FamilyDesc" = "Resolutores de DNS de Cloudflare para bloquear malware y contenido para adultos para protección familiar." +"Security" = "Escudo de Seguridad" +"SecurityDesc" = "Cambiar la plantilla de configuración para la protección de seguridad." +"Speedtest" = "Bloquear Sitios Web de Pruebas de Velocidad" +"SpeedtestDesc" = "Cambia la plantilla de configuración para evitar la conexión a sitios web de pruebas de velocidad." +"IRIp" = "Desactivar Conexión a Rangos de IP de Irán" +"IRIpDesc" = "Cambia la plantilla de configuración para evitar la conexión a rangos de IP de Irán." +"IRDomain" = "Desactivar Conexión a Dominios de Irán" +"IRDomainDesc" = "Cambia la plantilla de configuración para evitar la conexión a dominios de Irán." +"ChinaIp" = "Desactivar Conexión a Rangos de IP de China" +"ChinaIpDesc" = "Cambia la plantilla de configuración para evitar la conexión a rangos de IP de China." +"ChinaDomain" = "Desactivar Conexión a Dominios de China" +"ChinaDomainDesc" = "Cambia la plantilla de configuración para evitar la conexión a dominios de China." +"RussiaIp" = "Desactivar Conexión a Rangos de IP de Rusia" +"RussiaIpDesc" = "Cambia la plantilla de configuración para evitar la conexión a rangos de IP de Rusia." +"RussiaDomain" = "Desactivar Conexión a Dominios de Rusia" +"RussiaDomainDesc" = "Cambia la plantilla de configuración para evitar la conexión a dominios de Rusia." +"VNIp" = "Deshabilitar la conexión a las IP de Vietnam" +"VNIpDesc" = "Cambie la plantilla de configuración para evitar conectarse a rangos de IP de Vietnam." +"VNDomain" = "Deshabilitar la conexión a dominios de Vietnam" +"VNDomainDesc" = "Cambie la plantilla de configuración para evitar conectarse a dominios de Vietnam." +"DirectIRIp" = "Conexión Directa a Rangos de IP de Irán" +"DirectIRIpDesc" = "Cambia la plantilla de configuración para conectarse directamente a rangos de IP de Irán." +"DirectIRDomain" = "Conexión Directa a Dominios de Irán" +"DirectIRDomainDesc" = "Cambia la plantilla de configuración para conectarse directamente a dominios de Irán." +"DirectChinaIp" = "Conexión Directa a Rangos de IP de China" +"DirectChinaIpDesc" = "Cambia la plantilla de configuración para conectarse directamente a rangos de IP de China." +"DirectChinaDomain" = "Conexión Directa a Dominios de China" +"DirectChinaDomainDesc" = "Cambia la plantilla de configuración para conectarse directamente a dominios de China." +"DirectRussiaIp" = "Conexión Directa a Rangos de IP de Rusia" +"DirectRussiaIpDesc" = "Cambia la plantilla de configuración para conectarse directamente a rangos de IP de Rusia." +"DirectRussiaDomain" = "Conexión Directa a Dominios de Rusia" +"DirectRussiaDomainDesc" = "Cambia la plantilla de configuración para conectarse directamente a dominios de Rusia." +"DirectVNIp" = "Conexión directa a IP de Vietnam" +"DirectVNIpDesc" = "Cambie la plantilla de configuración para la conexión directa a rangos de IP de Vietnam." +"DirectVNDomain" = "Conexión directa a dominios de Vietnam" +"DirectVNDomainDesc" = "Cambie la plantilla de configuración para la conexión directa a dominios de Vietnam." +"GoogleIPv4" = "Usar IPv4 para Google" +"GoogleIPv4Desc" = "Agregar enrutamiento para que Google se conecte con IPv4." +"NetflixIPv4" = "Usar IPv4 para Netflix" +"NetflixIPv4Desc" = "Agregar enrutamiento para que Netflix se conecte con IPv4." +"GoogleWARP" = "Google" +"GoogleWARPDesc" = "Agrega enrutamiento para Google a través de WARP." +"OpenAIWARP" = "ChatGPT" +"OpenAIWARPDesc" = "Enruta el tráfico a ChatGPT a través de WARP." +"NetflixWARP" = "Netflix" +"NetflixWARPDesc" = "Enruta el tráfico a Netflix a través de WARP." +"MetaWARP" = "Meta" +"MetaWARPDesc" = "Enruta el tráfico a Meta (Instagram, Facebook, WhatsApp, Threads, etc.) a través de WARP." +"AppleWARP" = "Apple" +"AppleWARPDesc" = "Enruta el tráfico a Apple a través de WARP." +"RedditWARP" = "Reddit" +"RedditWARPDesc" = "Enruta el tráfico a Reddit a través de WARP." +"SpotifyWARP" = "Spotify" +"SpotifyWARPDesc" = "Enruta el tráfico a Spotify a través de WARP." +"IRWARP" = "Rutear dominios de Irán a través de WARP." +"IRWARPDesc" = "Agregar enrutamiento para dominios de Irán a través de WARP." +"Inbounds" = "Entrante" +"InboundsDesc" = "Cambia la plantilla de configuración para aceptar clientes específicos." +"Outbounds" = "Salidas" +"Balancers" = "Equilibradores" +"OutboundsDesc" = "Cambia la plantilla de configuración para definir formas de salida para este servidor." +"Routings" = "Reglas de enrutamiento" +"RoutingsDesc" = "¡La prioridad de cada regla es importante!" +"completeTemplate" = "Todos" +"logLevel" = "Nivel de registro" +"logLevelDesc" = "El nivel de registro para registros de errores, que indica la información que debe registrarse." +"accessLog" = "Registro de acceso" +"accessLogDesc" = "La ruta del archivo para el registro de acceso. El valor especial 'ninguno' deshabilita los registros de acceso" +"errorLog" = "Registro de Errores" +"errorLogDesc" = "La ruta del archivo para el registro de errores. El valor especial 'none' desactiva los registros de errores." + +[pages.xray.rules] +"first" = "Primero" +"last" = "Último" +"up" = "Arriba" +"down" = "Abajo" +"source" = "Fuente" +"dest" = "Destino" +"inbound" = "Entrante" +"outbound" = "Saliente" +"balancer" = "Equilibrador" +"info" = "Información" +"add" = "Agregar Regla" +"edit" = "Editar Regla" +"useComma" = "Elementos separados por comas" + +[pages.xray.outbound] +"addOutbound" = "Agregar salida" +"addReverse" = "Agregar reverso" +"editOutbound" = "Editar salida" +"editReverse" = "Editar reverso" +"tag" = "Etiqueta" +"tagDesc" = "etiqueta única" +"address" = "Dirección" +"reverse" = "Reverso" +"domain" = "Dominio" +"type" = "Tipo" +"bridge" = "puente" +"portal" = "portal" +"intercon" = "Interconexión" + +[pages.xray.balancer] +"addBalancer" = "Agregar equilibrador" +"editBalancer" = "Editar balanceador" +"balancerStrategy" = "Estrategia" +"balancerSelectors" = "Selectores" +"tag" = "Etiqueta" +"tagDesc" = "etiqueta única" +"balancerDesc" = "No es posible utilizar balancerTag y outboundTag al mismo tiempo. Si se utilizan al mismo tiempo, sólo funcionará outboundTag." + +[pages.xray.wireguard] +"secretKey" = "Llave secreta" +"publicKey" = "Llave pública" +"allowedIPs" = "IP permitidas" +"endpoint" = "Punto final" +"psk" = "Clave precompartida" +"domainStrategy" = "Estrategia de dominio" + +[pages.xray.dns] +"enable" = "Habilitar DNS" +"enableDesc" = "Habilitar servidor DNS incorporado" +"strategy" = "Estrategia de Consulta" +"strategyDesc" = "Estrategia general para resolver nombres de dominio" +"add" = "Agregar Servidor" +"edit" = "Editar Servidor" +"domains" = "Dominios" + +[pages.xray.fakedns] +"add" = "Agregar DNS Falso" +"edit" = "Editar DNS Falso" +"ipPool" = "Subred del grupo de IP" +"poolSize" = "Tamaño del grupo" + +[pages.settings.security] +"admin" = "Administrador" +"secret" = "Token Secreto" +"loginSecurity" = "Seguridad de Inicio de Sesión" +"loginSecurityDesc" = "Habilitar un paso adicional de seguridad para el inicio de sesión de usuarios." +"secretToken" = "Token Secreto" +"secretTokenDesc" = "Por favor, copia y guarda este token de forma segura en un lugar seguro. Este token es necesario para iniciar sesión y no se puede recuperar con la herramienta de comando x-ui." + +[pages.settings.toasts] +"modifySettings" = "Modificar Configuraciones " +"getSettings" = "Obtener Configuraciones " +"modifyUser" = "Modificar Usuario " +"originalUserPassIncorrect" = "Nombre de usuario o contraseña original incorrectos" +"userPassMustBeNotEmpty" = "El nuevo nombre de usuario y la nueva contraseña no pueden estar vacíos" + +[tgbot] +"keyboardClosed" = "❌ ¡Teclado personalizado cerrado!" +"noResult" = "❗ ¡Sin resultados!" +"noQuery" = "❌ ¡Consulta no encontrada! ¡Por favor utiliza el comando nuevamente!" +"wentWrong" = "❌ ¡Algo salió mal!" +"noIpRecord" = "❗ ¡Sin Registro de IP!" +"noInbounds" = "❗ ¡No se encontraron entradas!" +"unlimited" = "♾ Ilimitado" +"add" = "Agregar" +"month" = "Mes" +"months" = "Meses" +"day" = "Día" +"days" = "Días" +"hours" = "Horas" +"unknown" = "Desconocido" +"inbounds" = "Entradas" +"clients" = "Clientes" +"offline" = "🔴 Sin conexión" +"online" = "🟢 En línea" + +[tgbot.commands] +"unknown" = "❗ Comando desconocido" +"pleaseChoose" = "👇 Por favor elige:\r\n" +"help" = "🤖 ¡Bienvenido a este bot! Está diseñado para ofrecerte datos específicos del servidor y te permite hacer modificaciones según sea necesario.\r\n\r\n" +"start" = "👋 Hola {{ .Firstname }}.\r\n" +"welcome" = "🤖 Bienvenido al bot de gestión de {{ .Hostname }}.\r\n" +"status" = "✅ ¡El bot está bien!" +"usage" = "❗ ¡Por favor proporciona un texto para buscar!" +"getID" = "🆔 Tu ID: {{ .ID }}" +"helpAdminCommands" = "Buscar un correo electrónico de cliente:\r\n/usage [Email]\r\n\r\nBuscar entradas (con estadísticas de cliente):\r\n/inbound [Nota]" +"helpClientCommands" = "Para buscar estadísticas, simplemente usa el siguiente comando:\r\n\r\n/usage [UUID|Contraseña]" + +[tgbot.messages] +"cpuThreshold" = "🔴 El uso de CPU {{ .Percent }}% es mayor que el umbral {{ .Threshold }}%" +"selectUserFailed" = "❌ ¡Error al seleccionar usuario!" +"userSaved" = "✅ Usuario de Telegram guardado." +"loginSuccess" = "✅ Has iniciado sesión en el panel con éxito.\r\n" +"loginFailed" = "❗️ Falló el inicio de sesión en el panel.\r\n" +"report" = "🕰 Informes programados: {{ .RunTime }}\r\n" +"datetime" = "⏰ Fecha y Hora: {{ .DateTime }}\r\n" +"hostname" = "💻 Nombre del Host: {{ .Hostname }}\r\n" +"version" = "🚀 Versión de X-UI: {{ .Version }}\r\n" +"ipv6" = "🌐 IPv6: {{ .IPv6 }}\r\n" +"ipv4" = "🌐 IPv4: {{ .IPv4 }}\r\n" +"ip" = "🌐 IP: {{ .IP }}\r\n" +"ips" = "🔢 IPs:\r\n{{ .IPs }}\r\n" +"serverUpTime" = "⏳ Tiempo de actividad del servidor: {{ .UpTime }} {{ .Unit }}\r\n" +"serverLoad" = "📈 Carga del servidor: {{ .Load1 }}, {{ .Load2 }}, {{ .Load3 }}\r\n" +"serverMemory" = "📋 Memoria del servidor: {{ .Current }}/{{ .Total }}\r\n" +"tcpCount" = "🔹 Conteo de TCP: {{ .Count }}\r\n" +"udpCount" = "🔸 Conteo de UDP: {{ .Count }}\r\n" +"traffic" = "🚦 Tráfico: {{ .Total }} (↑{{ .Upload }},↓{{ .Download }})\r\n" +"xrayStatus" = "ℹ️ Estado de Xray: {{ .State }}\r\n" +"username" = "👤 Nombre de usuario: {{ .Username }}\r\n" +"time" = "⏰ Hora: {{ .Time }}\r\n" +"inbound" = "📍 Inbound: {{ .Remark }}\r\n" +"port" = "🔌 Puerto: {{ .Port }}\r\n" +"expire" = "📅 Fecha de Vencimiento: {{ .Time }}\r\n" +"expireIn" = "📅 Vence en: {{ .Time }}\r\n" +"active" = "💡 Activo: {{ .Enable }}\r\n" +"enabled" = "🚨 Habilitado: {{ .Enable }}\r\n" +"online" = "🌐 Estado de conexión: {{ .Status }}\r\n" +"email" = "📧 Email: {{ .Email }}\r\n" +"upload" = "🔼 Subida: ↑{{ .Upload }}\r\n" +"download" = "🔽 Bajada: ↓{{ .Download }}\r\n" +"total" = "📊 Total: ↑↓{{ .UpDown }} / {{ .Total }}\r\n" +"TGUser" = "👤 Usuario de Telegram: {{ .TelegramID }}\r\n" +"exhaustedMsg" = "🚨 Agotado {{ .Type }}:\r\n" +"exhaustedCount" = "🚨 Cantidad de Agotados {{ .Type }}:\r\n" +"onlinesCount" = "🌐 Clientes en línea: {{ .Count }}\r\n" +"disabled" = "🛑 Desactivado: {{ .Disabled }}\r\n" +"depleteSoon" = "🔜 Se agotará pronto: {{ .Deplete }}\r\n\r\n" +"backupTime" = "🗄 Hora de la Copia de Seguridad: {{ .Time }}\r\n" +"refreshedOn" = "\r\n📋🔄 Actualizado en: {{ .Time }}\r\n\r\n" +"yes" = "✅ Sí" +"no" = "❌ No" + +[tgbot.buttons] +"closeKeyboard" = "❌ Cerrar Teclado" +"cancel" = "❌ Cancelar" +"cancelReset" = "❌ Cancelar Reinicio" +"cancelIpLimit" = "❌ Cancelar Límite de IP" +"confirmResetTraffic" = "✅ ¿Confirmar Reinicio de Tráfico?" +"confirmClearIps" = "✅ ¿Confirmar Limpiar IPs?" +"confirmRemoveTGUser" = "✅ ¿Confirmar Eliminar Usuario de Telegram?" +"confirmToggle" = " ✅ ¿Confirmar habilitar/deshabilitar usuario?" +"dbBackup" = "Obtener Copia de Seguridad de BD" +"serverUsage" = "Uso del Servidor" +"getInbounds" = "Obtener Entradas" +"depleteSoon" = "Pronto se Agotará" +"clientUsage" = "Obtener Uso" +"onlines" = "Clientes en línea" +"commands" = "Comandos" +"refresh" = "🔄 Actualizar" +"clearIPs" = "❌ Limpiar IPs" +"removeTGUser" = "❌ Eliminar Usuario de Telegram" +"selectTGUser" = "👤 Seleccionar Usuario de Telegram" +"selectOneTGUser" = "👤 Selecciona un usuario de telegram:" +"resetTraffic" = "📈 Reiniciar Tráfico" +"resetExpire" = "📅 Cambiar fecha de Vencimiento" +"ipLog" = "🔢 Registro de IP" +"ipLimit" = "🔢 Límite de IP" +"setTGUser" = "👤 Establecer Usuario de Telegram" +"toggle" = "🔘 Habilitar / Deshabilitar" +"custom" = "🔢 Costumbre" +"confirmNumber" = "✅ Confirmar: {{ .Num }}" +"confirmNumberAdd" = "✅ Confirmar agregando: {{ .Num }}" +"limitTraffic" = "🚧 Límite de tráfico" +"getBanLogs" = "Registros de prohibición" + +[tgbot.answers] +"successfulOperation" = "✅ ¡Exitosa!" +"errorOperation" = "❗ Error en la Operación." +"getInboundsFailed" = "❌ Error al obtener las entradas" +"canceled" = "❌ {{ .Email }} : Operación cancelada." +"clientRefreshSuccess" = "✅ {{ .Email }} : Cliente actualizado exitosamente." +"IpRefreshSuccess" = "✅ {{ .Email }} : IPs actualizadas exitosamente." +"TGIdRefreshSuccess" = "✅ {{ .Email }} : Usuario de Telegram del cliente actualizado exitosamente." +"resetTrafficSuccess" = "✅ {{ .Email }} : Tráfico reiniciado exitosamente." +"setTrafficLimitSuccess" = "✅ {{ .Email }} : Límite de Tráfico guardado exitosamente." +"expireResetSuccess" = "✅ {{ .Email }} : Días de vencimiento reiniciados exitosamente." +"resetIpSuccess" = "✅ {{ .Email }} : Límite de IP {{ .Count }} guardado exitosamente." +"clearIpSuccess" = "✅ {{ .Email }} : IPs limpiadas exitosamente." +"getIpLog" = "✅ {{ .Email }} : Obtener Registro de IP." +"getUserInfo" = "✅ {{ .Email }} : Obtener Información de Usuario de Telegram." +"removedTGUserSuccess" = "✅ {{ .Email }} : Usuario de Telegram eliminado exitosamente." +"enableSuccess" = "✅ {{ .Email }} : Habilitado exitosamente." +"disableSuccess" = "✅ {{ .Email }} : Deshabilitado exitosamente." +"askToAddUserId" = "¡No se encuentra su configuración!\r\nPor favor, pídale a su administrador que use su ID de usuario de Telegram en su(s) configuración(es).\r\n\r\nSu ID de usuario: {{ .TgUserID }}" \ No newline at end of file From 5ba9d6e1186bd03575078b492f1978548662b746 Mon Sep 17 00:00:00 2001 From: somebodywashere <68244480+somebodywashere@users.noreply.github.com> Date: Tue, 5 Mar 2024 16:39:20 +0300 Subject: [PATCH 06/55] [IPLimit] Optimize + Debian 12 compability --- web/job/check_client_ip_job.go | 82 ++++++++++------------------------ web/job/clear_logs_job.go | 11 +++-- x-ui.sh | 6 +++ xray/process.go | 6 +++ 4 files changed, 43 insertions(+), 62 deletions(-) diff --git a/web/job/check_client_ip_job.go b/web/job/check_client_ip_job.go index c38112f6..16aa24e3 100644 --- a/web/job/check_client_ip_job.go +++ b/web/job/check_client_ip_job.go @@ -10,10 +10,8 @@ import ( "regexp" "sort" "strings" - "sync" "time" - "x-ui/config" "x-ui/database" "x-ui/database/model" "x-ui/logger" @@ -21,17 +19,11 @@ import ( ) type CheckClientIpJob struct { + lastClear int64 disAllowedIps []string } var job *CheckClientIpJob -var ipFiles = []string{ - xray.GetIPLimitLogPath(), - xray.GetIPLimitBannedLogPath(), - xray.GetIPLimitBannedPrevLogPath(), - xray.GetAccessPersistentLogPath(), - xray.GetAccessPersistentPrevLogPath(), -} func NewCheckClientIpJob() *CheckClientIpJob { job = new(CheckClientIpJob) @@ -39,54 +31,43 @@ func NewCheckClientIpJob() *CheckClientIpJob { } func (j *CheckClientIpJob) Run() { - var wg sync.WaitGroup - - if j.checkFail2BanInstalled() { - j.openLogFiles(ipFiles) + if j.lastClear == 0 { + j.lastClear = time.Now().Unix() } + f2bInstalled := j.checkFail2BanInstalled() + accessLogPath := xray.GetAccessLogPath() + clearAccessLog := false + if j.hasLimitIp() { - if j.checkFail2BanInstalled() && xray.GetAccessLogPath() == "./access.log" { - j.processLogFile() + if f2bInstalled && accessLogPath == "./access.log" { + clearAccessLog = j.processLogFile() } else { - if !j.checkFail2BanInstalled() { + if !f2bInstalled { logger.Warning("fail2ban is not installed. IP limiting may not work properly.") } - switch xray.GetAccessLogPath() { + switch accessLogPath { case "none": logger.Warning("Access log is set to 'none', check your Xray Configs") case "": logger.Warning("Access log doesn't exist in your Xray Configs") + default: + logger.Warning("Current access.log path is not compatible with IP Limit") } } } - if !j.checkFail2BanInstalled() && xray.GetAccessLogPath() == "./access.log" { - wg.Add(1) - go func() { - defer wg.Done() - j.clearLogTime() - }() - wg.Wait() - } -} - -func (j *CheckClientIpJob) clearLogTime() { - ticker := time.NewTicker(time.Hour) - defer ticker.Stop() - - for range ticker.C { + if clearAccessLog || time.Now().Unix() - j.lastClear > 3600 { j.clearAccessLog() } } func (j *CheckClientIpJob) clearAccessLog() { - accessLogPath := xray.GetAccessLogPath() - logAccessP, err := os.OpenFile(xray.GetAccessPersistentLogPath(), os.O_CREATE|os.O_APPEND|os.O_RDWR, 0644) + logAccessP, err := os.OpenFile(xray.GetAccessPersistentLogPath(), os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644) j.checkError(err) - defer logAccessP.Close() // reopen the access log file for reading + accessLogPath := xray.GetAccessLogPath() file, err := os.Open(accessLogPath) j.checkError(err) @@ -95,12 +76,13 @@ func (j *CheckClientIpJob) clearAccessLog() { j.checkError(err) // close the file after copying content + logAccessP.Close() file.Close() // clean access log err = os.Truncate(accessLogPath, 0) j.checkError(err) - + j.lastClear = time.Now().Unix() } func (j *CheckClientIpJob) hasLimitIp() bool { @@ -139,22 +121,11 @@ func (j *CheckClientIpJob) checkFail2BanInstalled() bool { return err == nil } -func (j *CheckClientIpJob) openLogFiles(ipFiles []string) { - for i := 0; i < len(ipFiles); i++ { - err := os.MkdirAll(config.GetLogFolder(), 0770) - j.checkError(err) - file, err := os.OpenFile(ipFiles[i], os.O_CREATE|os.O_APPEND|os.O_RDWR, 0644) - j.checkError(err) - defer file.Close() - } -} - -func (j *CheckClientIpJob) processLogFile() { +func (j *CheckClientIpJob) processLogFile() bool { accessLogPath := xray.GetAccessLogPath() file, err := os.Open(accessLogPath) j.checkError(err) - defer file.Close() InboundClientIps := make(map[string][]string) @@ -190,6 +161,7 @@ func (j *CheckClientIpJob) processLogFile() { } j.checkError(scanner.Err()) + file.Close() shouldCleanLog := false @@ -203,12 +175,7 @@ func (j *CheckClientIpJob) processLogFile() { } } - // added delay before cleaning logs to reduce chance of logging IP that already has been banned - time.Sleep(time.Second * 2) - - if shouldCleanLog { - j.clearAccessLog() - } + return shouldCleanLog } func (j *CheckClientIpJob) checkError(e error) { @@ -286,7 +253,7 @@ func (j *CheckClientIpJob) updateInboundClientIps(inboundClientIps *model.Inboun j.disAllowedIps = []string{} // create iplimit log file channel - logIpFile, err := os.OpenFile(xray.GetIPLimitLogPath(), os.O_CREATE|os.O_APPEND|os.O_RDWR, 0644) + logIpFile, err := os.OpenFile(xray.GetIPLimitLogPath(), os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644) if err != nil { logger.Errorf("failed to create or open ip limit log file: %s", err) } @@ -319,9 +286,8 @@ func (j *CheckClientIpJob) updateInboundClientIps(inboundClientIps *model.Inboun db := database.GetDB() err = db.Save(inboundClientIps).Error - if err != nil { - return shouldCleanLog - } + j.checkError(err) + return shouldCleanLog } diff --git a/web/job/clear_logs_job.go b/web/job/clear_logs_job.go index c6312006..c4cb3c87 100644 --- a/web/job/clear_logs_job.go +++ b/web/job/clear_logs_job.go @@ -1,6 +1,7 @@ package job import ( + "io" "os" "x-ui/logger" "x-ui/xray" @@ -28,21 +29,23 @@ func (j *ClearLogsJob) Run() { for i := 0; i < len(logFiles); i++ { if i > 0 { // copy to previous logs - logFilePrev, err := os.OpenFile(logFilesPrev[i-1], os.O_CREATE|os.O_APPEND|os.O_RDWR, 0644) + logFilePrev, err := os.OpenFile(logFilesPrev[i-1], os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644) if err != nil { logger.Warning("clear logs job err:", err) } - logFile, err := os.ReadFile(logFiles[i]) + logFile, err := os.Open(logFiles[i]) if err != nil { logger.Warning("clear logs job err:", err) } - _, err = logFilePrev.Write(logFile) + _, err = io.Copy(logFilePrev, logFile) if err != nil { logger.Warning("clear logs job err:", err) } - defer logFilePrev.Close() + + logFile.Close() + logFilePrev.Close() } err := os.Truncate(logFiles[i], 0) diff --git a/x-ui.sh b/x-ui.sh index 91efe7cf..a94852f2 100644 --- a/x-ui.sh +++ b/x-ui.sh @@ -953,9 +953,15 @@ create_iplimit_jails() { # Uncomment 'allowipv6 = auto' in fail2ban.conf sed -i 's/#allowipv6 = auto/allowipv6 = auto/g' /etc/fail2ban/fail2ban.conf + #On Debian 12+ fail2ban's default backend should be changed to systemd + if [[ "${release}" == "debian" && ${os_version} -ge 12 ]]; then + sed -i '0,/action =/s/backend = auto/backend = systemd/' /etc/fail2ban/jail.conf + fi + cat << EOF > /etc/fail2ban/jail.d/3x-ipl.conf [3x-ipl] enabled=true +backend=auto filter=3x-ipl action=3x-ipl logpath=${iplimit_log_path} diff --git a/xray/process.go b/xray/process.go index e37a0649..03d2eced 100644 --- a/xray/process.go +++ b/xray/process.go @@ -202,6 +202,12 @@ func (p *process) Start() (err error) { if err != nil { return common.NewErrorf("Failed to generate xray configuration file: %v", err) } + + err = os.MkdirAll(config.GetLogFolder(), 0770) + if err != nil { + logger.Warningf("Something went wrong: %s", err) + } + configPath := GetConfigPath() err = os.WriteFile(configPath, data, fs.ModePerm) if err != nil { From 34ab6ed7eeb2fdaac27adac45a2ee74747fe304c Mon Sep 17 00:00:00 2001 From: somebodywashere <68244480+somebodywashere@users.noreply.github.com> Date: Tue, 5 Mar 2024 17:20:00 +0300 Subject: [PATCH 07/55] [IPLimit] Added check for accessLogPath --- web/job/check_client_ip_job.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/job/check_client_ip_job.go b/web/job/check_client_ip_job.go index 16aa24e3..344160e1 100644 --- a/web/job/check_client_ip_job.go +++ b/web/job/check_client_ip_job.go @@ -57,7 +57,7 @@ func (j *CheckClientIpJob) Run() { } } - if clearAccessLog || time.Now().Unix() - j.lastClear > 3600 { + if clearAccessLog || accessLogPath == "./access.log" && time.Now().Unix() - j.lastClear > 3600 { j.clearAccessLog() } } From 6f807823b985cd0bf36cf00b3df2f39c0d63bb74 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 6 Mar 2024 20:36:23 +0330 Subject: [PATCH 08/55] Bump google.golang.org/grpc from 1.62.0 to 1.62.1 (#1987) Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.62.0 to 1.62.1. - [Release notes](https://github.com/grpc/grpc-go/releases) - [Commits](https://github.com/grpc/grpc-go/compare/v1.62.0...v1.62.1) --- updated-dependencies: - dependency-name: google.golang.org/grpc dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index f6566e55..f8f29685 100644 --- a/go.mod +++ b/go.mod @@ -17,7 +17,7 @@ require ( github.com/xtls/xray-core v1.8.8 go.uber.org/atomic v1.11.0 golang.org/x/text v0.14.0 - google.golang.org/grpc v1.62.0 + google.golang.org/grpc v1.62.1 gorm.io/driver/sqlite v1.5.5 gorm.io/gorm v1.25.7 ) diff --git a/go.sum b/go.sum index 5a92f611..e793ac17 100644 --- a/go.sum +++ b/go.sum @@ -415,8 +415,8 @@ google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmE google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.62.0 h1:HQKZ/fa1bXkX1oFOvSjmZEUL8wLSaZTjCcLAlmZRtdk= -google.golang.org/grpc v1.62.0/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE= +google.golang.org/grpc v1.62.1 h1:B4n+nfKzOICUXMgyrNd19h/I9oH0L1pizfk1d4zSgTk= +google.golang.org/grpc v1.62.1/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= From 05deb89451b53b57b6ebe90f6b6df6a0b98bd156 Mon Sep 17 00:00:00 2001 From: Steven <1066106590@qq.com> Date: Sat, 9 Mar 2024 20:49:15 +0800 Subject: [PATCH 09/55] update translation-zh (#1999) --- web/translation/translate.zh_Hans.toml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/web/translation/translate.zh_Hans.toml b/web/translation/translate.zh_Hans.toml index feb8a6ee..16ec5add 100644 --- a/web/translation/translate.zh_Hans.toml +++ b/web/translation/translate.zh_Hans.toml @@ -214,13 +214,13 @@ "request" = "要求" "response" = "回复" "name" = "姓名" -"value" = "价值" +"value" = "值" [pages.inbounds.stream.tcp] "version" = "版本" "method" = "方法" -"path" = "小路" -"status" = "地位" +"path" = "路径" +"status" = "状态" "statusDescription" = "状态说明" "requestHeader" = "请求头" "responseHeader" = "响应头" @@ -234,7 +234,7 @@ "infoDesc" = "此处的所有更改都需要保存并重启面板才能生效" "restartPanel" = "重启面板" "restartPanelDesc" = "确定要重启面板吗?点击确定将于 3 秒后重启,若重启后无法访问面板,请前往服务器查看面板日志信息" -"actions" = "动作" +"actions" = "操作" "resetDefaultConfig" = "重置为默认配置" "panelSettings" = "面板配置" "securitySettings" = "安全设定" @@ -276,7 +276,7 @@ "tgNotifyBackupDesc" = "正在发送数据库备份文件和报告通知" "tgNotifyLogin" = "登录通知" "tgNotifyLoginDesc" = "当有人试图登录您的面板时显示用户名、IP 地址和时间" -"sessionMaxAge" = "会话最大年龄" +"sessionMaxAge" = "会话最大时间" "sessionMaxAgeDesc" = "您可以保持登录状态的时间(单位:分钟)" "expireTimeDiff" = "耗尽时间阈值" "expireTimeDiffDesc" = "到期前检测耗尽(单位:天)" @@ -426,7 +426,7 @@ "dest" = "目的地" "inbound" = "入站" "outbound" = "出站" -"balancer" = "平衡器" +"balancer" = "负载均衡" "info" = "信息" "add" = "添加规则" "edit" = "编辑规则" @@ -440,7 +440,7 @@ "tag" = "标签" "tagDesc" = "唯一标记" "address" = "地址" -"reverse" = "反转" +"reverse" = "反向" "domain" = "域名" "type" = "类型" "bridge" = "桥" @@ -448,9 +448,9 @@ "intercon" = "互连" [pages.xray.balancer] -"addBalancer" = "添加平衡器" -"editBalancer" = "编辑平衡器" -"balancerStrategy" = "战略" +"addBalancer" = "添加负载均衡" +"editBalancer" = "编辑负载均衡" +"balancerStrategy" = "策略" "balancerSelectors" = "选择器" "tag" = "标签" "tagDesc" = "唯一标记" From ffa531f9cac29802af8778404679d6112b7a6b97 Mon Sep 17 00:00:00 2001 From: Tara Rostami <132676256+TaraRostami@users.noreply.github.com> Date: Sat, 9 Mar 2024 16:36:16 +0330 Subject: [PATCH 10/55] Minor Fixes (#1992) autocomplete max - min for input-number --- web/assets/css/custom.css | 21 ++++++++++++++++++- web/html/login.html | 24 +++++++++------------- web/html/xui/common_sider.html | 26 ++---------------------- web/html/xui/component/setting.html | 4 ++-- web/html/xui/component/themeSwitch.html | 12 ++++++++--- web/html/xui/form/inbound.html | 2 +- web/html/xui/form/stream/stream_kcp.html | 12 +++++------ web/html/xui/inbounds.html | 2 +- web/html/xui/settings.html | 14 ++++++------- 9 files changed, 58 insertions(+), 59 deletions(-) diff --git a/web/assets/css/custom.css b/web/assets/css/custom.css index 4ab6c5a8..506a0439 100644 --- a/web/assets/css/custom.css +++ b/web/assets/css/custom.css @@ -80,6 +80,11 @@ html[data-theme='ultra-dark'] { .dark .waves-header { background-color: #0a2227; } + .dark .ant-calendar-year-panel-year:hover, + .dark .ant-calendar-month-panel-month:hover, + .dark .ant-calendar-decade-panel-decade:hover { + background-color: var(--dark-color-surface-600); + } } html, @@ -175,7 +180,7 @@ style attribute { position: relative; clear: both; } -.ant-table-body { +.ant-table-wrapper > div > div > div > div > div > div { overflow-x: auto !important; } .ant-card-hoverable { @@ -791,6 +796,15 @@ style attribute { border-color: var(--dark-color-surface-500); } +@media (max-width: 768px) { + .dark .ant-popover-inner { + background-color: var(--dark-color-surface-200); + } + .dark > .ant-popover-content > .ant-popover-arrow { + border-color: var(--dark-color-surface-200); + } +} + .ant-dropdown-menu-dark .ant-dropdown-menu-item:hover, .dark .ant-select-dropdown-menu-item-selected, .dark .ant-calendar-time-picker-select-option-selected { @@ -1257,3 +1271,8 @@ b, strong { .ant-empty-small .ant-empty-image { height: 20px; } + +.ant-menu-theme-switch:hover { + background-color: transparent !important; + cursor: default !important; +} diff --git a/web/html/login.html b/web/html/login.html index 7655c66f..dfb01cea 100644 --- a/web/html/login.html +++ b/web/html/login.html @@ -374,6 +374,12 @@ transform: translateZ(-100px); } } + .ant-menu-item .anticon { + margin-right: 4px; + } + .ant-menu-inline .ant-menu-item { + padding: 0 16px !important; + } @@ -410,19 +416,19 @@ - - - @@ -455,17 +461,7 @@ - -   - - - - - Ultra - - + diff --git a/web/html/xui/common_sider.html b/web/html/xui/common_sider.html index 78b833b9..bc8f4608 100644 --- a/web/html/xui/common_sider.html +++ b/web/html/xui/common_sider.html @@ -24,18 +24,7 @@ {{define "commonSider"}} - - - - - - - Ultra - - - + {{template "menuItems" .}} @@ -49,18 +38,7 @@
- - - - - - - Ultra - - - + {{template "menuItems" .}} diff --git a/web/html/xui/component/setting.html b/web/html/xui/component/setting.html index 82c0ae75..8adc000c 100644 --- a/web/html/xui/component/setting.html +++ b/web/html/xui/component/setting.html @@ -16,7 +16,7 @@