ITEM6 B4: frontend reshaping — xray page retired, stats switches to settings, template read paths closed

- Delete the xray settings page (routes/menu/title), geodata UI/service,
  XraySettingController/Service and routing-sync write paths; the stored
  xrayTemplateConfig row loses its last readers (GetXrayConfigTemplate now
  returns the embedded factory template; B1 already deleted the row).
- Settings GeneralTab gains the four trafficStatsSwitches switches
  (settings.trafficStatsSwitches, B1) with the dual warning copy; entity
  AllSetting carries the key end to end (TestTrafficStatsSwitchesPlumbing).
- Tag-directory consumers (panelOutbound, mtproto egress, node/host
  outbound pickers) move from POST /xray/ to GET /server/outboundDirectory.
- Shared Mux/Sockopt forms and balancer helpers relocated to lib/xray;
  converter preserves sniffing ips/domains exclusions into the panel
  sidecar instead of dropping them (B2 gap closed by retention).
- Freedom final-rules DB fix seeders retired; getDefaultJsonConfig
  endpoint removed (no in-repo consumer); locales pruned to referenced
  keys (pages.xray 331 -> 68) plus the five new settings keys.
This commit is contained in:
Muse
2026-10-22 23:40:54 +00:00
parent 1762e54d5f
commit 277838d7ca
151 changed files with 780 additions and 22140 deletions
+10 -297
View File
@@ -98,9 +98,6 @@
"clients": "Clientes",
"nodes": "Nodos",
"settings": "Ajustes del panel",
"xray": "Configuración Xray",
"routing": "Enrutamiento",
"outbounds": "Salidas",
"donate": "Donar",
"hosts": "Hosts",
"docs": "Documentación",
@@ -272,7 +269,6 @@
"clone": "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?",
"email": "Email",
"IPLimit": "Límite de IP",
"IPLimitlog": "Registro de IP",
@@ -1211,269 +1207,29 @@
"tgNotifyBackup": "Respaldo de Base de Datos",
"tgNotifyBackupDesc": "Incluir archivo de respaldo de base de datos con notificación de informe.",
"tgTestFailed": "La prueba de Telegram falló",
"tgTestSuccess": "Mensaje de prueba enviado a Telegram"
"tgTestSuccess": "Mensaje de prueba enviado a Telegram",
"trafficStatsInboundUplink": "Inbound Upload Accounting",
"trafficStatsInboundDownlink": "Inbound Download Accounting",
"trafficStatsOutboundUplink": "Outbound Upload Accounting",
"trafficStatsOutboundDownlink": "Outbound Download Accounting",
"trafficStatsDesc": "Applies to the whole network. A direction pair (inbound or outbound) is counted only while both of its switches are on. While counting is off, that traffic is never recorded and the gap is permanent — it is not backfilled later — and expiry / over-quota auto-disable loses the data it relies on."
},
"xray": {
"save": "Guardar configuración",
"basicTemplate": "Perfil Básico",
"advancedTemplate": "Perfil Avanzado",
"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.",
"basicRouting": "Enrutamiento Básico",
"blockConnectionsConfigsDesc": "Estas opciones bloquearán el tráfico según el país solicitado específico.",
"directConnectionsConfigsDesc": "Una conexión directa asegura que el tráfico específico no sea enrutado a través de otro servidor.",
"blockips": "Bloquear IPs",
"blockdomains": "Bloquear Dominios",
"directips": "IPs Directas",
"directdomains": "Dominios Directos",
"ipv4Routing": "Enrutamiento IPv4",
"ipv4RoutingDesc": "Estas opciones solo enrutarán a los dominios objetivo a través de IPv4.",
"FreedomStrategy": "Configurar Estrategia para el Protocolo Freedom",
"FreedomStrategyDesc": "Establece la estrategia de salida de la red en el Protocolo Freedom.",
"FreedomHappyEyeballs": "Freedom Happy Eyeballs (IPv4/IPv6)",
"FreedomHappyEyeballsDesc": "Marcado de doble pila para la salida directa (freedom): útil en servidores de salida con IPv4 e IPv6.",
"FreedomHappyEyeballsTryDelayDesc": "Milisegundos antes de probar la otra familia de direcciones. 150–250 ms es un buen punto de partida.",
"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",
"Inbounds": "Entradas",
"Outbounds": "Salidas",
"Balancers": "Equilibradores",
"balancerTagRequired": "La etiqueta es obligatoria",
"balancerSelectorRequired": "Elige al menos una salida",
"routeTester": "Prueba de ruta",
"routeTesterDesc": "Pregunta al Xray en ejecución qué salida gestionaría una conexión. No se envía tráfico real — la decisión viene directamente del motor de enrutamiento en vivo.",
"routeTesterDest": "Dominio o IP",
"routeTesterPort": "Puerto",
"routeTesterInbound": "Entrante",
"routeTesterProtocol": "Protocolo detectado",
"routeTesterTest": "Probar ruta",
"routeTesterMatchedOutbound": "Salida coincidente",
"routeTesterViaBalancer": "vía balanceador",
"routeTesterDefaultOutbound": "Ninguna regla de enrutamiento coincidió — el tráfico va a la salida predeterminada (primera).",
"Routings": "Reglas de enrutamiento",
"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.",
"dnsLog": "Registro DNS",
"dnsLogDesc": "Si habilitar los registros de consulta DNS",
"maskAddress": "Enmascarar Dirección",
"maskAddressDesc": "Máscara de dirección IP, cuando se habilita, reemplazará automáticamente la dirección IP que aparece en el registro.",
"statistics": "Estadísticas",
"statsInboundUplink": "Estadísticas de Subida de Entrada",
"statsInboundDownlink": "Estadísticas de Bajada de Entrada",
"statsOutboundUplink": "Estadísticas de Subida de Salida",
"statsOutboundDownlink": "Estadísticas de Bajada de Salida",
"connectionLimits": "Límites de conexión",
"connectionLimitsDesc": "Políticas a nivel de conexión para el nivel de usuario 0. Deja un campo vacío para usar el valor predeterminado de Xray.",
"connIdle": "Tiempo de inactividad",
"connIdleDesc": "Cierra una conexión después de que permanezca inactiva durante esta cantidad de segundos. Reducirlo libera memoria y descriptores de archivo más rápido en servidores con mucha carga (predeterminado de Xray: 300).",
"bufferSize": "Tamaño del búfer",
"bufferSizeDesc": "Tamaño del búfer interno por conexión en KB. Ponlo en 0 para minimizar el uso de memoria en servidores con poca RAM (el valor predeterminado de Xray depende de la plataforma).",
"bufferSizePlaceholder": "automático",
"seconds": "segundos",
"importRules": "Importar reglas",
"exportRules": "Exportar reglas",
"importOutbounds": "Importar salidas",
"exportOutbounds": "Exportar salidas",
"importInvalidJson": "JSON no válido — se esperaba un array o un objeto con una clave coincidente.",
"metricsListen": "Punto de métricas",
"metricsListenDesc": "Expone las métricas estilo Prometheus de Xray en esta dirección:puerto (por ejemplo, 127.0.0.1:11111). Déjalo vacío para deshabilitarlo. Vincúlalo a localhost y ponlo tras un proxy inverso — no está autenticado.",
"metricsTag": "Etiqueta de métricas",
"rules": {
"source": "Fuente",
"dest": "Destino",
"inbound": "Entrante",
"useComma": "Elementos separados por comas"
},
"routing": {
"dragToReorder": "Arrastra para reordenar"
},
"geoBrowser": {
"title": "Categorías geo",
"openTooltip": "Explorar categorías geo",
"database": "Base de datos",
"searchCategory": "Buscar categoría",
"searchEntries": "Filtrar dentro de la categoría",
"selectFound": "Seleccionar encontradas",
"selected": "Seleccionadas: {count}",
"clearAll": "Limpiar todo",
"apply": "Aplicar",
"emptySelection": "Marca categorías: se convertirán en tokens de la regla",
"pickCategory": "Elige una categoría a la izquierda para ver su contenido",
"noMatches": "No se encontró nada",
"noFiles": "No hay bases geo en la carpeta de Xray",
"noFilesHint": "Aparecerán cuando Xray descargue geosite.dat y geoip.dat",
"fileMeta": "{count} categorías · {size} · actualizado {date}",
"entriesCount": "{count} entradas",
"subnetsCount": "{count} subredes",
"shownRange": "Mostrando {from}–{to} de {total}",
"loadFailed": "No se pudieron cargar las bases geo",
"checkFailed": "No se pudieron verificar estos valores con las bases geo",
"parseFailed": "Archivo dañado o no es una base geosite/geoip",
"tooLarge": "Demasiado grande para explorarlo",
"unknownCategories": "No están en la base: {tokens}",
"missingDatabase": "No se encontró el archivo de la base: {tokens} — añádelo en la sección Geodata",
"unknownAttribute": "Atributo no encontrado, la regla no coincidirá con nada: {tokens}",
"invalidToken": "Xray no aceptará esta entrada: {tokens}",
"wrongKind": "Tipo de base incorrecto para este campo: {tokens}"
},
"ruleForm": {
"sourceIps": "IPs de origen",
"sourcePort": "Puerto de origen",
"vlessRoute": "Ruta VLESS",
"attributes": "Atributos",
"value": "Valor",
"user": "Usuario",
"userPlaceholder": "Seleccionar usuarios",
"userEmpty": "No hay usuarios disponibles",
"userLoadError": "No se pudieron cargar los usuarios",
"inboundTags": "Etiquetas de entradas",
"outboundTag": "Etiqueta de salida",
"balancerTag": "Etiqueta de balanceador",
"balancerTagTooltip": "Enruta el tráfico a través de uno de los balanceadores configurados"
"inbound": "Entrante"
},
"outboundForm": {
"tagDuplicate": "Etiqueta ya usada por otra salida",
"tagRequired": "La etiqueta es obligatoria",
"tagPlaceholder": "etiqueta-única",
"localIpPlaceholder": "IP local",
"dialerProxyPlaceholder": "Selecciona una salida para encadenar",
"dialerProxyHint": "Conecta esta salida a través de otra salida (por etiqueta) para crear una cadena de proxy. Déjalo vacío para conectar directamente.",
"targetStrategyHint": "Cómo se resuelve el dominio de destino antes de conectar: AsIs (predeterminado) lo envía sin resolver, UseIP… resuelve con respaldo, ForceIP… exige resolución.",
"addressRequired": "La dirección es obligatoria",
"portRequired": "El puerto es obligatorio",
"optional": "opcional",
"udpOverTcp": "UDP sobre TCP",
"uotVersion": "Versión UoT",
"inboundTag": "Etiqueta de entrada",
"inboundTagPlaceholder": "etiqueta de entrada usada en reglas de enrutamiento",
"responseType": "Tipo de respuesta",
"rewriteNetwork": "Reescribir red",
"unchanged": "(sin cambios)",
"unchangedAddress": "(sin cambios) p. ej. 1.1.1.1",
"rules": "Reglas",
"ruleN": "Regla {n}",
"action": "Acción",
"redirect": "Redirect",
"finalRules": "Reglas finales",
"overrideXrayPrivateIp": "Sobrescribir el bloqueo de IP privada por defecto de Xray",
"blockDelay": "Retraso de bloqueo (ms)",
"reverseSniffing": "Sniffing inverso",
"reserved": "Reservado",
"minUploadInterval": "Intervalo mín. de subida (ms)",
"maxUploadSizeBytes": "Tamaño máx. de subida (bytes)",
"uplinkChunkSize": "Tamaño de chunk Uplink",
"noGrpcHeader": "Sin cabecera gRPC",
"maxConcurrency": "Máx. concurrencia",
"maxConnections": "Máx. conexiones",
"maxReuseTimes": "Máx. reutilizaciones",
"maxRequestTimes": "Máx. peticiones",
"maxReusableSecs": "Máx. segundos reutilizables",
"keepAlivePeriod": "Periodo keep alive",
"authPassword": "Contraseña de auth",
"visionTestpre": "Vision testpre",
"serverNamePlaceholder": "nombre del servidor",
"verifyPeerName": "Verificar nombre del peer",
"pinnedSha256": "SHA256 pinned",
"shortId": "Short ID",
"sockopts": "Sockopts",
"keepAliveInterval": "Intervalo keep alive",
"markFwmark": "Mark (fwmark)",
"interface": "Interfaz",
"proxyProtocol": "Proxy protocol",
"tcpUserTimeoutMs": "TCP user timeout (ms)",
"tcpKeepAliveIdleS": "TCP keep-alive idle (s)"
},
"outbound": {
"tag": "Etiqueta",
"outboundStatus": "Estado de Salida",
"sendThrough": "Enviar a través de",
"targetStrategy": "Estrategia de destino",
"country": "País",
"server": "Servidor",
"city": "Ciudad",
"allCities": "Todas las ciudades",
"moveToTop": "Mover al principio"
},
"outboundSub": {
"manage": "Suscripciones",
"title": "Suscripciones de salida",
"remark": "Notas (opcional)",
"remarkPlaceholder": "p. ej. nodos HK",
"url": "URL de suscripción",
"urlPlaceholder": "https://... (lista de enlaces en base64)",
"tagPrefix": "Prefijo de etiqueta",
"tagPrefixPlaceholder": "hk-",
"interval": "Intervalo de actualización",
"hours": "h",
"minutes": "min",
"intervalHint": "Por defecto 10 minutos. La tarea en segundo plano comprueba con frecuencia; cada suscripción solo vuelve a descargarse cuando ha transcurrido su propio intervalo.",
"enabled": "Habilitado",
"allowPrivate": "Permitir direcciones privadas",
"allowPrivateHint": "Permite localhost, la red local (LAN) y las IP privadas en la URL de esta suscripción. Desactivado por defecto por seguridad; actívalo solo para una fuente local de confianza.",
"prepend": "Antes de las salidas manuales",
"prependHint": "Coloca las salidas de esta suscripción antes de las configuradas manualmente, de modo que una de ellas pueda convertirse en la predeterminada.",
"preview": "Vista previa",
"previewEmpty": "No se encontraron salidas en esta URL.",
"refreshAll": "Actualizar todo",
"statusOk": "Correcto",
"toastUpdated": "Suscripción actualizada",
"addButton": "Añadir",
"active": "Suscripciones activas",
"empty": "Aún no hay suscripciones. Añade una arriba.",
"colRemark": "Notas",
"colLastFetch": "Última descarga",
"colEnabled": "Habilitado",
"auto": "auto",
"never": "nunca",
"refreshNow": "Actualizar ahora",
"deleteConfirm": "¿Eliminar esta suscripción?",
"restartHint": "Después de añadir o actualizar, reinicia Xray (o espera a la próxima recarga automática) para activar las salidas.",
"fromSubsTitle": "Desde suscripciones de salida (solo lectura)",
"fromSubsDesc": "Importadas desde tus suscripciones activas. Gestiónalas en el panel de Suscripciones de arriba.",
"toastLoadFailed": "No se pudieron cargar las suscripciones",
"toastUrlRequired": "La URL de suscripción es obligatoria",
"toastAdded": "Suscripción añadida",
"toastAddFailed": "No se pudo añadir la suscripción",
"toastRefreshed": "Actualizada",
"toastRefreshFailed": "Error al actualizar",
"toastDeleted": "Eliminada",
"toastDeleteFailed": "Error al eliminar"
},
"pia": {
"menu": "PIA",
"username": "Usuario de PIA",
"password": "Contraseña de PIA",
"account": "Cuenta",
"region": "Región",
"allRegions": "Todas las regiones",
"noServers": "No hay servidores para el país seleccionado",
"outboundAdded": "Salida PIA añadida",
"outboundUpdated": "Salida PIA actualizada",
"addedServers": "Servidores añadidos",
"alreadyAdded": "Este servidor ya está en la lista de salidas. Usa {reset} para renovar la clave.",
"provisionFailed": "No se pudo crear la salida PIA. Inténtalo de nuevo."
},
"observatory": {
"deleteAlsoObservatory": "Este es el último balanceador que usa el Observatorio, por lo que también se eliminará.",
"deleteAlsoBurst": "Este es el último balanceador que usa el Observatorio Burst, por lo que también se eliminará."
},
"refCleanup": {
"header": "Al eliminar esto también se actualiza tu enrutamiento:",
"ruleRemoved": "Regla {label} — eliminada (sin destino restante)",
"ruleModified": "Regla {label} — conservada (ahora usa {keeps})",
"balancerRemoved": "Balanceador {tag} — eliminado (sin destinos restantes)"
},
"balancer": {
"balancerStrategy": "Estrategia",
"balancerFallbackCycle": "No se puede establecer este balanceador como respaldo — crearía una dependencia circular.",
"reservedPrefix": "El prefijo _bl_ está reservado para objetos loopback internos del balanceador"
"keepAlivePeriod": "Periodo keep alive"
},
"wireguard": {
"secretKey": "Llave secreta",
@@ -1481,7 +1237,6 @@
"subnetIp": "Subred",
"subnetCidr": "CIDR de la subred",
"allowedIPs": "IP permitidas",
"endpoint": "Punto final",
"domainStrategy": "Estrategia de dominio"
},
"amneziawg": {
@@ -1537,48 +1292,7 @@
},
"tun": {
"userLevel": "Nivel de Usuario"
},
"nord": {
"accessToken": "Access token",
"privateKey": "Clave privada",
"noServers": "No se encontraron servidores para el país seleccionado",
"noPublicKey": "El servidor seleccionado no anuncia una clave pública NordLynx.",
"outboundAdded": "Salida NordVPN añadida",
"outboundUpdated": "Salida NordVPN actualizada"
},
"warp": {
"changeIp": "Cambiar IP",
"changeIpSuccess": "¡IP de WARP cambiada correctamente!",
"autoUpdateIp": "Actualizar IP automáticamente",
"intervalDays": "Intervalo (días)",
"intervalDesc": "0 para desactivar. Cambia la dirección IP automáticamente.",
"licenseError": "No se pudo establecer la licencia WARP.",
"fetchFirst": "Obtén primero la configuración WARP.",
"createAccount": "Crear cuenta WARP",
"accessToken": "Access token",
"deviceId": "ID del dispositivo",
"licenseKey": "Clave de licencia",
"privateKey": "Clave privada",
"deleteAccount": "Eliminar cuenta",
"settings": "Ajustes",
"licenseKeyLabel": "Clave de licencia WARP / WARP+",
"key": "Clave",
"keyPlaceholder": "clave WARP+ de 26 caracteres",
"accountInfo": "Información de cuenta",
"deviceName": "Nombre del dispositivo",
"deviceModel": "Modelo del dispositivo",
"deviceEnabled": "Dispositivo habilitado",
"accountType": "Tipo de cuenta",
"role": "Rol",
"warpPlusData": "Datos WARP+",
"quota": "Cuota",
"usage": "Uso",
"addOutbound": "Añadir salida"
},
"dns": {},
"fakedns": {},
"defaultOutbound": "Salida predeterminada",
"defaultOutboundDesc": "El tráfico que no coincide con ninguna regla de enrutamiento usa esta salida (la primera de la lista)."
}
},
"hosts": {
"addHost": "Agregar host",
@@ -1876,6 +1590,5 @@
"chooseClient": "Elige un Cliente para Inbound {{ .Inbound }}",
"chooseInbound": "Elige un Inbound"
}
},
"email": {}
}
}