diff --git a/README-RU.md b/README-RU.md index 1ca1d0b..378874b 100644 --- a/README-RU.md +++ b/README-RU.md @@ -16,6 +16,16 @@ > **ЭТОТ РЕПОЗИТОРИЙ - УЧЕБНЫЙ ПРИМЕР ДЛЯ ИЗУЧЕНИЯ NGINX, РЕВЕРС-ПРОКСИ И ОСНОВ СЕТЕВОЙ БЕЗОПАСНОСТИ. ЭТОТ СКРИПТ ДЕМОНСТРИРУЕТ НАСТРОЙКУ NGINX КАК REVERSE PROXY. НЕ ДЛЯ ПРОДА И НЕ ДЛЯ ПРОДАКШН-ИСПОЛЬЗОВАНИЯ! ЕСЛИ ВЫ НЕ ПОНИМАЕТЕ КАК РАБОТАЕТ ПАНЕЛЬ УПРАВЛЕНИЯ - ЭТО ВАШИ ПРОБЛЕМЫ, А НЕ АВТОРА СКРИПТА. ИСПОЛЬЗУЙТЕ НА СВОЙ СТРАХ И РИСК!** --- +# Это немного переделанный скрипт от eGames. + +## Основные отличия от оригинального скрипта: + +### - Убрана поддержка английского языка + +### - Установка происходит по правильным путям в системе, как если бы ставили по инструкции с сайта RemnaWave. Панель устанавливается в папку /opt/remnawave, а нода в папку /opt/remnanode. + +## Функционал оригинального скрипта не нарушен. + ## Обзор @@ -192,16 +202,4 @@ bash <(curl -Ls https://raw.githubusercontent.com/eGamesAPI/remnawave-reverse-pr > > **Если вы не уверены, нарушает ли использование данного инструмента или его компонентов законодательство вашей страны- откажитесь от любого взаимодействия с данным инструментом.** -## Сообщество - -Присоединяйтесь к нашему Telegram-сообществу для поддержки и обсуждений: - -**Telegram чат**: [https://t.me/remnawave_reverse](https://t.me/remnawave_reverse) - -## Пожертвования - -Если вам нравится этот проект и вы хотите поддержать его дальнейшее развитие, пожалуйста, рассмотрите возможность сделать пожертвование. Ваш вклад помогает финансировать будущие обновления и улучшения! - -**Способы пожертвования:** -- **TON USDT:** `UQAxyZDwKUPQ5Bp09JOFcaDVakjYQT46rf3iP3lnl_qc9xVS` diff --git a/README.md b/README.md index 2e9e837..8030359 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,15 @@ > **THIS REPOSITORY IS AN EDUCATIONAL EXAMPLE FOR LEARNING NGINX, REVERSE PROXY, AND NETWORK SECURITY BASICS. THIS SCRIPT DEMONSTRATES NGINX SETUP AS A REVERSE PROXY. NOT FOR PRODUCTION AND NOT FOR PRODUCTION USE! IF YOU DON'T UNDERSTAND HOW THE CONTROL PANEL WORKS - THAT'S YOUR PROBLEM, NOT THE SCRIPT AUTHOR'S. USE AT YOUR OWN RISK!** --- +# This is a slightly modified script from eGames. + +### The main differences from the original script + +### - Removed English language support + +### - Installation takes place according to the correct paths in the system, as if installed according to the instructions from the RemnaWave website. The panel is installed in the /opt/remnawave folder, and the node is installed in the /opt/remnanode folder. + +### The functionality of the original script is intact. ## Overview @@ -192,16 +201,4 @@ bash <(curl -Ls https://raw.githubusercontent.com/eGamesAPI/remnawave-reverse-pr > > **If you are unsure whether using this tool or its components violates the laws of your country - refrain from any interaction with this tool.** -## Community - -Join our Telegram community for support and discussions: - -**Telegram chat**: [https://t.me/remnawave_reverse](https://t.me/remnawave_reverse) - -## Donations - -If you like this project and want to support its further development, please consider making a donation. Your contribution helps fund future updates and improvements! - -**Donation Methods:** -- **TON USDT:** `UQAxyZDwKUPQ5Bp09JOFcaDVakjYQT46rf3iP3lnl_qc9xVS` diff --git a/docs/astro.config.mjs b/docs/astro.config.mjs index a846c25..81242ee 100644 --- a/docs/astro.config.mjs +++ b/docs/astro.config.mjs @@ -37,6 +37,21 @@ export default defineConfig({ ] }) ], + head: [ + // Plausible Analytics + { + tag: 'script', + attrs: { + async: true, + src: 'https://ps.log.rw/js/pa-TLPk4Xvnye6Dh8hvwvH9n.js', + }, + }, + { + tag: 'script', + content: + 'window.plausible=window.plausible||function(){(plausible.q=plausible.q||[]).push(arguments)},plausible.init=plausible.init||function(i){plausible.o=i||{}};plausible.init()', + }, + ], title: 'Remnawave Reverse-Proxy', logo: { src: './src/assets/logo.webp', diff --git a/docs/package-lock.json b/docs/package-lock.json index 87e22a0..d2a51d4 100644 --- a/docs/package-lock.json +++ b/docs/package-lock.json @@ -1,22 +1,22 @@ { "name": "docs", - "version": "v2.2.2", + "version": "v2.3.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "docs", - "version": "v2.2.2", + "version": "v2.3.3", "dependencies": { - "@astrojs/starlight": "^0.37.0", + "@astrojs/starlight": "^0.37.3", "@lorenzo_lewis/starlight-utils": "^0.3.2", - "astro": "^5.16.4", + "astro": "^5.16.11", "astro-auto-import": "^0.4.4", "sharp": "^0.34.5", "starlight-contributor-list": "^0.3.1", "starlight-github-alerts": "^0.1.1", - "starlight-kbd": "^0.2.1", - "starlight-links-validator": "^0.19.1", + "starlight-kbd": "^0.3.0", + "starlight-links-validator": "^0.19.2", "starlight-package-managers": "^0.11.1", "starlight-scroll-to-top": "^0.4.0", "starlight-showcases": "^0.3.1", @@ -203,9 +203,9 @@ } }, "node_modules/@astrojs/starlight": { - "version": "0.37.0", - "resolved": "https://registry.npmjs.org/@astrojs/starlight/-/starlight-0.37.0.tgz", - "integrity": "sha512-1AlaEjYYRO+5o6P5maPUBQZr6Q3wtuhMQTmsDQExI07wJVwe7EC2wGhXnFo+jpCjwHv/Bdg33PQheY4UhMj01g==", + "version": "0.37.3", + "resolved": "https://registry.npmjs.org/@astrojs/starlight/-/starlight-0.37.3.tgz", + "integrity": "sha512-p7cqbAkBYkBTiK1NIomxAEoF9Wko+mTV503qDm5Wgh+0MGGJnSsIzCSSJ+rWm8toFk9mnzNwNbxcnjwzIBEU3w==", "license": "MIT", "peer": true, "dependencies": { @@ -316,12 +316,12 @@ } }, "node_modules/@capsizecss/unpack": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@capsizecss/unpack/-/unpack-3.0.1.tgz", - "integrity": "sha512-8XqW8xGn++Eqqbz3e9wKuK7mxryeRjs4LOHLxbh2lwKeSbuNR4NFifDZT4KzvjU6HMOPbiNTsWpniK5EJfTWkg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@capsizecss/unpack/-/unpack-4.0.0.tgz", + "integrity": "sha512-VERIM64vtTP1C4mxQ5thVT9fK0apjPFobqybMtA1UdUujWka24ERHbRHFGmpbbhp73MhV+KSsHQH9C6uOTdEQA==", "license": "MIT", "dependencies": { - "fontkit": "^2.0.2" + "fontkitten": "^1.0.0" }, "engines": { "node": ">=18" @@ -1720,60 +1720,60 @@ ] }, "node_modules/@shikijs/core": { - "version": "3.15.0", - "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-3.15.0.tgz", - "integrity": "sha512-8TOG6yG557q+fMsSVa8nkEDOZNTSxjbbR8l6lF2gyr6Np+jrPlslqDxQkN6rMXCECQ3isNPZAGszAfYoJOPGlg==", + "version": "3.20.0", + "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-3.20.0.tgz", + "integrity": "sha512-f2ED7HYV4JEk827mtMDwe/yQ25pRiXZmtHjWF8uzZKuKiEsJR7Ce1nuQ+HhV9FzDcbIo4ObBCD9GPTzNuy9S1g==", "license": "MIT", "dependencies": { - "@shikijs/types": "3.15.0", + "@shikijs/types": "3.20.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "node_modules/@shikijs/engine-javascript": { - "version": "3.15.0", - "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-3.15.0.tgz", - "integrity": "sha512-ZedbOFpopibdLmvTz2sJPJgns8Xvyabe2QbmqMTz07kt1pTzfEvKZc5IqPVO/XFiEbbNyaOpjPBkkr1vlwS+qg==", + "version": "3.20.0", + "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-3.20.0.tgz", + "integrity": "sha512-OFx8fHAZuk7I42Z9YAdZ95To6jDePQ9Rnfbw9uSRTSbBhYBp1kEOKv/3jOimcj3VRUKusDYM6DswLauwfhboLg==", "license": "MIT", "dependencies": { - "@shikijs/types": "3.15.0", + "@shikijs/types": "3.20.0", "@shikijs/vscode-textmate": "^10.0.2", - "oniguruma-to-es": "^4.3.3" + "oniguruma-to-es": "^4.3.4" } }, "node_modules/@shikijs/engine-oniguruma": { - "version": "3.15.0", - "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-3.15.0.tgz", - "integrity": "sha512-HnqFsV11skAHvOArMZdLBZZApRSYS4LSztk2K3016Y9VCyZISnlYUYsL2hzlS7tPqKHvNqmI5JSUJZprXloMvA==", + "version": "3.20.0", + "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-3.20.0.tgz", + "integrity": "sha512-Yx3gy7xLzM0ZOjqoxciHjA7dAt5tyzJE3L4uQoM83agahy+PlW244XJSrmJRSBvGYELDhYXPacD4R/cauV5bzQ==", "license": "MIT", "dependencies": { - "@shikijs/types": "3.15.0", + "@shikijs/types": "3.20.0", "@shikijs/vscode-textmate": "^10.0.2" } }, "node_modules/@shikijs/langs": { - "version": "3.15.0", - "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-3.15.0.tgz", - "integrity": "sha512-WpRvEFvkVvO65uKYW4Rzxs+IG0gToyM8SARQMtGGsH4GDMNZrr60qdggXrFOsdfOVssG/QQGEl3FnJ3EZ+8w8A==", + "version": "3.20.0", + "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-3.20.0.tgz", + "integrity": "sha512-le+bssCxcSHrygCWuOrYJHvjus6zhQ2K7q/0mgjiffRbkhM4o1EWu2m+29l0yEsHDbWaWPNnDUTRVVBvBBeKaA==", "license": "MIT", "dependencies": { - "@shikijs/types": "3.15.0" + "@shikijs/types": "3.20.0" } }, "node_modules/@shikijs/themes": { - "version": "3.15.0", - "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-3.15.0.tgz", - "integrity": "sha512-8ow2zWb1IDvCKjYb0KiLNrK4offFdkfNVPXb1OZykpLCzRU6j+efkY+Y7VQjNlNFXonSw+4AOdGYtmqykDbRiQ==", + "version": "3.20.0", + "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-3.20.0.tgz", + "integrity": "sha512-U1NSU7Sl26Q7ErRvJUouArxfM2euWqq1xaSrbqMu2iqa+tSp0D1Yah8216sDYbdDHw4C8b75UpE65eWorm2erQ==", "license": "MIT", "dependencies": { - "@shikijs/types": "3.15.0" + "@shikijs/types": "3.20.0" } }, "node_modules/@shikijs/types": { - "version": "3.15.0", - "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-3.15.0.tgz", - "integrity": "sha512-BnP+y/EQnhihgHy4oIAN+6FFtmfTekwOLsQbRw9hOKwqgNy8Bdsjq8B05oAt/ZgvIWWFrshV71ytOrlPfYjIJw==", + "version": "3.20.0", + "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-3.20.0.tgz", + "integrity": "sha512-lhYAATn10nkZcBQ0BlzSbJA3wcmL5MXUUF8d2Zzon6saZDlToKaiRX60n2+ZaHJCmXEcZRWNzn+k9vplr8Jhsw==", "license": "MIT", "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", @@ -1786,15 +1786,6 @@ "integrity": "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==", "license": "MIT" }, - "node_modules/@swc/helpers": { - "version": "0.5.17", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.17.tgz", - "integrity": "sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.8.0" - } - }, "node_modules/@types/debug": { "version": "4.1.12", "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", @@ -1819,15 +1810,6 @@ "@types/estree": "*" } }, - "node_modules/@types/fontkit": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/@types/fontkit/-/fontkit-2.0.8.tgz", - "integrity": "sha512-wN+8bYxIpJf+5oZdrdtaX04qUuWHcKxcDEgRS9Qm9ZClSHjzEn13SxUC+5eRM+4yXIeTYk8mTzLAWGF64847ew==", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@types/hast": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", @@ -2098,17 +2080,17 @@ } }, "node_modules/astro": { - "version": "5.16.4", - "resolved": "https://registry.npmjs.org/astro/-/astro-5.16.4.tgz", - "integrity": "sha512-rgXI/8/tnO3Y9tfAaUyg/8beKhlIMltbiC8Q6jCoAfEidOyaue4KYKzbe0gJIb6qEdEaG3Kf3BY3EOSLkbWOLg==", + "version": "5.16.11", + "resolved": "https://registry.npmjs.org/astro/-/astro-5.16.11.tgz", + "integrity": "sha512-Z7kvkTTT5n6Hn5lCm6T3WU6pkxx84Hn25dtQ6dR7ATrBGq9eVa8EuB/h1S8xvaoVyCMZnIESu99Z9RJfdLRLDA==", "license": "MIT", "peer": true, "dependencies": { "@astrojs/compiler": "^2.13.0", "@astrojs/internal-helpers": "0.7.5", - "@astrojs/markdown-remark": "6.3.9", + "@astrojs/markdown-remark": "6.3.10", "@astrojs/telemetry": "3.3.0", - "@capsizecss/unpack": "^3.0.1", + "@capsizecss/unpack": "^4.0.0", "@oslojs/encoding": "^1.1.0", "@rollup/pluginutils": "^5.3.0", "acorn": "^8.15.0", @@ -2118,19 +2100,19 @@ "ci-info": "^4.3.1", "clsx": "^2.1.1", "common-ancestor-path": "^1.0.1", - "cookie": "^1.0.2", + "cookie": "^1.1.1", "cssesc": "^3.0.0", "debug": "^4.4.3", "deterministic-object-hash": "^2.0.2", - "devalue": "^5.5.0", - "diff": "^5.2.0", + "devalue": "^5.6.2", + "diff": "^8.0.3", "dlv": "^1.1.3", "dset": "^3.1.4", "es-module-lexer": "^1.7.0", "esbuild": "^0.25.0", "estree-walker": "^3.0.3", "flattie": "^1.1.1", - "fontace": "~0.3.1", + "fontace": "~0.4.0", "github-slugger": "^2.0.0", "html-escaper": "3.0.3", "http-cache-semantics": "^4.2.0", @@ -2142,20 +2124,20 @@ "neotraverse": "^0.6.18", "p-limit": "^6.2.0", "p-queue": "^8.1.1", - "package-manager-detector": "^1.5.0", + "package-manager-detector": "^1.6.0", "piccolore": "^0.1.3", "picomatch": "^4.0.3", "prompts": "^2.4.2", "rehype": "^13.0.2", "semver": "^7.7.3", - "shiki": "^3.15.0", - "smol-toml": "^1.5.2", + "shiki": "^3.20.0", + "smol-toml": "^1.6.0", "svgo": "^4.0.0", "tinyexec": "^1.0.2", "tinyglobby": "^0.2.15", "tsconfck": "^3.1.6", "ultrahtml": "^1.6.0", - "unifont": "~0.6.0", + "unifont": "~0.7.1", "unist-util-visit": "^5.0.0", "unstorage": "^1.17.3", "vfile": "^6.0.3", @@ -2165,7 +2147,7 @@ "yargs-parser": "^21.1.1", "yocto-spinner": "^0.2.3", "zod": "^3.25.76", - "zod-to-json-schema": "^3.25.0", + "zod-to-json-schema": "^3.25.1", "zod-to-ts": "^1.2.0" }, "bin": { @@ -2247,9 +2229,9 @@ "license": "MIT" }, "node_modules/astro/node_modules/@astrojs/markdown-remark": { - "version": "6.3.9", - "resolved": "https://registry.npmjs.org/@astrojs/markdown-remark/-/markdown-remark-6.3.9.tgz", - "integrity": "sha512-hX2cLC/KW74Io1zIbn92kI482j9J7LleBLGCVU9EP3BeH5MVrnFawOnqD0t/q6D1Z+ZNeQG2gNKMslCcO36wng==", + "version": "6.3.10", + "resolved": "https://registry.npmjs.org/@astrojs/markdown-remark/-/markdown-remark-6.3.10.tgz", + "integrity": "sha512-kk4HeYR6AcnzC4QV8iSlOfh+N8TZ3MEStxPyenyCtemqn8IpEATBFMTJcfrNW32dgpt6MY3oCkMM/Tv3/I4G3A==", "license": "MIT", "dependencies": { "@astrojs/internal-helpers": "0.7.5", @@ -2258,7 +2240,7 @@ "hast-util-from-html": "^2.0.3", "hast-util-to-text": "^4.0.2", "import-meta-resolve": "^4.2.0", - "js-yaml": "^4.1.0", + "js-yaml": "^4.1.1", "mdast-util-definitions": "^6.0.0", "rehype-raw": "^7.0.0", "rehype-stringify": "^10.0.1", @@ -2266,8 +2248,8 @@ "remark-parse": "^11.0.0", "remark-rehype": "^11.1.2", "remark-smartypants": "^3.0.2", - "shiki": "^3.13.0", - "smol-toml": "^1.4.2", + "shiki": "^3.19.0", + "smol-toml": "^1.5.2", "unified": "^11.0.5", "unist-util-remove-position": "^5.0.0", "unist-util-visit": "^5.0.0", @@ -2306,26 +2288,6 @@ "integrity": "sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg==", "license": "MIT" }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, "node_modules/bcp-47": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/bcp-47/-/bcp-47-2.1.0.tgz", @@ -2389,15 +2351,6 @@ "concat-map": "0.0.1" } }, - "node_modules/brotli": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/brotli/-/brotli-1.3.3.tgz", - "integrity": "sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg==", - "license": "MIT", - "dependencies": { - "base64-js": "^1.1.2" - } - }, "node_modules/camelcase": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-8.0.0.tgz", @@ -2514,15 +2467,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/clone": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", - "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", - "license": "MIT", - "engines": { - "node": ">=0.8" - } - }, "node_modules/clsx": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", @@ -2574,12 +2518,16 @@ "license": "MIT" }, "node_modules/cookie": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz", - "integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz", + "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==", "license": "MIT", "engines": { "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/cookie-es": { @@ -2778,9 +2726,9 @@ } }, "node_modules/devalue": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.5.0.tgz", - "integrity": "sha512-69sM5yrHfFLJt0AZ9QqZXGCPfJ7fQjvpln3Rq5+PS03LD32Ost1Q9N+eEnaQwGRIriKkMImXD56ocjQmfjbV3w==", + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.6.2.tgz", + "integrity": "sha512-nPRkjWzzDQlsejL1WVifk5rvcFi/y1onBRxjaFMjZeR9mFpqu2gmAZ9xUB9/IEanEP/vBtGeGganC/GO1fmufg==", "license": "MIT" }, "node_modules/devlop": { @@ -2796,16 +2744,10 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/dfa": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/dfa/-/dfa-1.2.0.tgz", - "integrity": "sha512-ED3jP8saaweFTjeGX8HQPjeC1YYyZs98jGNZx6IiBvxW7JG5v492kamAQB3m2wop07CvU/RQmzcKr6bgcC5D/Q==", - "license": "MIT" - }, "node_modules/diff": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", - "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.3.tgz", + "integrity": "sha512-qejHi7bcSD4hQAZE0tNAawRK1ZtafHDmMTMkrrIGgSLl7hTnQHmKCeB45xAcbfTqK2zowkM3j3bHt/4b/ARbYQ==", "license": "BSD-3-Clause", "engines": { "node": ">=0.3.1" @@ -3155,12 +3097,6 @@ "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", "license": "MIT" }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "license": "MIT" - }, "node_modules/fdir": { "version": "6.5.0", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", @@ -3208,30 +3144,24 @@ } }, "node_modules/fontace": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/fontace/-/fontace-0.3.1.tgz", - "integrity": "sha512-9f5g4feWT1jWT8+SbL85aLIRLIXUaDygaM2xPXRmzPYxrOMNok79Lr3FGJoKVNKibE0WCunNiEVG2mwuE+2qEg==", + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/fontace/-/fontace-0.4.0.tgz", + "integrity": "sha512-moThBCItUe2bjZip5PF/iZClpKHGLwMvR79Kp8XpGRBrvoRSnySN4VcILdv3/MJzbhvUA5WeiUXF5o538m5fvg==", "license": "MIT", "dependencies": { - "@types/fontkit": "^2.0.8", - "fontkit": "^2.0.4" + "fontkitten": "^1.0.0" } }, - "node_modules/fontkit": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/fontkit/-/fontkit-2.0.4.tgz", - "integrity": "sha512-syetQadaUEDNdxdugga9CpEYVaQIxOwk7GlwZWWZ19//qW4zE5bknOKeMBDYAASwnpaSHKJITRLMF9m1fp3s6g==", + "node_modules/fontkitten": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/fontkitten/-/fontkitten-1.0.2.tgz", + "integrity": "sha512-piJxbLnkD9Xcyi7dWJRnqszEURixe7CrF/efBfbffe2DPyabmuIuqraruY8cXTs19QoM8VJzx47BDRVNXETM7Q==", "license": "MIT", "dependencies": { - "@swc/helpers": "^0.5.12", - "brotli": "^1.3.2", - "clone": "^2.1.2", - "dfa": "^1.2.0", - "fast-deep-equal": "^3.1.3", - "restructure": "^3.0.0", - "tiny-inflate": "^1.0.3", - "unicode-properties": "^1.4.0", - "unicode-trie": "^2.0.0" + "tiny-inflate": "^1.0.3" + }, + "engines": { + "node": ">=20" } }, "node_modules/fs.realpath": { @@ -4341,9 +4271,9 @@ } }, "node_modules/mdast-util-to-hast": { - "version": "13.2.0", - "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.0.tgz", - "integrity": "sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==", + "version": "13.2.1", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz", + "integrity": "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==", "license": "MIT", "dependencies": { "@types/hast": "^3.0.0", @@ -5290,9 +5220,9 @@ "license": "MIT" }, "node_modules/oniguruma-to-es": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/oniguruma-to-es/-/oniguruma-to-es-4.3.3.tgz", - "integrity": "sha512-rPiZhzC3wXwE59YQMRDodUwwT9FZ9nNBwQQfsd1wfdtlKEyCdRV0avrTcSZ5xlIvGRVPd/cx6ZN45ECmS39xvg==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/oniguruma-to-es/-/oniguruma-to-es-4.3.4.tgz", + "integrity": "sha512-3VhUGN3w2eYxnTzHn+ikMI+fp/96KoRSVK9/kMTcFqj1NRDh2IhQCKvYxDnWePKRXY/AqH+Fuiyb7VHSzBjHfA==", "license": "MIT", "dependencies": { "oniguruma-parser": "^0.12.1", @@ -5353,9 +5283,9 @@ } }, "node_modules/package-manager-detector": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-1.5.0.tgz", - "integrity": "sha512-uBj69dVlYe/+wxj8JOpr97XfsxH/eumMt6HqjNTmJDf/6NO9s+0uxeOneIz3AsPt2m6y9PqzDzd3ATcU17MNfw==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-1.6.0.tgz", + "integrity": "sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA==", "license": "MIT" }, "node_modules/pagefind": { @@ -5374,12 +5304,6 @@ "@pagefind/windows-x64": "1.3.0" } }, - "node_modules/pako": { - "version": "0.2.9", - "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz", - "integrity": "sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==", - "license": "MIT" - }, "node_modules/parse-entities": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.2.tgz", @@ -5691,9 +5615,9 @@ } }, "node_modules/regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/regex/-/regex-6.0.1.tgz", - "integrity": "sha512-uorlqlzAKjKQZ5P+kTJr3eeJGSVroLKoHmquUj4zHWuR+hEyNqlXsSKlYYF5F4NI6nl7tWCs0apKJ0lmfsXAPA==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/regex/-/regex-6.1.0.tgz", + "integrity": "sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg==", "license": "MIT", "dependencies": { "regex-utilities": "^2.3.0" @@ -5924,12 +5848,6 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/restructure": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/restructure/-/restructure-3.0.2.tgz", - "integrity": "sha512-gSfoiOEA0VPE6Tukkrr7I0RBdE0s7H1eFCDBk05l1KIQT1UIKNc5JZy6jdyW6eYH3aR3g5b3PuL77rq0hvwtAw==", - "license": "MIT" - }, "node_modules/retext": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/retext/-/retext-9.0.0.tgz", @@ -6110,17 +6028,17 @@ } }, "node_modules/shiki": { - "version": "3.15.0", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-3.15.0.tgz", - "integrity": "sha512-kLdkY6iV3dYbtPwS9KXU7mjfmDm25f5m0IPNFnaXO7TBPcvbUOY72PYXSuSqDzwp+vlH/d7MXpHlKO/x+QoLXw==", + "version": "3.20.0", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-3.20.0.tgz", + "integrity": "sha512-kgCOlsnyWb+p0WU+01RjkCH+eBVsjL1jOwUYWv0YDWkM2/A46+LDKVs5yZCUXjJG6bj4ndFoAg5iLIIue6dulg==", "license": "MIT", "dependencies": { - "@shikijs/core": "3.15.0", - "@shikijs/engine-javascript": "3.15.0", - "@shikijs/engine-oniguruma": "3.15.0", - "@shikijs/langs": "3.15.0", - "@shikijs/themes": "3.15.0", - "@shikijs/types": "3.15.0", + "@shikijs/core": "3.20.0", + "@shikijs/engine-javascript": "3.20.0", + "@shikijs/engine-oniguruma": "3.20.0", + "@shikijs/langs": "3.20.0", + "@shikijs/themes": "3.20.0", + "@shikijs/types": "3.20.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } @@ -6157,9 +6075,9 @@ "license": "MIT" }, "node_modules/smol-toml": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.5.2.tgz", - "integrity": "sha512-QlaZEqcAH3/RtNyet1IPIYPsEWAaYyXXv1Krsi+1L/QHppjX4Ifm8MQsBISz9vE8cHicIq3clogsheili5vhaQ==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.6.0.tgz", + "integrity": "sha512-4zemZi0HvTnYwLfrpk/CF9LOd9Lt87kAt50GnqhMpyF9U3poDAP2+iukq2bZsO/ufegbYehBkqINbsWxj4l4cw==", "license": "BSD-3-Clause", "engines": { "node": ">= 18" @@ -6227,9 +6145,9 @@ } }, "node_modules/starlight-kbd": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/starlight-kbd/-/starlight-kbd-0.2.1.tgz", - "integrity": "sha512-kFDWkB99GSk1korUrHezRxcjLhQ53axDyI9oZakEPB5ZNU3b+uwp2kVMhEg6B5yzekwFgf84UlNiWu9lYpfMoA==", + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/starlight-kbd/-/starlight-kbd-0.3.0.tgz", + "integrity": "sha512-bhG1kWGEXCkuV8pkYW6sWEVmwD2bnpQpVIxq4QDJp7tLsprAbIKnD7RKFCP/QtITfwaVgKq3uMNK0+p4TUAgGg==", "license": "MIT", "engines": { "node": ">=18.17.1" @@ -6239,9 +6157,9 @@ } }, "node_modules/starlight-links-validator": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/starlight-links-validator/-/starlight-links-validator-0.19.1.tgz", - "integrity": "sha512-Ermpe7zJG5uRJUqIWflX4VNLozOBX2/N4XvHvyaPQcHsfdN7y81qEd3n56PYdVb3Tj4/6ZFFSfAtiCe78XtBVw==", + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/starlight-links-validator/-/starlight-links-validator-0.19.2.tgz", + "integrity": "sha512-IHeK3R78fsmv53VfRkGbXkwK1CQEUBHM9QPzBEyoAxjZ/ssi5gjV+F4oNNUppTR48iPp+lEY0MTAmvkX7yNnkw==", "license": "MIT", "dependencies": { "@types/picomatch": "^3.0.1", @@ -6260,7 +6178,8 @@ "node": ">=18.17.1" }, "peerDependencies": { - "@astrojs/starlight": ">=0.32.0" + "@astrojs/starlight": ">=0.32.0", + "astro": ">=5.1.5" } }, "node_modules/starlight-package-managers": { @@ -6634,26 +6553,6 @@ "integrity": "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==", "license": "MIT" }, - "node_modules/unicode-properties": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/unicode-properties/-/unicode-properties-1.4.1.tgz", - "integrity": "sha512-CLjCCLQ6UuMxWnbIylkisbRj31qxHPAurvena/0iwSVbQ2G1VY5/HjV0IRabOEbDHlzZlRdCrD4NhB0JtU40Pg==", - "license": "MIT", - "dependencies": { - "base64-js": "^1.3.0", - "unicode-trie": "^2.0.0" - } - }, - "node_modules/unicode-trie": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-trie/-/unicode-trie-2.0.0.tgz", - "integrity": "sha512-x7bc76x0bm4prf1VLg79uhAzKw8DVboClSN5VxJuQ+LKDOVEW9CdH+VY7SP+vX7xCYQqzzgQpFqz15zeLvAtZQ==", - "license": "MIT", - "dependencies": { - "pako": "^0.2.5", - "tiny-inflate": "^1.0.0" - } - }, "node_modules/unified": { "version": "11.0.5", "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz", @@ -6674,14 +6573,14 @@ } }, "node_modules/unifont": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/unifont/-/unifont-0.6.0.tgz", - "integrity": "sha512-5Fx50fFQMQL5aeHyWnZX9122sSLckcDvcfFiBf3QYeHa7a1MKJooUy52b67moi2MJYkrfo/TWY+CoLdr/w0tTA==", + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/unifont/-/unifont-0.7.3.tgz", + "integrity": "sha512-b0GtQzKCyuSHGsfj5vyN8st7muZ6VCI4XD4vFlr7Uy1rlWVYxC3npnfk8MyreHxJYrz1ooLDqDzFe9XqQTlAhA==", "license": "MIT", "dependencies": { - "css-tree": "^3.0.0", - "ofetch": "^1.4.1", - "ohash": "^2.0.0" + "css-tree": "^3.1.0", + "ofetch": "^1.5.1", + "ohash": "^2.0.11" } }, "node_modules/unist-util-find-after": { @@ -7196,9 +7095,9 @@ } }, "node_modules/zod-to-json-schema": { - "version": "3.25.0", - "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.0.tgz", - "integrity": "sha512-HvWtU2UG41LALjajJrML6uQejQhNJx+JBO9IflpSja4R03iNWfKXrj6W2h7ljuLyc1nKS+9yDyL/9tD1U/yBnQ==", + "version": "3.25.1", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.1.tgz", + "integrity": "sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA==", "license": "ISC", "peerDependencies": { "zod": "^3.25 || ^4" diff --git a/docs/package.json b/docs/package.json index 248d090..bbca1ad 100644 --- a/docs/package.json +++ b/docs/package.json @@ -1,7 +1,7 @@ { "name": "docs", "type": "module", - "version": "v2.2.2", + "version": "v2.3.3", "scripts": { "dev": "astro dev", "start": "astro dev", @@ -10,15 +10,15 @@ "astro": "astro" }, "dependencies": { - "@astrojs/starlight": "^0.37.0", + "@astrojs/starlight": "^0.37.3", "@lorenzo_lewis/starlight-utils": "^0.3.2", - "astro": "^5.16.4", + "astro": "^5.16.11", "astro-auto-import": "^0.4.4", "sharp": "^0.34.5", "starlight-contributor-list": "^0.3.1", "starlight-github-alerts": "^0.1.1", - "starlight-kbd": "^0.2.1", - "starlight-links-validator": "^0.19.1", + "starlight-kbd": "^0.3.0", + "starlight-links-validator": "^0.19.2", "starlight-package-managers": "^0.11.1", "starlight-scroll-to-top": "^0.4.0", "starlight-showcases": "^0.3.1", diff --git a/install_remnawave.sh b/install_remnawave.sh index 05067c2..cdfaf76 100644 --- a/install_remnawave.sh +++ b/install_remnawave.sh @@ -1,6 +1,6 @@ #!/bin/bash -SCRIPT_VERSION="2.3.0" +SCRIPT_VERSION="2.3.4" UPDATE_AVAILABLE=false DIR_REMNAWAVE="/usr/local/remnawave_reverse/" LANG_FILE="${DIR_REMNAWAVE}selected_language" @@ -13,850 +13,422 @@ COLOR_WHITE="\033[1;37m" COLOR_RED="\033[1;31m" COLOR_GRAY='\033[0;90m' -load_language() { - if [ -f "$LANG_FILE" ]; then - local saved_lang=$(cat "$LANG_FILE") - case $saved_lang in - 1) set_language en ;; - 2) set_language ru ;; - *) - rm -f "$LANG_FILE" - return 1 ;; - esac - return 0 - fi - return 1 -} - -# Language variables +# Russian language variables declare -A LANG=( - [CHOOSE_LANG]="Select language:" - [LANG_EN]="English" - [LANG_RU]="Русский" + #Alias + [ALIAS_ADDED]="Алиас 'rr' для 'remnawave_reverse' добавлен в %s" + [ALIAS_ACTIVATE_GLOBAL]="Алиас 'rr' теперь доступен для всех пользователей. Выполните 'source %s' или перезапустите терминал, чтобы применить алиас." + #Check + [ERROR_ROOT]="Скрипт нужно запускать с правами root" + [ERROR_OS]="Поддержка только Debian 11/12 и Ubuntu 22.04/24.04" + [MENU_TITLE]="REMNAWAVE REVERSE-PROXY by eGames" + [AVAILABLE_UPDATE]="доступно обновление" + [VERSION_LABEL]="Версия: %s" + #Install Packages + [ERROR_UPDATE_LIST]="Ошибка: Не удалось обновить список пакетов" + [ERROR_INSTALL_PACKAGES]="Ошибка: Не удалось установить необходимые пакеты" + [ERROR_INSTALL_CRON]="Ошибка: Не удалось установить cron" + [ERROR_START_CRON]="Ошибка: Не удалось запустить cron" + [ERROR_CONFIGURE_LOCALES]="Ошибка: Не удалось настроить локали" + [ERROR_DOWNLOAD_DOCKER_KEY]="Ошибка: Не удалось загрузить ключ Docker" + [ERROR_UPDATE_DOCKER_LIST]="Ошибка: Не удалось обновить список пакетов после добавления репозитория Docker" + [ERROR_INSTALL_DOCKER]="Ошибка: Не удалось установить Docker" + [ERROR_DOCKER_NOT_INSTALLED]="Ошибка: Docker не установлен" + [ERROR_START_DOCKER]="Ошибка: Не удалось запустить Docker" + [ERROR_ENABLE_DOCKER]="Ошибка: Не удалось включить автозапуск Docker" + [ERROR_DOCKER_NOT_WORKING]="Ошибка: Docker не работает корректно" + [ERROR_CONFIGURE_UFW]="Ошибка: Не удалось настроить UFW" + [ERROR_CONFIGURE_UPGRADES]="Ошибка: Не удалось настроить unattended-upgrades" + [ERROR_DOCKER_DNS]="Ошибка: Не удалось разрешить домен download.docker.com. Проверьте настройки DNS." + [ERROR_INSTALL_CERTBOT]="Ошибка: Не удалось установить certbot" + [SUCCESS_INSTALL]="Все пакеты успешно установлены" + #Main menu + [EXIT]="Выход" + [MENU_1]="Установка компонентов Remnawave" + [MENU_2]="Переустановить панель/ноду" + [MENU_3]="Управление панелью/нодой" + [MENU_4]="Установить случайный шаблон для selfsteal ноды" + [MENU_5]="Кастомные расширения от legiz" + [MENU_6]="Управление расширениями от distillium" + [MENU_7]="Управление IPv6" + [MENU_8]="Управление сертификатами домена" + [MENU_9]="Проверить обновления скрипта" + [MENU_10]="Удалить скрипт" + [PROMPT_ACTION]="Выберите действие (0-10):" + [INVALID_CHOICE]="Неверный выбор. Выберите 0-10." + [WARNING_LABEL]="ВНИМАНИЕ:" + [CONFIRM_PROMPT]="Введите 'y' для продолжения или 'n' для выхода (y/n):" + [WARNING_NODE_PANEL]="Добавление ноды должно выполняться только на сервере, где установлена панель, а не на сервере ноды." + [CONFIRM_SERVER_PANEL]="Вы уверены, что находитесь на сервере с установленной панелью?" + #Remove Script + [REMOVE_SCRIPT_ONLY]="Удалить скрипт и его локальные файлы" + [REMOVE_SCRIPT_AND_PANEL]="Удалить скрипт и данные панели/ноды remnawave" + [CONFIRM_REMOVE_SCRIPT]="Все данные скрипта будут удалены с сервера. Вы уверены? (y/n): " + [CONFIRM_REMOVE_ALL]="Все данные скрипта и панели/ноды будут удалены с сервера. Вы уверены? (y/n): " + [SCRIPT_REMOVED]="Скрипт и его локальные файлы успешно удалены!" + [ALL_REMOVED]="Скрипт и данные панели/ноды успешно удалены!" + #Extensions by distillium + [EXTENSIONS_MENU]="Расширения by distillium" + [EXTENSIONS_MENU_TITLE]="Управление расширениями" + [EXTENSIONS_PROMPT]="Выберите действие (0-2):" + [EXTENSIONS_INVALID_CHOICE]="Неверный выбор. Выберите 0-2." + [BACKUP_RESTORE]="Backup and Restore" + #Warp by distillium + [WARP_MENU]="WARP Native" + [WARP_MENU_TITLE]="Управление WARP Native" + [WARP_INSTALL]="Установить WARP Native" + [WARP_ADD_CONFIG]="Добавить WARP-настройки в конфигурацию ноды" + [WARP_DELETE_WARP_SETTINGS]="Удалить WARP-настройки из конфигурации ноды" + [WARP_CONFIRM_SERVER_PANEL]="Вы уверены, что находитесь на сервере с установленной панелью?\nДобавление WARP-настроек должно выполняться только на сервере, где установлена панель, а не на сервере ноды" + [WARP_UNINSTALL]="Удалить WARP Native" + [WARP_PROMPT]="Выберите действие (0-4):" + [WARP_PROMPT1]="Выберите действие:" + [WARP_INVALID_CHOICE]="Неверный выбор. Выберите 0-4." + [WARP_INVALID_CHOICE2]="Неверный выбор." + [WARP_NO_NODE]="Нода Remnawave не найдена. Сначала установите ноду." + [WARP_SELECT_CONFIG]="На какую ноду добавить WARP-настройки?" + [WARP_SELECT_CONFIG_DELETE]="На какой ноде удалить WARP-настройки?" + [WARP_NO_CONFIGS]="Конфигурации не найдены." + [WARP_UPDATE_SUCCESS]="Конфигурация успешно обновлена!" + [WARP_UPDATE_FAIL]="Не удалось обновить конфигурацию." + [WARP_WARNING]="warp-out уже существует в outbounds." + [WARP_WARNING2]="warp rule уже существует в routing rules." + [WARP_REMOVED_WARP_SETTINGS1]="Удален warp-out из outbounds" + [WARP_NO_WARP_SETTINGS1]="warp-out не найден в outbounds" + [WARP_REMOVED_WARP_SETTINGS2]="Удален warp rule из routing rules" + [WARP_NO_WARP_SETTINGS2]="warp rule не найден в routing rules" + #Manage Panel/Node + [START_PANEL_NODE]="Запустить панель/ноду" + [STOP_PANEL_NODE]="Остановить панель/ноду" + [UPDATE_PANEL_NODE]="Обновить панель/ноду" + [VIEW_LOGS]="Просмотр логов" + [PRESS_ENTER_RETURN_MENU]="Нажмите Enter для возврата в меню..." + [REMNAWAVE_CLI]="Remnawave CLI" + [ACCESS_PANEL]="Доступ к панели через порт 8443 (только для панели + ноды)" + [MANAGE_PANEL_NODE_PROMPT]="Выберите действие (0-6):" + [MANAGE_PANEL_NODE_INVALID_CHOICE]="Неверный выбор. Выберите 0-6." + #Manage Certificates + [CERT_UPDATE]="Обновить текущие сертификаты" + [CERT_GENERATE]="Сгенерировать новые сертификаты для другого домена" + [CERT_PROMPT1]="Выберите действие (0-2):" + [CERT_INVALID_CHOICE]="Неверный выбор. Выберите 0-2." + [CERT_UPDATE_CHECK]="Проверка метода генерации сертификата..." + [CERT_UPDATE_SUCCESS]="Сертификаты успешно обновлены." + [CERT_UPDATE_FAIL]="Не удалось обновить сертификаты." + [CERT_GENERATE_PROMPT]="Введите домен для новых сертификатов (например, example.com):" + [CERT_METHOD_UNKNOWN]="Неизвестный метод генерации сертификата." + [CERT_NOT_DUE]="Сертификат для %s еще не требует обновления." + #Install Remnawave Components + [INSTALL_MENU_TITLE]="Установка компонентов Remnawave" + [INSTALL_PANEL_NODE]="Установить панель и ноду на один сервер (не рекомендуется)" + [INSTALL_PANEL]="Установить только панель" + [INSTALL_ADD_NODE]="Добавить ноду в панель" + [INSTALL_NODE]="Установить только ноду" + [INSTALL_PROMPT]="Выберите действие (0-4):" + [INSTALL_INVALID_CHOICE]="Неверный выбор. Выберите 0-4." + #Manage IPv6 + [IPV6_MENU_TITLE]="Управление IPv6" + [IPV6_ENABLE]="Включить IPv6" + [IPV6_DISABLE]="Отключить IPv6" + [IPV6_PROMPT]="Выберите действие (0-2):" + [IPV6_INVALID_CHOICE]="Неверный выбор. Выберите 0-2." + [IPV6_ALREADY_ENABLED]="IPv6 уже включен" + [ENABLE_IPV6]="Включение IPv6..." + [IPV6_ENABLED]="IPv6 включен." + [IPV6_ALREADY_DISABLED]="IPv6 уже отключен" + [DISABLING_IPV6]="Отключение IPv6..." + [IPV6_DISABLED]="IPv6 отключен." + #Remna + [INSTALL_PACKAGES]="Установка необходимых пакетов..." + [INSTALLING]="Установка панели и ноды" + [INSTALLING_PANEL]="Установка панели" + [INSTALLING_NODE]="Установка ноды" + [ENTER_PANEL_DOMAIN]="Введите домен панели (например, panel.example.com):" + [ENTER_SUB_DOMAIN]="Введите домен подписки (например, sub.example.com):" + [ENTER_NODE_DOMAIN]="Введите selfsteal домен для ноды (например, node.example.com):" + [ENTER_CF_TOKEN]="Введите Cloudflare API токен или глобальный ключ:" + [ENTER_CF_EMAIL]="Введите зарегистрированную почту Cloudflare:" + [ENTER_GCORE_TOKEN]="Введите API‑токен Gcore:" + [CERT_GCORE_FILE_NOT_FOUND]="Файл с реквизитами Gcore не найден. Повторно введите токен." + [ERROR_INSTALL_GCORE_PLUGIN]="Не удалось установить плагин certbot-dns-gcore" + [CHECK_CERTS]="Проверка сертификатов..." + [CERT_FOUND]="Сертификаты найдены в /etc/letsencrypt/live/" + [CF_VALIDATING]="Cloudflare API ключ и email валидны" + [CF_INVALID]="Неверный Cloudflare API ключ или email после %d попыток." + [CF_INVALID_ATTEMPT]="Неверный Cloudflare API ключ или email. Попытка %d из %d." + [WAITING]="Пожалуйста, подождите..." + #API + [REGISTERING_REMNAWAVE]="Регистрируем пользователя в панели Remnawave" + [CHECK_CONTAINERS]="Проверка доступности контейнеров..." + [CONTAINERS_NOT_READY_ATTEMPT]="Контейнеры не готовы, ожидание... Попытка %d из %d." + [CONTAINERS_TIMEOUT]="Контейнеры не готовы после %d попыток.\n\nПроверьте логи:\n cd /opt/remnawave && docker compose logs -f\n\nТакже посмотрите типичные ошибки Docker:\n https://wiki.egam.es/ru/troubleshooting/docker-issues/" + [REGISTRATION_SUCCESS]="Регистрация прошла успешно!" + [GET_PUBLIC_KEY]="Получаем публичный ключ..." + [PUBLIC_KEY_SUCCESS]="Публичный ключ успешно получен" + [GENERATE_KEYS]="Генерация ключей x25519..." + [GENERATE_KEYS_SUCCESS]="Ключи успешно сгенерированы" + [CREATING_CONFIG_PROFILE]="Создаем конфигурационный профиль..." + [CONFIG_PROFILE_CREATED]="Конфигурационный профиль успешно создан" + [CREATING_NODE]="Создание ноды" + [NODE_CREATED]="Нода успешно создана" + [CREATE_HOST]="Создаем хост" + [HOST_CREATED]="Хост успешно создан" + [GET_DEFAULT_SQUAD]="Получение внутреннего сквада" + [UPDATE_SQUAD]="Сквад успешно обновлен" + [NO_SQUADS_FOUND]="Нет внутренних сквадов" + [INVALID_UUID_FORMAT]="Неверный формат UUID" + [NO_VALID_SQUADS_FOUND]="Нет валидных сквадов" + [ERROR_GET_SQUAD]="Не удалось получить сквад" + [INVALID_SQUAD_UUID]="Неверный UUID сквада" + [INVALID_INBOUND_UUID]="Неверный UUID входа" + #Stop/Start/Update + [CHANGE_DIR_FAILED]="Не удалось перейти в директорию %s" + [DIR_NOT_FOUND]="Директория не найдена" + [PANEL_RUNNING]="Панель/нода уже запущена" + [PANEL_RUN]="Панель/нода запущена" + [PANEL_STOP]="Панель/нода остановлена" + [PANEL_STOPPED]="Панель/нода уже остановлена" + [NO_UPDATE]="Нет доступных обновлений для панели/ноды" + [UPDATING]="Обновление панели/ноды..." + [UPDATE_SUCCESS1]="Панель/нода успешно обновлена" + [STARTING_PANEL_NODE]="Запуск панели и ноды" + [STARTING_PANEL]="Запуск панели" + [STARTING_NODE]="Запуск ноды" + [STOPPING_REMNAWAVE]="Остановка панели и ноды" + [IMAGES_DETECTED]="Обнаружены новые образы, перезапускаем контейнеры..." + #Menu End + [INSTALL_COMPLETE]=" УСТАНОВКА ЗАВЕРШЕНА!" + [PANEL_ACCESS]="Панель доступна по адресу:" + [ADMIN_CREDS]="Для входа в панель используйте следующие данные:" + [USERNAME]="Логин:" + [PASSWORD]="Пароль:" + [RELAUNCH_CMD]="Для повторного запуска менеджера используйте команду:" + #RandomHTML + [DOWNLOAD_FAIL]="Ошибка загрузки, повторная попытка..." + [UNPACK_ERROR]="Ошибка распаковки архива" + [RANDOM_TEMPLATE]="Установка случайного шаблона для маскировочного сайта" + [TEMPLATE_COPY]="Шаблон скопирован в /var/www/html/" + [SELECT_TEMPLATE]="Выбран шаблон:" + #Error + [ERROR_TOKEN]="Не удалось получить токен." + [ERROR_PUBLIC_KEY]="Не удалось получить публичный ключ." + [ERROR_EXTRACT_PUBLIC_KEY]="Не удалось извлечь публичный ключ из ответа." + [ERROR_GENERATE_KEYS]="Не удалось сгенерировать ключи." + [ERROR_NO_CONFIGS]="Не найдены профили конфигураций" + [NO_DEFAULT_PROFILE]="Default-Profile не найден" + [ERROR_DELETE_PROFILE]="Не удалось удалить профиль" + [ERROR_EMPTY_RESPONSE_NODE]="Пустой ответ от сервера при создании ноды." + [ERROR_CREATE_NODE]="Не удалось создать ноду." + [ERROR_EMPTY_RESPONSE_HOST]="Пустой ответ от сервера при создании хоста." + [ERROR_CREATE_HOST]="Не удалось создать хост." + [ERROR_EMPTY_RESPONSE_REGISTER]="Ошибка при регистрации - пустой ответ сервера" + [ERROR_REGISTER]="Ошибка регистрации" + [ERROR_UPDATE_SQUAD]="Ошибка обновления squad" + [ERROR_GET_SQUAD_LIST]="Ошибка получения списка squadов" + [NO_SQUADS_TO_UPDATE]="Нет сквадов для обновления" + #Reinstall Panel/Node + [REINSTALL_WARNING]="Все данные панели/ноды будут удалены с сервера. Вы уверены? (y/n):" + [REINSTALL_TYPE_TITLE]="Выберите способ переустановки:" + [REINSTALL_PROMPT]="Выберите действие (0-3):" + [INVALID_REINSTALL_CHOICE]="Неверный выбор. Выберите 0-3." + [POST_PANEL_MESSAGE]="Панель успешно установлена!" + [POST_PANEL_INSTRUCTION]="Для установки ноды выполните следующие шаги:\n1. Запустите этот скрипт на сервере, где будет установлена нода.\n2. Выберите 'Установка компонентов Remnawave', а затем 'Установить только ноду'." + [SELFSTEAL]="Введите selfsteal домен для ноды, который указали при установке панели:" + [PANEL_IP_PROMPT]="Введите IP адрес панели, чтобы установить соединение между панелью и ноды:" + [IP_ERROR]="Введите корректный IP-адрес в формате X.X.X.X (например, 192.168.1.1)" + [CERT_PROMPT]="Введите сертификат, полученный от панели (вставьте содержимое и 2 раза нажмите Enter):" + [CERT_CONFIRM]="Вы уверены, что сертификат правильный? (y/n):" + [ABORT_MESSAGE]="Установка прервана пользователем" + [SUCCESS_MESSAGE]="Нода успешно подключена" + #Node Check + [NODE_CHECK]="Проверка подключения ноды для %s..." + [NODE_ATTEMPT]="Попытка %d из %d..." + [NODE_UNAVAILABLE]="Нода недоступна на попытке %d." + [NODE_LAUNCHED]="Нода успешно подключена!" + [NODE_NOT_CONNECTED]="Нода не подключена после %d попыток!" + [CHECK_CONFIG]="Проверьте конфигурацию или перезапустите панель." + #Add node to panel + [ADD_NODE_TO_PANEL]="Добавить ноду в панель" + [ENTER_NODE_NAME]="Введите имя для вашей ноды (например, Germany):" + [USING_SAVED_TOKEN]="Используем сохранённый токен..." + [INVALID_SAVED_TOKEN]="Сохранённый токен недействителен. Запрашиваем новый..." + [ENTER_PANEL_USERNAME]="Введите логин панели: " + [ENTER_PANEL_PASSWORD]="Введите пароль панели: " + [TOKEN_RECEIVED_AND_SAVED]="Токен успешно получен и сохранён" + [TOKEN_USED_SUCCESSFULLY]="Токен успешно использован" + [DOMAIN_ALREADY_EXISTS]="Домен уже используется" + [TRY_ANOTHER_DOMAIN]="Пожалуйста, используйте другой домен" + [ERROR_CHECK_DOMAIN]="Ошибка при проверке домена" + [NODE_ADDED_SUCCESS]="Нода успешно добавлена!" + [CREATE_NEW_NODE]="Создаём новую ноду для %s" + [CF_INVALID_NAME]="Ошибка: Имя конфигурационного профиля %s уже используется.\nПожалуйста, выберите другое имя." + [CF_INVALID_LENGTH]="Ошибка: Имя конфигурационного профиля должно содержать от 3 до 20 символов." + [CF_INVALID_CHARS]="Ошибка: Имя конфигурационного профиля должно содержать только английские буквы, цифры и дефис." + #check + [CHECK_UPDATE]="Проверить обновления" + [GENERATING_CERTS]="Генерируем сертификаты для %s" + [REQUIRED_DOMAINS]="Требуемые домены для сертификатов:" + [CHECK_DOMAIN_IP_FAIL]="Не удалось определить IP-адрес домена или сервера." + [CHECK_DOMAIN_IP_FAIL_INSTRUCTION]="Убедитесь, что домен %s правильно настроен и указывает на этот сервер (%s)." + [CHECK_DOMAIN_CLOUDFLARE]="Домен %s указывает на IP Cloudflare (%s)." + [CHECK_DOMAIN_CLOUDFLARE_INSTRUCTION]="Проксирование Cloudflare недопустимо для selfsteal домена. Отключите проксирование (переключите в режим 'DNS Only')." + [CHECK_DOMAIN_MISMATCH]="Домен %s указывает на IP-адрес %s, который отличается от IP этого сервера (%s)." + [CHECK_DOMAIN_MISMATCH_INSTRUCTION]="Для корректной работы домен должен указывать на текущий сервер." + [NO_PANEL_NODE_INSTALLED]="Панель или нода не установлены. Пожалуйста, сначала установите панель или ноду." + #update + [UPDATE_AVAILABLE]="Доступна новая версия скрипта: %s (текущая версия: %s)." + [UPDATE_CONFIRM]="Обновить скрипт? (y/n):" + [UPDATE_CANCELLED]="Обновление отменено пользователем." + [UPDATE_SUCCESS]="Скрипт успешно обновлён до версии %s!" + [UPDATE_FAILED]="Ошибка при скачивании новой версии скрипта." + [VERSION_CHECK_FAILED]="Не удалось определить версию удалённого скрипта. Пропускаем обновление." + [LATEST_VERSION]="У вас уже установлена последняя версия скрипта (%s)." + [RESTART_REQUIRED]="Пожалуйста, перезапустите скрипт для применения изменений." + [LOCAL_FILE_NOT_FOUND]="Локальный файл скрипта не найден, загружаем новую версию..." + #CLI + [RUNNING_CLI]="Запуск Remnawave CLI..." + [CLI_SUCCESS]="Remnawave CLI успешно выполнен!" + [CLI_FAILED]="Не удалось выполнить Remnawave CLI. Убедитесь, что контейнер 'remnawave' запущен." + [CONTAINER_NOT_RUNNING]="Контейнер 'remnawave' не запущен. Пожалуйста, запустите его сначала." + #Cert_choise + [CERT_METHOD_PROMPT]="Выберите метод генерации сертификатов для всех доменов:" + [CERT_METHOD_CF]="Cloudflare API (поддерживает wildcard)" + [CERT_METHOD_ACME]="ACME HTTP-01 (один домен, без wildcard)" + [CERT_METHOD_GCORE]="Gcore DNS API (поддерживает wildcard)" + [CERT_METHOD_CHOOSE]="Выберите действие (0-3):" + [EMAIL_PROMPT]="Введите ваш email для регистрации в Let's Encrypt:" + [CERTS_SKIPPED]="Все сертификаты уже существуют. Пропускаем генерацию." + [ACME_METHOD]="Используем ACME (Let's Encrypt) с HTTP-01 вызовом (без поддержки wildcard)..." + [CERT_GENERATION_FAILED]="Не удалось сгенерировать сертификаты. Проверьте введенные данные и настройки DNS." + [ADDING_CRON_FOR_EXISTING_CERTS]="Добавление cron-задачи для обновления сертификатов..." + [CRON_ALREADY_EXISTS]="Задача cron для обновления сертификатов уже существует." + [CERT_NOT_FOUND]="Сертификат для домена не найден." + [ERROR_PARSING_CERT]="Ошибка при разборе даты истечения сертификата." + [CERT_EXPIRY_SOON]="Сертификаты скоро истекут через" + [DAYS]="дней" + [UPDATING_CRON]="Обновление задачи cron в соответствии со сроком действия сертификата." + [GENERATING_WILDCARD_CERT]="Генерация wildcard-сертификата для" + [WILDCARD_CERT_FOUND]="Wildcard-сертификат найден в /etc/letsencrypt/live/" + [FOR_DOMAIN]="для" + [START_CRON_ERROR]="Не удалось запустить cron. Пожалуйста, запустите его вручную." + [DOMAINS_MUST_BE_UNIQUE]="Ошибка: Все домены (панель, подписка, и нода) должны быть уникальными." + [CHOOSE_TEMPLATE_SOURCE]="Выберите источник шаблонов:" + [SIMPLE_WEB_TEMPLATES]="Simple web templates" + [SNI_TEMPLATES]="SNI templates" + [NOTHING_TEMPLATES]="Nothing Sni templates" + [CHOOSE_TEMPLATE_OPTION]="Выберите действие (0-3):" + [INVALID_TEMPLATE_CHOICE]="Неверный выбор. Выберите 0-3." + #Manage panel access + [PORT_8443_OPEN]="Открыть доступ к панели на порту 8443" + [PORT_8443_CLOSE]="Закрыть доступ к панели на порту 8443" + [PORT_8443_IN_USE]="Порт 8443 уже занят другим процессом. Проверьте, какие службы используют порт, и освободите его." + [NO_PORT_CHECK_TOOLS]="Не найдены инструменты для проверки порта (ss или netstat). Установите один из них." + [OPEN_PANEL_LINK]="Ваша ссылка для входа в панель:" + [PORT_8443_WARNING]="Не забудьте, что порт 8443 сейчас открыт для внешнего доступа. После восстановления панели выберите пункт закрытия порта 8443." + [PORT_8443_CLOSED]="Порт 8443 закрыт." + [NGINX_CONF_NOT_FOUND]="Файл nginx.conf не найден в $dir" + [NGINX_CONF_ERROR]="Не удалось извлечь необходимые параметры из nginx.conf" + [NGINX_CONF_MODIFY_FAILED]="Не удалось изменить конфигурацию Nginx." + [PORT_8443_ALREADY_CONFIGURED]="Порт 8443 уже настроен в конфигурации Nginx." + [UFW_RELOAD_FAILED]="Не удалось перезагрузить UFW." + [PORT_8443_ALREADY_CLOSED]="Порт 8443 уже закрыт в UFW." + #Legiz Extensions + [LEGIZ_EXTENSIONS_PROMPT]="Выберите действие (0-2):" + # Sub Page Upload + [UPLOADING_SUB_PAGE]="Загрузка пользовательского шаблона страницы подписки..." + [ERROR_FETCH_SUB_PAGE]="Не удалось получить пользовательский шаблон страницы подписки." + [SUB_PAGE_UPDATED_SUCCESS]="Пользовательский шаблон страницы подписки успешно обновлён." + [SELECT_SUB_PAGE_CUSTOM]="Выберите действие (0-2):" + [SELECT_SUB_PAGE_CUSTOM1]="Шаблоны страниц подписки" + [SELECT_SUB_PAGE_CUSTOM2]="Шаблоны страниц подписки\nЗапускать только на сервере с панелью" + [SELECT_SUB_PAGE_CUSTOM3]="Списки приложений для оригинальной страницы подписки:" + [SELECT_SUB_PAGE_CUSTOM4]="Кастомные страницы подписки:" + [SUB_PAGE_SELECT_CHOICE]="Недопустимый выбор. Пожалуйста, выберите от 0 до 2." + [RESTORE_SUB_PAGE]="Восстановить шаблон страницы подписки по умолчанию" + [CONTAINER_NOT_FOUND]="Контейнер %s не найден" + [SUB_WITH_APPCONFIG_ASK]="Добавить файл конфигурации app-config.json?" + [SUB_WITH_APPCONFIG_OPTION1]="Простой список приложений clash&sing" + [SUB_WITH_APPCONFIG_OPTION2]="Множественный список приложений" + [SUB_WITH_APPCONFIG_OPTION3]="Список приложений с поддержкой HWID" + [SUB_WITH_APPCONFIG_SKIP]="Нет, пропустить добавление конфигурации" + [SUB_WITH_APPCONFIG_INVALID]="Неверный выбор, конфигурация не будет добавлена" + [SUB_WITH_APPCONFIG_INPUT]="Выберите действие (0–3):" + # Custom Branding + [BRANDING_SUPPORT_ASK]="Добавить поддержку брендирования страницы подписки?" + [BRANDING_SUPPORT_YES]="Да, добавить поддержку брендирования" + [BRANDING_SUPPORT_NO]="Нет, пропустить брендирование" + [BRANDING_NAME_PROMPT]="Введите название вашего бренда:" + [BRANDING_SUPPORT_URL_PROMPT]="Введите ссылку на страницу поддержки:" + [BRANDING_LOGO_URL_PROMPT]="Введите ссылку на логотип вашего бренда:" + [BRANDING_ADDED_SUCCESS]="Конфигурация брендирования успешно добавлена" + [CUSTOM_APP_LIST_MENU]="Редактирование кастомного списка приложений и брендирования" + [CUSTOM_APP_LIST_PANEL_MESSAGE]="Редактирование кастомного списка приложений и брендирования теперь осуществляется на стороне панели" + [CUSTOM_APP_LIST_NOT_FOUND]="Кастомный список приложений не найден" + [EDIT_BRANDING]="Редактирование брендирования" + [EDIT_LOGO]="Изменить логотип" + [EDIT_NAME]="Изменить имя в брендировании" + [EDIT_SUPPORT_URL]="Изменить ссылку поддержки" + [DELETE_APPS]="Удалить определенные приложения" + [BRANDING_CURRENT_VALUES]="Текущие значения брендирования:" + [BRANDING_LOGO_URL]="URL логотипа:" + [BRANDING_NAME]="Имя:" + [BRANDING_SUPPORT_URL]="URL поддержки:" + [ENTER_NEW_LOGO]="Введите новый URL логотипа:" + [ENTER_NEW_NAME]="Введите новое имя бренда:" + [ENTER_NEW_SUPPORT]="Введите новую ссылку поддержки:" + [CONFIRM_CHANGE]="Подтвердить изменение? (y/n):" + [PLATFORM_SELECT]="Выберите платформу:" + [APP_SELECT]="Какое приложение вы хотите удалить?" + [CONFIRM_DELETE_APP]="Вы точно хотите удалить приложение %s из списка платформы %s? (y/n):" + [APP_DELETED_SUCCESS]="Приложение успешно удалено" + [NO_APPS_FOUND]="Приложения не найдены в этой платформе" + [RENEWAL_CONF_NOT_FOUND]="Файл конфигурации обновления сертификатов не найден." + [ARCHIVE_DIR_MISMATCH]="Несоответствие директории архива в конфигурации." + [CERT_VERSION_NOT_FOUND]="Не удалось определить версию сертификатов." + [RESULTS_CERTIFICATE_UPDATES]="Результаты обновления сертификатов:" + [CERTIFICATE_FOR]="Сертификат для " + [SUCCESSFULLY_UPDATED]="успешно обновлен" + [FAILED_TO_UPDATE_CERTIFICATE_FOR]="Не удалось обновить сертификат для " + [ERROR_CHECKING_EXPIRY_FOR]="Ошибка проверки даты истечения для " + [DOES_NOT_REQUIRE_UPDATE]="не требует обновления (" + [UPDATED]="Обновлен" + [REMAINING]="Осталось" + [ERROR_UPDATE]="Ошибка обновления" + [ALREADY_EXPIRED]="уже истек" + [CERT_CLOUDFLARE_FILE_NOT_FOUND]="Файл учетных данных Cloudflare не найден." + [TELEGRAM_OAUTH_WARNING]="Включена авторизация через Telegram (TELEGRAM_OAUTH_ENABLED=true)." + [CREATE_API_TOKEN_INSTRUCTION]="Зайдите в панель по адресу: https://%s\nПерейдите в раздел 'API токены' -> 'Создать новый токен' и создайте токен.\nСкопируйте созданный токен и введите его ниже." + [ENTER_API_TOKEN]="Введите API-токен: " + [EMPTY_TOKEN_ERROR]="Токен не введен. Завершение работы." + [RATE_LIMIT_EXCEEDED]="Превышен лимит выдачи сертификатов Let's Encrypt" + [FAILED_TO_MODIFY_HTML_FILES]="Не удалось изменить HTML файлы" + [INSTALLING_YQ]="Установка yq..." + [ERROR_SETTING_YQ_PERMISSIONS]="Ошибка установки прав yq!" + [YQ_SUCCESSFULLY_INSTALLED]="yq успешно установлен!" + [YQ_DOESNT_WORK_AFTER_INSTALLATION]="Ошибка: yq не работает после установки!" + [ERROR_DOWNLOADING_YQ]="Ошибка загрузки yq!" + [FAST_START]="Быстрый запуск: remnawave_reverse" + [CREATING_API_TOKEN]="Создание API токена для Subscription Page..." + [API_TOKEN_ADDED]="API токен Subscription Page успешно добавлен в docker-compose.yml" + [ERROR_CREATE_API_TOKEN]="Ошибка создания API токена" + [ERROR_API_TOKEN]="Не удалось добавить API токен" + [STOPPING_REMNAWAVE_SUBSCRIPTION_PAGE]="Остановка Remnawave Subscription Page..." + [STARTING_REMNAWAVE_SUBSCRIPTION_PAGE]="Запуск Remnawave Subscription Page..." + [DOWNLOAD_FALLBACK]="Попытка резервного метода загрузки..." + [WARP_DELETE_SUCCESS]="WARP успешно удалён" + [UPDATING_SQUAD]="Обновление squad" + [PORT_8443_NOT_CONFIGURED]="Порт 8443 не настроен в docker-compose.yml" + [ARCHIVE_NOT_FOUND]="Директория archive не найдена" + [FILE_NOT_FOUND]="Файл не найден:" + [UPDATED_RENEW_AUTH]="Обновлён hook обновления сертификата" + [ERROR_CREATE_CONFIG_PROFILE]="Ошибка создания профиля конфигурации" + [ERROR_EXTRACT_PRIVATE_KEY]="Не удалось извлечь приватный ключ" + [INVALID_CERT_METHOD]="Неверный метод получения сертификата" + [WARNING_PANEL_NODE_TOGETHER]="ВНИМАНИЕ: Установка панели и ноды на одном сервере не рекомендуется для продакшн среды!\nРекомендуется устанавливать панель и ноды на разных серверах для лучшей производительности и безопасности." + [PANEL_DIR]="/opt/remnawave" + [NODE_DIR]="/opt/remnanode" ) -show_language() { - echo -e "${COLOR_GREEN}${LANG[CHOOSE_LANG]}${COLOR_RESET}" - echo -e "" - echo -e "${COLOR_YELLOW}1. ${LANG[LANG_EN]}${COLOR_RESET}" - echo -e "${COLOR_YELLOW}2. ${LANG[LANG_RU]}${COLOR_RESET}" - echo -e "" -} - -set_language() { - case $1 in - en) - LANG=( - #Alias - [ALIAS_ADDED]="Alias 'rr' for 'remnawave_reverse' added to %s" - [ALIAS_ACTIVATE_GLOBAL]="Alias 'rr' is now available for all users. Run 'source %s' or open a new terminal to apply." - #Lang - [CHOOSE_LANG]="Select language:" - [LANG_EN]="English" - [LANG_RU]="Русский" - #check - [ERROR_ROOT]="Script must be run as root" - [ERROR_OS]="Supported only Debian 11/12 and Ubuntu 22.04/24.04" - #Install Packages - [ERROR_UPDATE_LIST]="Failed to update package list" - [ERROR_INSTALL_PACKAGES]="Failed to install required packages" - [ERROR_INSTALL_CRON]="Failed to install cron" - [ERROR_START_CRON]="Failed to start cron" - [ERROR_CONFIGURE_LOCALES]="Error: Failed to configure locales" - [ERROR_DOWNLOAD_DOCKER_KEY]="Failed to download Docker GPG key" - [ERROR_UPDATE_DOCKER_LIST]="Failed to update package list after adding Docker repository" - [ERROR_INSTALL_DOCKER]="Failed to install Docker" - [ERROR_DOCKER_NOT_INSTALLED]="Docker is not installed" - [ERROR_START_DOCKER]="Failed to start Docker" - [ERROR_ENABLE_DOCKER]="Failed to enable Docker auto-start" - [ERROR_DOCKER_NOT_WORKING]="Docker is not working properly" - [ERROR_CONFIGURE_UFW]="Failed to configure UFW" - [ERROR_CONFIGURE_UPGRADES]="Failed to configure unattended-upgrades" - [ERROR_DOCKER_DNS]="Error: Unable to resolve download.docker.com. Check your DNS settings." - [ERROR_INSTALL_CERTBOT]="Error: Failed to install certbot" - [SUCCESS_INSTALL]="All packages installed successfully" - #Menu - [MENU_TITLE]="REMNAWAVE REVERSE-PROXY by eGames" - [AVAILABLE_UPDATE]="update available" - [VERSION_LABEL]="Version: %s" - [EXIT]="Exit" - [MENU_1]="Install Remnawave Components" - [MENU_2]="Reinstall panel/node" - [MENU_3]="Manage Panel/Node" - [MENU_4]="Install random template for selfsteal node" - [MENU_5]="Custom subscriptions by legiz" - [MENU_6]="Extensions by distillium" - [MENU_7]="Manage IPv6" - [MENU_8]="Manage certificates domain" - [MENU_9]="Check for updates script" - [MENU_10]="Remove script" - [PROMPT_ACTION]="Select action (0-10):" - [INVALID_CHOICE]="Invalid choice. Please select 0-10" - [WARNING_LABEL]="WARNING:" - [CONFIRM_PROMPT]="Enter 'y' to continue or 'n' to exit (y/n):" - [WARNING_NODE_PANEL]="Adding a node should only be done on the server where the panel is installed, not on the node server." - [CONFIRM_SERVER_PANEL]="Are you sure you are on the server with the installed panel?" - #Remove Script - [REMOVE_SCRIPT_ONLY]="Remove script and its local files" - [REMOVE_SCRIPT_AND_PANEL]="Remove script and remnawave panel/node data" - [CONFIRM_REMOVE_SCRIPT]="All script data will be removed from the server. Are you sure? (y/n): " - [CONFIRM_REMOVE_ALL]="All script and panel/node data will be removed from the server. Are you sure? (y/n): " - [SCRIPT_REMOVED]="Script and its local files successfully removed!" - [ALL_REMOVED]="Script and panel/node data successfully removed!" - #Extensions by distillium - [EXTENSIONS_MENU]="Extensions by distillium" - [EXTENSIONS_MENU_TITLE]="Manage Extensions by distillium" - [EXTENSIONS_PROMPT]="Select action (0-2):" - [EXTENSIONS_INVALID_CHOICE]="Invalid choice. Please select 0-2." - [BACKUP_RESTORE]="Backup and Restore" - #Warp by distillium - [WARP_MENU]="WARP Native" - [WARP_MENU_TITLE]="Manage WARP Native" - [WARP_INSTALL]="Install WARP Native" - [WARP_ADD_CONFIG]="Add WARP-configuration to node configuration" - [WARP_DELETE_WARP_SETTINGS]="Remove WARP-configuration from node configuration" - [WARP_CONFIRM_SERVER_PANEL]="Are you sure you are on the server with the installed panel?\nAdding WARP-configuration should only be done on the server where the panel is installed, not on the node server" - [WARP_UNINSTALL]="Uninstall WARP Native" - [WARP_PROMPT]="Select action (0-4):" - [WARP_PROMPT1]="Select action:" - [WARP_INVALID_CHOICE]="Invalid choice. Please select 0-4." - [WARP_INVALID_CHOICE2]="Invalid choice." - [WARP_NO_NODE]="Node Remnawave not found. First install the node." - [WARP_SELECT_CONFIG]="Select node to add WARP-configuration?" - [WARP_SELECT_CONFIG_DELETE]="Select node to remove WARP-configuration?" - [WARP_NO_CONFIGS]="No configurations found." - [WARP_UPDATE_SUCCESS]="Configuration successfully updated!" - [WARP_UPDATE_FAIL]="Failed to update configuration." - [WARP_WARNING]="warp-out already exists in outbounds." - [WARP_WARNING2]="warp rule already exists in routing rules." - [WARP_REMOVED_WARP_SETTINGS1]="Removed warp-out from outbounds" - [WARP_NO_WARP_SETTINGS1]="warp-out not found in outbounds" - [WARP_REMOVED_WARP_SETTINGS2]="Removed warp rule from routing rules" - [WARP_NO_WARP_SETTINGS2]="warp rule not found in routing rules" - #Manage Panel/Node - [START_PANEL_NODE]="Start panel/node" - [STOP_PANEL_NODE]="Stop panel/node" - [UPDATE_PANEL_NODE]="Update panel/node" - [VIEW_LOGS]="View logs" - [PRESS_ENTER_RETURN_MENU]="Press Enter to return to the menu..." - [REMNAWAVE_CLI]="Remnawave CLI" - [ACCESS_PANEL]="Access panel via port 8443 (Only for panel + node)" - [MANAGE_PANEL_NODE_PROMPT]="Select action (0-6):" - [MANAGE_PANEL_NODE_INVALID_CHOICE]="Invalid choice. Please select 0-6." - #Manage Certificates - [CERT_UPDATE]="Update current certificates" - [CERT_GENERATE]="Generate new certificates for another domain" - [CERT_PROMPT1]="Select action (0-2):" - [CERT_INVALID_CHOICE]="Invalid choice. Please select 0-2." - [CERT_UPDATE_CHECK]="Checking certificate generation method..." - [CERT_UPDATE_SUCCESS]="Certificates successfully updated." - [CERT_UPDATE_FAIL]="Failed to update certificates." - [CERT_GENERATE_PROMPT]="Enter the domain for new certificates (e.g., example.com):" - [CERT_METHOD_UNKNOWN]="Unknown certificate generation method." - [CERT_NOT_DUE]="Certificate for %s is not yet due for renewal." - #Install Remnawave Components - [INSTALL_MENU_TITLE]="Install Remnawave Components" - [INSTALL_PANEL_NODE]="Install panel and node on one server" - [INSTALL_PANEL]="Install only the panel" - [INSTALL_ADD_NODE]="Add node to panel" - [INSTALL_NODE]="Install only the node" - [INSTALL_PROMPT]="Select action (0-3):" - [INSTALL_INVALID_CHOICE]="Invalid choice. Please select 0-3." - #Manage IPv6 - [IPV6_MENU_TITLE]="Manage IPv6" - [IPV6_ENABLE]="Enable IPv6" - [IPV6_DISABLE]="Disable IPv6" - [IPV6_PROMPT]="Select action (0-2):" - [IPV6_INVALID_CHOICE]="Invalid choice. Please select 0-2." - [IPV6_ALREADY_ENABLED]="IPv6 already enabled" - [IPV6_ALREADY_DISABLED]="IPv6 already disabled" - [ENABLE_IPV6]="Enabling IPv6..." - [IPV6_ENABLED]="IPv6 has been enabled." - [DISABLING_IPV6]="Disabling IPv6..." - [IPV6_DISABLED]="IPv6 has been disabled." - #Remna - [INSTALL_PACKAGES]="Installing required packages..." - [INSTALLING]="Installing panel and node" - [INSTALLING_PANEL]="Installing panel" - [INSTALLING_NODE]="Installing node" - [ENTER_PANEL_DOMAIN]="Enter panel domain (e.g. panel.example.com):" - [ENTER_SUB_DOMAIN]="Enter subscription domain (e.g. sub.example.com):" - [ENTER_NODE_DOMAIN]="Enter selfsteal domain for node (e.g. node.example.com):" - [ENTER_CF_TOKEN]="Enter your Cloudflare API token or global API key:" - [ENTER_CF_EMAIL]="Enter your Cloudflare registered email:" - [ENTER_GCORE_TOKEN]="Enter Gcore API token:" - [CERT_GCORE_FILE_NOT_FOUND]="Gcore credentials file not found. Please re-enter token." - [CHECK_CERTS]="Checking certificates..." - [CERT_FOUND]="Certificates found in /etc/letsencrypt/live/" - [CF_VALIDATING]="Cloudflare API key and email are valid" - [CF_INVALID]="Invalid Cloudflare API token or email after %d attempts." - [CF_INVALID_ATTEMPT]="Invalid Cloudflare API key or email. Attempt %d of %d." - [WAITING]="Please wait..." - #API - [REGISTERING_REMNAWAVE]="Registration in Remnawave" - [CHECK_CONTAINERS]="Checking containers availability..." - [CONTAINERS_NOT_READY_ATTEMPT]="Containers are not ready, waiting... Attempt %d of %d." - [CONTAINERS_TIMEOUT]="Containers not ready after %d attempts.\n\nCheck logs:\n cd /opt/remnawave && docker compose logs -f\n\nAlso check typical Docker issues:\n https://wiki.egam.es/troubleshooting/docker-issues/" - [REGISTRATION_SUCCESS]="Registration completed successfully!" - [GET_PUBLIC_KEY]="Getting public key..." - [PUBLIC_KEY_SUCCESS]="Public key successfully obtained" - [GENERATE_KEYS]="Generating x25519 keys..." - [GENERATE_KEYS_SUCCESS]="Keys successfully generated" - [ERROR_NO_CONFIGS]="No config profiles found" - [NO_DEFAULT_PROFILE]="Default-Profile not found" - [ERROR_DELETE_PROFILE]="Failed to delete profile" - [CREATING_CONFIG_PROFILE]="Creating config profile..." - [CONFIG_PROFILE_CREATED]="Config profile successfully created" - [CREATING_NODE]="Creating node" - [NODE_CREATED]="Node successfully created" - [CREATE_HOST]="Creating host" - [HOST_CREATED]="Host successfully created" - [GET_DEFAULT_SQUAD]="Getting default squad" - [UPDATE_SQUAD]="Squad successfully updated" - [NO_SQUADS_FOUND]="No squads found" - [INVALID_UUID_FORMAT]="Invalid UUID format" - [NO_VALID_SQUADS_FOUND]="No valid squads found" - [ERROR_GET_SQUAD]="Failed to get squad" - [INVALID_SQUAD_UUID]="Invalid squad UUID" - [INVALID_INBOUND_UUID]="Invalid inbound UUID" - #Stop/Start/Update - [CHANGE_DIR_FAILED]="Failed to change to directory %s" - [DIR_NOT_FOUND]="Directory /opt/remnawave not found" - [PANEL_RUNNING]="Panel/node already running" - [PANEL_RUN]="Panel/node running" - [PANEL_STOP]="Panel/node stopped" - [PANEL_STOPPED]="Panel/node already stopped" - [NO_UPDATE]="No updates available for panel/node" - [UPDATING]="Updating panel/node..." - [UPDATE_SUCCESS1]="Panel/node successfully updated" - [STARTING_PANEL_NODE]="Starting panel and node" - [STARTING_PANEL]="Starting panel" - [STARTING_NODE]="Starting node" - [STOPPING_REMNAWAVE]="Stopping panel and node" - [IMAGES_DETECTED]="Images detected, restarting containers..." - #Menu End - [INSTALL_COMPLETE]=" INSTALLATION COMPLETE!" - [PANEL_ACCESS]="Panel URL:" - [ADMIN_CREDS]="To log into the panel, use the following data:" - [USERNAME]="Username:" - [PASSWORD]="Password:" - [RELAUNCH_CMD]="To relaunch the manager, use the following command:" - #RandomHTML - [RANDOM_TEMPLATE]="Installing random template for camouflage site" - [DOWNLOAD_FAIL]="Download failed, retrying..." - [UNPACK_ERROR]="Error unpacking archive" - [TEMPLATE_COPY]="Template copied to /var/www/html/" - [SELECT_TEMPLATE]="Selected template:" - #Error - [ERROR_TOKEN]="Failed to get token." - [ERROR_PUBLIC_KEY]="Failed to get public key." - [ERROR_EXTRACT_PUBLIC_KEY]="Failed to extract public key from response." - [ERROR_GENERATE_KEYS]="Failed to generate keys." - [ERROR_EMPTY_RESPONSE_NODE]="Empty response from server when creating node." - [ERROR_CREATE_NODE]="Failed to create node." - [ERROR_EMPTY_RESPONSE_HOST]="Empty response from server when creating host." - [ERROR_CREATE_HOST]="Failed to create host." - [ERROR_EMPTY_RESPONSE_REGISTER]="Registration error - empty server response" - [ERROR_REGISTER]="Registration error" - [ERROR_UPDATE_SQUAD]="Failed to update squad" - [ERROR_GET_SQUAD_LIST]="Failed to get squad list" - [NO_SQUADS_TO_UPDATE]="No squads to update" - #Reinstall Panel/Node - [REINSTALL_WARNING]="All data panel/node will be deleted from the server. Are you sure? (y/n):" - [REINSTALL_TYPE_TITLE]="Select reinstallation method:" - [REINSTALL_PROMPT]="Select action (0-3):" - [INVALID_REINSTALL_CHOICE]="Invalid choice. Please select 0-3." - [POST_PANEL_MESSAGE]="Panel successfully installed!" - [POST_PANEL_INSTRUCTION]="To install the node, follow these steps:\n1. Run this script on the server where the node will be installed.\n2. Select 'Install Remnawave Components', then 'Install only the node'." - [SELFSTEAL_PROMPT]="Enter the selfsteal domain for the node (e.g. node.example.com):" - [SELFSTEAL]="Enter the selfsteal domain for the node specified during panel installation:" - [PANEL_IP_PROMPT]="Enter the IP address of the panel to establish a connection between the panel and the node:" - [IP_ERROR]="Enter a valid IP address in the format X.X.X.X (e.g., 192.168.1.1)" - [CERT_PROMPT]="Enter the certificate obtained from the panel (paste the content and press Enter twice):" - [CERT_CONFIRM]="Are you sure the certificate is correct? (y/n):" - [ABORT_MESSAGE]="Installation aborted by user" - [SUCCESS_MESSAGE]="Node successfully connected" - #Node Check - [NODE_CHECK]="Checking node connection for %s..." - [NODE_ATTEMPT]="Attempt %d of %d..." - [NODE_UNAVAILABLE]="Node is unavailable on attempt %d." - [NODE_LAUNCHED]="Node successfully launched!" - [NODE_NOT_CONNECTED]="Node not connected after %d attempts!" - [CHECK_CONFIG]="Check the configuration or restart the panel." - #Add node to panel - [ADD_NODE_TO_PANEL]="Adding node to panel" - [ENTER_NODE_NAME]="Enter node name (e.g., Germany):" - [USING_SAVED_TOKEN]="Using saved token..." - [INVALID_SAVED_TOKEN]="Saved token is invalid. Requesting a new one..." - [ENTER_PANEL_USERNAME]="Enter panel username: " - [ENTER_PANEL_PASSWORD]="Enter panel password: " - [TOKEN_RECEIVED_AND_SAVED]="Token successfully received and saved" - [TOKEN_USED_SUCCESSFULLY]="Token successfully used" - [DOMAIN_ALREADY_EXISTS]="Domain already exists" - [TRY_ANOTHER_DOMAIN]="Please use another domain" - [ERROR_CHECK_DOMAIN]="Error checking domain" - [NODE_ADDED_SUCCESS]="Node successfully added!" - [CREATE_NEW_NODE]="Creating new node for %s..." - [CF_INVALID_NAME]="Error: The name of the configuration profile %s is already in use.\nPlease choose another name." - [CF_INVALID_LENGTH]="Error: The name of the configuration profile should contain from 3 to 20 characters." - [CF_INVALID_CHARS]="Error: The name of the configuration profile should contain only English letters, numbers, and hyphens." - #check - [CHECK_UPDATE]="Check for updates" - [GENERATING_CERTS]="Generating certificates for %s" - [REQUIRED_DOMAINS]="Required domains for certificates:" - [CHECK_DOMAIN_IP_FAIL]="Failed to determine the domain or server IP address." - [CHECK_DOMAIN_IP_FAIL_INSTRUCTION]="Ensure that the domain %s is correctly configured and points to this server (%s)." - [CHECK_DOMAIN_CLOUDFLARE]="The domain %s points to a Cloudflare IP (%s)." - [CHECK_DOMAIN_CLOUDFLARE_INSTRUCTION]="Cloudflare proxying is not allowed for the selfsteal domain. Disable proxying (switch to 'DNS Only')." - [CHECK_DOMAIN_MISMATCH]="The domain %s points to IP address %s, which differs from this server's IP (%s)." - [CHECK_DOMAIN_MISMATCH_INSTRUCTION]="For proper operation, the domain must point to the current server." - [NO_PANEL_NODE_INSTALLED]="Panel or node is not installed. Please install panel or node first." - #update - [UPDATE_AVAILABLE]="A new version of the script is available: %s (current version: %s)." - [UPDATE_CONFIRM]="Update the script? (y/n):" - [UPDATE_CANCELLED]="Update cancelled by user." - [UPDATE_SUCCESS]="Script successfully updated to version %s!" - [UPDATE_FAILED]="Error downloading the new version of the script." - [VERSION_CHECK_FAILED]="Could not determine the version of the remote script. Skipping update." - [LATEST_VERSION]="You already have the latest version of the script (%s)." - [RESTART_REQUIRED]="Please restart the script to apply changes." - [LOCAL_FILE_NOT_FOUND]="Local script file not found, downloading new version..." - #CLI - [RUNNING_CLI]="Running Remnawave CLI..." - [CLI_SUCCESS]="Remnawave CLI executed successfully!" - [CLI_FAILED]="Failed to execute Remnawave CLI. Ensure the 'remnawave' container is running." - [CONTAINER_NOT_RUNNING]="Container 'remnawave' is not running. Please start it first." - #Cert_choise - [CERT_METHOD_PROMPT]="Select certificate generation method for all domains:" - [CERT_METHOD_CF]="Cloudflare API (supports wildcard)" - [CERT_METHOD_ACME]="ACME HTTP-01 (single domain, no wildcard)" - [CERT_METHOD_GCORE]="Gcore DNS API (supports wildcard)" - [ERROR_INSTALL_GCORE_PLUGIN]="Failed to install certbot-dns-gcore plugin" - [CERT_METHOD_CHOOSE]="Select action (0-3):" - [EMAIL_PROMPT]="Enter your email for Let's Encrypt registration:" - [CERTS_SKIPPED]="All certificates already exist. Skipping generation." - [ACME_METHOD]="Using ACME (Let's Encrypt) with HTTP-01 challenge (no wildcard support)..." - [CERT_GENERATION_FAILED]="Certificate generation failed. Please check your input and DNS settings." - [ADDING_CRON_FOR_EXISTING_CERTS]="Adding cron job for certificate renewal..." - [CRON_ALREADY_EXISTS]="Cron job for certificate renewal already exists." - [CERT_NOT_FOUND]="Certificate not found for domain." - [ERROR_PARSING_CERT]="Error parsing certificate expiry date." - [CERT_EXPIRY_SOON]="Certificates will expire soon in" - [DAYS]="days" - [UPDATING_CRON]="Updating cron job to match certificate expiry." - [GENERATING_WILDCARD_CERT]="Generating wildcard certificate for" - [WILDCARD_CERT_FOUND]="Wildcard certificate found in /etc/letsencrypt/live/" - [FOR_DOMAIN]="for" - [START_CRON_ERROR]="Not able to start cron. Please start it manually." - [DOMAINS_MUST_BE_UNIQUE]="Error: All domains (panel, subscription, and node) must be unique." - [CHOOSE_TEMPLATE_SOURCE]="Select template source:" - [SIMPLE_WEB_TEMPLATES]="Simple web templates" - [SNI_TEMPLATES]="Sni templates" - [NOTHING_TEMPLATES]="Nothing Sni templates" - [CHOOSE_TEMPLATE_OPTION]="Select action (0-3):" - [INVALID_TEMPLATE_CHOICE]="Invalid choice. Please select 0-3." - # Manage Panel Access - [PORT_8443_OPEN]="Open port 8443 for panel access" - [PORT_8443_CLOSE]="Close port 8443 for panel access" - [PORT_8443_IN_USE]="Port 8443 already in use by another process. Check which services are using the port and free it." - [NO_PORT_CHECK_TOOLS]="Port checking tools (ss or netstat) not found. Install one of them." - [OPEN_PANEL_LINK]="Your panel access link:" - [PORT_8443_WARNING]="Don't forget, port 8443 is now open to the world. After fixing the panel, select the option to close port 8443." - [PORT_8443_CLOSED]="Port 8443 has been closed." - [NGINX_CONF_NOT_FOUND]="File nginx.conf not found in $dir" - [NGINX_CONF_ERROR]="Failed to extract necessary parameters from nginx.conf" - [NGINX_CONF_MODIFY_FAILED]="Failed to modify nginx.conf" - [PORT_8443_ALREADY_CONFIGURED]="Port 8443 already configured in nginx.conf" - [UFW_RELOAD_FAILED]="Failed to reload UFW." - [PORT_8443_ALREADY_CLOSED]="Port 8443 already closed in UFW." - #Legiz Extensions - [LEGIZ_EXTENSIONS_PROMPT]="Select action (0-2):" - # Sub Page Upload - [UPLOADING_SUB_PAGE]="Uploading custom sub page template..." - [ERROR_FETCH_SUB_PAGE]="Failed to fetch custom sub page template." - [SUB_PAGE_UPDATED_SUCCESS]="Custom sub page template successfully updated." - [SELECT_SUB_PAGE_CUSTOM]="Select action (0-7):" - [SELECT_SUB_PAGE_CUSTOM1]="Custom Sub Page Templates" - [SELECT_SUB_PAGE_CUSTOM2]="Custom Sub Page Templates\nOnly run on panel server" - [SELECT_SUB_PAGE_CUSTOM3]="Custom App Lists for original sub page:" - [SELECT_SUB_PAGE_CUSTOM4]="Custom Sub Page:" - [SUB_PAGE_SELECT_CHOICE]="Invalid choice. Please select 0-7." - [RESTORE_SUB_PAGE]="Restore default sub page" - [CONTAINER_NOT_FOUND]="Container %s not found" - [SUB_WITH_APPCONFIG_ASK]="Do you want to include app-config.json?" - [SUB_WITH_APPCONFIG_OPTION1]="Yes, use config from option 1 (Simple custom app list)" - [SUB_WITH_APPCONFIG_OPTION2]="Yes, use config from option 2 (Multiapp custom app list)" - [SUB_WITH_APPCONFIG_OPTION3]="Yes, use config from option 3 (HWID only app list)" - [SUB_WITH_APPCONFIG_SKIP]="No, skip app-config.json" - [SUB_WITH_APPCONFIG_INVALID]="Invalid option, skipping app-config.json" - [SUB_WITH_APPCONFIG_INPUT]="Select action (0-3):" - # Custom Branding - [BRANDING_SUPPORT_ASK]="Add branding support to subscription page?" - [BRANDING_SUPPORT_YES]="Yes, add branding support" - [BRANDING_SUPPORT_NO]="No, skip branding" - [BRANDING_NAME_PROMPT]="Enter your brand name:" - [BRANDING_SUPPORT_URL_PROMPT]="Enter your support page URL:" - [BRANDING_LOGO_URL_PROMPT]="Enter your brand logo URL:" - [BRANDING_ADDED_SUCCESS]="Branding configuration successfully added" - [CUSTOM_APP_LIST_MENU]="Edit custom application list and branding" - [CUSTOM_APP_LIST_NOT_FOUND]="Custom application list not found" - [EDIT_BRANDING]="Edit branding" - [EDIT_LOGO]="Change logo" - [EDIT_NAME]="Change name in branding" - [EDIT_SUPPORT_URL]="Change support link" - [DELETE_APPS]="Delete specific applications" - [BRANDING_CURRENT_VALUES]="Current branding values:" - [BRANDING_LOGO_URL]="Logo URL:" - [BRANDING_NAME]="Name:" - [BRANDING_SUPPORT_URL]="Support URL:" - [ENTER_NEW_LOGO]="Enter new logo URL:" - [ENTER_NEW_NAME]="Enter new brand name:" - [ENTER_NEW_SUPPORT]="Enter new support URL:" - [CONFIRM_CHANGE]="Confirm change? (y/n):" - [PLATFORM_SELECT]="Select platform:" - [APP_SELECT]="Which application do you want to delete?" - [CONFIRM_DELETE_APP]="Are you sure you want to delete application %s from platform %s? (y/n):" - [APP_DELETED_SUCCESS]="Application successfully deleted" - [NO_APPS_FOUND]="No applications found in this platform" - [RENEWAL_CONF_NOT_FOUND]="Renewal configuration file not found." - [ARCHIVE_DIR_MISMATCH]="Archive directory mismatch in configuration." - [CERT_VERSION_NOT_FOUND]="Failed to determine certificate version." - [RESULTS_CERTIFICATE_UPDATES]="Results of certificate updates:" - [CERTIFICATE_FOR]="Certificate for " - [SUCCESSFULLY_UPDATED]="successfully updated" - [FAILED_TO_UPDATE_CERTIFICATE_FOR]="Failed to update certificate for " - [ERROR_CHECKING_EXPIRY_FOR]="Error checking expiry date for " - [DOES_NOT_REQUIRE_UPDATE]="does not require updating (" - [UPDATED]="Updated" - [REMAINING]="Remaining" - [ERROR_UPDATE]="Error updating" - [ALREADY_EXPIRED]="already expired" - [CERT_CLOUDFLARE_FILE_NOT_FOUND]="Cloudflare credentials file not found." - [TELEGRAM_OAUTH_WARNING]="Telegram OAuth is enabled (TELEGRAM_OAUTH_ENABLED=true)." - [CREATE_API_TOKEN_INSTRUCTION]="Go to the panel at: https://%s\nNavigate to 'API Tokens' -> 'Create New Token' and create a token.\nCopy the created token and enter it below." - [ENTER_API_TOKEN]="Enter the API token: " - [EMPTY_TOKEN_ERROR]="No token provided. Exiting." - [RATE_LIMIT_EXCEEDED]="Rate limit exceeded for Let's Encrypt" - [FAILED_TO_MODIFY_HTML_FILES]="Failed to modify HTML files" - [INSTALLING_YQ]="Installing yq..." - [ERROR_SETTING_YQ_PERMISSIONS]="Error setting yq permissions!" - [YQ_SUCCESSFULLY_INSTALLED]="yq successfully installed!" - [YQ_DOESNT_WORK_AFTER_INSTALLATION]="Error: yq doesn't work after installation!" - [ERROR_DOWNLOADING_YQ]="Error downloading yq!" - [FAST_START]="Quick start: remnawave_reverse" - [CREATING_API_TOKEN]="Creating API token for Subscription Page..." - [API_TOKEN_ADDED]="API token Subscription Page successfully added to docker-compose.yml" - [ERROR_CREATE_API_TOKEN]="Error creating API token" - [ERROR_API_TOKEN]="Failed to add API token" - [STOPPING_REMNAWAVE_SUBSCRIPTION_PAGE]="Stopping Remnawave Subscription Page..." - [STARTING_REMNAWAVE_SUBSCRIPTION_PAGE]="Starting Remnawave Subscription Page..." - ) - ;; - ru) - LANG=( - #Alias - [ALIAS_ADDED]="Алиас 'rr' для 'remnawave_reverse' добавлен в %s" - [ALIAS_ACTIVATE_GLOBAL]="Алиас 'rr' теперь доступен для всех пользователей. Выполните 'source %s' или перезапустите терминал, чтобы применить алиас." - #Check - [ERROR_ROOT]="Скрипт нужно запускать с правами root" - [ERROR_OS]="Поддержка только Debian 11/12 и Ubuntu 22.04/24.04" - [MENU_TITLE]="REMNAWAVE REVERSE-PROXY by eGames" - [AVAILABLE_UPDATE]="доступно обновление" - [VERSION_LABEL]="Версия: %s" - #Install Packages - [ERROR_UPDATE_LIST]="Ошибка: Не удалось обновить список пакетов" - [ERROR_INSTALL_PACKAGES]="Ошибка: Не удалось установить необходимые пакеты" - [ERROR_INSTALL_CRON]="Ошибка: Не удалось установить cron" - [ERROR_START_CRON]="Ошибка: Не удалось запустить cron" - [ERROR_CONFIGURE_LOCALES]="Ошибка: Не удалось настроить локали" - [ERROR_DOWNLOAD_DOCKER_KEY]="Ошибка: Не удалось загрузить ключ Docker" - [ERROR_UPDATE_DOCKER_LIST]="Ошибка: Не удалось обновить список пакетов после добавления репозитория Docker" - [ERROR_INSTALL_DOCKER]="Ошибка: Не удалось установить Docker" - [ERROR_DOCKER_NOT_INSTALLED]="Ошибка: Docker не установлен" - [ERROR_START_DOCKER]="Ошибка: Не удалось запустить Docker" - [ERROR_ENABLE_AUTOSTART_DOCKER]="Ошибка: Не удалось включить автозапуск Docker" - [ERROR_DOCKER_NOT_WORKING]="Ошибка: Docker не работает корректно" - [ERROR_CONFIGURE_UFW]="Ошибка: Не удалось настроить UFW" - [ERROR_CONFIGURE_UPGRADES]="Ошибка: Не удалось настроить unattended-upgrades" - [ERROR_DOCKER_DNS]="Ошибка: Не удалось разрешить домен download.docker.com. Проверьте настройки DNS." - [ERROR_INSTALL_CERTBOT]="Ошибка: Не удалось установить certbot" - [SUCCESSFUL_INSTALL]="Все пакеты успешно установлены" - #Main menu - [EXIT]="Выход" - [MENU_1]="Установка компонентов Remnawave" - [MENU_2]="Переустановить панель/ноду" - [MENU_3]="Управление панелью/нодой" - [MENU_4]="Установить случайный шаблон для selfsteal ноды" - [MENU_5]="Кастомные подписки от legiz" - [MENU_6]="Управление расширениями от distillium" - [MENU_7]="Управление IPv6" - [MENU_8]="Управление сертификатами домена" - [MENU_9]="Проверить обновления скрипта" - [MENU_10]="Удалить скрипт" - [PROMPT_ACTION]="Выберите действие (0-10):" - [INVALID_CHOICE]="Неверный выбор. Выберите 0-10." - [WARNING_LABEL]="ВНИМАНИЕ:" - [CONFIRM_PROMPT]="Введите 'y' для продолжения или 'n' для выхода (y/n):" - [WARNING_NODE_PANEL]="Добавление ноды должно выполняться только на сервере, где установлена панель, а не на сервере ноды." - [CONFIRM_SERVER_PANEL]="Вы уверены, что находитесь на сервере с установленной панелью?" - #Remove Script - [REMOVE_SCRIPT_ONLY]="Удалить скрипт и его локальные файлы" - [REMOVE_SCRIPT_AND_PANEL]="Удалить скрипт и данные панели/ноды remnawave" - [CONFIRM_REMOVE_SCRIPT]="Все данные скрипта будут удалены с сервера. Вы уверены? (y/n): " - [CONFIRM_REMOVE_ALL]="Все данные скрипта и панели/ноды будут удалены с сервера. Вы уверены? (y/n): " - [SCRIPT_REMOVED]="Скрипт и его локальные файлы успешно удалены!" - [ALL_REMOVED]="Скрипт и данные панели/ноды успешно удалены!" - #Extensions by distillium - [EXTENSIONS_MENU]="Расширения by distillium" - [EXTENSIONS_MENU_TITLE]="Управление расширениями" - [EXTENSIONS_PROMPT]="Выберите действие (0-2):" - [EXTENSIONS_INVALID_CHOICE]="Неверный выбор. Выберите 0-2." - [BACKUP_RESTORE]="Backup and Restore" - #Warp by distillium - [WARP_MENU]="WARP Native" - [WARP_MENU_TITLE]="Управление WARP Native" - [WARP_INSTALL]="Установить WARP Native" - [WARP_ADD_CONFIG]="Добавить WARP-настройки в конфигурацию ноды" - [WARP_DELETE_WARP_SETTINGS]="Удалить WARP-настройки из конфигурации ноды" - [WARP_CONFIRM_SERVER_PANEL]="Вы уверены, что находитесь на сервере с установленной панелью?\nДобавление WARP-настроек должно выполняться только на сервере, где установлена панель, а не на сервере ноды" - [WARP_UNINSTALL]="Удалить WARP Native" - [WARP_PROMPT]="Выберите действие (0-4):" - [WARP_PROMPT1]="Выберите действие:" - [WARP_INVALID_CHOICE]="Неверный выбор. Выберите 0-4." - [WARP_INVALID_CHOICE2]="Неверный выбор." - [WARP_NO_NODE]="Нода Remnawave не найдена. Сначала установите ноду." - [WARP_SELECT_CONFIG]="На какую ноду добавить WARP-настройки?" - [WARP_SELECT_CONFIG_DELETE]="На какой ноде удалить WARP-настройки?" - [WARP_NO_CONFIGS]="Конфигурации не найдены." - [WARP_UPDATE_SUCCESS]="Конфигурация успешно обновлена!" - [WARP_UPDATE_FAIL]="Не удалось обновить конфигурацию." - [WARP_WARNING]="warp-out уже существует в outbounds." - [WARP_WARNING2]="warp rule уже существует в routing rules." - [WARP_REMOVED_WARP_SETTINGS1]="Удален warp-out из outbounds" - [WARP_NO_WARP_SETTINGS1]="warp-out не найден в outbounds" - [WARP_REMOVED_WARP_SETTINGS2]="Удален warp rule из routing rules" - [WARP_NO_WARP_SETTINGS2]="warp rule не найден в routing rules" - #Manage Panel/Node - [START_PANEL_NODE]="Запустить панель/ноду" - [STOP_PANEL_NODE]="Остановить панель/ноду" - [UPDATE_PANEL_NODE]="Обновить панель/ноду" - [VIEW_LOGS]="Просмотр логов" - [PRESS_ENTER_RETURN_MENU]="Нажмите Enter для возврата в меню..." - [REMNAWAVE_CLI]="Remnawave CLI" - [ACCESS_PANEL]="Доступ к панели через порт 8443 (только для панели + ноды)" - [MANAGE_PANEL_NODE_PROMPT]="Выберите действие (0-6):" - [MANAGE_PANEL_NODE_INVALID_CHOICE]="Неверный выбор. Выберите 0-6." - #Manage Certificates - [CERT_UPDATE]="Обновить текущие сертификаты" - [CERT_GENERATE]="Сгенерировать новые сертификаты для другого домена" - [CERT_PROMPT1]="Выберите действие (0-2):" - [CERT_INVALID_CHOICE]="Неверный выбор. Выберите 0-2." - [CERT_UPDATE_CHECK]="Проверка метода генерации сертификата..." - [CERT_UPDATE_SUCCESS]="Сертификаты успешно обновлены." - [CERT_UPDATE_FAIL]="Не удалось обновить сертификаты." - [CERT_GENERATE_PROMPT]="Введите домен для новых сертификатов (например, example.com):" - [CERT_METHOD_UNKNOWN]="Неизвестный метод генерации сертификата." - [CERT_NOT_DUE]="Сертификат для %s еще не требует обновления." - #Install Remnawave Components - [INSTALL_MENU_TITLE]="Установка компонентов Remnawave" - [INSTALL_PANEL_NODE]="Установить панель и ноду на один сервер" - [INSTALL_PANEL]="Установить только панель" - [INSTALL_ADD_NODE]="Добавить ноду в панель" - [INSTALL_NODE]="Установить только ноду" - [INSTALL_PROMPT]="Выберите действие (0-4):" - [INSTALL_INVALID_CHOICE]="Неверный выбор. Выберите 0-4." - #Manage IPv6 - [IPV6_MENU_TITLE]="Управление IPv6" - [IPV6_ENABLE]="Включить IPv6" - [IPV6_DISABLE]="Отключить IPv6" - [IPV6_PROMPT]="Выберите действие (0-2):" - [IPV6_INVALID_CHOICE]="Неверный выбор. Выберите 0-2." - [IPV6_ALREADY_ENABLED]="IPv6 уже включен" - [ENABLE_IPV6]="Включение IPv6..." - [IPV6_ENABLED]="IPv6 включен." - [IPV6_ALREADY_DISABLED]="IPv6 уже отключен" - [DISABLING_IPV6]="Отключение IPv6..." - [IPV6_DISABLED]="IPv6 отключен." - #Remna - [INSTALL_PACKAGES]="Установка необходимых пакетов..." - [INSTALLING]="Установка панели и ноды" - [INSTALLING_PANEL]="Установка панели" - [INSTALLING_NODE]="Установка ноды" - [ENTER_PANEL_DOMAIN]="Введите домен панели (например, panel.example.com):" - [ENTER_SUB_DOMAIN]="Введите домен подписки (например, sub.example.com):" - [ENTER_NODE_DOMAIN]="Введите selfsteal домен для ноды (например, node.example.com):" - [ENTER_CF_TOKEN]="Введите Cloudflare API токен или глобальный ключ:" - [ENTER_CF_EMAIL]="Введите зарегистрированную почту Cloudflare:" - [ENTER_GCORE_TOKEN]="Введите API‑токен Gcore:" - [CERT_GCORE_FILE_NOT_FOUND]="Файл с реквизитами Gcore не найден. Повторно введите токен." - [ERROR_INSTALL_GCORE_PLUGIN]="Не удалось установить плагин certbot-dns-gcore" - [CHECK_CERTS]="Проверка сертификатов..." - [CERT_FOUND]="Сертификаты найдены в /etc/letsencrypt/live/" - [CF_VALIDATING]="Cloudflare API ключ и email валидны" - [CF_INVALID]="Неверный Cloudflare API ключ или email после %d попыток." - [CF_INVALID_ATTEMPT]="Неверный Cloudflare API ключ или email. Попытка %d из %d." - [WAITING]="Пожалуйста, подождите..." - #API - [REGISTERING_REMNAWAVE]="Регистрируем пользователя в панели Remnawave" - [CHECK_CONTAINERS]="Проверка доступности контейнеров..." - [CONTAINERS_NOT_READY_ATTEMPT]="Контейнеры не готовы, ожидание... Попытка %d из %d." - [CONTAINERS_TIMEOUT]="Контейнеры не готовы после %d попыток.\n\nПроверьте логи:\n cd /opt/remnawave && docker compose logs -f\n\nТакже посмотрите типичные ошибки Docker:\n https://wiki.egam.es/ru/troubleshooting/docker-issues/" - [REGISTRATION_SUCCESS]="Регистрация прошла успешно!" - [GET_PUBLIC_KEY]="Получаем публичный ключ..." - [PUBLIC_KEY_SUCCESS]="Публичный ключ успешно получен" - [GENERATE_KEYS]="Генерация ключей x25519..." - [GENERATE_KEYS_SUCCESS]="Ключи успешно сгенерированы" - [CREATING_CONFIG_PROFILE]="Создаем конфигурационный профиль..." - [CONFIG_PROFILE_CREATED]="Конфигурационный профиль успешно создан" - [CREATING_NODE]="Создание ноды" - [NODE_CREATED]="Нода успешно создана" - [CREATE_HOST]="Создаем хост" - [HOST_CREATED]="Хост успешно создан" - [GET_DEFAULT_SQUAD]="Получение внутреннего сквада" - [UPDATE_SQUAD]="Сквад успешно обновлен" - [NO_SQUADS_FOUND]="Нет внутренних сквадов" - [INVALID_UUID_FORMAT]="Неверный формат UUID" - [NO_VALID_SQUADS_FOUND]="Нет валидных сквадов" - [ERROR_GET_SQUAD]="Не удалось получить сквад" - [INVALID_SQUAD_UUID]="Неверный UUID сквада" - [INVALID_INBOUND_UUID]="Неверный UUID входа" - #Stop/Start/Update - [CHANGE_DIR_FAILED]="Не удалось перейти в директорию %s" - [DIR_NOT_FOUND]="Директория /opt/remnawave не найдена" - [PANEL_RUNNING]="Панель/нода уже запущена" - [PANEL_RUN]="Панель/нода запущена" - [PANEL_STOP]="Панель/нода остановлена" - [PANEL_STOPPED]="Панель/нода уже остановлена" - [NO_UPDATE]="Нет доступных обновлений для панели/ноды" - [UPDATING]="Обновление панели/ноды..." - [UPDATE_SUCCESS1]="Панель/нода успешно обновлена" - [STARTING_PANEL_NODE]="Запуск панели и ноды" - [STARTING_PANEL]="Запуск панели" - [STARTING_NODE]="Запуск ноды" - [STOPPING_REMNAWAVE]="Остановка панели и ноды" - [IMAGES_DETECTED]="Обнаружены новые образы, перезапускаем контейнеры..." - #Menu End - [INSTALL_COMPLETE]=" УСТАНОВКА ЗАВЕРШЕНА!" - [PANEL_ACCESS]="Панель доступна по адресу:" - [ADMIN_CREDS]="Для входа в панель используйте следующие данные:" - [USERNAME]="Логин:" - [PASSWORD]="Пароль:" - [RELAUNCH_CMD]="Для повторного запуска менеджера используйте команду:" - #RandomHTML - [DOWNLOAD_FAIL]="Ошибка загрузки, повторная попытка..." - [UNPACK_ERROR]="Ошибка распаковки архива" - [RANDOM_TEMPLATE]="Установка случайного шаблона для маскировочного сайта" - [TEMPLATE_COPY]="Шаблон скопирован в /var/www/html/" - [SELECT_TEMPLATE]="Выбран шаблон:" - #Error - [ERROR_TOKEN]="Не удалось получить токен." - [ERROR_PUBLIC_KEY]="Не удалось получить публичный ключ." - [ERROR_EXTRACT_PUBLIC_KEY]="Не удалось извлечь публичный ключ из ответа." - [ERROR_GENERATE_KEYS]="Не удалось сгенерировать ключи." - [ERROR_NO_CONFIGS]="Не найдены профили конфигураций" - [NO_DEFAULT_PROFILE]="Default-Profile не найден" - [ERROR_DELETE_PROFILE]="Не удалось удалить профиль" - [ERROR_EMPTY_RESPONSE_NODE]="Пустой ответ от сервера при создании ноды." - [ERROR_CREATE_NODE]="Не удалось создать ноду." - [ERROR_EMPTY_RESPONSE_HOST]="Пустой ответ от сервера при создании хоста." - [ERROR_CREATE_HOST]="Не удалось создать хост." - [ERROR_EMPTY_RESPONSE_REGISTER]="Ошибка при регистрации - пустой ответ сервера" - [ERROR_REGISTER]="Ошибка регистрации" - [ERROR_UPDATE_SQUAD]="Ошибка обновления squad" - [ERROR_GET_SQUAD_LIST]="Ошибка получения списка squadов" - [NO_SQUADS_TO_UPDATE]="Нет сквадов для обновления" - #Reinstall Panel/Node - [REINSTALL_WARNING]="Все данные панели/ноды будут удалены с сервера. Вы уверены? (y/n):" - [REINSTALL_TYPE_TITLE]="Выберите способ переустановки:" - [REINSTALL_PROMPT]="Выберите действие (0-3):" - [INVALID_REINSTALL_CHOICE]="Неверный выбор. Выберите 0-3." - [POST_PANEL_MESSAGE]="Панель успешно установлена!" - [POST_PANEL_INSTRUCTION]="Для установки ноды выполните следующие шаги:\n1. Запустите этот скрипт на сервере, где будет установлена нода.\n2. Выберите 'Установка компонентов Remnawave', а затем 'Установить только ноду'." - [SELFSTEAL]="Введите selfsteal домен для ноды, который указали при установке панели:" - [PANEL_IP_PROMPT]="Введите IP адрес панели, чтобы установить соединение между панелью и ноды:" - [IP_ERROR]="Введите корректный IP-адрес в формате X.X.X.X (например, 192.168.1.1)" - [CERT_PROMPT]="Введите сертификат, полученный от панели (вставьте содержимое и 2 раза нажмите Enter):" - [CERT_CONFIRM]="Вы уверены, что сертификат правильный? (y/n):" - [ABORT_MESSAGE]="Установка прервана пользователем" - [SUCCESS_MESSAGE]="Нода успешно подключена" - #Node Check - [NODE_CHECK]="Проверка подключения ноды для %s..." - [NODE_ATTEMPT]="Попытка %d из %d..." - [NODE_UNAVAILABLE]="Нода недоступна на попытке %d." - [NODE_LAUNCHED]="Нода успешно подключена!" - [NODE_NOT_CONNECTED]="Нода не подключена после %d попыток!" - [CHECK_CONFIG]="Проверьте конфигурацию или перезапустите панель." - #Add node to panel - [ADD_NODE_TO_PANEL]="Добавить ноду в панель" - [ENTER_NODE_NAME]="Введите имя для вашей ноды (например, Germany):" - [USING_SAVED_TOKEN]="Используем сохранённый токен..." - [INVALID_SAVED_TOKEN]="Сохранённый токен недействителен. Запрашиваем новый..." - [ENTER_PANEL_USERNAME]="Введите логин панели: " - [ENTER_PANEL_PASSWORD]="Введите пароль панели: " - [TOKEN_RECEIVED_AND_SAVED]="Токен успешно получен и сохранён" - [TOKEN_USED_SUCCESSFULLY]="Токен успешно использован" - [DOMAIN_ALREADY_EXISTS]="Домен уже используется" - [TRY_ANOTHER_DOMAIN]="Пожалуйста, используйте другой домен" - [ERROR_CHECK_DOMAIN]="Ошибка при проверке домена" - [NODE_ADDED_SUCCESS]="Нода успешно добавлена!" - [CREATE_NEW_NODE]="Создаём новую ноду для %s" - [CF_INVALID_NAME]="Ошибка: Имя конфигурационного профиля %s уже используется.\nПожалуйста, выберите другое имя." - [CF_INVALID_LENGTH]="Ошибка: Имя конфигурационного профиля должно содержать от 3 до 20 символов." - [CF_INVALID_CHARS]="Ошибка: Имя конфигурационного профиля должно содержать только английские буквы, цифры и дефис." - #check - [CHECK_UPDATE]="Проверить обновления" - [GENERATING_CERTS]="Генерируем сертификаты для %s" - [REQUIRED_DOMAINS]="Требуемые домены для сертификатов:" - [CHECK_DOMAIN_IP_FAIL]="Не удалось определить IP-адрес домена или сервера." - [CHECK_DOMAIN_IP_FAIL_INSTRUCTION]="Убедитесь, что домен %s правильно настроен и указывает на этот сервер (%s)." - [CHECK_DOMAIN_CLOUDFLARE]="Домен %s указывает на IP Cloudflare (%s)." - [CHECK_DOMAIN_CLOUDFLARE_INSTRUCTION]="Проксирование Cloudflare недопустимо для selfsteal домена. Отключите проксирование (переключите в режим 'DNS Only')." - [CHECK_DOMAIN_MISMATCH]="Домен %s указывает на IP-адрес %s, который отличается от IP этого сервера (%s)." - [CHECK_DOMAIN_MISMATCH_INSTRUCTION]="Для корректной работы домен должен указывать на текущий сервер." - [NO_PANEL_NODE_INSTALLED]="Панель или нода не установлены. Пожалуйста, сначала установите панель или ноду." - #update - [UPDATE_AVAILABLE]="Доступна новая версия скрипта: %s (текущая версия: %s)." - [UPDATE_CONFIRM]="Обновить скрипт? (y/n):" - [UPDATE_CANCELLED]="Обновление отменено пользователем." - [UPDATE_SUCCESS]="Скрипт успешно обновлён до версии %s!" - [UPDATE_FAILED]="Ошибка при скачивании новой версии скрипта." - [VERSION_CHECK_FAILED]="Не удалось определить версию удалённого скрипта. Пропускаем обновление." - [LATEST_VERSION]="У вас уже установлена последняя версия скрипта (%s)." - [RESTART_REQUIRED]="Пожалуйста, перезапустите скрипт для применения изменений." - [LOCAL_FILE_NOT_FOUND]="Локальный файл скрипта не найден, загружаем новую версию..." - #CLI - [RUNNING_CLI]="Запуск Remnawave CLI..." - [CLI_SUCCESS]="Remnawave CLI успешно выполнен!" - [CLI_FAILED]="Не удалось выполнить Remnawave CLI. Убедитесь, что контейнер 'remnawave' запущен." - [CONTAINER_NOT_RUNNING]="Контейнер 'remnawave' не запущен. Пожалуйста, запустите его сначала." - #Cert_choise - [CERT_METHOD_PROMPT]="Выберите метод генерации сертификатов для всех доменов:" - [CERT_METHOD_CF]="Cloudflare API (поддерживает wildcard)" - [CERT_METHOD_ACME]="ACME HTTP-01 (один домен, без wildcard)" - [CERT_METHOD_GCORE]="Gcore DNS API (поддерживает wildcard)" - [CERT_METHOD_CHOOSE]="Выберите действие (0-3):" - [EMAIL_PROMPT]="Введите ваш email для регистрации в Let's Encrypt:" - [CERTS_SKIPPED]="Все сертификаты уже существуют. Пропускаем генерацию." - [ACME_METHOD]="Используем ACME (Let's Encrypt) с HTTP-01 вызовом (без поддержки wildcard)..." - [CERT_GENERATION_FAILED]="Не удалось сгенерировать сертификаты. Проверьте введенные данные и настройки DNS." - [ADDING_CRON_FOR_EXISTING_CERTS]="Добавление cron-задачи для обновления сертификатов..." - [CRON_ALREADY_EXISTS]="Задача cron для обновления сертификатов уже существует." - [CERT_NOT_FOUND]="Сертификат для домена не найден." - [ERROR_PARSING_CERT]="Ошибка при разборе даты истечения сертификата." - [CERT_EXPIRY_SOON]="Сертификаты скоро истекут через" - [DAYS]="дней" - [UPDATING_CRON]="Обновление задачи cron в соответствии со сроком действия сертификата." - [GENERATING_WILDCARD_CERT]="Генерация wildcard-сертификата для" - [WILDCARD_CERT_FOUND]="Wildcard-сертификат найден в /etc/letsencrypt/live/" - [FOR_DOMAIN]="для" - [START_CRON_ERROR]="Не удалось запустить cron. Пожалуйста, запустите его вручную." - [DOMAINS_MUST_BE_UNIQUE]="Ошибка: Все домены (панель, подписка, и нода) должны быть уникальными." - [CHOOSE_TEMPLATE_SOURCE]="Выберите источник шаблонов:" - [SIMPLE_WEB_TEMPLATES]="Simple web templates" - [SNI_TEMPLATES]="SNI templates" - [NOTHING_TEMPLATES]="Nothing Sni templates" - [CHOOSE_TEMPLATE_OPTION]="Выберите действие (0-3):" - [INVALID_TEMPLATE_CHOICE]="Неверный выбор. Выберите 0-3." - #Manage panel access - [PORT_8443_OPEN]="Открыть доступ к панели на порту 8443" - [PORT_8443_CLOSE]="Закрыть доступ к панели на порту 8443" - [PORT_8443_IN_USE]="Порт 8443 уже занят другим процессом. Проверьте, какие службы используют порт, и освободите его." - [NO_PORT_CHECK_TOOLS]="Не найдены инструменты для проверки порта (ss или netstat). Установите один из них." - [OPEN_PANEL_LINK]="Ваша ссылка для входа в панель:" - [PORT_8443_WARNING]="Не забудьте, что порт 8443 сейчас открыт для внешнего доступа. После восстановления панели выберите пункт закрытия порта 8443." - [PORT_8443_CLOSED]="Порт 8443 закрыт." - [NGINX_CONF_NOT_FOUND]="Файл nginx.conf не найден в $dir" - [NGINX_CONF_ERROR]="Не удалось извлечь необходимые параметры из nginx.conf" - [NGINX_CONF_MODIFY_FAILED]="Не удалось изменить конфигурацию Nginx." - [PORT_8443_ALREADY_CONFIGURED]="Порт 8443 уже настроен в конфигурации Nginx." - [UFW_RELOAD_FAILED]="Не удалось перезагрузить UFW." - [PORT_8443_ALREADY_CLOSED]="Порт 8443 уже закрыт в UFW." - #Legiz Extensions - [LEGIZ_EXTENSIONS_PROMPT]="Выберите действие (0-2):" - # Sub Page Upload - [UPLOADING_SUB_PAGE]="Загрузка пользовательского шаблона страницы подписки..." - [ERROR_FETCH_SUB_PAGE]="Не удалось получить пользовательский шаблон страницы подписки." - [SUB_PAGE_UPDATED_SUCCESS]="Пользовательский шаблон страницы подписки успешно обновлён." - [SELECT_SUB_PAGE_CUSTOM]="Выберите действие (0-7):" - [SELECT_SUB_PAGE_CUSTOM1]="Шаблоны страниц подписки" - [SELECT_SUB_PAGE_CUSTOM2]="Шаблоны страниц подписки\nЗапускать только на сервере с панелью" - [SELECT_SUB_PAGE_CUSTOM3]="Списки приложений для оригинальной страницы подписки:" - [SELECT_SUB_PAGE_CUSTOM4]="Кастомные страницы подписки:" - [SUB_PAGE_SELECT_CHOICE]="Недопустимый выбор. Пожалуйста, выберите от 0 до 7." - [RESTORE_SUB_PAGE]="Восстановить шаблон страницы подписки по умолчанию" - [CONTAINER_NOT_FOUND]="Контейнер %s не найден" - [SUB_WITH_APPCONFIG_ASK]="Добавить файл конфигурации app-config.json?" - [SUB_WITH_APPCONFIG_OPTION1]="Простой список приложений clash&sing" - [SUB_WITH_APPCONFIG_OPTION2]="Множественный список приложений" - [SUB_WITH_APPCONFIG_OPTION3]="Список приложений с поддержкой HWID" - [SUB_WITH_APPCONFIG_SKIP]="Нет, пропустить добавление конфигурации" - [SUB_WITH_APPCONFIG_INVALID]="Неверный выбор, конфигурация не будет добавлена" - [SUB_WITH_APPCONFIG_INPUT]="Выберите действие (0–3):" - # Custom Branding - [BRANDING_SUPPORT_ASK]="Добавить поддержку брендирования страницы подписки?" - [BRANDING_SUPPORT_YES]="Да, добавить поддержку брендирования" - [BRANDING_SUPPORT_NO]="Нет, пропустить брендирование" - [BRANDING_NAME_PROMPT]="Введите название вашего бренда:" - [BRANDING_SUPPORT_URL_PROMPT]="Введите ссылку на страницу поддержки:" - [BRANDING_LOGO_URL_PROMPT]="Введите ссылку на логотип вашего бренда:" - [BRANDING_ADDED_SUCCESS]="Конфигурация брендирования успешно добавлена" - [CUSTOM_APP_LIST_MENU]="Редактирование кастомного списка приложений и брендирования" - [CUSTOM_APP_LIST_NOT_FOUND]="Кастомный список приложений не найден" - [EDIT_BRANDING]="Редактирование брендирования" - [EDIT_LOGO]="Изменить логотип" - [EDIT_NAME]="Изменить имя в брендировании" - [EDIT_SUPPORT_URL]="Изменить ссылку поддержки" - [DELETE_APPS]="Удалить определенные приложения" - [BRANDING_CURRENT_VALUES]="Текущие значения брендирования:" - [BRANDING_LOGO_URL]="URL логотипа:" - [BRANDING_NAME]="Имя:" - [BRANDING_SUPPORT_URL]="URL поддержки:" - [ENTER_NEW_LOGO]="Введите новый URL логотипа:" - [ENTER_NEW_NAME]="Введите новое имя бренда:" - [ENTER_NEW_SUPPORT]="Введите новую ссылку поддержки:" - [CONFIRM_CHANGE]="Подтвердить изменение? (y/n):" - [PLATFORM_SELECT]="Выберите платформу:" - [APP_SELECT]="Какое приложение вы хотите удалить?" - [CONFIRM_DELETE_APP]="Вы точно хотите удалить приложение %s из списка платформы %s? (y/n):" - [APP_DELETED_SUCCESS]="Приложение успешно удалено" - [NO_APPS_FOUND]="Приложения не найдены в этой платформе" - [RENEWAL_CONF_NOT_FOUND]="Файл конфигурации обновления сертификатов не найден." - [ARCHIVE_DIR_MISMATCH]="Несоответствие директории архива в конфигурации." - [CERT_VERSION_NOT_FOUND]="Не удалось определить версию сертификатов." - [RESULTS_CERTIFICATE_UPDATES]="Результаты обновления сертификатов:" - [CERTIFICATE_FOR]="Сертификат для " - [SUCCESSFULLY_UPDATED]="успешно обновлен" - [FAILED_TO_UPDATE_CERTIFICATE_FOR]="Не удалось обновить сертификат для " - [ERROR_CHECKING_EXPIRY_FOR]="Ошибка проверки даты истечения для " - [DOES_NOT_REQUIRE_UPDATE]="не требует обновления (" - [UPDATED]="Обновлен" - [REMAINING]="Осталось" - [ERROR_UPDATE]="Ошибка обновления" - [ALREADY_EXPIRED]="уже истек" - [CERT_CLOUDFLARE_FILE_NOT_FOUND]="Файл учетных данных Cloudflare не найден." - [TELEGRAM_OAUTH_WARNING]="Включена авторизация через Telegram (TELEGRAM_OAUTH_ENABLED=true)." - [CREATE_API_TOKEN_INSTRUCTION]="Зайдите в панель по адресу: https://%s\nПерейдите в раздел 'API токены' -> 'Создать новый токен' и создайте токен.\nСкопируйте созданный токен и введите его ниже." - [ENTER_API_TOKEN]="Введите API-токен: " - [EMPTY_TOKEN_ERROR]="Токен не введен. Завершение работы." - [RATE_LIMIT_EXCEEDED]="Превышен лимит выдачи сертификатов Let's Encrypt" - [FAILED_TO_MODIFY_HTML_FILES]="Не удалось изменить HTML файлы" - [INSTALLING_YQ]="Установка yq..." - [ERROR_SETTING_YQ_PERMISSIONS]="Ошибка установки прав yq!" - [YQ_SUCCESSFULLY_INSTALLED]="yq успешно установлен!" - [YQ_DOESNT_WORK_AFTER_INSTALLATION]="Ошибка: yq не работает после установки!" - [ERROR_DOWNLOADING_YQ]="Ошибка загрузки yq!" - [FAST_START]="Быстрый запуск: remnawave_reverse" - [CREATING_API_TOKEN]="Создание API токена для Subscription Page..." - [API_TOKEN_ADDED]="API токен Subscription Page успешно добавлен в docker-compose.yml" - [ERROR_CREATE_API_TOKEN]="Ошибка создания API токена" - [ERROR_API_TOKEN]="Не удалось добавить API токен" - [STOPPING_REMNAWAVE_SUBSCRIPTION_PAGE]="Остановка Remnawave Subscription Page..." - [STARTING_REMNAWAVE_SUBSCRIPTION_PAGE]="Запуск Remnawave Subscription Page..." - ) - ;; - esac -} - question() { echo -e "${COLOR_GREEN}[?]${COLOR_RESET} ${COLOR_YELLOW}$*${COLOR_RESET}" } @@ -893,6 +465,14 @@ log_entry() { } run_remnawave_cli() { + local dir="${LANG[PANEL_DIR]}" + if [ ! -d "$dir" ]; then + echo -e "${COLOR_RED}${LANG[DIR_NOT_FOUND]}${COLOR_RESET}" + return 1 + fi + + cd "$dir" || { echo -e "${COLOR_RED}${LANG[CHANGE_DIR_FAILED]} $dir${COLOR_RESET}"; return 1; } + if ! docker ps --format '{{.Names}}' | grep -q '^remnawave$'; then echo -e "${COLOR_YELLOW}${LANG[CONTAINER_NOT_RUNNING]}${COLOR_RESET}" return 1 @@ -914,94 +494,181 @@ run_remnawave_cli() { } start_panel_node() { - local dir="" - if [ -d "/opt/remnawave" ]; then - dir="/opt/remnawave" - else + local panel_dir="${LANG[PANEL_DIR]}" + local node_dir="${LANG[NODE_DIR]}" + + # Start panel if exists + if [ -d "$panel_dir" ]; then + cd "$panel_dir" || { echo -e "${COLOR_RED}${LANG[CHANGE_DIR_FAILED]} $panel_dir${COLOR_RESET}"; exit 1; } + + if docker ps -q --filter "ancestor=remnawave/backend:latest" | grep -q . || docker ps -q --filter "ancestor=remnawave/backend:2" | grep -q .; then + echo -e "${COLOR_GREEN}Панель уже запущена${COLOR_RESET}" + else + echo -e "${COLOR_YELLOW}Запуск панели...${COLOR_RESET}" + sleep 1 + docker compose up -d > /dev/null 2>&1 & + spinner $! "${LANG[WAITING]}" + echo -e "${COLOR_GREEN}Панель запущена${COLOR_RESET}" + fi + fi + + # Start node if exists + if [ -d "$node_dir" ]; then + cd "$node_dir" || { echo -e "${COLOR_RED}${LANG[CHANGE_DIR_FAILED]} $node_dir${COLOR_RESET}"; exit 1; } + + if docker ps -q --filter "ancestor=remnawave/node:latest" | grep -q .; then + echo -e "${COLOR_GREEN}Нода уже запущена${COLOR_RESET}" + else + echo -e "${COLOR_YELLOW}Запуск ноды...${COLOR_RESET}" + sleep 1 + docker compose up -d > /dev/null 2>&1 & + spinner $! "${LANG[WAITING]}" + echo -e "${COLOR_GREEN}Нода запущена${COLOR_RESET}" + fi + fi + + if [ ! -d "$panel_dir" ] && [ ! -d "$node_dir" ]; then echo -e "${COLOR_RED}${LANG[DIR_NOT_FOUND]}${COLOR_RESET}" exit 1 fi - - cd "$dir" || { echo -e "${COLOR_RED}${LANG[CHANGE_DIR_FAILED]} $dir${COLOR_RESET}"; exit 1; } - - if docker ps -q --filter "ancestor=remnawave/backend:latest" | grep -q . || docker ps -q --filter "ancestor=remnawave/node:latest" | grep -q . || docker ps -q --filter "ancestor=remnawave/backend:2" | grep -q .; then - echo -e "${COLOR_GREEN}${LANG[PANEL_RUNNING]}${COLOR_RESET}" - else - echo -e "${COLOR_YELLOW}${LANG[STARTING_PANEL_NODE]}...${COLOR_RESET}" - sleep 1 - docker compose up -d > /dev/null 2>&1 & - spinner $! "${LANG[WAITING]}" - echo -e "${COLOR_GREEN}${LANG[PANEL_RUN]}${COLOR_RESET}" - fi } stop_panel_node() { - local dir="" - if [ -d "/opt/remnawave" ]; then - dir="/opt/remnawave" - else + local panel_dir="${LANG[PANEL_DIR]}" + local node_dir="${LANG[NODE_DIR]}" + + # Stop panel if exists + if [ -d "$panel_dir" ]; then + cd "$panel_dir" || { echo -e "${COLOR_RED}${LANG[CHANGE_DIR_FAILED]} $panel_dir${COLOR_RESET}"; exit 1; } + + if docker ps -q --filter "ancestor=remnawave/backend:latest" | grep -q . || docker ps -q --filter "ancestor=remnawave/backend:2" | grep -q .; then + echo -e "${COLOR_YELLOW}Остановка панели...${COLOR_RESET}" + docker compose down > /dev/null 2>&1 & + spinner $! "${LANG[WAITING]}" + echo -e "${COLOR_GREEN}Панель остановлена${COLOR_RESET}" + else + echo -e "${COLOR_GREEN}Панель уже остановлена${COLOR_RESET}" + fi + fi + + # Stop node if exists + if [ -d "$node_dir" ]; then + cd "$node_dir" || { echo -e "${COLOR_RED}${LANG[CHANGE_DIR_FAILED]} $node_dir${COLOR_RESET}"; exit 1; } + + if docker ps -q --filter "ancestor=remnawave/node:latest" | grep -q .; then + echo -e "${COLOR_YELLOW}Остановка ноды...${COLOR_RESET}" + docker compose down > /dev/null 2>&1 & + spinner $! "${LANG[WAITING]}" + echo -e "${COLOR_GREEN}Нода остановлена${COLOR_RESET}" + else + echo -e "${COLOR_GREEN}Нода уже остановлена${COLOR_RESET}" + fi + fi + + if [ ! -d "$panel_dir" ] && [ ! -d "$node_dir" ]; then echo -e "${COLOR_RED}${LANG[DIR_NOT_FOUND]}${COLOR_RESET}" exit 1 fi - - cd "$dir" || { echo -e "${COLOR_RED}${LANG[CHANGE_DIR_FAILED]} $dir${COLOR_RESET}"; exit 1; } - if ! docker ps -q --filter "ancestor=remnawave/backend:latest" | grep -q . && ! docker ps -q --filter "ancestor=remnawave/node:latest" | grep -q . && ! docker ps -q --filter "ancestor=remnawave/backend:2" | grep -q .; then - echo -e "${COLOR_GREEN}${LANG[PANEL_STOPPED]}${COLOR_RESET}" - else - echo -e "${COLOR_YELLOW}${LANG[STOPPING_REMNAWAVE]}...${COLOR_RESET}" - sleep 1 - docker compose down > /dev/null 2>&1 & - spinner $! "${LANG[WAITING]}" - echo -e "${COLOR_GREEN}${LANG[PANEL_STOP]}${COLOR_RESET}" - fi } update_panel_node() { - local dir="" - if [ -d "/opt/remnawave" ]; then - dir="/opt/remnawave" - else - echo -e "${COLOR_RED}${LANG[DIR_NOT_FOUND]}${COLOR_RESET}" - exit 1 - fi + local panel_dir="${LANG[PANEL_DIR]}" + local node_dir="${LANG[NODE_DIR]}" + local updated=false + + # Update panel if exists + if [ -d "$panel_dir" ]; then + cd "$panel_dir" || { echo -e "${COLOR_RED}${LANG[CHANGE_DIR_FAILED]} $panel_dir${COLOR_RESET}"; exit 1; } + echo -e "${COLOR_YELLOW}Обновление панели...${COLOR_RESET}" + sleep 1 - cd "$dir" || { echo -e "${COLOR_RED}${LANG[CHANGE_DIR_FAILED]} $dir${COLOR_RESET}"; exit 1; } - echo -e "${COLOR_YELLOW}${LANG[UPDATING]}${COLOR_RESET}" - sleep 1 + images_before=$(docker compose config --images | sort -u) + if [ -n "$images_before" ]; then + before=$(echo "$images_before" | xargs -I {} docker images -q {} | sort -u) + else + before="" + fi - images_before=$(docker compose config --images | sort -u) - if [ -n "$images_before" ]; then - before=$(echo "$images_before" | xargs -I {} docker images -q {} | sort -u) - else - before="" - fi + tmpfile=$(mktemp) + docker compose pull > "$tmpfile" 2>&1 & + spinner $! "${LANG[WAITING]}" + pull_output=$(cat "$tmpfile") + rm -f "$tmpfile" - tmpfile=$(mktemp) - docker compose pull > "$tmpfile" 2>&1 & - spinner $! "${LANG[WAITING]}" - pull_output=$(cat "$tmpfile") - rm -f "$tmpfile" + images_after=$(docker compose config --images | sort -u) + if [ -n "$images_after" ]; then + after=$(echo "$images_after" | xargs -I {} docker images -q {} | sort -u) + else + after="" + fi - images_after=$(docker compose config --images | sort -u) - if [ -n "$images_after" ]; then - after=$(echo "$images_after" | xargs -I {} docker images -q {} | sort -u) - else - after="" + if [ "$before" != "$after" ] || echo "$pull_output" | grep -q "Pull complete"; then + echo -e "" + echo -e "${COLOR_YELLOW}Обнаружены новые образы, перезапускаем контейнеры...${COLOR_RESET}" + docker compose down > /dev/null 2>&1 & + spinner $! "${LANG[WAITING]}" + sleep 5 + docker compose up -d > /dev/null 2>&1 & + spinner $! "${LANG[WAITING]}" + sleep 1 + docker image prune -f > /dev/null 2>&1 + echo -e "${COLOR_GREEN}Панель успешно обновлена${COLOR_RESET}" + updated=true + else + echo -e "${COLOR_YELLOW}Нет доступных обновлений для панели${COLOR_RESET}" + fi fi + + # Update node if exists + if [ -d "$node_dir" ]; then + cd "$node_dir" || { echo -e "${COLOR_RED}${LANG[CHANGE_DIR_FAILED]} $node_dir${COLOR_RESET}"; exit 1; } + echo -e "${COLOR_YELLOW}Обновление ноды...${COLOR_RESET}" + sleep 1 - if [ "$before" != "$after" ] || echo "$pull_output" | grep -q "Pull complete"; then - echo -e "" - echo -e "${COLOR_YELLOW}${LANG[IMAGES_DETECTED]}${COLOR_RESET}" - docker compose down > /dev/null 2>&1 & - spinner $! "${LANG[WAITING]}" - sleep 5 - docker compose up -d > /dev/null 2>&1 & + images_before=$(docker compose config --images | sort -u) + if [ -n "$images_before" ]; then + before=$(echo "$images_before" | xargs -I {} docker images -q {} | sort -u) + else + before="" + fi + + tmpfile=$(mktemp) + docker compose pull > "$tmpfile" 2>&1 & spinner $! "${LANG[WAITING]}" - sleep 1 - docker image prune -f > /dev/null 2>&1 - echo -e "${COLOR_GREEN}${LANG[UPDATE_SUCCESS1]}${COLOR_RESET}" - else - echo -e "${COLOR_YELLOW}${LANG[NO_UPDATE]}${COLOR_RESET}" + pull_output=$(cat "$tmpfile") + rm -f "$tmpfile" + + images_after=$(docker compose config --images | sort -u) + if [ -n "$images_after" ]; then + after=$(echo "$images_after" | xargs -I {} docker images -q {} | sort -u) + else + after="" + fi + + if [ "$before" != "$after" ] || echo "$pull_output" | grep -q "Pull complete"; then + echo -e "" + echo -e "${COLOR_YELLOW}Обнаружены новые образы, перезапускаем контейнеры...${COLOR_RESET}" + docker compose down > /dev/null 2>&1 & + spinner $! "${LANG[WAITING]}" + sleep 5 + docker compose up -d > /dev/null 2>&1 & + spinner $! "${LANG[WAITING]}" + sleep 1 + docker image prune -f > /dev/null 2>&1 + echo -e "${COLOR_GREEN}Нода успешно обновлена${COLOR_RESET}" + updated=true + else + echo -e "${COLOR_YELLOW}Нет доступных обновлений для ноды${COLOR_RESET}" + fi + fi + + if [ ! -d "$panel_dir" ] && [ ! -d "$node_dir" ]; then + echo -e "${COLOR_RED}${LANG[DIR_NOT_FOUND]}${COLOR_RESET}" + exit 1 + fi + + if [ "$updated" = false ]; then + echo -e "${COLOR_YELLOW}Нет доступных обновлений для панели/ноды${COLOR_RESET}" fi } @@ -1102,12 +769,22 @@ remove_script() { return 0 fi - if [ -d "/opt/remnawave" ]; then - cd /opt/remnawave || { echo -e "${COLOR_RED}${LANG[CHANGE_DIR_FAILED]} /opt/remnawave${COLOR_RESET}"; exit 1; } + # Remove panel if exists + if [ -d "${LANG[PANEL_DIR]}" ]; then + cd "${LANG[PANEL_DIR]}" || { echo -e "${COLOR_RED}${LANG[CHANGE_DIR_FAILED]} ${LANG[PANEL_DIR]}${COLOR_RESET}"; exit 1; } docker compose down -v --rmi all --remove-orphans > /dev/null 2>&1 & spinner $! "${LANG[WAITING]}" - rm -rf /opt/remnawave 2>/dev/null + rm -rf "${LANG[PANEL_DIR]}" 2>/dev/null fi + + # Remove node if exists + if [ -d "${LANG[NODE_DIR]}" ]; then + cd "${LANG[NODE_DIR]}" || { echo -e "${COLOR_RED}${LANG[CHANGE_DIR_FAILED]} ${LANG[NODE_DIR]}${COLOR_RESET}"; exit 1; } + docker compose down -v --rmi all --remove-orphans > /dev/null 2>&1 & + spinner $! "${LANG[WAITING]}" + rm -rf "${LANG[NODE_DIR]}" 2>/dev/null + fi + docker system prune -a --volumes -f > /dev/null 2>&1 & spinner $! "${LANG[WAITING]}" rm -rf /usr/local/remnawave_reverse 2>/dev/null @@ -1154,8 +831,6 @@ install_script_if_missing() { echo "$alias_line" >> "$bashrc_file" printf "${COLOR_GREEN}${LANG[ALIAS_ADDED]}${COLOR_RESET}\n" "$bashrc_file" printf "${COLOR_YELLOW}${LANG[ALIAS_ACTIVATE_GLOBAL]}${COLOR_RESET}\n" "$bashrc_file" - else - printf "${COLOR_YELLOW}${LANG[ALIAS_EXISTS]}${COLOR_RESET}\n" "$bashrc_file" fi } @@ -1184,7 +859,6 @@ generate_password() { echo "$password" } -#Displaying the availability of the update in the menu check_update_status() { local TEMP_REMOTE_VERSION_FILE TEMP_REMOTE_VERSION_FILE=$(mktemp) @@ -1243,31 +917,30 @@ check_update_status() { show_menu() { echo -e "${COLOR_GREEN}${LANG[MENU_TITLE]}${COLOR_RESET}" if [[ "$UPDATE_AVAILABLE" == true ]]; then - echo -e "${COLOR_GRAY}$(printf "${LANG[VERSION_LABEL]}" "$SCRIPT_VERSION ${COLOR_RED}${LANG[AVAILABLE_UPDATE]}${COLOR_RESET}")${COLOR_RESET}" + echo -e "${COLOR_GRAY}$(printf "${LANG[VERSION_LABEL]}" "$SCRIPT_VERSION ${COLOR_RED}${LANG[AVAILABLE_UPDATE]}${COLOR_RESET}")${COLOR_RESET}" else - echo -e "${COLOR_GRAY}$(printf "${LANG[VERSION_LABEL]}" "$SCRIPT_VERSION")${COLOR_RESET}" + echo -e "${COLOR_GRAY}$(printf "${LANG[VERSION_LABEL]}" "$SCRIPT_VERSION")${COLOR_RESET}" fi echo -e "" - echo -e "${COLOR_YELLOW}1. ${LANG[MENU_1]}${COLOR_RESET}" # Install Remnawave Components - echo -e "${COLOR_YELLOW}2. ${LANG[MENU_2]}${COLOR_RESET}" # Reinstall panel/node - echo -e "${COLOR_YELLOW}3. ${LANG[MENU_3]}${COLOR_RESET}" # Manage panel/node + echo -e "${COLOR_YELLOW}1. ${LANG[MENU_1]}${COLOR_RESET}" + echo -e "${COLOR_YELLOW}2. ${LANG[MENU_2]}${COLOR_RESET}" + echo -e "${COLOR_YELLOW}3. ${LANG[MENU_3]}${COLOR_RESET}" echo -e "" - echo -e "${COLOR_YELLOW}4. ${LANG[MENU_4]}${COLOR_RESET}" # Install random template - echo -e "${COLOR_YELLOW}5. ${LANG[MENU_5]}${COLOR_RESET}" # Custom Templates legiz - echo -e "${COLOR_YELLOW}6. ${LANG[MENU_6]}${COLOR_RESET}" # Extensions distilium + echo -e "${COLOR_YELLOW}4. ${LANG[MENU_4]}${COLOR_RESET}" + echo -e "${COLOR_YELLOW}5. ${LANG[MENU_5]}${COLOR_RESET}" + echo -e "${COLOR_YELLOW}6. ${LANG[MENU_6]}${COLOR_RESET}" echo -e "" - echo -e "${COLOR_YELLOW}7. ${LANG[MENU_7]}${COLOR_RESET}" # Manage IPv6 - echo -e "${COLOR_YELLOW}8. ${LANG[MENU_8]}${COLOR_RESET}" # Manage certificates domain + echo -e "${COLOR_YELLOW}7. ${LANG[MENU_7]}${COLOR_RESET}" + echo -e "${COLOR_YELLOW}8. ${LANG[MENU_8]}${COLOR_RESET}" echo -e "" - echo -e "${COLOR_YELLOW}9. ${LANG[MENU_9]}${COLOR_RESET}" # Check for updates - echo -e "${COLOR_YELLOW}10. ${LANG[MENU_10]}${COLOR_RESET}" # Remove script + echo -e "${COLOR_YELLOW}9. ${LANG[MENU_9]}${COLOR_RESET}" + echo -e "${COLOR_YELLOW}10. ${LANG[MENU_10]}${COLOR_RESET}" echo -e "" echo -e "${COLOR_YELLOW}0. ${LANG[EXIT]}${COLOR_RESET}" echo -e "${COLOR_YELLOW}- ${LANG[FAST_START]//remnawave_reverse/${COLOR_GREEN}remnawave_reverse${COLOR_RESET}}" echo -e "" } -#Manage Install Remnawave Components show_install_menu() { echo -e "" echo -e "${COLOR_GREEN}${LANG[INSTALL_MENU_TITLE]}${COLOR_RESET}" @@ -1286,6 +959,16 @@ manage_install() { reading "${LANG[INSTALL_PROMPT]}" INSTALL_OPTION case $INSTALL_OPTION in 1) + echo -e "" + echo -e "${COLOR_RED}${LANG[WARNING_LABEL]}${COLOR_RESET}" + echo -e "${COLOR_RED}${LANG[WARNING_PANEL_NODE_TOGETHER]}${COLOR_RESET}" + echo -e "" + reading "${LANG[CONFIRM_PROMPT]}" confirm + if [[ "$confirm" != "y" && "$confirm" != "Y" ]]; then + echo -e "${COLOR_YELLOW}${LANG[EXIT]}${COLOR_RESET}" + return 0 + fi + if [ ! -f "${DIR_REMNAWAVE}install_packages" ] || ! command -v docker >/dev/null 2>&1 || ! docker info >/dev/null 2>&1 || ! command -v certbot >/dev/null 2>&1; then install_packages || { echo -e "${COLOR_RED}${LANG[ERROR_INSTALL_DOCKER]}${COLOR_RESET}" @@ -1338,9 +1021,7 @@ manage_install() { ;; esac } -#Manage Install Remnawave Components -#Manage Panel Access show_panel_access() { echo -e "" echo -e "${COLOR_GREEN}${LANG[MENU_9]}${COLOR_RESET}" @@ -1383,10 +1064,8 @@ manage_panel_access() { } open_panel_access() { - local dir="" - if [ -d "/opt/remnawave" ]; then - dir="/opt/remnawave" - else + local dir="${LANG[PANEL_DIR]}" + if [ ! -d "$dir" ]; then echo -e "${COLOR_RED}${LANG[DIR_NOT_FOUND]}${COLOR_RESET}" exit 1 fi @@ -1451,10 +1130,8 @@ open_panel_access() { } close_panel_access() { - local dir="" - if [ -d "/opt/remnawave" ]; then - dir="/opt/remnawave" - else + local dir="${LANG[PANEL_DIR]}" + if [ ! -d "$dir" ]; then echo -e "${COLOR_RED}${LANG[DIR_NOT_FOUND]}${COLOR_RESET}" exit 1 fi @@ -1507,27 +1184,61 @@ close_panel_access() { } view_logs() { - local dir="" - if [ -d "/opt/remnawave" ]; then - dir="/opt/remnawave" - else - echo -e "${COLOR_RED}${LANG[DIR_NOT_FOUND]}${COLOR_RESET}" - exit 1 - fi - - cd "$dir" || { echo -e "${COLOR_RED}${LANG[CHANGE_DIR_FAILED]} $dir${COLOR_RESET}"; exit 1; } - - if ! docker ps -q --filter "ancestor=remnawave/backend:latest" | grep -q . && ! docker ps -q --filter "ancestor=remnawave/node:latest" | grep -q . && ! docker ps -q --filter "ancestor=remnawave/backend:2" | grep -q .; then - echo -e "${COLOR_RED}${LANG[CONTAINER_NOT_RUNNING]}${COLOR_RESET}" - exit 1 - fi - - echo -e "${COLOR_YELLOW}${LANG[VIEW_LOGS]}${COLOR_RESET}" - docker compose logs -f -t + local panel_dir="${LANG[PANEL_DIR]}" + local node_dir="${LANG[NODE_DIR]}" + + echo -e "${COLOR_GREEN}Выберите логи для просмотра:${COLOR_RESET}" + echo -e "" + echo -e "${COLOR_YELLOW}1. Логи панели${COLOR_RESET}" + echo -e "${COLOR_YELLOW}2. Логи ноды${COLOR_RESET}" + echo -e "" + echo -e "${COLOR_YELLOW}0. Выход${COLOR_RESET}" + echo -e "" + reading "Выберите действие (0-2):" LOG_OPTION + + case $LOG_OPTION in + 1) + if [ ! -d "$panel_dir" ]; then + echo -e "${COLOR_RED}Панель не установлена${COLOR_RESET}" + exit 1 + fi + cd "$panel_dir" || { echo -e "${COLOR_RED}${LANG[CHANGE_DIR_FAILED]} $panel_dir${COLOR_RESET}"; exit 1; } + + if ! docker ps -q --filter "ancestor=remnawave/backend:latest" | grep -q . && ! docker ps -q --filter "ancestor=remnawave/backend:2" | grep -q .; then + echo -e "${COLOR_RED}Контейнер панели не запущен${COLOR_RESET}" + exit 1 + fi + + echo -e "${COLOR_YELLOW}Просмотр логов панели${COLOR_RESET}" + docker compose logs -f -t + ;; + 2) + if [ ! -d "$node_dir" ]; then + echo -e "${COLOR_RED}Нода не установлена${COLOR_RESET}" + exit 1 + fi + cd "$node_dir" || { echo -e "${COLOR_RED}${LANG[CHANGE_DIR_FAILED]} $node_dir${COLOR_RESET}"; exit 1; } + + if ! docker ps -q --filter "ancestor=remnawave/node:latest" | grep -q .; then + echo -e "${COLOR_RED}Контейнер ноды не запущен${COLOR_RESET}" + exit 1 + fi + + echo -e "${COLOR_YELLOW}Просмотр логов ноды${COLOR_RESET}" + docker compose logs -f -t + ;; + 0) + echo -e "${COLOR_YELLOW}Выход${COLOR_RESET}" + return 0 + ;; + *) + echo -e "${COLOR_YELLOW}Неверный выбор${COLOR_RESET}" + sleep 2 + view_logs + ;; + esac } -#Manage Panel Access -#Show Reinstall Options show_reinstall_options() { echo -e "" echo -e "${COLOR_GREEN}${LANG[REINSTALL_TYPE_TITLE]}${COLOR_RESET}" @@ -1545,48 +1256,73 @@ choose_reinstall_type() { reading "${LANG[REINSTALL_PROMPT]}" REINSTALL_OPTION case $REINSTALL_OPTION in 1|2|3) - echo -e "${COLOR_RED}${LANG[REINSTALL_WARNING]}${COLOR_RESET}" - read confirm - if [[ "$confirm" == "y" || "$confirm" == "Y" ]]; then - reinstall_remnawave - if [ ! -f ${DIR_REMNAWAVE}install_packages ]; then - install_packages - fi - case $REINSTALL_OPTION in - 1) installation ;; - 2) installation_panel ;; - 3) installation_node ;; - esac - log_clear - else - echo -e "${COLOR_YELLOW}${LANG[EXIT]}${COLOR_RESET}" - exit 0 + echo -e "${COLOR_RED}${LANG[REINSTALL_WARNING]}${COLOR_RESET}" + read confirm + if [[ "$confirm" == "y" || "$confirm" == "Y" ]]; then + reinstall_remnawave "$REINSTALL_OPTION" + if [ ! -f ${DIR_REMNAWAVE}install_packages ]; then + install_packages fi - ;; - 0) + case $REINSTALL_OPTION in + 1) + echo -e "" + echo -e "${COLOR_RED}${LANG[WARNING_LABEL]}${COLOR_RESET}" + echo -e "${COLOR_RED}${LANG[WARNING_PANEL_NODE_TOGETHER]}${COLOR_RESET}" + echo -e "" + reading "${LANG[CONFIRM_PROMPT]}" confirm2 + if [[ "$confirm2" != "y" && "$confirm2" != "Y" ]]; then + echo -e "${COLOR_YELLOW}${LANG[EXIT]}${COLOR_RESET}" + exit 0 + fi + installation + ;; + 2) installation_panel ;; + 3) installation_node ;; + esac + log_clear + else echo -e "${COLOR_YELLOW}${LANG[EXIT]}${COLOR_RESET}" - remnawave_reverse - ;; - *) - echo -e "${COLOR_YELLOW}${LANG[INVALID_REINSTALL_CHOICE]}${COLOR_RESET}" - exit 1 - ;; - esac + exit 0 + fi + ;; + 0) + echo -e "${COLOR_YELLOW}${LANG[EXIT]}${COLOR_RESET}" + remnawave_reverse + ;; + *) + echo -e "${COLOR_YELLOW}${LANG[INVALID_REINSTALL_CHOICE]}${COLOR_RESET}" + exit 1 + ;; + esac } reinstall_remnawave() { - if [ -d "/opt/remnawave" ]; then - cd /opt/remnawave || return - docker compose down -v --rmi all --remove-orphans > /dev/null 2>&1 & - spinner $! "${LANG[WAITING]}" + local reinstall_type="$1" + + # Remove panel if selected or if installing panel+node + if [ "$reinstall_type" = "1" ] || [ "$reinstall_type" = "2" ]; then + if [ -d "${LANG[PANEL_DIR]}" ]; then + cd "${LANG[PANEL_DIR]}" || return + docker compose down -v --rmi all --remove-orphans > /dev/null 2>&1 & + spinner $! "${LANG[WAITING]}" + rm -rf "${LANG[PANEL_DIR]}" 2>/dev/null + fi + fi + + # Remove node if selected or if installing panel+node + if [ "$reinstall_type" = "1" ] || [ "$reinstall_type" = "3" ]; then + if [ -d "${LANG[NODE_DIR]}" ]; then + cd "${LANG[NODE_DIR]}" || return + docker compose down -v --rmi all --remove-orphans > /dev/null 2>&1 & + spinner $! "${LANG[WAITING]}" + rm -rf "${LANG[NODE_DIR]}" 2>/dev/null + fi fi + docker system prune -a --volumes -f > /dev/null 2>&1 & spinner $! "${LANG[WAITING]}" - rm -rf /opt/remnawave 2>/dev/null } -#Show Reinstall Options -#Show Panel Node Menu show_panel_node_menu() { echo -e "" echo -e "${COLOR_GREEN}${LANG[MENU_3]}${COLOR_RESET}" @@ -1649,7 +1385,6 @@ show_panel_node_menu() { ;; esac } -#Manage Panel Node Menu manage_extensions() { echo -e "" @@ -1670,9 +1405,9 @@ manage_extensions() { ;; 2) if [ -f ~/backup-restore.sh ]; then - rw-backup + bash ~/backup-restore.sh else - curl -o ~/backup-restore.sh https://raw.githubusercontent.com/distillium/remnawave-backup-restore/main/backup-restore.sh && chmod +x ~/backup-restore.sh && ~/backup-restore.sh + curl -o ~/backup-restore.sh https://raw.githubusercontent.com/distillium/remnawave-backup-restore/main/backup-restore.sh && chmod +x ~/backup-restore.sh && bash ~/backup-restore.sh fi log_clear manage_extensions @@ -1702,7 +1437,7 @@ manage_warp() { case $WARP_OPTION in 1) - if ! grep -q "remnanode:" /opt/remnawave/docker-compose.yml; then + if [ ! -d "${LANG[NODE_DIR]}" ]; then echo -e "${COLOR_RED}${LANG[WARP_NO_NODE]}${COLOR_RESET}" exit 1 fi @@ -1950,7 +1685,6 @@ manage_warp() { esac } -#Manage IPv6 show_ipv6_menu() { echo -e "" echo -e "${COLOR_GREEN}${LANG[IPV6_MENU_TITLE]}${COLOR_RESET}" @@ -2037,15 +1771,13 @@ disable_ipv6() { sysctl -p > /dev/null 2>&1 echo -e "${COLOR_GREEN}${LANG[IPV6_DISABLED]}${COLOR_RESET}" } -#Manage IPv6 -#Extensions by legiz show_custom_legiz_menu() { echo -e "" echo -e "${COLOR_GREEN}${LANG[MENU_5]}${COLOR_RESET}" echo -e "" - echo -e "${COLOR_YELLOW}1. ${LANG[SELECT_SUB_PAGE_CUSTOM1]}${COLOR_RESET}" # Custom sub page - echo -e "${COLOR_YELLOW}2. ${LANG[CUSTOM_APP_LIST_MENU]}${COLOR_RESET}" # Edit custom app list and branding + echo -e "${COLOR_YELLOW}1. ${LANG[SELECT_SUB_PAGE_CUSTOM1]}${COLOR_RESET}" + echo -e "${COLOR_YELLOW}2. ${LANG[CUSTOM_APP_LIST_MENU]}${COLOR_RESET}" echo -e "" echo -e "${COLOR_YELLOW}0. ${LANG[EXIT]}${COLOR_RESET}" echo -e "" @@ -2092,7 +1824,10 @@ manage_custom_legiz() { manage_custom_legiz ;; 2) - manage_custom_app_list + echo -e "" + echo -e "${COLOR_GREEN}${LANG[CUSTOM_APP_LIST_PANEL_MESSAGE]}${COLOR_RESET}" + echo -e "" + sleep 2 log_clear manage_custom_legiz ;; @@ -2113,17 +1848,9 @@ show_sub_page_menu() { echo -e "" echo -e "${COLOR_GREEN}${LANG[SELECT_SUB_PAGE_CUSTOM2]}${COLOR_RESET}" echo -e "" - echo -e "${COLOR_GREEN}${LANG[SELECT_SUB_PAGE_CUSTOM3]}${COLOR_RESET}" - echo -e "${COLOR_YELLOW}1. Simple custom app list (clash and sing)${COLOR_RESET}" - echo -e "${COLOR_YELLOW}2. Multiapp custom app list${COLOR_RESET}" - echo -e "${COLOR_YELLOW}3. HWID only custom app list${COLOR_RESET}" + echo -e "${COLOR_YELLOW}1. Orion web page template (support custom app list)${COLOR_RESET}" echo -e "" - echo -e "${COLOR_GREEN}${LANG[SELECT_SUB_PAGE_CUSTOM4]}${COLOR_RESET}" - echo -e "${COLOR_YELLOW}4. Orion web page template (support custom app list)${COLOR_RESET}" - echo -e "${COLOR_YELLOW}5. Material web page template (support custom app list)${COLOR_RESET}" - echo -e "${COLOR_YELLOW}6. Marzbanify web page template (clash and sing)${COLOR_RESET}" - echo -e "" - echo -e "${COLOR_YELLOW}7. ${LANG[RESTORE_SUB_PAGE]}${COLOR_RESET}" + echo -e "${COLOR_YELLOW}2. ${LANG[RESTORE_SUB_PAGE]}${COLOR_RESET}" echo -e "" echo -e "${COLOR_YELLOW}0. ${LANG[EXIT]}${COLOR_RESET}" echo -e "" @@ -2189,6 +1916,14 @@ branding_add_to_appconfig() { } manage_sub_page_upload() { + local panel_dir="${LANG[PANEL_DIR]}" + if [ ! -d "$panel_dir" ]; then + echo -e "${COLOR_RED}Панель не установлена${COLOR_RESET}" + exit 1 + fi + + cd "$panel_dir" || { echo -e "${COLOR_RED}${LANG[CHANGE_DIR_FAILED]} $panel_dir${COLOR_RESET}"; exit 1; } + if [ -d "/opt/remnawave/index.html" ] || [ -d "/opt/remnawave/app-config.json" ]; then rm -rf "/opt/remnawave/index.html" "/opt/remnawave/app-config.json" fi @@ -2208,35 +1943,7 @@ manage_sub_page_upload() { local docker_compose_file="/opt/remnawave/docker-compose.yml" case $SUB_PAGE_OPTION in - 1|2|3) - [ -f "$index_file" ] && rm -f "$index_file" - - echo -e "${COLOR_YELLOW}${LANG[UPLOADING_SUB_PAGE]}${COLOR_RESET}" - echo -e "" - local primary_url="https://raw.githubusercontent.com/legiz-ru/my-remnawave/refs/heads/main/sub-page/app-config.json" - local fallback_url="https://cdn.jsdelivr.net/gh/legiz-ru/my-remnawave@main/sub-page/app-config.json" - if [ "$SUB_PAGE_OPTION" == "2" ]; then - primary_url="https://raw.githubusercontent.com/legiz-ru/my-remnawave/refs/heads/main/sub-page/multiapp/app-config.json" - fallback_url="https://cdn.jsdelivr.net/gh/legiz-ru/my-remnawave@main/sub-page/multiapp/app-config.json" - elif [ "$SUB_PAGE_OPTION" == "3" ]; then - primary_url="https://raw.githubusercontent.com/legiz-ru/my-remnawave/refs/heads/main/sub-page/hwid/app-config.json" - fallback_url="https://cdn.jsdelivr.net/gh/legiz-ru/my-remnawave@main/sub-page/hwid/app-config.json" - fi - - if ! download_with_fallback "$primary_url" "$fallback_url" "$config_file"; then - echo -e "${COLOR_RED}${LANG[ERROR_FETCH_SUB_PAGE]}${COLOR_RESET}" - sleep 2 - log_clear - return 1 - fi - - branding_add_to_appconfig "$config_file" - - /usr/bin/yq eval 'del(.services."remnawave-subscription-page".volumes)' -i "$docker_compose_file" - /usr/bin/yq eval '.services."remnawave-subscription-page".volumes += ["./app-config.json:/opt/app/frontend/assets/app-config.json"]' -i "$docker_compose_file" - ;; - - 4) + 1) [ -f "$config_file" ] && rm -f "$config_file" [ -f "$index_file" ] && rm -f "$index_file" @@ -2251,137 +1958,11 @@ manage_sub_page_upload() { return 1 fi - echo -e "${COLOR_GREEN}${LANG[SUB_WITH_APPCONFIG_ASK]}${COLOR_RESET}" - echo -e "" - echo -e "${COLOR_YELLOW}1. ${LANG[SUB_WITH_APPCONFIG_OPTION1]}${COLOR_RESET}" - echo -e "${COLOR_YELLOW}2. ${LANG[SUB_WITH_APPCONFIG_OPTION2]}${COLOR_RESET}" - echo -e "${COLOR_YELLOW}3. ${LANG[SUB_WITH_APPCONFIG_OPTION3]}${COLOR_RESET}" - echo -e "" - echo -e "${COLOR_YELLOW}0. ${LANG[SUB_WITH_APPCONFIG_SKIP]}${COLOR_RESET}" - echo -e "" - reading "${LANG[SUB_WITH_APPCONFIG_INPUT]}" SUB_WITH_APPCONFIG - - case $SUB_WITH_APPCONFIG in - 1|2|3) - local primary_config_url="https://raw.githubusercontent.com/legiz-ru/my-remnawave/refs/heads/main/sub-page/app-config.json" - local fallback_config_url="https://cdn.jsdelivr.net/gh/legiz-ru/my-remnawave@main/sub-page/app-config.json" - if [ "$SUB_WITH_APPCONFIG" == "2" ]; then - primary_config_url="https://raw.githubusercontent.com/legiz-ru/my-remnawave/refs/heads/main/sub-page/multiapp/app-config.json" - fallback_config_url="https://cdn.jsdelivr.net/gh/legiz-ru/my-remnawave@main/sub-page/multiapp/app-config.json" - elif [ "$SUB_WITH_APPCONFIG" == "3" ]; then - primary_config_url="https://raw.githubusercontent.com/legiz-ru/my-remnawave/refs/heads/main/sub-page/hwid/app-config.json" - fallback_config_url="https://cdn.jsdelivr.net/gh/legiz-ru/my-remnawave@main/sub-page/hwid/app-config.json" - fi - - if ! download_with_fallback "$primary_config_url" "$fallback_config_url" "$config_file"; then - echo -e "${COLOR_RED}${LANG[ERROR_FETCH_SUB_PAGE]}${COLOR_RESET}" - sleep 2 - log_clear - return 1 - fi - - branding_add_to_appconfig "$config_file" - ;; - 0) - [ -f "$config_file" ] && rm -f "$config_file" - [ -f "$index_file" ] && rm -f "$index_file" - ;; - *) - echo -e "${COLOR_RED}${LANG[SUB_WITH_APPCONFIG_INVALID]}${COLOR_RESET}" - [ -f "$config_file" ] && rm -f "$config_file" - ;; - esac - /usr/bin/yq eval 'del(.services."remnawave-subscription-page".volumes)' -i "$docker_compose_file" /usr/bin/yq eval '.services."remnawave-subscription-page".volumes += ["./index.html:/opt/app/frontend/index.html"]' -i "$docker_compose_file" - - if [ -f "$config_file" ]; then - /usr/bin/yq eval '.services."remnawave-subscription-page".volumes += ["./app-config.json:/opt/app/frontend/assets/app-config.json"]' -i "$docker_compose_file" - fi ;; - 5) - [ -f "$config_file" ] && rm -f "$config_file" - [ -f "$index_file" ] && rm -f "$index_file" - - echo -e "${COLOR_YELLOW}${LANG[UPLOADING_SUB_PAGE]}${COLOR_RESET}" - echo -e "" - local primary_index_url="https://raw.githubusercontent.com/legiz-ru/material-remnawave-subscription-page/refs/heads/main/index.html" - local fallback_index_url="https://cdn.jsdelivr.net/gh/legiz-ru/material-remnawave-subscription-page@main/index.html" - if ! download_with_fallback "$primary_index_url" "$fallback_index_url" "$index_file"; then - echo -e "${COLOR_RED}${LANG[ERROR_FETCH_SUB_PAGE]}${COLOR_RESET}" - sleep 2 - log_clear - return 1 - fi - - echo -e "${COLOR_GREEN}${LANG[SUB_WITH_APPCONFIG_ASK]}${COLOR_RESET}" - echo -e "" - echo -e "${COLOR_YELLOW}1. ${LANG[SUB_WITH_APPCONFIG_OPTION1]}${COLOR_RESET}" - echo -e "${COLOR_YELLOW}2. ${LANG[SUB_WITH_APPCONFIG_OPTION2]}${COLOR_RESET}" - echo -e "${COLOR_YELLOW}3. ${LANG[SUB_WITH_APPCONFIG_OPTION3]}${COLOR_RESET}" - echo -e "" - echo -e "${COLOR_YELLOW}0. ${LANG[SUB_WITH_APPCONFIG_SKIP]}${COLOR_RESET}" - echo -e "" - reading "${LANG[SUB_WITH_APPCONFIG_INPUT]}" SUB_WITH_APPCONFIG - - case $SUB_WITH_APPCONFIG in - 1|2|3) - local primary_config_url="https://raw.githubusercontent.com/legiz-ru/my-remnawave/refs/heads/main/sub-page/app-config.json" - local fallback_config_url="https://cdn.jsdelivr.net/gh/legiz-ru/my-remnawave@main/sub-page/app-config.json" - if [ "$SUB_WITH_APPCONFIG" == "2" ]; then - primary_config_url="https://raw.githubusercontent.com/legiz-ru/my-remnawave/refs/heads/main/sub-page/multiapp/app-config.json" - fallback_config_url="https://cdn.jsdelivr.net/gh/legiz-ru/my-remnawave@main/sub-page/multiapp/app-config.json" - elif [ "$SUB_WITH_APPCONFIG" == "3" ]; then - primary_config_url="https://raw.githubusercontent.com/legiz-ru/my-remnawave/refs/heads/main/sub-page/hwid/app-config.json" - fallback_config_url="https://cdn.jsdelivr.net/gh/legiz-ru/my-remnawave@main/sub-page/hwid/app-config.json" - fi - - if ! download_with_fallback "$primary_config_url" "$fallback_config_url" "$config_file"; then - echo -e "${COLOR_RED}${LANG[ERROR_FETCH_SUB_PAGE]}${COLOR_RESET}" - sleep 2 - log_clear - return 1 - fi - branding_add_to_appconfig "$config_file" - ;; - 0) - [ -f "$config_file" ] && rm -f "$config_file" - [ -f "$index_file" ] && rm -f "$index_file" - ;; - *) - echo -e "${COLOR_RED}${LANG[SUB_WITH_APPCONFIG_INVALID]}${COLOR_RESET}" - [ -f "$config_file" ] && rm -f "$config_file" - ;; - esac - - /usr/bin/yq eval 'del(.services."remnawave-subscription-page".volumes)' -i "$docker_compose_file" - /usr/bin/yq eval '.services."remnawave-subscription-page".volumes += ["./index.html:/opt/app/frontend/index.html"]' -i "$docker_compose_file" - - if [ -f "$config_file" ]; then - /usr/bin/yq eval '.services."remnawave-subscription-page".volumes += ["./app-config.json:/opt/app/frontend/assets/app-config.json"]' -i "$docker_compose_file" - fi - ;; - - 6) - [ -f "$config_file" ] && rm -f "$config_file" - - echo -e "${COLOR_YELLOW}${LANG[UPLOADING_SUB_PAGE]}${COLOR_RESET}" - echo -e "" - local primary_url="https://raw.githubusercontent.com/legiz-ru/my-remnawave/refs/heads/main/sub-page/customweb/clash-sing/index.html" - local fallback_url="https://cdn.jsdelivr.net/gh/legiz-ru/my-remnawave@main/sub-page/customweb/clash-sing/index.html" - if ! download_with_fallback "$primary_url" "$fallback_url" "$index_file"; then - echo -e "${COLOR_RED}${LANG[ERROR_FETCH_SUB_PAGE]}${COLOR_RESET}" - sleep 2 - log_clear - return 1 - fi - - /usr/bin/yq eval 'del(.services."remnawave-subscription-page".volumes)' -i "$docker_compose_file" - /usr/bin/yq eval '.services."remnawave-subscription-page".volumes += ["./index.html:/opt/app/frontend/index.html"]' -i "$docker_compose_file" - ;; - - 7) + 2) [ -f "$config_file" ] && rm -f "$config_file" [ -f "$index_file" ] && rm -f "$index_file" @@ -2412,7 +1993,7 @@ manage_sub_page_upload() { sed -i -e '/^networks:/i\' -e '' "$docker_compose_file" sed -i -e '/^volumes:/i\' -e '' "$docker_compose_file" - cd /opt/remnawave || return 1 + cd "$panel_dir" || return 1 docker compose down remnawave-subscription-page > /dev/null 2>&1 & spinner $! "${LANG[WAITING]}" docker compose up -d remnawave-subscription-page > /dev/null 2>&1 & @@ -2532,7 +2113,7 @@ edit_branding() { echo -e "${COLOR_GREEN}${LANG[BRANDING_ADDED_SUCCESS]}${COLOR_RESET}" # Restart subscription page container - cd /opt/remnawave || return 1 + cd "/opt/remnawave" || return 1 docker compose down remnawave-subscription-page > /dev/null 2>&1 & spinner $! "${LANG[WAITING]}" docker compose up -d remnawave-subscription-page > /dev/null 2>&1 & @@ -2635,7 +2216,7 @@ delete_applications() { echo -e "${COLOR_GREEN}${LANG[APP_DELETED_SUCCESS]}${COLOR_RESET}" # Restart subscription page container - cd /opt/remnawave || return 1 + cd "/opt/remnawave" || return 1 docker compose down remnawave-subscription-page > /dev/null 2>&1 & spinner $! "${LANG[WAITING]}" docker compose up -d remnawave-subscription-page > /dev/null 2>&1 & @@ -2644,7 +2225,6 @@ delete_applications() { echo -e "${COLOR_YELLOW}${LANG[EXIT]}${COLOR_RESET}" fi } -#Extensions by legiz add_cron_rule() { local rule="$1" @@ -2685,7 +2265,6 @@ spinner() { printf "\r\033[K" > /dev/tty } -#Manage Template for steal show_template_source_options() { echo -e "" echo -e "${COLOR_GREEN}${LANG[CHOOSE_TEMPLATE_SOURCE]}${COLOR_RESET}" @@ -2833,7 +2412,6 @@ randomhtml() { cd /opt/ rm -rf simple-web-templates-main/ sni-templates-main/ nothing-sni-main/ } -#Manage Template for steal install_packages() { echo -e "${COLOR_YELLOW}${LANG[INSTALL_PACKAGES]}${COLOR_RESET}" @@ -2848,12 +2426,6 @@ install_packages() { return 1 fi - if command -v certbot >/dev/null 2>&1; then - if ! pip install --break-system-packages certbot-dns-gcore >/dev/null 2>&1; then - return 1 - fi - fi - if ! dpkg -l | grep -q '^ii.*cron '; then if ! apt-get install -y cron; then echo -e "${COLOR_RED}${LANG[ERROR_INSTALL_CRON]}" "${COLOR_RESET}" >&2 @@ -2874,32 +2446,18 @@ install_packages() { fi fi - if grep -q "Ubuntu" /etc/os-release; then - install -m 0755 -d /etc/apt/keyrings - if ! curl -fsSL https://download.docker.com/linux/ubuntu/gpg | tee /etc/apt/keyrings/docker.asc > /dev/null; then + if ! command -v docker >/dev/null 2>&1 || ! docker info >/dev/null 2>&1; then + echo -e "${COLOR_YELLOW}Installing Docker via get.docker.com...${COLOR_RESET}" + + if ! curl -fsSL https://get.docker.com -o /tmp/get-docker.sh; then echo -e "${COLOR_RED}${LANG[ERROR_DOWNLOAD_DOCKER_KEY]}${COLOR_RESET}" >&2 return 1 fi - chmod a+r /etc/apt/keyrings/docker.asc - echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null - elif grep -q "Debian" /etc/os-release; then - install -m 0755 -d /etc/apt/keyrings - if ! curl -fsSL https://download.docker.com/linux/debian/gpg | tee /etc/apt/keyrings/docker.asc > /dev/null; then - echo -e "${COLOR_RED}${LANG[ERROR_DOWNLOAD_DOCKER_KEY]}${COLOR_RESET}" >&2 + + if ! sh /tmp/get-docker.sh; then + echo -e "${COLOR_RED}${LANG[ERROR_INSTALL_DOCKER]}${COLOR_RESET}" >&2 return 1 fi - chmod a+r /etc/apt/keyrings/docker.asc - echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/debian $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null - fi - - if ! apt-get update; then - echo -e "${COLOR_RED}${LANG[ERROR_UPDATE_DOCKER_LIST]}${COLOR_RESET}" >&2 - return 1 - fi - - if ! apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin; then - echo -e "${COLOR_RED}${LANG[ERROR_INSTALL_DOCKER]}${COLOR_RESET}" >&2 - return 1 fi if ! command -v docker >/dev/null 2>&1; then @@ -3196,10 +2754,22 @@ EOL # Gcore DNS-01 (wildcard) if ! certbot plugins 2>/dev/null | grep -q "dns-gcore"; then - if ! pip install --break-system-packages certbot-dns-gcore >/dev/null 2>&1; then + echo -e "${COLOR_YELLOW}Installing certbot-dns-gcore plugin...${COLOR_RESET}" + + if python3 -m pip install --help 2>&1 | grep -q "break-system-packages"; then + python3 -m pip install --break-system-packages certbot-dns-gcore >/dev/null 2>&1 + else + python3 -m pip install certbot-dns-gcore >/dev/null 2>&1 + fi + + if certbot plugins 2>/dev/null | grep -q "dns-gcore"; then + echo -e "${COLOR_GREEN}Plugin installed successfully.${COLOR_RESET}" + else echo -e "${COLOR_RED}${LANG[ERROR_INSTALL_GCORE_PLUGIN]}${COLOR_RESET}" exit 1 fi + else + echo -e "${COLOR_GREEN}Gcore plugin already available.${COLOR_RESET}" fi reading "${LANG[ENTER_GCORE_TOKEN]}" GCORE_API_KEY @@ -3234,7 +2804,6 @@ EOL fi } -#Manage Certificates show_manage_certificates() { echo -e "" echo -e "${COLOR_GREEN}${LANG[MENU_8]}${COLOR_RESET}" @@ -3578,9 +3147,7 @@ fix_letsencrypt_structure() { chmod 600 "$live_dir/privkey.pem" return 0 } -#Manage Certificates -### API Functions ### make_api_request() { local method=$1 local url=$2 @@ -3602,7 +3169,6 @@ make_api_request() { fi } - register_remnawave() { local domain_url=$1 local username=$2 @@ -4041,27 +3607,32 @@ update_squad() { create_api_token() { local domain_url=$1 local token=$2 - local token_name="${3:-subscription-page-token}" + local target_dir=$3 + local token_name="${4:-subscription-page}" local token_data='{"tokenName":"'"$token_name"'"}' - local api_response=$(make_api_request "POST" "http://$domain_url/api/tokens" "$token" "$token_data") + local api_response + api_response=$(make_api_request "POST" "http://$domain_url/api/tokens" "$token" "$token_data") if [ -z "$api_response" ]; then - echo -e "${COLOR_RED}${LANG[ERROR_CREATE_API_TOKEN]}${COLOR_RESET}" + echo -e "${COLOR_RED}${LANG[ERROR_CREATE_API_TOKEN]}${COLOR_RESET}" >&2 return 1 fi - if echo "$api_response" | jq -e '.response.token' > /dev/null 2>&1; then - local api_token=$(echo "$api_response" | jq -r '.response.token') - echo "$api_token" - return 0 - else - echo -e "${COLOR_RED}${LANG[ERROR_CREATE_API_TOKEN]}: $(echo "$api_response" | jq -r '.message // "Unknown error"') ${COLOR_RESET}" + local api_token + api_token=$(echo "$api_response" | jq -r '.response.token') + + if [ -z "$api_token" ] || [ "$api_token" = "null" ]; then + echo -e "${COLOR_RED}${LANG[ERROR_CREATE_API_TOKEN]}: $(echo "$api_response" | jq -r '.message // "Unknown error"')" >&2 return 1 fi -} -### API Functions ### + sed -i "s|REMNAWAVE_API_TOKEN=.*|REMNAWAVE_API_TOKEN=$api_token|" "$target_dir/docker-compose.yml" + + sleep 1 + + echo -e "${COLOR_GREEN}${LANG[API_TOKEN_ADDED]}${COLOR_RESET}" >&2 +} handle_certificates() { local -n domains_to_check_ref=$1 @@ -4223,9 +3794,11 @@ handle_certificates() { done } -#Install Panel + Node install_remnawave() { - mkdir -p /opt/remnawave && cd /opt/remnawave + local panel_dir="${LANG[PANEL_DIR]}" + local node_dir="${LANG[NODE_DIR]}" + + mkdir -p "$panel_dir" && cd "$panel_dir" reading "${LANG[ENTER_PANEL_DOMAIN]}" PANEL_DOMAIN check_domain "$PANEL_DOMAIN" true true @@ -4372,6 +3945,10 @@ services: container_name: 'remnawave-db' hostname: remnawave-db restart: always + ulimits: + nofile: + soft: 1048576 + hard: 1048576 env_file: - .env environment: @@ -4401,6 +3978,10 @@ services: container_name: remnawave hostname: remnawave restart: always + ulimits: + nofile: + soft: 1048576 + hard: 1048576 env_file: - .env ports: @@ -4430,6 +4011,10 @@ services: container_name: remnawave-redis hostname: remnawave-redis restart: always + ulimits: + nofile: + soft: 1048576 + hard: 1048576 networks: - remnawave-network command: > @@ -4455,6 +4040,10 @@ services: hostname: remnawave-nginx network_mode: host restart: always + ulimits: + nofile: + soft: 1048576 + hard: 1048576 volumes: - ./nginx.conf:/etc/nginx/conf.d/default.conf:ro EOL @@ -4499,7 +4088,7 @@ installation() { cat >> /opt/remnawave/docker-compose.yml < /dev/null 2>&1 & spinner $! "${LANG[WAITING]}" + + # Install node in separate directory + echo -e "${COLOR_YELLOW}Установка ноды в отдельной директории...${COLOR_RESET}" + sleep 1 + mkdir -p "${LANG[NODE_DIR]}" && cd "${LANG[NODE_DIR]}" + + cat > docker-compose.yml < /dev/null 2>&1 & + spinner $! "${LANG[WAITING]}" clear @@ -4813,14 +4413,16 @@ EOL echo -e "${COLOR_YELLOW}${LANG[RELAUNCH_CMD]}${COLOR_RESET}" echo -e "${COLOR_GREEN}remnawave_reverse${COLOR_RESET}" echo -e "${COLOR_YELLOW}=================================================${COLOR_RESET}" + echo -e "${COLOR_YELLOW}Панель установлена в: ${LANG[PANEL_DIR]}${COLOR_RESET}" + echo -e "${COLOR_YELLOW}Нода установлена в: ${LANG[NODE_DIR]}${COLOR_RESET}" + echo -e "${COLOR_YELLOW}=================================================${COLOR_RESET}" randomhtml } -#Install Panel + Node -#Install Panel install_remnawave_panel() { - mkdir -p /opt/remnawave && cd /opt/remnawave + local panel_dir="${LANG[PANEL_DIR]}" + mkdir -p "$panel_dir" && cd "$panel_dir" reading "${LANG[ENTER_PANEL_DOMAIN]}" PANEL_DOMAIN check_domain "$PANEL_DOMAIN" true true @@ -4959,6 +4561,10 @@ services: container_name: 'remnawave-db' hostname: remnawave-db restart: always + ulimits: + nofile: + soft: 1048576 + hard: 1048576 env_file: - .env environment: @@ -4988,6 +4594,10 @@ services: container_name: remnawave hostname: remnawave restart: always + ulimits: + nofile: + soft: 1048576 + hard: 1048576 env_file: - .env ports: @@ -5017,6 +4627,10 @@ services: container_name: remnawave-redis hostname: remnawave-redis restart: always + ulimits: + nofile: + soft: 1048576 + hard: 1048576 networks: - remnawave-network command: > @@ -5040,7 +4654,12 @@ services: image: nginx:1.28 container_name: remnawave-nginx hostname: remnawave-nginx + network_mode: host restart: always + ulimits: + nofile: + soft: 1048576 + hard: 1048576 volumes: - ./nginx.conf:/etc/nginx/conf.d/default.conf:ro EOL @@ -5058,7 +4677,7 @@ installation_panel() { domains_to_check["$SUB_DOMAIN"]=1 handle_certificates domains_to_check "$CERT_METHOD" "$LETSENCRYPT_EMAIL" - + if [ -z "$CERT_METHOD" ]; then local base_domain=$(extract_domain "$PANEL_DOMAIN") if [ -d "/etc/letsencrypt/live/$base_domain" ] && is_wildcard_cert "$base_domain"; then @@ -5079,7 +4698,9 @@ installation_panel() { fi cat >> /opt/remnawave/docker-compose.yml < /dev/null 2>&1 & + docker compose down > /dev/null 2>&1 & spinner $! "${LANG[WAITING]}" - echo -e "${COLOR_YELLOW}${LANG[STARTING_REMNAWAVE_SUBSCRIPTION_PAGE]}${COLOR_RESET}" + echo -e "${COLOR_YELLOW}${LANG[STARTING_PANEL]}${COLOR_RESET}" sleep 1 - docker compose up -d remnawave-subscription-page > /dev/null 2>&1 & + docker compose up -d > /dev/null 2>&1 & spinner $! "${LANG[WAITING]}" clear echo -e "${COLOR_YELLOW}=================================================${COLOR_RESET}" - echo -e "${COLOR_GREEN}${LANG[INSTALL_COMPLETE]}${COLOR_RESET}" + echo -e "${COLOR_GREEN}${LANG[POST_PANEL_MESSAGE]}${COLOR_RESET}" echo -e "${COLOR_YELLOW}=================================================${COLOR_RESET}" echo -e "${COLOR_YELLOW}${LANG[PANEL_ACCESS]}${COLOR_RESET}" echo -e "${COLOR_WHITE}https://${PANEL_DOMAIN}/auth/login?${cookies_random1}=${cookies_random2}${COLOR_RESET}" @@ -5334,413 +4932,233 @@ EOL echo -e "${COLOR_YELLOW}${LANG[USERNAME]} ${COLOR_WHITE}$SUPERADMIN_USERNAME${COLOR_RESET}" echo -e "${COLOR_YELLOW}${LANG[PASSWORD]} ${COLOR_WHITE}$SUPERADMIN_PASSWORD${COLOR_RESET}" echo -e "${COLOR_YELLOW}-------------------------------------------------${COLOR_RESET}" + echo -e "${COLOR_YELLOW}${LANG[POST_PANEL_INSTRUCTION]}${COLOR_RESET}" + echo -e "${COLOR_YELLOW}-------------------------------------------------${COLOR_RESET}" echo -e "${COLOR_YELLOW}${LANG[RELAUNCH_CMD]}${COLOR_RESET}" echo -e "${COLOR_GREEN}remnawave_reverse${COLOR_RESET}" echo -e "${COLOR_YELLOW}=================================================${COLOR_RESET}" - echo -e "${COLOR_RED}${LANG[POST_PANEL_INSTRUCTION]}${COLOR_RESET}" + echo -e "${COLOR_YELLOW}Панель установлена в: ${LANG[PANEL_DIR]}${COLOR_RESET}" + echo -e "${COLOR_YELLOW}=================================================${COLOR_RESET}" } -#Install Panel -#Install Node -install_remnawave_node() { - mkdir -p /opt/remnawave && cd /opt/remnawave +add_node_to_panel() { + echo -e "${COLOR_YELLOW}${LANG[ADD_NODE_TO_PANEL]}${COLOR_RESET}" + + local panel_dir="${LANG[PANEL_DIR]}" + if [ ! -d "$panel_dir" ]; then + echo -e "${COLOR_RED}Панель не установлена${COLOR_RESET}" + exit 1 + fi + + echo -e "" + echo -e "${COLOR_RED}${LANG[WARNING_LABEL]}${COLOR_RESET}" + echo -e "${COLOR_YELLOW}${LANG[WARNING_NODE_PANEL]}${COLOR_RESET}" + echo -e "" + echo -e "${COLOR_GREEN}[?]${COLOR_RESET} ${COLOR_YELLOW}${LANG[CONFIRM_SERVER_PANEL]}${COLOR_RESET}" + reading "${LANG[CONFIRM_PROMPT]}" confirm + echo - reading "${LANG[SELFSTEAL]}" SELFSTEAL_DOMAIN + if [[ "$confirm" != "y" && "$confirm" != "Y" ]]; then + echo -e "${COLOR_YELLOW}${LANG[EXIT]}${COLOR_RESET}" + exit 0 + fi + + local domain_url="127.0.0.1:3000" + + get_panel_token + token=$(cat "$TOKEN_FILE") + + reading "${LANG[ENTER_NODE_NAME]}" NODE_NAME + reading "${LANG[SELFSTEAL]}" NODE_DOMAIN + + check_node_domain "$domain_url" "$token" "$NODE_DOMAIN" + if [ $? -ne 0 ]; then + echo -e "${COLOR_YELLOW}${LANG[TRY_ANOTHER_DOMAIN]}${COLOR_RESET}" + exit 1 + fi + + # Generate Xray keys + echo -e "${COLOR_YELLOW}${LANG[GENERATE_KEYS]}${COLOR_RESET}" + sleep 1 + local private_key=$(generate_xray_keys "$domain_url" "$token") + printf "${COLOR_GREEN}${LANG[GENERATE_KEYS_SUCCESS]}${COLOR_RESET}\n" + + # Create config profile + echo -e "${COLOR_YELLOW}${LANG[CREATING_CONFIG_PROFILE]}${COLOR_RESET}" + read config_profile_uuid inbound_uuid <<< $(create_config_profile "$domain_url" "$token" "StealConfig" "$NODE_DOMAIN" "$private_key") + echo -e "${COLOR_GREEN}${LANG[CONFIG_PROFILE_CREATED]}${COLOR_RESET}" + + # Create node with config profile binding + printf "${COLOR_YELLOW}${LANG[CREATE_NEW_NODE]}${COLOR_RESET}\n" "$NODE_NAME" + create_node "$domain_url" "$token" "$config_profile_uuid" "$inbound_uuid" "172.30.0.1" "$NODE_NAME" + + # Create host + echo -e "${COLOR_YELLOW}${LANG[CREATE_HOST]}${COLOR_RESET}" + create_host "$domain_url" "$token" "$inbound_uuid" "$NODE_DOMAIN" "$config_profile_uuid" + + # Get UUID default squad + echo -e "${COLOR_YELLOW}${LANG[GET_DEFAULT_SQUAD]}${COLOR_RESET}" + local squad_uuid=$(get_default_squad "$domain_url" "$token") + + # Update squad + echo -e "${COLOR_YELLOW}${LANG[UPDATING_SQUAD]}${COLOR_RESET}" + update_squad "$domain_url" "$token" "$squad_uuid" "$inbound_uuid" + echo -e "${COLOR_GREEN}${LANG[UPDATE_SQUAD]}${COLOR_RESET}" + + echo -e "${COLOR_GREEN}${LANG[NODE_ADDED_SUCCESS]}${COLOR_RESET}" +} - check_domain "$SELFSTEAL_DOMAIN" true false - local domain_check_result=$? - if [ $domain_check_result -eq 2 ]; then +installation_node() { + echo -e "${COLOR_YELLOW}${LANG[INSTALLING_NODE]}${COLOR_RESET}" + sleep 1 + + local node_dir="${LANG[NODE_DIR]}" + mkdir -p "$node_dir" && cd "$node_dir" + + reading "${LANG[SELFSTEAL]}" NODE_DOMAIN + check_domain "$NODE_DOMAIN" true false + local node_check_result=$? + if [ $node_check_result -eq 2 ]; then echo -e "${COLOR_RED}${LANG[ABORT_MESSAGE]}${COLOR_RESET}" exit 1 fi - - while true; do + + reading "${LANG[PANEL_IP_PROMPT]}" PANEL_IP + while ! [[ $PANEL_IP =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; do + echo -e "${COLOR_RED}${LANG[IP_ERROR]}${COLOR_RESET}" reading "${LANG[PANEL_IP_PROMPT]}" PANEL_IP - if echo "$PANEL_IP" | grep -E '^([0-9]{1,3}\.){3}[0-9]{1,3}$' >/dev/null && \ - [[ $(echo "$PANEL_IP" | tr '.' '\n' | wc -l) -eq 4 ]] && \ - [[ ! $(echo "$PANEL_IP" | tr '.' '\n' | grep -vE '^[0-9]{1,3}$') ]] && \ - [[ ! $(echo "$PANEL_IP" | tr '.' '\n' | grep -E '^(25[6-9]|2[6-9][0-9]|[3-9][0-9]{2})$') ]]; then - break - else - echo -e "${COLOR_RED}${LANG[IP_ERROR]}${COLOR_RESET}" - fi done - - echo -n "$(question "${LANG[CERT_PROMPT]}")" - CERTIFICATE="" + + echo -e "${COLOR_YELLOW}${LANG[CERT_PROMPT]}${COLOR_RESET}" + echo -e "${COLOR_WHITE}(Вставьте содержимое сертификата и нажмите Enter дважды)${COLOR_RESET}" + + local certificate_content="" + local line + local empty_lines=0 + while IFS= read -r line; do if [ -z "$line" ]; then - if [ -n "$CERTIFICATE" ]; then + ((empty_lines++)) + if [ $empty_lines -ge 2 ]; then break fi else - CERTIFICATE="$CERTIFICATE$line\n" + empty_lines=0 fi + certificate_content+="$line"$'\n' done - + echo -e "${COLOR_YELLOW}${LANG[CERT_CONFIRM]}${COLOR_RESET}" read confirm - echo - if [[ "$confirm" != "y" && "$confirm" != "Y" ]]; then echo -e "${COLOR_RED}${LANG[ABORT_MESSAGE]}${COLOR_RESET}" exit 1 fi - -SELFSTEAL_BASE_DOMAIN=$(extract_domain "$SELFSTEAL_DOMAIN") - -unique_domains["$SELFSTEAL_BASE_DOMAIN"]=1 - -cat > docker-compose.yml < "$cert_file" + + # Create docker-compose.yml for node + cat > docker-compose.yml <> /opt/remnawave/docker-compose.yml < /opt/remnawave/nginx.conf < /dev/null 2>&1 - ufw reload > /dev/null 2>&1 - + echo -e "${COLOR_YELLOW}${LANG[STARTING_NODE]}${COLOR_RESET}" - sleep 3 - cd /opt/remnawave + sleep 1 docker compose up -d > /dev/null 2>&1 & - spinner $! "${LANG[WAITING]}" - - randomhtml - - printf "${COLOR_YELLOW}${LANG[NODE_CHECK]}${COLOR_RESET}\n" "$SELFSTEAL_DOMAIN" - local max_attempts=5 - local attempt=1 - local delay=15 - - while [ $attempt -le $max_attempts ]; do - printf "${COLOR_YELLOW}${LANG[NODE_ATTEMPT]}${COLOR_RESET}\n" "$attempt" "$max_attempts" - if curl -s --fail --max-time 10 "https://$SELFSTEAL_DOMAIN" | grep -q "html"; then - echo -e "${COLOR_GREEN}${LANG[NODE_LAUNCHED]}${COLOR_RESET}" - break - else - printf "${COLOR_RED}${LANG[NODE_UNAVAILABLE]}${COLOR_RESET}\n" "$attempt" - if [ $attempt -eq $max_attempts ]; then - printf "${COLOR_RED}${LANG[NODE_NOT_CONNECTED]}${COLOR_RESET}\n" "$max_attempts" - echo -e "${COLOR_YELLOW}${LANG[CHECK_CONFIG]}${COLOR_RESET}" - exit 1 - fi - sleep $delay - fi - ((attempt++)) - done - -} -#Install Node - -#Add Node to Panel -add_node_to_panel() { - local domain_url="127.0.0.1:3000" - echo -e "" - echo -e "${COLOR_RED}${LANG[WARNING_LABEL]}${COLOR_RESET}" - echo -e "${COLOR_YELLOW}${LANG[WARNING_NODE_PANEL]}${COLOR_RESET}" - echo -e "${COLOR_YELLOW}${LANG[CONFIRM_SERVER_PANEL]}${COLOR_RESET}" - echo -e "" - echo -e "${COLOR_GREEN}[?]${COLOR_RESET} ${COLOR_YELLOW}${LANG[CONFIRM_PROMPT]}${COLOR_RESET}" - read confirm - echo - - if [[ "$confirm" != "y" && "$confirm" != "Y" ]]; then - echo -e "${COLOR_YELLOW}${LANG[EXIT]}${COLOR_RESET}" - exit 0 - fi - - echo -e "${COLOR_YELLOW}${LANG[ADD_NODE_TO_PANEL]}${COLOR_RESET}" - sleep 1 - - get_panel_token - token=$(cat "$TOKEN_FILE") - if [ $? -ne 0 ]; then - echo -e "${COLOR_RED}${LANG[ERROR_TOKEN]}${COLOR_RESET}" - return 1 - fi + # Allow connection from panel network + ufw allow from "$PANEL_IP" to any port 2222 proto tcp > /dev/null 2>&1 + ufw reload > /dev/null 2>&1 + + echo -e "${COLOR_GREEN}${LANG[SUCCESS_MESSAGE]}${COLOR_RESET}" + echo -e "${COLOR_YELLOW}Нода установлена в: $node_dir${COLOR_RESET}" +} - while true; do - reading "${LANG[ENTER_NODE_DOMAIN]}" SELFSTEAL_DOMAIN - check_node_domain "$domain_url" "$token" "$SELFSTEAL_DOMAIN" - if [ $? -eq 0 ]; then - break - fi - echo -e "${COLOR_YELLOW}${LANG[TRY_ANOTHER_DOMAIN]}${COLOR_RESET}" - done +remnawave_reverse() { + check_root + check_os + log_entry + install_script_if_missing + check_update_status while true; do - reading "${LANG[ENTER_NODE_NAME]}" entity_name - if [[ "$entity_name" =~ ^[a-zA-Z0-9-]+$ ]]; then - if [ ${#entity_name} -ge 3 ] && [ ${#entity_name} -le 20 ]; then - local response=$(make_api_request "GET" "http://$domain_url/api/config-profiles" "$token") - - if echo "$response" | jq -e ".response.configProfiles[] | select(.name == \"$entity_name\")" > /dev/null; then - echo -e "${COLOR_RED}$(printf "${LANG[CF_INVALID_NAME]}" "$entity_name")${COLOR_RESET}" - else - break - fi - else - echo -e "${COLOR_RED}${LANG[CF_INVALID_LENGTH]}${COLOR_RESET}" - fi - else - echo -e "${COLOR_RED}${LANG[CF_INVALID_CHARS]}${COLOR_RESET}" - fi + show_menu + reading "${LANG[PROMPT_ACTION]}" OPTION + + case $OPTION in + 1) + manage_install + ;; + 2) + choose_reinstall_type + ;; + 3) + show_panel_node_menu + ;; + 4) + show_template_source_options + reading "${LANG[CHOOSE_TEMPLATE_OPTION]}" TEMPLATE_OPTION + case $TEMPLATE_OPTION in + 1) randomhtml "simple" ;; + 2) randomhtml "sni" ;; + 3) randomhtml "nothing" ;; + 0) echo -e "${COLOR_YELLOW}${LANG[EXIT]}${COLOR_RESET}" ;; + *) echo -e "${COLOR_YELLOW}${LANG[INVALID_TEMPLATE_CHOICE]}${COLOR_RESET}" ;; + esac + ;; + 5) + manage_custom_legiz + ;; + 6) + manage_extensions + ;; + 7) + manage_ipv6 + ;; + 8) + manage_certificates + ;; + 9) + update_remnawave_reverse + ;; + 10) + remove_script + ;; + 0) + echo -e "${COLOR_YELLOW}${LANG[EXIT]}${COLOR_RESET}" + exit 0 + ;; + *) + echo -e "${COLOR_YELLOW}${LANG[INVALID_CHOICE]}${COLOR_RESET}" + sleep 2 + ;; + esac done - - echo -e "${COLOR_YELLOW}${LANG[GENERATE_KEYS]}${COLOR_RESET}" - local private_key=$(generate_xray_keys "$domain_url" "$token") - printf "${COLOR_GREEN}${LANG[GENERATE_KEYS_SUCCESS]}${COLOR_RESET}\n" - - echo -e "${COLOR_YELLOW}${LANG[CREATING_CONFIG_PROFILE]}${COLOR_RESET}" - read config_profile_uuid inbound_uuid <<< $(create_config_profile "$domain_url" "$token" "$entity_name" "$SELFSTEAL_DOMAIN" "$private_key" "$entity_name") - echo -e "${COLOR_GREEN}${LANG[CONFIG_PROFILE_CREATED]}: $entity_name${COLOR_RESET}" - - printf "${COLOR_YELLOW}${LANG[CREATE_NEW_NODE]}$SELFSTEAL_DOMAIN${COLOR_RESET}\n" - create_node "$domain_url" "$token" "$config_profile_uuid" "$inbound_uuid" "$SELFSTEAL_DOMAIN" "$entity_name" - - echo -e "${COLOR_YELLOW}${LANG[CREATE_HOST]}${COLOR_RESET}" - create_host "$domain_url" "$token" "$inbound_uuid" "$SELFSTEAL_DOMAIN" "$config_profile_uuid" "$entity_name" - - echo -e "${COLOR_YELLOW}${LANG[GET_DEFAULT_SQUAD]}${COLOR_RESET}" - local squad_uuids=$(get_default_squad "$domain_url" "$token") - if [ $? -ne 0 ]; then - echo -e "${COLOR_RED}${LANG[ERROR_GET_SQUAD_LIST]}${COLOR_RESET}" - elif [ -z "$squad_uuids" ]; then - echo -e "${COLOR_YELLOW}${LANG[NO_SQUADS_TO_UPDATE]}${COLOR_RESET}" - else - for squad_uuid in $squad_uuids; do - echo -e "${COLOR_YELLOW}${LANG[UPDATING_SQUAD]} $squad_uuid${COLOR_RESET}" - update_squad "$domain_url" "$token" "$squad_uuid" "$inbound_uuid" - if [ $? -eq 0 ]; then - echo -e "${COLOR_GREEN}${LANG[UPDATE_SQUAD]} $squad_uuid${COLOR_RESET}" - else - echo -e "${COLOR_RED}${LANG[ERROR_UPDATE_SQUAD]} $squad_uuid${COLOR_RESET}" - fi - done - fi - - echo -e "${COLOR_GREEN}${LANG[NODE_ADDED_SUCCESS]}${COLOR_RESET}" - echo -e "${COLOR_RED}-------------------------------------------------${COLOR_RESET}" - echo -e "${COLOR_RED}${LANG[POST_PANEL_INSTRUCTION]}${COLOR_RESET}" - echo -e "${COLOR_RED}-------------------------------------------------${COLOR_RESET}" } -#Add Node to Panel - -log_entry -if ! load_language; then - show_language - reading "Choose option (1-2):" LANG_OPTION - - case $LANG_OPTION in - 1) set_language en; echo "1" > "$LANG_FILE" ;; - 2) set_language ru; echo "2" > "$LANG_FILE" ;; - *) error "Invalid choice. Please select 1-2." ;; - esac -fi - -check_root -check_os -install_script_if_missing -check_update_status -show_menu - -reading "${LANG[PROMPT_ACTION]}" OPTION - -case $OPTION in - 1) - manage_install - ;; - 2) - choose_reinstall_type - ;; - 3) - show_panel_node_menu - ;; - 4) - if [ ! -d "/opt/remnawave" ]; then - echo -e "${COLOR_YELLOW}${LANG[NO_PANEL_NODE_INSTALLED]}${COLOR_RESET}" - exit 1 - else - show_template_source_options - reading "${LANG[CHOOSE_TEMPLATE_OPTION]}" TEMPLATE_OPTION - case $TEMPLATE_OPTION in - 1) - randomhtml "simple" - sleep 2 - log_clear - remnawave_reverse - ;; - 2) - randomhtml "sni" - sleep 2 - log_clear - remnawave_reverse - ;; - 3) - randomhtml "nothing" - sleep 2 - log_clear - remnawave_reverse - ;; - 0) - echo -e "${COLOR_YELLOW}${LANG[EXIT]}${COLOR_RESET}" - remnawave_reverse - ;; - *) - echo -e "${COLOR_YELLOW}${LANG[INVALID_TEMPLATE_CHOICE]}${COLOR_RESET}" - exit 1 - ;; - esac - fi - ;; - 5) - manage_custom_legiz - sleep 2 - log_clear - remnawave_reverse - ;; - 6) - manage_extensions - sleep 2 - log_clear - remnawave_reverse - ;; - 7) - manage_ipv6 - sleep 2 - log_clear - remnawave_reverse - ;; - 8) - manage_certificates - sleep 2 - log_clear - remnawave_reverse - ;; - 9) - update_remnawave_reverse - sleep 2 - log_clear - remnawave_reverse - ;; - 10) - remove_script - ;; - 0) - echo -e "${COLOR_YELLOW}${LANG[EXIT]}${COLOR_RESET}" - exit 0 - ;; - *) - echo -e "${COLOR_YELLOW}${LANG[INVALID_CHOICE]}${COLOR_RESET}" - exit 1 - ;; -esac -exit 0 \ No newline at end of file +# Start script +remnawave_reverse