├── LICENSE
├── README.en.md
├── README.md
├── data
├── app-config.json
├── backup
│ ├── backup_script.sh
│ └── backup_script_tg.sh
├── caddy
│ ├── caddyfile
│ ├── caddyfile-node
│ └── caddyfile-protection
├── docker
│ ├── caddy-compose.yml
│ ├── caddy-protection-compose.yml
│ ├── node-compose.yml
│ ├── panel-compose.yml
│ ├── panel.env
│ ├── subscription-compose.yml
│ └── subscription-protection-compose.yml
└── site
│ ├── assets
│ ├── main.js
│ └── style.css
│ └── index.html
├── install.sh
├── remnasetup.sh
└── scripts
├── backups
├── auto_backup.sh
├── backup.sh
└── restore.sh
├── common
├── colors.sh
├── functions.sh
└── languages.sh
├── remnanode
├── install-bbr.sh
├── install-caddy.sh
├── install-full.sh
├── install-ipv6.sh
├── install-node.sh
├── install-warp.sh
└── update.sh
└── remnawave
├── install-caddy.sh
├── install-full.sh
├── install-panel.sh
├── install-subscription.sh
├── update-full.sh
├── update-panel.sh
└── update-subscription.sh
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 Capybara
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/README.en.md:
--------------------------------------------------------------------------------
1 | # RemnaSetup 🛠️
2 |
3 |
4 |
5 | [English](README.en.md) | [Русский](README.md)
6 |
7 | 
8 | 
9 | 
10 |
11 | **Universal script for automatic installation, configuration, and updating of Remnawave and Remnanode infrastructure**
12 |
13 | [](https://github.com/Capybara-z/RemnaSetup)
14 | [](https://github.com/Capybara-z/RemnaSetup)
15 |
16 |
17 |
18 | ---
19 |
20 | ## 🚀 Features
21 |
22 |
23 |
24 | ### 🔥 Main Components
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 | ### 🎯 Remnawave
33 | - Installation and configuration of control panel
34 | - Installation of subscription page
35 | - Integration with Caddy for request proxying
36 | - Protection of panel and subscriptions
37 | - Automatic component updates
38 |
39 |
40 |
41 |
42 | ### 🌐 Remnanode
43 | - Installation and configuration of node
44 | - Integration with Caddy for self-steal
45 | - Network optimization through BBR
46 | - WARP-NATIVE (by distillium) integration
47 | - Automatic component updates
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 | ---
56 |
57 | ### 🗄️ Remnawave Backup/Restore
58 |
59 | - 💾 Create Remnawave backup
60 | - ♻️ Restore Remnawave from archive
61 | - 📂 Archives stored in /opt/backups
62 | - 📋 Backup types: manual, automatic, with Telegram sending
63 | - 🕒 Automatic backup with configurable schedule
64 |
65 | ---
66 |
67 |
68 |
69 | ### ⚡ Additional Features
70 | - **Modular structure** with separate scripts
71 | - **Interactive menu** with component selection
72 | - **Automatic updates** of all components
73 | - **Existing installation checks** before installation
74 | - **Reinstallation capability** with data preservation
75 | - **Enhanced error handling** and logging
76 | - **Remnawave backup and restore** through separate menu
77 |
78 | ---
79 |
80 | ## 📋 Menu Options
81 |
82 |
83 |
84 | ### 🎮 Interactive Menu
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 | ### 1️⃣ Remnawave
93 | - 📦 Full installation (Remnawave + Subscription Page + Caddy)
94 | - 🚀 Install Remnawave
95 | - 📄 Install Subscription Page
96 | - ⚙️ Install Caddy
97 | - 🔄 Update (Remnawave + Subscription Page)
98 | - 🔄 Update Remnawave
99 | - 🔄 Update Subscription Page
100 |
101 |
102 |
103 |
104 | ### 2️⃣ Remnanode
105 | - 📦 Full installation (Remnanode + Caddy + Tblocker + BBR + WARP)
106 | - 🚀 Install Remnanode
107 | - ⚙️ Install Caddy + self-steal
108 | - ⚡ Install BBR
109 | - 🌐 Install WARP-NATIVE (by distillium)
110 | - 🔄 Update Remnanode
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 | ---
119 |
120 | ### 3️⃣ Remnawave Backup/Restore
121 |
122 | - 💾 Create Remnawave backup
123 | - ♻️ Restore Remnawave from archive
124 | - 📂 Archives stored in /opt/backups
125 | - 🕒 Automatic backup with configurable schedule
126 | - 📤 Send backups to Telegram bot
127 | - 🗑️ Automatic cleanup of old backups
128 | - 🛡️ All actions through convenient menu
129 |
130 | ---
131 |
132 |
133 |
134 | ---
135 |
136 | ## 🖥️ Quick Start
137 |
138 | - Option 1
139 | ```bash
140 | bash <(curl -fsSL raw.githubusercontent.com/Capybara-z/RemnaSetup/refs/heads/main/install.sh)
141 | ```
142 | - Option 2
143 | ```bash
144 | curl -fsSL https://raw.githubusercontent.com/Capybara-z/RemnaSetup/refs/heads/main/install.sh -o install.sh && chmod +x install.sh && sudo bash ./install.sh
145 | ```
146 |
147 | ---
148 |
149 | ## 💡 How It Works
150 |
151 |
152 |
153 | ### 🔄 Installation Process
154 |
155 |
156 |
157 | 1. **🎯 Select option** in main menu
158 | 2. **📝 Enter data**:
159 | - 🌐 Domains for panel and subscriptions
160 | - 🔌 Ports for services
161 | - 🔑 Database credentials
162 | - 📊 Metrics settings
163 | - 🌐 WARP parameters
164 | 3. **🗄️ Backup and restore**
165 | 4. **⚡ Automation**:
166 | - ✅ Check existing installations
167 | - 📦 Install/update components
168 | - ⚙️ Configure settings
169 | - 🚀 Start services
170 | - 📋 View logs
171 |
172 | ---
173 |
174 | ## 🛡️ Security
175 |
176 |
177 |
178 | ### 🔒 Security Measures
179 |
180 |
181 |
182 | - 🔐 Use of sudo only for installation
183 | - 🔑 Manual entry of sensitive data
184 | - 🗑️ Temporary file cleanup
185 | - 📝 Secure configuration storage
186 | - 🔒 Access rights verification
187 | - 🛡️ Input data validation
188 |
189 | ---
190 |
191 | ## ⭐️ Project Support
192 |
193 |
194 |
195 | If the script was helpful — give it a ⭐️ on [GitHub](https://github.com/Capybara-z/RemnaSetup)!
196 |
197 | [](https://github.com/Capybara-z/RemnaSetup)
198 |
199 | ### 📱 Contacts
200 | Telegram: [@KaTTuBaRa](https://t.me/KaTTuBaRa)
201 |
202 |
203 |
204 | ---
205 |
206 | ## 📄 License
207 |
208 | MIT
209 |
210 | ---
211 |
212 |
213 |
214 | **RemnaSetup** — your universal assistant for quick start and maintenance of Remnawave and RemnaNode infrastructure! 🚀
215 |
216 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # RemnaSetup 🛠️
2 |
3 |
4 |
5 | [English](README.en.md) | [Русский](README.md)
6 |
7 | 
8 | 
9 | 
10 |
11 | **Универсальный скрипт для автоматической установки, настройки и обновления инфраструктуры Remnawave и Remnanode**
12 |
13 | [](https://github.com/Capybara-z/RemnaSetup)
14 | [](https://github.com/Capybara-z/RemnaSetup)
15 |
16 |
17 |
18 | ---
19 |
20 | ## 🚀 Возможности
21 |
22 |
23 |
24 | ### 🔥 Основные компоненты
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 | ### 🎯 Remnawave
33 | - Установка и настройка панели управления
34 | - Установка страницы подписок
35 | - Интеграция с Caddy для проксирования запросов
36 | - Защита панели и подпсиок
37 | - Автоматическое обновление компонентов
38 |
39 |
40 |
41 |
42 | ### 🌐 Remnanode
43 | - Установка и настройка ноды
44 | - Интеграция с Caddy для self-steal
45 | - Оптимизация сети через BBR
46 | - Интеграция с WARP-NATIVE (by distillium)
47 | - Автоматическое обновление компонентов
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 | ---
56 |
57 | ### 🗄️ Бэкап/Восстановление Remnawave
58 |
59 | - 💾 Создание резервной копии Remnawave
60 | - ♻️ Восстановление Remnawave из архива
61 | - 📂 Архивы хранятся в /opt/backups
62 | - 📋 Типы бэкапов: ручной, автоматический, с отправкой в Telegram
63 | - 🕒 Автоматический бэкап с настраиваемым расписанием
64 |
65 | ---
66 |
67 |
68 |
69 | ### ⚡ Дополнительные возможности
70 | - **Модульная структура** с разделением на отдельные скрипты
71 | - **Интерактивное меню** с возможностью выбора компонентов
72 | - **Автоматическое обновление** всех компонентов
73 | - **Проверка существующих установок** перед установкой
74 | - **Возможность переустановки** с сохранением данных
75 | - **Улучшенная обработка ошибок** и логирование
76 | - **Бэкап и восстановление Remnawave** через отдельное меню
77 |
78 | ---
79 |
80 | ## 📋 Опции меню
81 |
82 |
83 |
84 | ### 🎮 Интерактивное меню
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 | ### 1️⃣ Remnawave
93 | - 📦 Полная установка (Remnawave + Страница подписок + Caddy)
94 | - 🚀 Установка Remnawave
95 | - 📄 Установка Страницы подписок
96 | - ⚙️ Установка Caddy
97 | - 🔄 Обновление (Remnawave + Страницы подписок)
98 | - 🔄 Обновление Remnawave
99 | - 🔄 Обновление Страницы подписок
100 |
101 |
102 |
103 |
104 | ### 2️⃣ Remnanode
105 | - 📦 Полная установка (Remnanode + Caddy + Tblocker + BBR + WARP-NATIVE (by distillium))
106 | - 🚀 Установка Remnanode
107 | - ⚙️ Установка Caddy + self-steal
108 | - ⚡ Установка BBR
109 | - 🌐 Установка WARP-NATIVE (by distillium)
110 | - 🔄 Обновление Remnanode
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 | ---
119 |
120 | ### 3️⃣ Бэкап/Восстановление Remnawave
121 |
122 | - 💾 Создание резервной копии Remnawave
123 | - ♻️ Восстановление Remnawave из архива
124 | - 📂 Архивы хранятся в /opt/backups
125 | - 🕒 Автоматический бэкап с настраиваемым расписанием
126 | - 📤 Отправка бэкапов в Telegram бота
127 | - 🗑️ Автоматическая очистка старых бэкапов
128 | - 🛡️ Все действия через удобное меню
129 |
130 | ---
131 |
132 |
133 |
134 | ---
135 |
136 | ## 🖥️ Быстрый старт
137 |
138 | - Вариант 1
139 | ```bash
140 | bash <(curl -fsSL raw.githubusercontent.com/Capybara-z/RemnaSetup/refs/heads/main/install.sh)
141 | ```
142 | - Вариант 2
143 | ```bash
144 | curl -fsSL https://raw.githubusercontent.com/Capybara-z/RemnaSetup/refs/heads/main/install.sh -o install.sh && chmod +x install.sh && sudo bash ./install.sh
145 | ```
146 |
147 | ---
148 |
149 | ## 💡 Как это работает
150 |
151 |
152 |
153 | ### 🔄 Процесс установки
154 |
155 |
156 |
157 | 1. **🎯 Выбор опции** в главном меню
158 | 2. **📝 Ввод данных**:
159 | - 🌐 Домены для панели и подписок
160 | - 🔌 Порты для сервисов
161 | - 🔑 Учетные данные для базы данных
162 | - 📊 Настройки метрик
163 | - 🌐 Параметры WARP
164 | 3. **🗄️ Бэкап и восстановление**
165 | 4. **⚡ Автоматизация**:
166 | - ✅ Проверка существующих установок
167 | - 📦 Установка/обновление компонентов
168 | - ⚙️ Настройка конфигураций
169 | - 🚀 Запуск сервисов
170 | - 📋 Просмотр логов
171 |
172 | ---
173 |
174 | ## 🛡️ Безопасность
175 |
176 |
177 |
178 | ### 🔒 Меры безопасности
179 |
180 |
181 |
182 | - 🔐 Использование sudo только для установки
183 | - 🔑 Ручной ввод чувствительных данных
184 | - 🗑️ Удаление временных файлов
185 | - 📝 Защищенное хранение конфигураций
186 | - 🔒 Проверка прав доступа
187 | - 🛡️ Валидация вводимых данных
188 |
189 | ---
190 |
191 | ## ⭐️ Поддержка проекта
192 |
193 |
194 |
195 | Если скрипт был полезен — поставьте ⭐️ на [GitHub](https://github.com/Capybara-z/RemnaSetup)!
196 |
197 | [](https://github.com/Capybara-z/RemnaSetup)
198 |
199 | ### 📱 Контакты
200 | Telegram: [@KaTTuBaRa](https://t.me/KaTTuBaRa)
201 |
202 |
203 |
204 | ---
205 |
206 | ## 📄 Лицензия
207 |
208 | MIT
209 |
210 | ---
211 |
212 |
213 |
214 | **RemnaSetup** — ваш универсальный помощник для быстрого старта и поддержки инфраструктуры Remnawave и RemnaNode! 🚀
215 |
216 |
--------------------------------------------------------------------------------
/data/app-config.json:
--------------------------------------------------------------------------------
1 | {
2 | "config": {
3 | "additionalLocales": [
4 | "ru"
5 | ]
6 | },
7 | "platforms": {
8 | "android": [
9 | {
10 | "id": "happ",
11 | "name": "Happ",
12 | "isFeatured": true,
13 | "urlScheme": "happ://add/",
14 | "installationStep": {
15 | "buttons": [
16 | {
17 | "buttonLink": "https://play.google.com/store/apps/details?id=com.happproxy",
18 | "buttonText": {
19 | "en": "Open in Google Play",
20 | "ru": "Открыть в Google Play"
21 | }
22 | },
23 | {
24 | "buttonLink": "https://github.com/Happ-proxy/happ-android/releases/latest/download/Happ.apk",
25 | "buttonText": {
26 | "en": "Download APK",
27 | "ru": "Скачать APK"
28 | }
29 | }
30 | ],
31 | "description": {
32 | "en": "Open the page in Google Play and install the app. Or install the app directly from the APK file if Google Play is not working.",
33 | "ru": "Откройте страницу в Google Play и установите приложение. Или установите приложение из APK файла напрямую, если Google Play не работает."
34 | }
35 | },
36 | "addSubscriptionStep": {
37 | "description": {
38 | "en": "Click the button below to add subscription",
39 | "ru": "Нажмите кнопку ниже, чтобы добавить подписку"
40 | }
41 | },
42 | "connectAndUseStep": {
43 | "description": {
44 | "en": "Open the app and connect to the server",
45 | "ru": "Откройте приложение и подключитесь к серверу"
46 | }
47 | }
48 | },
49 | {
50 | "id": "clash-meta",
51 | "name": "Clash Meta",
52 | "isFeatured": false,
53 | "urlScheme": "clash://install-config?url=",
54 | "installationStep": {
55 | "buttons": [
56 | {
57 | "buttonLink": "https://github.com/MetaCubeX/ClashMetaForAndroid/releases/download/v2.11.7/cmfa-2.11.7-meta-universal-release.apk",
58 | "buttonText": {
59 | "en": "Download APK",
60 | "ru": "Скачать APK"
61 | }
62 | },
63 | {
64 | "buttonLink": "https://f-droid.org/packages/com.github.metacubex.clash.meta/",
65 | "buttonText": {
66 | "en": "Open in F-Droid",
67 | "ru": "Открыть в F-Droid"
68 | }
69 | }
70 | ],
71 | "description": {
72 | "en": "Download and install Clash Meta APK",
73 | "ru": "Скачайте и установите Clash Meta APK"
74 | }
75 | },
76 | "addSubscriptionStep": {
77 | "description": {
78 | "en": "Tap the button to import configuration",
79 | "ru": "Нажмите кнопку, чтобы импортировать конфигурацию"
80 | }
81 | },
82 | "connectAndUseStep": {
83 | "description": {
84 | "en": "Open Clash Meta and tap on Connect",
85 | "ru": "Откройте Clash Meta и нажмите Подключиться"
86 | }
87 | }
88 | }
89 | ],
90 | "ios": [
91 | {
92 | "id": "happ",
93 | "name": "Happ",
94 | "isFeatured": true,
95 | "urlScheme": "happ://add/",
96 | "installationStep": {
97 | "buttons": [
98 | {
99 | "buttonLink": "https://apps.apple.com/us/app/happ-proxy-utility/id6504287215",
100 | "buttonText": {
101 | "en": "Open in App Store [EU]",
102 | "ru": "Открыть в App Store [EU]"
103 | }
104 | },
105 | {
106 | "buttonLink": "https://apps.apple.com/ru/app/happ-proxy-utility-plus/id6746188973",
107 | "buttonText": {
108 | "en": "Open in App Store [RU]",
109 | "ru": "Открыть в App Store [RU]"
110 | }
111 | }
112 | ],
113 | "description": {
114 | "en": "Open the page in App Store and install the app. Launch it, in the VPN configuration permission window click Allow and enter your passcode.",
115 | "ru": "Откройте страницу в App Store и установите приложение. Запустите его, в окне разрешения VPN-конфигурации нажмите Allow и введите свой пароль."
116 | }
117 | },
118 | "addSubscriptionStep": {
119 | "description": {
120 | "en": "Click the button below — the app will open and the subscription will be added automatically",
121 | "ru": "Нажмите кнопку ниже — приложение откроется, и подписка добавится автоматически."
122 | }
123 | },
124 | "connectAndUseStep": {
125 | "description": {
126 | "en": "In the main section, click the large power button in the center to connect to VPN. Don't forget to select a server from the server list. If needed, choose another server from the server list.",
127 | "ru": "В главном разделе нажмите большую кнопку включения в центре для подключения к VPN. Не забудьте выбрать сервер в списке серверов. При необходимости выберите другой сервер из списка серверов."
128 | }
129 | }
130 | },
131 | {
132 | "id": "streisand",
133 | "name": "Streisand",
134 | "isFeatured": false,
135 | "urlScheme": "streisand://import/",
136 | "installationStep": {
137 | "buttons": [
138 | {
139 | "buttonLink": "https://apps.apple.com/ru/app/streisand/id6450534064",
140 | "buttonText": {
141 | "en": "Open in App Store",
142 | "ru": "Открыть в App Store"
143 | }
144 | }
145 | ],
146 | "description": {
147 | "en": "Open the page in App Store and install the app. Launch it, in the VPN configuration permission window click Allow and enter your passcode.",
148 | "ru": "Откройте страницу в App Store и установите приложение. Запустите его, в окне разрешения VPN-конфигурации нажмите Allow и введите свой пароль."
149 | }
150 | },
151 | "addSubscriptionStep": {
152 | "description": {
153 | "en": "Click the button below — the app will open and the subscription will be added automatically",
154 | "ru": "Нажмите кнопку ниже — приложение откроется, и подписка добавится автоматически."
155 | }
156 | },
157 | "connectAndUseStep": {
158 | "description": {
159 | "en": "In the main section, click the large power button in the center to connect to VPN. Don't forget to select a server from the server list. If needed, choose another server from the server list.",
160 | "ru": "В главном разделе нажмите большую кнопку включения в центре для подключения к VPN. Не забудьте выбрать сервер в списке серверов. При необходимости выберите другой сервер из списка серверов."
161 | }
162 | }
163 | }
164 | ],
165 | "linux": [
166 | {
167 | "id": "koala-clash",
168 | "name": "Koala Clash",
169 | "isFeatured": false,
170 | "urlScheme": "koala-clash://install-config?url=",
171 | "installationStep": {
172 | "buttons": [
173 | {
174 | "buttonLink": "https://github.com/coolcoala/clash-verge-rev-lite/releases",
175 | "buttonText": {
176 | "en": "Linux",
177 | "ru": "Linux"
178 | }
179 | }
180 | ],
181 | "description": {
182 | "en": "Choose the version for your device, click the button below and install the app.",
183 | "ru": "Выберите подходящую версию для вашего устройства, нажмите на кнопку ниже и установите приложение."
184 | }
185 | },
186 | "addSubscriptionStep": {
187 | "description": {
188 | "en": "Click the button below to add subscription",
189 | "ru": "Нажмите кнопку ниже, чтобы добавить подписку"
190 | }
191 | },
192 | "additionalAfterAddSubscriptionStep": {
193 | "buttons": [],
194 | "title": {
195 | "en": "If the subscription is not added",
196 | "ru": "Если подписка не добавилась"
197 | },
198 | "description": {
199 | "en": "If nothing happens after clicking the button, add the subscription manually. Click the Get Link button in the top right corner of this page, copy the link. In Koala Clash, go to the Profiles section and paste the link in the text field, then click the Import button.",
200 | "ru": "Если после нажатия на кнопку ничего не произошло, добавьте подписку вручную. Нажмите на этой страницу кнопку Получить ссылку в правом верхнем углу, скопируйте ссылку. В Koala Clash перейдите в раздел Профили и вставьте ссылку в текстовое поле, затем нажмите на кнопку Импорт."
201 | }
202 | },
203 | "connectAndUseStep": {
204 | "description": {
205 | "en": "You can select a server in the Proxy section, and enable VPN in the Settings section. Set the TUN Mode switch to ON.",
206 | "ru": "Выбрать сервер можно в разделе Прокси, включить VPN можно в разделе Настройки. Установите переключатель TUN Mode в положение ВКЛ."
207 | }
208 | }
209 | }
210 | ],
211 | "macos": [
212 | {
213 | "id": "happ",
214 | "name": "Happ",
215 | "isFeatured": true,
216 | "urlScheme": "happ://add/",
217 | "installationStep": {
218 | "buttons": [
219 | {
220 | "buttonLink": "https://apps.apple.com/us/app/happ-proxy-utility/id6504287215",
221 | "buttonText": {
222 | "en": "Open in App Store [EU]",
223 | "ru": "Открыть в App Store [EU]"
224 | }
225 | },
226 | {
227 | "buttonLink": "https://apps.apple.com/ru/app/happ-proxy-utility-plus/id6746188973",
228 | "buttonText": {
229 | "en": "Open in App Store [RU]",
230 | "ru": "Открыть в App Store [RU]"
231 | }
232 | }
233 | ],
234 | "description": {
235 | "en": "Open the page in App Store and install the app. Launch it, in the VPN configuration permission window click Allow and enter your passcode.",
236 | "ru": "Откройте страницу в App Store и установите приложение. Запустите его, в окне разрешения VPN-конфигурации нажмите Allow и введите свой пароль."
237 | }
238 | },
239 | "addSubscriptionStep": {
240 | "description": {
241 | "en": "Click the button below — the app will open and the subscription will be added automatically",
242 | "ru": "Нажмите кнопку ниже — приложение откроется, и подписка добавится автоматически."
243 | }
244 | },
245 | "connectAndUseStep": {
246 | "description": {
247 | "en": "In the main section, click the large power button in the center to connect to VPN. Don't forget to select a server from the server list. If needed, choose another server from the server list.",
248 | "ru": "В главном разделе нажмите большую кнопку включения в центре для подключения к VPN. Не забудьте выбрать сервер в списке серверов. При необходимости выберите другой сервер из списка серверов."
249 | }
250 | }
251 | }
252 | ],
253 | "windows": [
254 | {
255 | "id": "happ",
256 | "name": "Happ",
257 | "isFeatured": true,
258 | "urlScheme": "happ://add/",
259 | "installationStep": {
260 | "buttons": [
261 | {
262 | "buttonLink": "https://github.com/Happ-proxy/happ-desktop/releases/latest/download/setup-Happ.x86.exe",
263 | "buttonText": {
264 | "en": "Windows",
265 | "ru": "Windows"
266 | }
267 | }
268 | ],
269 | "description": {
270 | "en": "Click the button below and install the application.",
271 | "ru": "Нажмите на кнопку ниже и установите приложение."
272 | }
273 | },
274 | "addSubscriptionStep": {
275 | "description": {
276 | "en": "Click the button below to add subscription",
277 | "ru": "Нажмите кнопку ниже, чтобы добавить подписку"
278 | }
279 | },
280 | "connectAndUseStep": {
281 | "description": {
282 | "en": "Select the desired country and click on the connection circle on the right.",
283 | "ru": "Выберите нужную страну и нажмите справа на круг подключения."
284 | }
285 | },
286 | "additionalAfterAddSubscriptionStep": {
287 | "title": {
288 | "en": "If the subscription is not added",
289 | "ru": "Если подписка не добавилась"
290 | },
291 | "description": {
292 | "en": "\nIf nothing happens after clicking the button, add a subscription manually. Click on this page the Get link button in the upper right corner, copy the link. In the application, paste the link if asked, or click \"From buffer\" at the bottom left",
293 | "ru": "Если после нажатия на кнопку ничего не произошло, добавьте подписку вручную. Нажмите на этой страницу кнопку Получить ссылку в правом верхнем углу, скопируйте ссылку. В приложении вставьте ссылку если просит, или слева внизу нажать \"Из буфера\""
294 | },
295 | "buttons": []
296 | }
297 | },
298 | {
299 | "id": "koala-clash",
300 | "name": "Koala Clash",
301 | "isFeatured": false,
302 | "urlScheme": "koala-clash://install-config?url=",
303 | "installationStep": {
304 | "buttons": [
305 | {
306 | "buttonLink": "https://github.com/coolcoala/clash-verge-rev-lite/releases/download/v0.2.6/Koala.Clash_x64-setup.exe",
307 | "buttonText": {
308 | "en": "Windows",
309 | "ru": "Windows"
310 | }
311 | }
312 | ],
313 | "description": {
314 | "en": "Choose the version for your device, click the button below and install the app.",
315 | "ru": "Выберите подходящую версию для вашего устройства, нажмите на кнопку ниже и установите приложение."
316 | }
317 | },
318 | "addSubscriptionStep": {
319 | "description": {
320 | "en": "Click the button below to add subscription",
321 | "ru": "Нажмите кнопку ниже, чтобы добавить подписку"
322 | }
323 | },
324 | "additionalAfterAddSubscriptionStep": {
325 | "buttons": [],
326 | "title": {
327 | "en": "If the subscription is not added",
328 | "ru": "Если подписка не добавилась"
329 | },
330 | "description": {
331 | "en": "If nothing happens after clicking the button, add the subscription manually. Click the Get Link button in the top right corner of this page, copy the link. In Koala Clash, go to the Profiles section and paste the link in the text field, then click the Import button.",
332 | "ru": "Если после нажатия на кнопку ничего не произошло, добавьте подписку вручную. Нажмите на этой страницу кнопку Получить ссылку в правом верхнем углу, скопируйте ссылку. В Koala Clash перейдите в раздел Профили и вставьте ссылку в текстовое поле, затем нажмите на кнопку Импорт."
333 | }
334 | },
335 | "connectAndUseStep": {
336 | "description": {
337 | "en": "You can select a server in the Proxy section, and enable VPN in the Settings section. Set the TUN Mode switch to ON.",
338 | "ru": "Выбрать сервер можно в разделе Прокси, включить VPN можно в разделе Настройки. Установите переключатель TUN Mode в положение ВКЛ."
339 | }
340 | }
341 | },
342 | {
343 | "id": "hiddify",
344 | "name": "Hiddify",
345 | "isFeatured": false,
346 | "urlScheme": "hiddify://import/",
347 | "installationStep": {
348 | "buttons": [
349 | {
350 | "buttonLink": "https://github.com/hiddify/hiddify-app/releases/download/v2.5.7/Hiddify-Windows-Setup-x64.exe",
351 | "buttonText": {
352 | "en": "Windows",
353 | "ru": "Windows"
354 | }
355 | }
356 | ],
357 | "description": {
358 | "en": "In the main section, click the large power button in the center to connect to VPN. If needed, select a different server in the Proxy section",
359 | "ru": "В главном разделе нажмите большую кнопку включения в центре для подключения к VPN. При необходимости выберите другой сервер в разделе Прокси."
360 | }
361 | },
362 | "addSubscriptionStep": {
363 | "description": {
364 | "en": "Click the button below to add subscription",
365 | "ru": "Нажмите кнопку ниже, чтобы добавить подписку"
366 | }
367 | },
368 | "connectAndUseStep": {
369 | "description": {
370 | "en": "In the main section, click the large power button in the center to connect to VPN. Don't forget to select a server from the server list. If needed, select a different server from the server list.",
371 | "ru": "В главном разделе нажмите большую кнопку включения в центре для подключения к VPN. Не забудьте выбрать сервер в списке серверов. При необходимости выберите другой сервер из списка серверов."
372 | }
373 | }
374 | }
375 | ],
376 | "androidTV": [
377 | {
378 | "id": "happ",
379 | "name": "Happ",
380 | "isFeatured": true,
381 | "urlScheme": "happ://add/",
382 | "installationStep": {
383 | "buttons": [
384 | {
385 | "buttonLink": "https://play.google.com/store/apps/details?id=com.happproxy",
386 | "buttonText": {
387 | "en": "Open in Google Play",
388 | "ru": "Открыть в Google Play"
389 | }
390 | },
391 | {
392 | "buttonLink": "https://github.com/Happ-proxy/happ-android/releases/latest/download/Happ.apk",
393 | "buttonText": {
394 | "en": "Download APK",
395 | "ru": "Скачать APK"
396 | }
397 | }
398 | ],
399 | "description": {
400 | "en": "Open the page in Google Play and install the app. Or install the app directly from the APK file if Google Play is not working.",
401 | "ru": "Откройте страницу в Google Play и установите приложение. Или установите приложение из APK файла напрямую, если Google Play не работает."
402 | }
403 | },
404 | "additionalBeforeAddSubscriptionStep": {
405 | "buttons": [
406 | {
407 | "buttonLink": "https://www.happ.su/main/ru/faq/android-tv",
408 | "buttonText": {
409 | "en": "In Russian",
410 | "ru": "На русском"
411 | }
412 | },
413 | {
414 | "buttonLink": "https://www.happ.su/main/faq/android-tv",
415 | "buttonText": {
416 | "en": "In English",
417 | "ru": "На английском"
418 | }
419 | }
420 | ],
421 | "description": {
422 | "en": "Detailed instructions to help you set up Happ on your device.",
423 | "ru": "Подробные инструкции, чтобы помочь вам настроить Happ на вашем устройстве."
424 | },
425 | "title": {
426 | "en": "Installation instructions",
427 | "ru": "Инструкции по установке"
428 | }
429 | },
430 | "addSubscriptionStep": {
431 | "description": {
432 | "en": "Click the button below to add subscription, if you opened the subscription page on your TV",
433 | "ru": "Нажмите кнопку ниже, чтобы добавить подписку, если вы открыли страницу подписки на телевизоре"
434 | }
435 | },
436 | "connectAndUseStep": {
437 | "description": {
438 | "en": "Open the app and connect to the server",
439 | "ru": "Откройте приложение и подключитесь к серверу"
440 | }
441 | }
442 | }
443 | ],
444 | "appleTV": [
445 | {
446 | "id": "happ",
447 | "name": "Happ",
448 | "isFeatured": true,
449 | "urlScheme": "happ://add/",
450 | "installationStep": {
451 | "buttons": [
452 | {
453 | "buttonLink": "https://apps.apple.com/us/app/happ-proxy-utility-for-tv/id6748297274",
454 | "buttonText": {
455 | "en": "App Store",
456 | "ru": "App Store"
457 | }
458 | }
459 | ],
460 | "description": {
461 | "en": "Open the page in the App Store on your Apple TV and install the app. Launch it, allow VPN configuration if prompted, and enter your passcode if required.",
462 | "ru": "Откройте страницу в App Store на Apple TV и установите приложение. Запустите его, предоставьте разрешение на VPN-конфигурацию, если потребуется, и введите свой пароль."
463 | }
464 | },
465 | "additionalBeforeAddSubscriptionStep": {
466 | "buttons": [
467 | {
468 | "buttonLink": "https://www.happ.su/main/ru/faq/android-tv",
469 | "buttonText": {
470 | "en": "In Russian",
471 | "ru": "На русском"
472 | }
473 | },
474 | {
475 | "buttonLink": "https://www.happ.su/main/faq/android-tv",
476 | "buttonText": {
477 | "en": "In English",
478 | "ru": "На английском"
479 | }
480 | }
481 | ],
482 | "description": {
483 | "en": "Detailed instructions to help you set up Happ on your device.",
484 | "ru": "Подробные инструкции, чтобы помочь вам настроить Happ на вашем устройстве."
485 | },
486 | "title": {
487 | "en": "Installation instructions",
488 | "ru": "Инструкции по установке"
489 | }
490 | },
491 | "addSubscriptionStep": {
492 | "description": {
493 | "en": "Click the button below to add subscription, if you opened the subscription page on your TV",
494 | "ru": "Нажмите кнопку ниже, чтобы добавить подписку, если вы открыли страницу подписки на телевизоре"
495 | }
496 | },
497 | "connectAndUseStep": {
498 | "description": {
499 | "en": "Open the app and connect to the server",
500 | "ru": "Откройте приложение и подключитесь к серверу"
501 | }
502 | }
503 | }
504 | ]
505 | }
506 | }
507 |
--------------------------------------------------------------------------------
/data/backup/backup_script.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | BACKUP_DIR="/opt/backups"
4 | DATE=$(date +%F_%H-%M-%S)
5 | DB_VOLUME="remnawave-db-data"
6 | REMWAVE_DIR="/opt/remnawave"
7 | DB_TAR="remnawave-db-backup-$DATE.tar.gz"
8 | FINAL_ARCHIVE="remnawave-backup-$DATE.7z"
9 | TMP_DIR="$BACKUP_DIR/tmp-$DATE"
10 |
11 | PASSWORD=""
12 |
13 | mkdir -p "$BACKUP_DIR"
14 | mkdir -p "$TMP_DIR"
15 |
16 | if docker volume inspect $DB_VOLUME &>/dev/null; then
17 | docker run --rm \
18 | -v ${DB_VOLUME}:/volume \
19 | -v "$TMP_DIR":/backup \
20 | alpine \
21 | tar czf /backup/$DB_TAR -C /volume .
22 | fi
23 |
24 | cp "$REMWAVE_DIR/.env" "$TMP_DIR/"
25 | cp "$REMWAVE_DIR/docker-compose.yml" "$TMP_DIR/"
26 |
27 | 7z a -t7z -m0=lzma2 -mx=9 -mfb=273 -md=64m -ms=on -p"$PASSWORD" "$BACKUP_DIR/$FINAL_ARCHIVE" "$TMP_DIR/*" >/dev/null 2>&1
28 |
29 | rm -rf "$TMP_DIR"
30 |
31 | find "$BACKUP_DIR" -maxdepth 1 -type f -name 'remnawave-backup-*.7z' -mtime +3 -delete
32 |
33 | exit 0
--------------------------------------------------------------------------------
/data/backup/backup_script_tg.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | BACKUP_DIR="/opt/backups"
4 | DATE=$(date +%F_%H-%M-%S)
5 | DB_VOLUME="remnawave-db-data"
6 | REMWAVE_DIR="/opt/remnawave"
7 | DB_TAR="remnawave-db-backup-$DATE.tar.gz"
8 | FINAL_ARCHIVE="remnawave-backup-$DATE.7z"
9 | TMP_DIR="$BACKUP_DIR/tmp-$DATE"
10 |
11 | BOT_TOKEN=""
12 | CHAT_ID=""
13 | PASSWORD=""
14 | LANGUAGE=""
15 |
16 | START_TIME=$(date +%s)
17 |
18 | mkdir -p "$BACKUP_DIR"
19 | mkdir -p "$TMP_DIR"
20 |
21 | if docker volume inspect $DB_VOLUME &>/dev/null; then
22 | docker run --rm \
23 | -v ${DB_VOLUME}:/volume \
24 | -v "$TMP_DIR":/backup \
25 | alpine \
26 | tar czf /backup/$DB_TAR -C /volume .
27 | fi
28 |
29 | cp "$REMWAVE_DIR/.env" "$TMP_DIR/"
30 | cp "$REMWAVE_DIR/docker-compose.yml" "$TMP_DIR/"
31 |
32 | 7z a -t7z -m0=lzma2 -mx=9 -mfb=273 -md=64m -ms=on -p"$PASSWORD" "$BACKUP_DIR/$FINAL_ARCHIVE" "$TMP_DIR/*" >/dev/null 2>&1
33 |
34 | rm -rf "$TMP_DIR"
35 |
36 | find "$BACKUP_DIR" -maxdepth 1 -type f -name 'remnawave-backup-*.7z' -mtime +3 -delete
37 |
38 | END_TIME=$(date +%s)
39 | DURATION_SEC=$((END_TIME - START_TIME))
40 | DURATION=$(date -u -d @${DURATION_SEC} +%H:%M:%S)
41 | ARCHIVE_SIZE=$(du -h "$BACKUP_DIR/$FINAL_ARCHIVE" | awk '{print $1}')
42 | FREE_SPACE=$(df -h "$BACKUP_DIR" | awk 'NR==2{print $4}')
43 |
44 | if [ "$LANGUAGE" = "en" ]; then
45 | MSG=$(cat <:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(.5rem * var(--tw-space-x-reverse));margin-left:calc(.5rem * calc(1 - var(--tw-space-x-reverse)))}.space-x-3>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(.75rem * var(--tw-space-x-reverse));margin-left:calc(.75rem * calc(1 - var(--tw-space-x-reverse)))}.space-x-4>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(1rem * var(--tw-space-x-reverse));margin-left:calc(1rem * calc(1 - var(--tw-space-x-reverse)))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem * var(--tw-space-y-reverse))}.space-y-6>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.5rem * var(--tw-space-y-reverse))}.space-y-8>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(2rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(2rem * var(--tw-space-y-reverse))}.break-all{word-break:break-all}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-xl{border-radius:.75rem}.border{border-width:1px}.border-slate-800\/30{border-color:#1e293b4d}.border-slate-800\/40{border-color:#1e293b66}.border-slate-800\/50{border-color:#1e293b80}.border-yellow-500\/10{border-color:#eab3081a}.bg-red-500{--tw-bg-opacity: 1;background-color:rgb(239 68 68 / var(--tw-bg-opacity))}.bg-red-500\/10{background-color:#ef44441a}.bg-slate-900\/40{background-color:#0f172a66}.bg-slate-950\/30{background-color:#0206174d}.bg-slate-950\/60{background-color:#02061799}.bg-\[url\(\'https\:\/\/images\.unsplash\.com\/photo-1528818955841-a7f1425131b5\?auto\=format\&fit\=crop\&q\=80\'\)\]{background-image:url(https://images.unsplash.com/photo-1528818955841-a7f1425131b5?auto=format&fit=crop&q=80)}.bg-gradient-to-br{background-image:linear-gradient(to bottom right,var(--tw-gradient-stops))}.bg-gradient-to-r{background-image:linear-gradient(to right,var(--tw-gradient-stops))}.from-slate-950{--tw-gradient-from: #020617 var(--tw-gradient-from-position);--tw-gradient-to: rgb(2 6 23 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to)}.from-yellow-500\/5{--tw-gradient-from: rgb(234 179 8 / .05) var(--tw-gradient-from-position);--tw-gradient-to: rgb(234 179 8 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to)}.via-slate-900{--tw-gradient-to: rgb(15 23 42 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), #0f172a var(--tw-gradient-via-position), var(--tw-gradient-to)}.via-yellow-500\/10{--tw-gradient-to: rgb(234 179 8 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), rgb(234 179 8 / .1) var(--tw-gradient-via-position), var(--tw-gradient-to)}.to-slate-800{--tw-gradient-to: #1e293b var(--tw-gradient-to-position)}.to-yellow-500\/5{--tw-gradient-to: rgb(234 179 8 / .05) var(--tw-gradient-to-position)}.bg-cover{background-size:cover}.bg-center{background-position:center}.p-4{padding:1rem}.p-5{padding:1.25rem}.p-6{padding:1.5rem}.px-4{padding-left:1rem;padding-right:1rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.text-center{text-align:center}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-2xl{font-size:1.5rem;line-height:2rem}.text-4xl{font-size:2.25rem;line-height:2.5rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.font-bold{font-weight:700}.font-medium{font-weight:500}.text-blue-300{--tw-text-opacity: 1;color:rgb(147 197 253 / var(--tw-text-opacity))}.text-blue-400{--tw-text-opacity: 1;color:rgb(96 165 250 / var(--tw-text-opacity))}.text-emerald-300{--tw-text-opacity: 1;color:rgb(110 231 183 / var(--tw-text-opacity))}.text-emerald-400{--tw-text-opacity: 1;color:rgb(52 211 153 / var(--tw-text-opacity))}.text-gray-300{--tw-text-opacity: 1;color:rgb(209 213 219 / var(--tw-text-opacity))}.text-gray-400{--tw-text-opacity: 1;color:rgb(156 163 175 / var(--tw-text-opacity))}.text-gray-500{--tw-text-opacity: 1;color:rgb(107 114 128 / var(--tw-text-opacity))}.text-orange-500{--tw-text-opacity: 1;color:rgb(249 115 22 / var(--tw-text-opacity))}.text-red-400{--tw-text-opacity: 1;color:rgb(248 113 113 / var(--tw-text-opacity))}.text-red-500{--tw-text-opacity: 1;color:rgb(239 68 68 / var(--tw-text-opacity))}.text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity))}.text-yellow-400{--tw-text-opacity: 1;color:rgb(250 204 21 / var(--tw-text-opacity))}.opacity-\[0\.03\]{opacity:.03}.mix-blend-overlay{mix-blend-mode:overlay}.blur-3xl{--tw-blur: blur(64px);filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.backdrop-blur{--tw-backdrop-blur: blur(8px);-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}@media (min-width: 768px){.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}}
2 |
--------------------------------------------------------------------------------
/data/site/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | 503 - Критическая нагрузка системы
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/install.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | if [ "$(id -u)" != "0" ]; then
4 | echo "This script must be run as root"
5 | echo "Этот скрипт должен быть запущен с правами root"
6 | exit 1
7 | fi
8 |
9 | TEMP_DIR=$(mktemp -d)
10 |
11 | if [ -d "/opt/remnasetup" ]; then
12 | echo "Removing existing RemnaSetup installation..."
13 | echo "Удаление существующей установки RemnaSetup..."
14 | rm -rf /opt/remnasetup
15 | fi
16 |
17 | if ! command -v curl &> /dev/null; then
18 | echo "Installing curl..."
19 | echo "Установка curl..."
20 | if command -v apt-get &> /dev/null; then
21 | apt update -y && apt install -y curl
22 | elif command -v yum &> /dev/null; then
23 | yum install -y curl
24 | elif command -v dnf &> /dev/null; then
25 | dnf install -y curl
26 | else
27 | echo "Failed to install curl. Please install it manually."
28 | echo "Не удалось установить curl. Пожалуйста, установите его вручную."
29 | exit 1
30 | fi
31 | fi
32 |
33 | cd "$TEMP_DIR" || exit 1
34 |
35 | echo "Downloading RemnaSetup..."
36 | echo "Загрузка RemnaSetup..."
37 | curl -L https://github.com/Capybara-z/RemnaSetup/archive/refs/heads/main.zip -o remnasetup.zip
38 |
39 | if [ ! -f remnasetup.zip ]; then
40 | echo "Error: Failed to download archive"
41 | echo "Ошибка: Не удалось загрузить архив"
42 | rm -rf "$TEMP_DIR"
43 | exit 1
44 | fi
45 |
46 | if ! command -v unzip &> /dev/null; then
47 | echo "Installing unzip..."
48 | echo "Установка unzip..."
49 | if command -v apt-get &> /dev/null; then
50 | echo "Updating package list..."
51 | echo "Обновление списка пакетов..."
52 | sudo apt update -y && sudo apt install -y unzip
53 | elif command -v yum &> /dev/null; then
54 | sudo yum install -y unzip
55 | elif command -v dnf &> /dev/null; then
56 | sudo dnf install -y unzip
57 | else
58 | echo "Failed to install unzip. Please install it manually."
59 | echo "Не удалось установить unzip. Пожалуйста, установите его вручную."
60 | rm -rf "$TEMP_DIR"
61 | exit 1
62 | fi
63 | fi
64 |
65 | echo "Extracting files..."
66 | echo "Распаковка файлов..."
67 | unzip -q remnasetup.zip
68 |
69 | if [ ! -d "RemnaSetup-main" ]; then
70 | echo "Error: Failed to extract archive"
71 | echo "Ошибка: Не удалось распаковать архив"
72 | rm -rf "$TEMP_DIR"
73 | exit 1
74 | fi
75 |
76 | mkdir -p /opt/remnasetup
77 |
78 | echo "Installing RemnaSetup to /opt/remnasetup..."
79 | echo "Установка RemnaSetup в /opt/remnasetup..."
80 | cp -r RemnaSetup-main/* /opt/remnasetup/
81 |
82 | if [ ! -f "/opt/remnasetup/remnasetup.sh" ]; then
83 | echo "Error: Failed to copy files"
84 | echo "Ошибка: Не удалось скопировать файлы"
85 | rm -rf "$TEMP_DIR"
86 | exit 1
87 | fi
88 |
89 | echo "Setting permissions..."
90 | echo "Установка прав доступа..."
91 | chown -R $SUDO_USER:$SUDO_USER /opt/remnasetup
92 | chmod -R 755 /opt/remnasetup
93 | chmod +x /opt/remnasetup/remnasetup.sh
94 | chmod +x /opt/remnasetup/scripts/common/*.sh
95 | chmod +x /opt/remnasetup/scripts/remnawave/*.sh
96 | chmod +x /opt/remnasetup/scripts/remnanode/*.sh
97 | chmod +x /opt/remnasetup/scripts/backups/*.sh
98 |
99 | rm -rf "$TEMP_DIR"
100 |
101 | cd /opt/remnasetup || exit 1
102 |
103 | echo "Starting RemnaSetup..."
104 | echo "Запуск RemnaSetup..."
105 | bash /opt/remnasetup/remnasetup.sh
106 |
--------------------------------------------------------------------------------
/remnasetup.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | SCRIPT_DIR="/opt/remnasetup"
4 |
5 | . /opt/remnasetup/scripts/common/colors.sh
6 | . /opt/remnasetup/scripts/common/functions.sh
7 | . /opt/remnasetup/scripts/common/languages.sh
8 |
9 | menu() {
10 | echo -e "${BOLD_MAGENTA}$1${RESET}"
11 | }
12 |
13 | question() {
14 | echo -e "${BOLD_CYAN}$1${RESET}"
15 | }
16 |
17 | info() {
18 | echo -e "${BOLD_CYAN}[INFO]${RESET} $1"
19 | }
20 |
21 | warn() {
22 | echo -e "${BOLD_YELLOW}[WARN]${RESET} $1"
23 | }
24 |
25 | error() {
26 | echo -e "${BOLD_RED}[ERROR]${RESET} $1"
27 | }
28 |
29 | success() {
30 | echo -e "${BOLD_GREEN}[SUCCESS]${RESET} $1"
31 | }
32 |
33 | print_header() {
34 | clear
35 | echo -e "${MAGENTA}────────────────────────────────────────────────────────────${RESET}"
36 | echo -e "\033[1;32m"
37 | echo -e "┌───────────────────────────────────────────────────────────────────┐"
38 | echo -e "│ ██████╗ █████╗ ██████╗ ██╗ ██╗██████╗ █████╗ ██████╗ █████╗ │"
39 | echo -e "│ ██╔════╝██╔══██╗██╔══██╗╚██╗ ██╔╝██╔══██╗██╔══██╗██╔══██╗██╔══██╗ │"
40 | echo -e "│ ██║ ███████║██████╔╝ ╚████╔╝ ██████╔╝███████║██████╔╝███████║ │"
41 | echo -e "│ ██║ ██╔══██║██╔═══╝ ╚██╔╝ ██╔══██╗██╔══██║██╔══██╗██╔══██║ │"
42 | echo -e "│ ╚██████╗██║ ██║██║ ██║ ██████╔╝██║ ██║██║ ██║██║ ██║ │"
43 | echo -e "│ ╚═════╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝ │"
44 | echo -e "└───────────────────────────────────────────────────────────────────┘"
45 | echo -e "\033[0m"
46 | echo -e "${MAGENTA}────────────────────────────────────────────────────────────${RESET}"
47 | if [ "$LANGUAGE" = "en" ]; then
48 | echo -e "${GREEN}RemnaSetup by capybara${RESET}"
49 | echo -e "${CYAN}Project: https://github.com/Capybara-z/RemnaSetup${RESET}"
50 | echo -e "${YELLOW}Contacts: @KaTTuBaRa${RESET}"
51 | echo -e "${CYAN}Version: 2.5${RESET}"
52 | echo
53 | echo -e "${MAGENTA}────────────────────────────────────────────────────────────${RESET}"
54 | echo -e "${YELLOW}Made with support from:${RESET}"
55 | echo -e "${CYAN}GitHub SoloBot: https://github.com/Vladless/Solo_bot${RESET}"
56 | echo -e "${YELLOW}Contacts: @Vladless${RESET}"
57 | else
58 | echo -e "${GREEN}RemnaSetup by capybara${RESET}"
59 | echo -e "${CYAN}Проект: https://github.com/Capybara-z/RemnaSetup${RESET}"
60 | echo -e "${YELLOW}Контакты: @KaTTuBaRa${RESET}"
61 | echo -e "${CYAN}Версия: 2.5${RESET}"
62 | echo
63 | echo -e "${MAGENTA}────────────────────────────────────────────────────────────${RESET}"
64 | echo -e "${YELLOW}Сделано при поддержке проекта:${RESET}"
65 | echo -e "${CYAN}GitHub SoloBot: https://github.com/Vladless/Solo_bot${RESET}"
66 | echo -e "${YELLOW}Контакты: @Vladless${RESET}"
67 | fi
68 | echo -e "${MAGENTA}────────────────────────────────────────────────────────────${RESET}"
69 | echo
70 | }
71 |
72 | display_remnawave_menu() {
73 | clear
74 | print_header
75 | menu "$(get_string "remnawave_menu")"
76 | if [ "$LANGUAGE" = "en" ]; then
77 | echo -e "${BLUE}1. Full installation (Remnawave + Subscription Page + Caddy)${RESET}"
78 | echo -e "${BLUE}2. Install Remnawave${RESET}"
79 | echo -e "${BLUE}3. Install Subscription Page${RESET}"
80 | echo -e "${BLUE}4. Install Caddy${RESET}"
81 | echo -e "${BLUE}5. Update (Remnawave + Subscription Page)${RESET}"
82 | echo -e "${BLUE}6. Update Remnawave${RESET}"
83 | echo -e "${BLUE}7. Update Subscription Page${RESET}"
84 | echo -e "${BLUE}8. Back${RESET}"
85 | else
86 | echo -e "${BLUE}1. Полная установка (Remnawave + Страница подписок + Caddy)${RESET}"
87 | echo -e "${BLUE}2. Установка Remnawave${RESET}"
88 | echo -e "${BLUE}3. Установка Страницы подписок${RESET}"
89 | echo -e "${BLUE}4. Установка Caddy${RESET}"
90 | echo -e "${BLUE}5. Обновление (Remnawave + Страницы подписок)${RESET}"
91 | echo -e "${BLUE}6. Обновление Remnawave${RESET}"
92 | echo -e "${BLUE}7. Обновление Страницы подписок${RESET}"
93 | echo -e "${BLUE}8. Назад${RESET}"
94 | fi
95 | echo
96 | read -p "$(echo -e "${BOLD_CYAN}$(get_string "select_option"):${RESET}") " REMNAWAVE_OPTION
97 | echo
98 | }
99 |
100 | display_remnanode_menu() {
101 | clear
102 | print_header
103 | menu "$(get_string "remnanode_menu")"
104 | if [ "$LANGUAGE" = "en" ]; then
105 | echo -e "${BLUE}1. Full installation (Remnanode + Caddy + BBR + WARP-NATIVE (by distillium))${RESET}"
106 | echo -e "${BLUE}2. Install Remnanode only${RESET}"
107 | echo -e "${BLUE}3. Install Caddy + self-steal only${RESET}"
108 | echo -e "${BLUE}4. IPv6 Management${RESET}"
109 | echo -e "${BLUE}5. Install BBR only${RESET}"
110 | echo -e "${BLUE}6. Install WARP-NATIVE (by distillium)${RESET}"
111 | echo -e "${BLUE}7. Update Remnanode${RESET}"
112 | echo -e "${BLUE}8. Back${RESET}"
113 | else
114 | echo -e "${BLUE}1. Полная установка (Remnanode + Caddy + BBR + WARP-NATIVE (by distillium))${RESET}"
115 | echo -e "${BLUE}2. Только Remnanode${RESET}"
116 | echo -e "${BLUE}3. Только Caddy + self-steal${RESET}"
117 | echo -e "${BLUE}4. Управление IPv6${RESET}"
118 | echo -e "${BLUE}5. Только BBR${RESET}"
119 | echo -e "${BLUE}6. Установить WARP-NATIVE (by distillium)${RESET}"
120 | echo -e "${BLUE}7. Обновить Remnanode${RESET}"
121 | echo -e "${BLUE}8. Назад${RESET}"
122 | fi
123 | echo
124 | read -p "$(echo -e "${BOLD_CYAN}$(get_string "select_option"):${RESET}") " REMNANODE_OPTION
125 | echo
126 | }
127 |
128 | display_backup_menu() {
129 | clear
130 | print_header
131 | menu "$(get_string "backup_menu")"
132 | if [ "$LANGUAGE" = "en" ]; then
133 | echo -e "${BLUE}1. Create Remnawave backup${RESET}"
134 | echo -e "${BLUE}2. Restore from Remnawave backup${RESET}"
135 | echo -e "${BLUE}3. Configure automatic backup${RESET}"
136 | echo -e "${RED}0. Exit${RESET}"
137 | else
138 | echo -e "${BLUE}1. Создать резервную копию Remnawave${RESET}"
139 | echo -e "${BLUE}2. Восстановить из резервной копии Remnawave${RESET}"
140 | echo -e "${BLUE}3. Настроить автоматическое резервное копирование${RESET}"
141 | echo -e "${RED}0. Выход${RESET}"
142 | fi
143 | echo
144 | read -p "$(echo -e "${BOLD_CYAN}$(get_string "select_option"):${RESET}") " BACKUP_OPTION
145 | echo
146 | }
147 |
148 | display_main_menu() {
149 | clear
150 | print_header
151 | menu "$(get_string "main_menu")"
152 | if [ "$LANGUAGE" = "en" ]; then
153 | echo -e "${BLUE}1. Install/Update Remnawave${RESET}"
154 | echo -e "${BLUE}2. Install/Update Remnanode${RESET}"
155 | echo -e "${BLUE}3. Remnawave Backup and Restore${RESET}"
156 | echo -e "${RED}0. Exit${RESET}"
157 | else
158 | echo -e "${BLUE}1. Установка/Обновление Remnawave${RESET}"
159 | echo -e "${BLUE}2. Установка/Обновление Remnanode${RESET}"
160 | echo -e "${BLUE}3. Резервное копирование и восстановление Remnawave${RESET}"
161 | echo -e "${RED}0. Выход${RESET}"
162 | fi
163 | echo
164 | read -p "$(echo -e "${BOLD_CYAN}$(get_string "select_option"):${RESET}") " MAIN_OPTION
165 | echo
166 | }
167 |
168 | run_script() {
169 | local script="$1"
170 | if [ -f "$script" ]; then
171 | bash "$script"
172 | local result=$?
173 | if [ $result -ne 0 ]; then
174 | warn "$(get_string "script_error")"
175 | read -n 1 -s -r -p "$(get_string "press_any_key")"
176 | fi
177 | else
178 | error "$(get_string "script_not_found"): $script"
179 | read -n 1 -s -r -p "$(get_string "press_any_key")"
180 | fi
181 | }
182 |
183 | main() {
184 | select_language
185 | while true; do
186 | display_main_menu
187 | case $MAIN_OPTION in
188 | 1)
189 | while true; do
190 | display_remnawave_menu
191 | case $REMNAWAVE_OPTION in
192 | 1) run_script "${SCRIPT_DIR}/scripts/remnawave/install-full.sh" ;;
193 | 2) run_script "${SCRIPT_DIR}/scripts/remnawave/install-panel.sh" ;;
194 | 3) run_script "${SCRIPT_DIR}/scripts/remnawave/install-subscription.sh" ;;
195 | 4) run_script "${SCRIPT_DIR}/scripts/remnawave/install-caddy.sh" ;;
196 | 5) run_script "${SCRIPT_DIR}/scripts/remnawave/update-full.sh" ;;
197 | 6) run_script "${SCRIPT_DIR}/scripts/remnawave/update-panel.sh" ;;
198 | 7) run_script "${SCRIPT_DIR}/scripts/remnawave/update-subscription.sh" ;;
199 | 8) break ;;
200 | *) warn "$(get_string "invalid_choice")" ;;
201 | esac
202 | done
203 | ;;
204 | 2)
205 | while true; do
206 | display_remnanode_menu
207 | case $REMNANODE_OPTION in
208 | 1) run_script "${SCRIPT_DIR}/scripts/remnanode/install-full.sh" ;;
209 | 2) run_script "${SCRIPT_DIR}/scripts/remnanode/install-node.sh" ;;
210 | 3) run_script "${SCRIPT_DIR}/scripts/remnanode/install-caddy.sh" ;;
211 | 4) run_script "${SCRIPT_DIR}/scripts/remnanode/install-ipv6.sh" ;;
212 | 5) run_script "${SCRIPT_DIR}/scripts/remnanode/install-bbr.sh" ;;
213 | 6) run_script "${SCRIPT_DIR}/scripts/remnanode/install-warp.sh" ;;
214 | 7) run_script "${SCRIPT_DIR}/scripts/remnanode/update.sh" ;;
215 | 8) break ;;
216 | *) warn "$(get_string "invalid_choice")" ;;
217 | esac
218 | done
219 | ;;
220 | 3)
221 | while true; do
222 | display_backup_menu
223 | case $BACKUP_OPTION in
224 | 1) run_script "${SCRIPT_DIR}/scripts/backups/backup.sh" ;;
225 | 2) run_script "${SCRIPT_DIR}/scripts/backups/restore.sh" ;;
226 | 3) run_script "${SCRIPT_DIR}/scripts/backups/auto_backup.sh" ;;
227 | 0) break ;;
228 | *) warn "$(get_string "invalid_choice")" ;;
229 | esac
230 | done
231 | ;;
232 | 0)
233 | info "$(get_string "exiting")"
234 | echo -e "${RESET}"
235 | exit 0
236 | ;;
237 | *)
238 | warn "$(get_string "invalid_choice")"
239 | ;;
240 | esac
241 | done
242 | }
243 |
244 | main
--------------------------------------------------------------------------------
/scripts/backups/auto_backup.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | source "/opt/remnasetup/scripts/common/colors.sh"
4 | source "/opt/remnasetup/scripts/common/functions.sh"
5 | source "/opt/remnasetup/scripts/common/languages.sh"
6 |
7 | BACKUP_DIR="/opt/backups"
8 | AUTO_BACKUP_DIR="$BACKUP_DIR/auto_backup"
9 | SCRIPT_DIR="/opt/remnasetup/data/backup"
10 |
11 | mkdir -p "$AUTO_BACKUP_DIR"
12 |
13 | LANGUAGE_FILE="/opt/remnasetup/.language"
14 | if [ -f "$LANGUAGE_FILE" ]; then
15 | LANGUAGE=$(cat "$LANGUAGE_FILE")
16 | else
17 | LANGUAGE="ru"
18 | fi
19 |
20 | check_time_format() {
21 | local time=$1
22 | if [[ ! $time =~ ^([01]?[0-9]|2[0-3]):[0-5][0-9]$ ]]; then
23 | return 1
24 | fi
25 | return 0
26 | }
27 |
28 | get_hours_word() {
29 | local hours=$1
30 | local last_digit=$((hours % 10))
31 | local last_two_digits=$((hours % 100))
32 |
33 | if [ $last_two_digits -ge 11 ] && [ $last_two_digits -le 19 ]; then
34 | get_string "auto_backup_hours_5_20"
35 | elif [ $last_digit -eq 1 ]; then
36 | get_string "auto_backup_hours_1"
37 | elif [ $last_digit -ge 2 ] && [ $last_digit -le 4 ]; then
38 | get_string "auto_backup_hours_2_4"
39 | else
40 | get_string "auto_backup_hours_5_20"
41 | fi
42 | }
43 |
44 | get_days_word() {
45 | local days=$1
46 | local last_digit=$((days % 10))
47 | local last_two_digits=$((days % 100))
48 |
49 | if [ $last_two_digits -ge 11 ] && [ $last_two_digits -le 19 ]; then
50 | get_string "auto_backup_days_5_20"
51 | elif [ $last_digit -eq 1 ]; then
52 | get_string "auto_backup_days_1"
53 | elif [ $last_digit -ge 2 ] && [ $last_digit -le 4 ]; then
54 | get_string "auto_backup_days_2_4"
55 | else
56 | get_string "auto_backup_days_5_20"
57 | fi
58 | }
59 |
60 | ensure_cron_installed() {
61 | if ! command -v crontab &>/dev/null; then
62 | info "$(get_string auto_backup_cron_not_found)"
63 | if command -v apt-get &>/dev/null; then
64 | sudo apt-get update
65 | sudo apt-get install -y cron
66 | sudo systemctl enable cron
67 | sudo systemctl start cron
68 | elif command -v yum &>/dev/null; then
69 | sudo yum install -y cronie
70 | sudo systemctl enable crond
71 | sudo systemctl start crond
72 | elif command -v apk &>/dev/null; then
73 | sudo apk add dcron
74 | sudo rc-update add crond
75 | sudo service crond start
76 | else
77 | error "$(get_string auto_backup_cron_install_failed)"
78 | exit 1
79 | fi
80 | success "$(get_string auto_backup_cron_installed)"
81 | fi
82 | }
83 |
84 | cleanup_old_crons() {
85 | info "$(get_string "auto_backup_cleanup_old_crons")"
86 | if crontab -l 2>/dev/null | grep -q "$AUTO_BACKUP_DIR/backup.sh"; then
87 | crontab -l 2>/dev/null | grep -v "$AUTO_BACKUP_DIR/backup.sh" | crontab -
88 | info "$(get_string "auto_backup_old_crons_removed")"
89 | else
90 | info "$(get_string "auto_backup_no_old_crons")"
91 | fi
92 | }
93 |
94 | ensure_backup_dependencies() {
95 | for cmd in docker tar 7z; do
96 | if ! command -v $cmd &>/dev/null; then
97 | if command -v apt-get &>/dev/null; then
98 | sudo apt-get update
99 | if [ "$cmd" = "7z" ]; then
100 | sudo apt-get install -y p7zip-full
101 | else
102 | sudo apt-get install -y $cmd
103 | fi
104 | elif command -v yum &>/dev/null; then
105 | sudo yum install -y $cmd
106 | elif command -v apk &>/dev/null; then
107 | sudo apk add $cmd
108 | else
109 | exit 1
110 | fi
111 | if ! command -v $cmd &>/dev/null; then
112 | exit 1
113 | fi
114 | fi
115 | done
116 | }
117 |
118 | while true; do
119 | question "$(get_string "auto_backup_select_mode")"
120 | case $REPLY in
121 | [Yy]* ) BACKUP_MODE="daily"; break;;
122 | [Nn]* ) BACKUP_MODE="hourly"; break;;
123 | * ) warn "$(get_string "auto_backup_please_answer_yn")";;
124 | esac
125 | done
126 |
127 | if [ "$BACKUP_MODE" = "daily" ]; then
128 | info "$(get_string "auto_backup_current_time" "$(date +%H:%M)")"
129 | while true; do
130 | question "$(get_string "auto_backup_enter_time")"
131 | if check_time_format "$REPLY"; then
132 | BACKUP_TIME="$REPLY"
133 | break
134 | else
135 | warn "$(get_string "auto_backup_invalid_time")"
136 | fi
137 | done
138 |
139 | HOUR=${BACKUP_TIME%%:*}
140 | MINUTE=${BACKUP_TIME#*:}
141 | CRON_SCHEDULE="$MINUTE $HOUR * * *"
142 | else
143 | while true; do
144 | question "$(get_string "auto_backup_enter_interval")"
145 | if [[ "$REPLY" =~ ^[1-9]$|^1[0-9]$|^2[0-3]$ ]]; then
146 | INTERVAL_HOURS="$REPLY"
147 | break
148 | else
149 | warn "$(get_string "auto_backup_enter_number")"
150 | fi
151 | done
152 | CRON_SCHEDULE="0 */$INTERVAL_HOURS * * *"
153 | fi
154 |
155 | question "$(get_string "auto_backup_enter_storage")"
156 | STORAGE_DAYS="$REPLY"
157 | STORAGE_DAYS=${STORAGE_DAYS:-3}
158 |
159 | while true; do
160 | question "$(get_string "auto_backup_enter_password")"
161 | PASSWORD="$REPLY"
162 | if [ ${#PASSWORD} -ge 8 ]; then
163 | break
164 | else
165 | warn "$(get_string "auto_backup_password_short")"
166 | fi
167 | done
168 |
169 | while true; do
170 | question "$(get_string "auto_backup_use_telegram")"
171 | case $REPLY in
172 | [Yy]* ) USE_TELEGRAM=true; break;;
173 | [Nn]* ) USE_TELEGRAM=false; break;;
174 | * ) warn "$(get_string "auto_backup_please_answer_yn")";;
175 | esac
176 | done
177 |
178 | if [ "$USE_TELEGRAM" = true ]; then
179 | question "$(get_string "auto_backup_enter_bot_token")"
180 | BOT_TOKEN="$REPLY"
181 |
182 | question "$(get_string "auto_backup_enter_chat_id")"
183 | CHAT_ID="$REPLY"
184 |
185 | cp "$SCRIPT_DIR/backup_script_tg.sh" "$AUTO_BACKUP_DIR/backup.sh"
186 | sed -i "s/BOT_TOKEN=\"\"/BOT_TOKEN=\"$BOT_TOKEN\"/" "$AUTO_BACKUP_DIR/backup.sh"
187 | sed -i "s/CHAT_ID=\"\"/CHAT_ID=\"$CHAT_ID\"/" "$AUTO_BACKUP_DIR/backup.sh"
188 | sed -i "s/LANGUAGE=\"\"/LANGUAGE=\"$LANGUAGE\"/" "$AUTO_BACKUP_DIR/backup.sh"
189 | else
190 | cp "$SCRIPT_DIR/backup_script.sh" "$AUTO_BACKUP_DIR/backup.sh"
191 | fi
192 |
193 | sed -i "s/PASSWORD=\"\"/PASSWORD=\"$PASSWORD\"/" "$AUTO_BACKUP_DIR/backup.sh"
194 | sed -i "s/-mtime +3/-mtime +$STORAGE_DAYS/" "$AUTO_BACKUP_DIR/backup.sh"
195 |
196 | chmod +x "$AUTO_BACKUP_DIR/backup.sh"
197 |
198 | ensure_cron_installed
199 |
200 | ensure_backup_dependencies
201 |
202 | cleanup_old_crons
203 |
204 | (crontab -l 2>/dev/null; echo "$CRON_SCHEDULE $AUTO_BACKUP_DIR/backup.sh") | crontab -
205 |
206 | success "$(get_string "auto_backup_configured")"
207 | if [ "$BACKUP_MODE" = "daily" ]; then
208 | success "$(get_string "auto_backup_daily_at" "$BACKUP_TIME")"
209 | else
210 | HOURS_WORD=$(get_hours_word "$INTERVAL_HOURS")
211 | success "$(get_string "auto_backup_every_hours" "$INTERVAL_HOURS" "$HOURS_WORD")"
212 | fi
213 | DAYS_WORD=$(get_days_word "$STORAGE_DAYS")
214 | success "$(get_string "auto_backup_storage_days" "$STORAGE_DAYS" "$DAYS_WORD")"
215 | if [ "$USE_TELEGRAM" = true ]; then
216 | success "$(get_string "auto_backup_telegram_configured")"
217 | fi
218 |
219 | read -n 1 -s -r -p "$(get_string "auto_backup_press_key")"
220 | exit 0
--------------------------------------------------------------------------------
/scripts/backups/backup.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | source "/opt/remnasetup/scripts/common/colors.sh"
4 | source "/opt/remnasetup/scripts/common/functions.sh"
5 | source "/opt/remnasetup/scripts/common/languages.sh"
6 |
7 | BACKUP_DIR="/opt/backups"
8 | DATE=$(date +%F_%H-%M-%S)
9 | DB_VOLUME="remnawave-db-data"
10 | REMWAVE_DIR="/opt/remnawave"
11 |
12 | DB_TAR="remnawave-db-backup-$DATE.tar.gz"
13 | FINAL_ARCHIVE="remnawave-backup-$DATE.7z"
14 | TMP_DIR="$BACKUP_DIR/tmp-$DATE"
15 |
16 | mkdir -p "$BACKUP_DIR"
17 |
18 | while true; do
19 | question "$(get_string "backup_enter_password")"
20 | ARCHIVE_PASSWORD="$REPLY"
21 | if [ ${#ARCHIVE_PASSWORD} -ge 8 ]; then
22 | break
23 | else
24 | warn "$(get_string "backup_password_short")"
25 | fi
26 | done
27 |
28 | echo
29 |
30 | for cmd in docker tar 7z; do
31 | if ! command -v $cmd &>/dev/null; then
32 | warn "$(get_string "backup_cmd_not_found" "$cmd")"
33 | if command -v apt-get &>/dev/null; then
34 | sudo apt-get update
35 | if [ "$cmd" = "7z" ]; then
36 | sudo apt-get install -y p7zip-full
37 | else
38 | sudo apt-get install -y $cmd
39 | fi
40 | elif command -v yum &>/dev/null; then
41 | sudo yum install -y $cmd
42 | elif command -v apk &>/dev/null; then
43 | sudo apk add $cmd
44 | else
45 | error "$(get_string "backup_install_failed" "$cmd")"
46 | read -n 1 -s -r -p "$(get_string "backup_press_key")"; exit 1
47 | fi
48 | if ! command -v $cmd &>/dev/null; then
49 | error "$(get_string "backup_cmd_not_installed" "$cmd")"
50 | read -n 1 -s -r -p "$(get_string "backup_press_key")"; exit 1
51 | fi
52 | fi
53 | done
54 |
55 | if ! docker volume inspect $DB_VOLUME &>/dev/null; then
56 | error "$(get_string "backup_volume_not_found" "$DB_VOLUME")"
57 | read -n 1 -s -r -p "$(get_string "backup_press_key")"; exit 1
58 | fi
59 |
60 | if [ ! -d "$REMWAVE_DIR" ]; then
61 | error "$(get_string "backup_dir_not_found" "$REMWAVE_DIR")"
62 | read -n 1 -s -r -p "$(get_string "backup_press_key")"; exit 1
63 | fi
64 |
65 | if [ ! -f "$REMWAVE_DIR/.env" ]; then
66 | error "$(get_string "backup_env_not_found" "$REMWAVE_DIR")"
67 | read -n 1 -s -r -p "$(get_string "backup_press_key")"; exit 1
68 | fi
69 |
70 | if [ ! -f "$REMWAVE_DIR/docker-compose.yml" ]; then
71 | error "$(get_string "backup_compose_not_found" "$REMWAVE_DIR")"
72 | read -n 1 -s -r -p "$(get_string "backup_press_key")"; exit 1
73 | fi
74 |
75 | mkdir -p "$TMP_DIR"
76 |
77 | info "$(get_string "backup_volume" "$DB_VOLUME")"
78 | docker run --rm \
79 | -v ${DB_VOLUME}:/volume \
80 | -v "$TMP_DIR":/backup \
81 | alpine \
82 | tar czf /backup/$DB_TAR -C /volume .
83 |
84 | info "$(get_string "backup_copying_configs")"
85 | cp "$REMWAVE_DIR/.env" "$TMP_DIR/"
86 | cp "$REMWAVE_DIR/docker-compose.yml" "$TMP_DIR/"
87 |
88 | info "$(get_string "backup_creating_archive")"
89 | 7z a -t7z -m0=lzma2 -mx=9 -mfb=273 -md=64m -ms=on -p"$ARCHIVE_PASSWORD" "$BACKUP_DIR/$FINAL_ARCHIVE" "$TMP_DIR/*" >/dev/null 2>&1
90 | if [ $? -ne 0 ]; then
91 | error "$(get_string "backup_archive_error")"
92 | ls -l "$TMP_DIR"
93 | rm -rf "$TMP_DIR"
94 | exit 1
95 | fi
96 |
97 | rm -rf "$TMP_DIR"
98 |
99 | success "$(get_string "backup_ready" "$BACKUP_DIR/$FINAL_ARCHIVE")"
100 | read -n 1 -s -r -p "$(get_string "backup_press_key")"
101 | exit 0
--------------------------------------------------------------------------------
/scripts/backups/restore.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | source "/opt/remnasetup/scripts/common/colors.sh"
4 | source "/opt/remnasetup/scripts/common/functions.sh"
5 | source "/opt/remnasetup/scripts/common/languages.sh"
6 |
7 | BACKUP_DIR="/opt/backups"
8 | WORK_DIR="$BACKUP_DIR/restore_work"
9 | DATE=$(date +%F_%H-%M-%S)
10 | DB_VOLUME="remnawave-db-data"
11 | REDIS_VOLUME="remnawave-redis-data"
12 | REMWAVE_DIR="/opt/remnawave"
13 | PANEL_CONTAINER="remnawave"
14 | DB_CONTAINER="remnawave-db"
15 |
16 | info "$(get_string "restore_start")"
17 |
18 | get_telegram_backups() {
19 | local bot_token=$1
20 | local chat_id=$2
21 | local temp_file="/tmp/telegram_backups.json"
22 |
23 | curl -s "https://api.telegram.org/bot${bot_token}/getUpdates?chat_id=${chat_id}&limit=5" > "$temp_file"
24 |
25 | local backups=()
26 | while IFS= read -r line; do
27 | if [[ $line =~ remnawave-backup-[0-9]{4}-[0-9]{2}-[0-9]{2}_[0-9]{2}-[0-9]{2}-[0-9]{2}\.7z ]]; then
28 | backups+=("$line")
29 | fi
30 | done < <(jq -r '.result[].message.document.file_name' "$temp_file" 2>/dev/null)
31 |
32 | rm -f "$temp_file"
33 | echo "${backups[@]}"
34 | }
35 |
36 | download_telegram_backup() {
37 | local bot_token=$1
38 | local chat_id=$2
39 | local file_name=$3
40 | local temp_file="/tmp/telegram_file.json"
41 |
42 | curl -s "https://api.telegram.org/bot${bot_token}/getUpdates?chat_id=${chat_id}&limit=5" > "$temp_file"
43 | local file_id=$(jq -r --arg name "$file_name" '.result[].message.document | select(.file_name == $name) | .file_id' "$temp_file")
44 |
45 | if [ -n "$file_id" ]; then
46 | local file_path=$(curl -s "https://api.telegram.org/bot${bot_token}/getFile?file_id=${file_id}" | jq -r '.result.file_path')
47 | curl -s "https://api.telegram.org/file/bot${bot_token}/${file_path}" -o "$BACKUP_DIR/$file_name"
48 | rm -f "$temp_file"
49 | return 0
50 | fi
51 |
52 | rm -f "$temp_file"
53 | return 1
54 | }
55 |
56 | while true; do
57 | question "$(get_string "restore_select_source")"
58 | SOURCE="$REPLY"
59 | if [[ "$SOURCE" == "y" || "$SOURCE" == "Y" ]]; then
60 | break
61 | elif [[ "$SOURCE" == "n" || "$SOURCE" == "N" ]]; then
62 | break
63 | else
64 | warn "$(get_string "restore_please_answer_yn")"
65 | fi
66 | done
67 |
68 | mkdir -p "$BACKUP_DIR"
69 |
70 | if [ "$SOURCE" = "telegram" ]; then
71 | question "$(get_string "restore_enter_bot_token")"
72 | BOT_TOKEN="$REPLY"
73 |
74 | question "$(get_string "restore_enter_chat_id")"
75 | CHAT_ID="$REPLY"
76 |
77 | while true; do
78 | info "$(get_string "restore_send_backup")"
79 | read -n 1 -s -r
80 | info "$(get_string "restore_getting_backups")"
81 | mapfile -t TG_BACKUPS < <(get_telegram_backups "$BOT_TOKEN" "$CHAT_ID")
82 | if [ ${#TG_BACKUPS[@]} -eq 0 ]; then
83 | warn "$(get_string "restore_no_backups")"
84 | else
85 | break
86 | fi
87 | done
88 |
89 | echo "$(get_string "restore_available_backups"):"
90 | for i in "${!TG_BACKUPS[@]}"; do
91 | echo "$((i+1)). ${TG_BACKUPS[$i]}"
92 | done
93 |
94 | while true; do
95 | question "$(get_string "restore_enter_backup_number")"
96 | if [[ "$REPLY" =~ ^[0-9]+$ ]] && (( REPLY >= 1 && REPLY <= ${#TG_BACKUPS[@]} )); then
97 | SELECTED_BACKUP="${TG_BACKUPS[$((REPLY-1))]}"
98 | info "$(get_string "restore_selected_backup" "$SELECTED_BACKUP")"
99 | break
100 | else
101 | warn "$(get_string "restore_invalid_choice")"
102 | fi
103 | done
104 |
105 | info "$(get_string "restore_downloading_archive")"
106 | if ! download_telegram_backup "$BOT_TOKEN" "$CHAT_ID" "$SELECTED_BACKUP"; then
107 | error "$(get_string "restore_download_failed")"
108 | read -n 1 -s -r -p "$(get_string "restore_press_key")"; exit 1
109 | fi
110 | ARCHIVE_PATH="$BACKUP_DIR/$SELECTED_BACKUP"
111 | else
112 | mapfile -t ARCHIVES < <(find "$BACKUP_DIR" -maxdepth 1 -type f -name 'remnawave-backup-*.7z' | sort)
113 |
114 | if [[ ${#ARCHIVES[@]} -eq 0 ]]; then
115 | info "$(get_string "restore_no_local_backups" "$BACKUP_DIR")"
116 | read -n 1 -s -r -p "$(get_string "restore_press_key_continue")"
117 | echo
118 |
119 | while true; do
120 | mapfile -t ARCHIVES < <(find "$BACKUP_DIR" -maxdepth 1 -type f -name 'remnawave-backup-*.7z' | sort)
121 | if [[ ${#ARCHIVES[@]} -eq 0 ]]; then
122 | warn "$(get_string "restore_no_backups_found" "$BACKUP_DIR")"
123 | read -n 1 -s KEY
124 | if [[ "$KEY" == "n" || "$KEY" == "N" ]]; then
125 | info "$(get_string "restore_exit")"
126 | exit 0
127 | fi
128 | else
129 | break
130 | fi
131 | done
132 | fi
133 |
134 | echo "$(get_string "restore_available_local_backups"):"
135 | for i in "${!ARCHIVES[@]}"; do
136 | echo "$((i+1)). ${ARCHIVES[$i]}"
137 | done
138 |
139 | while true; do
140 | question "$(get_string "restore_enter_backup_number")"
141 | if [[ "$REPLY" =~ ^[0-9]+$ ]] && (( REPLY >= 1 && REPLY <= ${#ARCHIVES[@]} )); then
142 | ARCHIVE_PATH="${ARCHIVES[$((REPLY-1))]}"
143 | info "$(get_string "restore_selected_backup" "$ARCHIVE_PATH")"
144 | break
145 | else
146 | warn "$(get_string "restore_invalid_choice")"
147 | fi
148 | done
149 | fi
150 |
151 | while true; do
152 | question "$(get_string "restore_enter_password")"
153 | PASSWORD="$REPLY"
154 | if [ ${#PASSWORD} -ge 8 ]; then
155 | break
156 | else
157 | warn "$(get_string "restore_password_short")"
158 | fi
159 | done
160 |
161 | if ! command -v docker &>/dev/null; then
162 | warn "$(get_string "restore_docker_not_found")"
163 | sudo curl -fsSL https://get.docker.com | sh
164 | sudo systemctl start docker
165 | sudo systemctl enable docker
166 | fi
167 |
168 | if ! command -v 7z &>/dev/null; then
169 | warn "$(get_string "restore_7z_not_found")"
170 | sudo apt-get install -y p7zip-full
171 | fi
172 |
173 | if [ ! -d "$REMWAVE_DIR" ]; then
174 | info "$(get_string "restore_creating_directory" "$REMWAVE_DIR")"
175 | mkdir -p "$REMWAVE_DIR"
176 | fi
177 |
178 | info "$(get_string "restore_checking_archive")"
179 | TMP_RESTORE_DIR="$WORK_DIR/unpack"
180 | mkdir -p "$TMP_RESTORE_DIR"
181 | 7z x -p"$PASSWORD" "$ARCHIVE_PATH" -o"$TMP_RESTORE_DIR" >/dev/null 2>&1
182 | if [ $? -ne 0 ]; then
183 | error "$(get_string "restore_invalid_password")"
184 | read -n 1 -s -r -p "$(get_string "restore_press_key")"; exit 1
185 | fi
186 |
187 | info "$(get_string "restore_backup_before")"
188 | RESERVE_ARCHIVE="remnawave-backup-before-restore-$DATE.7z"
189 |
190 | if [ -f "$REMWAVE_DIR/.env" ] && [ -f "$REMWAVE_DIR/docker-compose.yml" ]; then
191 | cp "$REMWAVE_DIR/.env" "$BACKUP_DIR/"
192 | cp "$REMWAVE_DIR/docker-compose.yml" "$BACKUP_DIR/"
193 | fi
194 |
195 | if docker volume inspect $DB_VOLUME &>/dev/null; then
196 | docker run --rm \
197 | -v ${DB_VOLUME}:/volume \
198 | -v "$BACKUP_DIR":/backup \
199 | alpine \
200 | tar czf /backup/db_backup.tar.gz -C /volume .
201 | fi
202 |
203 | 7z a -t7z -m0=lzma2 -mx=9 -mfb=273 -md=64m -ms=on -p"$PASSWORD" "$BACKUP_DIR/$RESERVE_ARCHIVE" "$BACKUP_DIR/db_backup.tar.gz" "$BACKUP_DIR/.env" "$BACKUP_DIR/docker-compose.yml" >/dev/null 2>&1
204 | rm -f "$BACKUP_DIR/db_backup.tar.gz" "$BACKUP_DIR/.env" "$BACKUP_DIR/docker-compose.yml"
205 |
206 | if [ "$SOURCE" = "telegram" ]; then
207 | curl -F "chat_id=$CHAT_ID" \
208 | -F document=@"$BACKUP_DIR/$RESERVE_ARCHIVE" \
209 | "https://api.telegram.org/bot$BOT_TOKEN/sendDocument" >/dev/null 2>&1
210 | fi
211 |
212 | success "$(get_string "restore_backup_saved" "$BACKUP_DIR/$RESERVE_ARCHIVE")"
213 |
214 | if [ -d "$REMWAVE_DIR" ]; then
215 | info "$(get_string "restore_stopping_containers")"
216 | cd "$REMWAVE_DIR" && docker compose down
217 |
218 | info "$(get_string "restore_removing_old_data")"
219 | docker volume rm $DB_VOLUME $REDIS_VOLUME 2>/dev/null || true
220 | rm -f "$REMWAVE_DIR/.env" "$REMWAVE_DIR/docker-compose.yml"
221 | fi
222 |
223 | info "$(get_string "restore_restoring_configs")"
224 | cp "$TMP_RESTORE_DIR/.env" "$REMWAVE_DIR/"
225 | cp "$TMP_RESTORE_DIR/docker-compose.yml" "$REMWAVE_DIR/"
226 |
227 | info "$(get_string "restore_starting_containers")"
228 | cd "$REMWAVE_DIR" && docker compose up -d
229 | sleep 10
230 |
231 | info "$(get_string "restore_stopping_containers_again")"
232 | docker compose down
233 |
234 | info "$(get_string "restore_clearing_database")"
235 | docker run --rm \
236 | -v ${DB_VOLUME}:/volume \
237 | alpine \
238 | sh -c "rm -rf /volume/*"
239 |
240 | info "$(get_string "restore_restoring_database")"
241 | DB_BACKUP_FILE=$(ls "$TMP_RESTORE_DIR"/remnawave-db-backup-*.tar.gz 2>/dev/null | head -n1)
242 | docker run --rm \
243 | -v ${DB_VOLUME}:/volume \
244 | -v "$TMP_RESTORE_DIR":/backup \
245 | alpine \
246 | tar xzf /backup/$(basename "$DB_BACKUP_FILE") -C /volume
247 |
248 | info "$(get_string "restore_removing_temp")"
249 | rm -rf "$WORK_DIR"
250 |
251 | info "$(get_string "restore_starting_remnawave")"
252 | cd "$REMWAVE_DIR" && docker compose up -d
253 |
254 | success "$(get_string "restore_complete")"
255 | read -n 1 -s -r -p "$(get_string "restore_press_key")"
256 | exit 0
--------------------------------------------------------------------------------
/scripts/common/colors.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | RESET='\033[0m'
4 |
5 | RED='\033[0;31m'
6 | GREEN='\033[0;32m'
7 | YELLOW='\033[0;33m'
8 | BLUE='\033[0;34m'
9 | MAGENTA='\033[0;35m'
10 | CYAN='\033[0;36m'
11 |
12 | BOLD_RED='\033[1;31m'
13 | BOLD_GREEN='\033[1;32m'
14 | BOLD_YELLOW='\033[1;33m'
15 | BOLD_BLUE='\033[1;34m'
16 | BOLD_MAGENTA='\033[1;35m'
17 | BOLD_CYAN='\033[1;36m'
--------------------------------------------------------------------------------
/scripts/common/functions.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | source "/opt/remnasetup/scripts/common/colors.sh"
4 | source "/opt/remnasetup/scripts/common/languages.sh"
5 |
6 | info() {
7 | echo -e "${BOLD_CYAN}[INFO]${RESET} $1"
8 | }
9 |
10 | warn() {
11 | echo -e "${BOLD_YELLOW}[WARN]${RESET} $1"
12 | }
13 |
14 | error() {
15 | echo -e "${BOLD_RED}[ERROR]${RESET} $1"
16 | }
17 |
18 | success() {
19 | echo -e "${BOLD_GREEN}[SUCCESS]${RESET} $1"
20 | }
21 |
22 | menu() {
23 | echo -e "${BOLD_MAGENTA}$1${RESET}"
24 | read -p "$(echo -e "${BOLD_CYAN}$(get_string "select_menu_option"):${RESET}") " choice
25 | echo "$choice"
26 | }
27 |
28 | question() {
29 | read -p "$(echo -e "${BOLD_CYAN}$1${RESET}") " REPLY
30 | }
31 |
32 | command_exists() {
33 | command -v "$1" >/dev/null 2>&1
34 | }
35 |
36 | check_root() {
37 | if [ "$(id -u)" != "0" ]; then
38 | error "$(get_string "root_required")"
39 | exit 1
40 | fi
41 | }
42 |
43 | check_directory() {
44 | if [ ! -d "$1" ]; then
45 | error "$(get_string "directory_not_exist" "$1")"
46 | exit 1
47 | fi
48 | }
49 |
50 | check_file() {
51 | if [ ! -f "$1" ]; then
52 | error "$(get_string "file_not_exist" "$1")"
53 | exit 1
54 | fi
55 | }
56 |
57 | create_directory() {
58 | if [ ! -d "$1" ]; then
59 | mkdir -p "$1"
60 | fi
61 | }
62 |
63 | backup_file() {
64 | if [ -f "$1" ]; then
65 | cp "$1" "$1.bak"
66 | fi
67 | }
68 |
69 | restore_file() {
70 | if [ -f "$1.bak" ]; then
71 | mv "$1.bak" "$1"
72 | fi
73 | }
74 |
75 | export -f info
76 | export -f warn
77 | export -f error
78 | export -f success
79 | export -f menu
80 | export -f question
81 | export -f command_exists
82 | export -f check_root
83 | export -f check_directory
84 | export -f check_file
85 | export -f create_directory
86 | export -f backup_file
87 | export -f restore_file
88 |
--------------------------------------------------------------------------------
/scripts/remnanode/install-bbr.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | source "/opt/remnasetup/scripts/common/colors.sh"
4 | source "/opt/remnasetup/scripts/common/functions.sh"
5 | source "/opt/remnasetup/scripts/common/languages.sh"
6 |
7 | check_bbr() {
8 | if sysctl net.ipv4.tcp_congestion_control | grep -q "bbr"; then
9 | info "$(get_string "install_bbr_already_configured")"
10 | read -n 1 -s -r -p "$(get_string "install_bbr_press_key")"
11 | exit 0
12 | return 1
13 | fi
14 | return 0
15 | }
16 |
17 | install_bbr() {
18 | info "$(get_string "install_bbr_installing")"
19 | echo "net.core.default_qdisc=fq" | sudo tee -a /etc/sysctl.conf
20 | echo "net.ipv4.tcp_congestion_control=bbr" | sudo tee -a /etc/sysctl.conf
21 | sudo sysctl -p
22 | success "$(get_string "install_bbr_installed")"
23 | }
24 |
25 | main() {
26 | if ! check_bbr; then
27 | return 0
28 | fi
29 |
30 | install_bbr
31 | read -n 1 -s -r -p "$(get_string "install_bbr_press_key")"
32 | exit 0
33 | }
34 |
35 | main
36 |
--------------------------------------------------------------------------------
/scripts/remnanode/install-caddy.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | source "/opt/remnasetup/scripts/common/colors.sh"
4 | source "/opt/remnasetup/scripts/common/functions.sh"
5 | source "/opt/remnasetup/scripts/common/languages.sh"
6 |
7 | check_caddy() {
8 | if command -v caddy >/dev/null 2>&1; then
9 | info "$(get_string "install_caddy_node_already_installed")"
10 | while true; do
11 | question "$(get_string "install_caddy_node_update_config")"
12 | UPDATE_CONFIG="$REPLY"
13 | if [[ "$UPDATE_CONFIG" == "y" || "$UPDATE_CONFIG" == "Y" ]]; then
14 | return 0
15 | elif [[ "$UPDATE_CONFIG" == "n" || "$UPDATE_CONFIG" == "N" ]]; then
16 | info "$(get_string "install_caddy_node_already_installed")"
17 | read -n 1 -s -r -p "$(get_string "install_caddy_node_press_key")"
18 | exit 0
19 | return 1
20 | else
21 | warn "$(get_string "install_caddy_node_please_enter_yn")"
22 | fi
23 | done
24 | fi
25 | return 0
26 | }
27 |
28 | install_caddy() {
29 | info "$(get_string "install_caddy_node_installing")"
30 | sudo apt update -y
31 | sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https
32 | curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
33 | curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
34 | sudo apt update
35 | sudo apt install -y caddy
36 |
37 | success "$(get_string "install_caddy_node_installed")"
38 | }
39 |
40 | setup_site() {
41 | info "$(get_string "install_caddy_node_setup_site")"
42 | sudo chmod -R 777 /var
43 | sudo mkdir -p /var/www/site
44 |
45 | RANDOM_META_ID=$(openssl rand -hex 16)
46 | RANDOM_CLASS=$(openssl rand -hex 8)
47 | RANDOM_COMMENT=$(openssl rand -hex 12)
48 |
49 | META_NAMES=("render-id" "view-id" "page-id" "config-id")
50 | RANDOM_META_NAME=${META_NAMES[$RANDOM % ${#META_NAMES[@]}]}
51 |
52 | sudo cp -r "/opt/remnasetup/data/site/"* /var/www/site/
53 |
54 | sudo sed -i "/ \n " /var/www/site/index.html
55 | sudo sed -i "s//dev/null 2>&1; then
97 | install_caddy
98 | setup_site
99 | fi
100 |
101 | update_caddy_config
102 |
103 | success "$(get_string "install_caddy_node_installation_complete")"
104 | read -n 1 -s -r -p "$(get_string "install_caddy_node_press_key")"
105 | exit 0
106 | }
107 |
108 | main
109 |
--------------------------------------------------------------------------------
/scripts/remnanode/install-full.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | source "/opt/remnasetup/scripts/common/colors.sh"
4 | source "/opt/remnasetup/scripts/common/functions.sh"
5 | source "/opt/remnasetup/scripts/common/languages.sh"
6 |
7 | check_docker() {
8 | if command -v docker >/dev/null 2>&1; then
9 | return 0
10 | else
11 | return 1
12 | fi
13 | }
14 |
15 | install_docker() {
16 | info "$(get_string "install_full_node_installing_docker")"
17 | sudo curl -fsSL https://get.docker.com | sh || {
18 | error "$(get_string "install_full_node_docker_error")"
19 | exit 1
20 | }
21 | success "$(get_string "install_full_node_docker_installed_success")"
22 | }
23 |
24 | check_components() {
25 | if command -v docker >/dev/null 2>&1; then
26 | info "$(get_string "install_full_node_docker_installed")"
27 | else
28 | info "$(get_string "install_full_node_docker_not_installed")"
29 | fi
30 |
31 | if [ -f "/opt/remnanode/docker-compose.yml" ]; then
32 | info "$(get_string "install_full_node_remnanode_installed")"
33 | while true; do
34 | question "$(get_string "install_full_node_update_remnanode")"
35 | UPDATE_NODE="$REPLY"
36 | if [[ "$UPDATE_NODE" == "y" || "$UPDATE_NODE" == "Y" ]]; then
37 | UPDATE_REMNANODE=true
38 | break
39 | elif [[ "$UPDATE_NODE" == "n" || "$UPDATE_NODE" == "N" ]]; then
40 | SKIP_REMNANODE=true
41 | break
42 | else
43 | warn "$(get_string "install_full_node_please_enter_yn")"
44 | fi
45 | done
46 | fi
47 |
48 | if command -v caddy >/dev/null 2>&1; then
49 | info "$(get_string "install_full_node_caddy_installed")"
50 | while true; do
51 | question "$(get_string "install_full_node_update_caddy")"
52 | UPDATE_CADDY="$REPLY"
53 | if [[ "$UPDATE_CADDY" == "y" || "$UPDATE_CADDY" == "Y" ]]; then
54 | UPDATE_CADDY=true
55 | break
56 | elif [[ "$UPDATE_CADDY" == "n" || "$UPDATE_CADDY" == "N" ]]; then
57 | SKIP_CADDY=true
58 | break
59 | else
60 | warn "$(get_string "install_full_node_please_enter_yn")"
61 | fi
62 | done
63 | fi
64 |
65 | if command -v wgcf >/dev/null 2>&1 && [ -f "/etc/wireguard/warp.conf" ]; then
66 | info "$(get_string "warp_native_already_installed")"
67 | while true; do
68 | question "$(get_string "warp_native_reconfigure")"
69 | RECONFIGURE="$REPLY"
70 | if [[ "$RECONFIGURE" == "y" || "$RECONFIGURE" == "Y" ]]; then
71 | SKIP_WARP=false
72 | break
73 | elif [[ "$RECONFIGURE" == "n" || "$RECONFIGURE" == "N" ]]; then
74 | SKIP_WARP=true
75 | info "$(get_string "warp_native_skip_installation")"
76 | break
77 | else
78 | warn "$(get_string "warp_native_please_enter_yn")"
79 | fi
80 | done
81 | else
82 | SKIP_WARP=false
83 | fi
84 |
85 | if sysctl net.ipv4.tcp_congestion_control | grep -q bbr; then
86 | info "$(get_string "install_full_node_bbr_configured")"
87 | SKIP_BBR=true
88 | fi
89 | }
90 |
91 | request_data() {
92 | if [[ "$SKIP_CADDY" != "true" ]]; then
93 | while true; do
94 | question "$(get_string "install_full_node_enter_domain")"
95 | DOMAIN="$REPLY"
96 | if [[ "$DOMAIN" == "n" || "$DOMAIN" == "N" ]]; then
97 | while true; do
98 | question "$(get_string "install_full_node_confirm_skip_caddy")"
99 | CONFIRM="$REPLY"
100 | if [[ "$CONFIRM" == "y" || "$CONFIRM" == "Y" ]]; then
101 | SKIP_CADDY=true
102 | break
103 | elif [[ "$CONFIRM" == "n" || "$CONFIRM" == "N" ]]; then
104 | break
105 | else
106 | warn "$(get_string "install_full_node_please_enter_yn")"
107 | fi
108 | done
109 | if [[ "$SKIP_CADDY" == "true" ]]; then
110 | break
111 | fi
112 | elif [[ -n "$DOMAIN" ]]; then
113 | break
114 | fi
115 | warn "$(get_string "install_full_node_domain_empty")"
116 | done
117 |
118 | if [[ "$SKIP_CADDY" != "true" ]]; then
119 | while true; do
120 | question "$(get_string "install_full_node_enter_port")"
121 | MONITOR_PORT="$REPLY"
122 | if [[ "$MONITOR_PORT" == "n" || "$MONITOR_PORT" == "N" ]]; then
123 | while true; do
124 | question "$(get_string "install_full_node_confirm_skip_caddy")"
125 | CONFIRM="$REPLY"
126 | if [[ "$CONFIRM" == "y" || "$CONFIRM" == "Y" ]]; then
127 | SKIP_CADDY=true
128 | break
129 | elif [[ "$CONFIRM" == "n" || "$CONFIRM" == "N" ]]; then
130 | break
131 | else
132 | warn "$(get_string "install_full_node_please_enter_yn")"
133 | fi
134 | done
135 | if [[ "$SKIP_CADDY" == "true" ]]; then
136 | break
137 | fi
138 | fi
139 | MONITOR_PORT=${MONITOR_PORT:-8443}
140 | if [[ "$MONITOR_PORT" =~ ^[0-9]+$ ]]; then
141 | break
142 | fi
143 | warn "$(get_string "install_full_node_port_must_be_number")"
144 | done
145 | fi
146 | fi
147 |
148 | if [[ "$SKIP_REMNANODE" != "true" ]]; then
149 | while true; do
150 | question "$(get_string "install_full_node_enter_app_port")"
151 | APP_PORT="$REPLY"
152 | if [[ "$APP_PORT" == "n" || "$APP_PORT" == "N" ]]; then
153 | while true; do
154 | question "$(get_string "install_full_node_confirm_skip_remnanode")"
155 | CONFIRM="$REPLY"
156 | if [[ "$CONFIRM" == "y" || "$CONFIRM" == "Y" ]]; then
157 | SKIP_REMNANODE=true
158 | break
159 | elif [[ "$CONFIRM" == "n" || "$CONFIRM" == "N" ]]; then
160 | break
161 | else
162 | warn "$(get_string "install_full_node_please_enter_yn")"
163 | fi
164 | done
165 | if [[ "$SKIP_REMNANODE" == "true" ]]; then
166 | break
167 | fi
168 | fi
169 | APP_PORT=${APP_PORT:-3001}
170 | if [[ "$APP_PORT" =~ ^[0-9]+$ ]]; then
171 | break
172 | fi
173 | warn "$(get_string "install_full_node_port_must_be_number")"
174 | done
175 |
176 | if [[ "$SKIP_REMNANODE" != "true" ]]; then
177 | while true; do
178 | question "$(get_string "install_full_node_enter_ssl_cert")"
179 | SSL_CERT_FULL="$REPLY"
180 | if [[ "$SSL_CERT_FULL" == "n" || "$SSL_CERT_FULL" == "N" ]]; then
181 | while true; do
182 | question "$(get_string "install_full_node_confirm_skip_remnanode")"
183 | CONFIRM="$REPLY"
184 | if [[ "$CONFIRM" == "y" || "$CONFIRM" == "Y" ]]; then
185 | SKIP_REMNANODE=true
186 | break
187 | elif [[ "$CONFIRM" == "n" || "$CONFIRM" == "N" ]]; then
188 | break
189 | else
190 | warn "$(get_string "install_full_node_please_enter_yn")"
191 | fi
192 | done
193 | if [[ "$SKIP_REMNANODE" == "true" ]]; then
194 | break
195 | fi
196 | elif [[ -n "$SSL_CERT_FULL" ]]; then
197 | break
198 | fi
199 | warn "$(get_string "install_full_node_ssl_cert_empty")"
200 | done
201 | fi
202 | fi
203 |
204 | if [[ "$SKIP_WARP" != "true" ]]; then
205 | while true; do
206 | question "$(get_string "install_full_node_install_warp_native")"
207 | INSTALL_WARP="$REPLY"
208 | if [[ "$INSTALL_WARP" == "n" || "$INSTALL_WARP" == "N" ]]; then
209 | while true; do
210 | question "$(get_string "install_full_node_confirm_skip_warp")"
211 | CONFIRM="$REPLY"
212 | if [[ "$CONFIRM" == "y" || "$CONFIRM" == "Y" ]]; then
213 | SKIP_WARP=true
214 | break
215 | elif [[ "$CONFIRM" == "n" || "$CONFIRM" == "N" ]]; then
216 | break
217 | else
218 | warn "$(get_string "install_full_node_please_enter_yn")"
219 | fi
220 | done
221 | if [[ "$SKIP_WARP" == "true" ]]; then
222 | break
223 | fi
224 | elif [[ "$INSTALL_WARP" == "y" || "$INSTALL_WARP" == "Y" ]]; then
225 | break
226 | else
227 | warn "$(get_string "install_full_node_please_enter_yn")"
228 | fi
229 | done
230 | fi
231 |
232 | if [[ "$SKIP_BBR" != "true" ]]; then
233 | while true; do
234 | question "$(get_string "install_full_node_need_bbr")"
235 | BBR_ANSWER="$REPLY"
236 | if [[ "$BBR_ANSWER" == "n" || "$BBR_ANSWER" == "N" ]]; then
237 | SKIP_BBR=true
238 | break
239 | elif [[ "$BBR_ANSWER" == "y" || "$BBR_ANSWER" == "Y" ]]; then
240 | SKIP_BBR=false
241 | break
242 | else
243 | warn "$(get_string "install_full_node_please_enter_yn")"
244 | fi
245 | done
246 | fi
247 | }
248 |
249 | RESTORE_DNS_REQUIRED=false
250 |
251 | restore_dns() {
252 | if [[ "$RESTORE_DNS_REQUIRED" == true && -f /etc/resolv.conf.backup ]]; then
253 | cp /etc/resolv.conf.backup /etc/resolv.conf
254 | success "$(get_string "warp_native_dns_restored")"
255 | RESTORE_DNS_REQUIRED=false
256 | fi
257 | }
258 |
259 | uninstall_warp_native() {
260 | info "$(get_string "warp_native_stopping_warp")"
261 |
262 | if ip link show warp &>/dev/null; then
263 | wg-quick down warp &>/dev/null || true
264 | fi
265 |
266 | systemctl disable wg-quick@warp &>/dev/null || true
267 |
268 | rm -f /etc/wireguard/warp.conf &>/dev/null
269 | rm -rf /etc/wireguard &>/dev/null
270 | rm -f /usr/local/bin/wgcf &>/dev/null
271 | rm -f wgcf-account.toml wgcf-profile.conf &>/dev/null
272 |
273 | info "$(get_string "warp_native_removing_packages")"
274 | DEBIAN_FRONTEND=noninteractive apt remove --purge -y wireguard &>/dev/null || true
275 | DEBIAN_FRONTEND=noninteractive apt autoremove -y &>/dev/null || true
276 |
277 | success "$(get_string "warp_native_uninstall_complete")"
278 | }
279 |
280 | install_warp() {
281 | info "$(get_string "warp_native_start_install")"
282 | echo ""
283 |
284 | info "$(get_string "warp_native_install_wireguard")"
285 | apt update -qq &>/dev/null || {
286 | error "$(get_string "warp_native_update_failed")"
287 | exit 1
288 | }
289 | apt install wireguard -y &>/dev/null || {
290 | error "$(get_string "warp_native_wireguard_failed")"
291 | exit 1
292 | }
293 | success "$(get_string "warp_native_wireguard_ok")"
294 | echo ""
295 |
296 | info "$(get_string "warp_native_temp_dns")"
297 | cp /etc/resolv.conf /etc/resolv.conf.backup
298 | RESTORE_DNS_REQUIRED=true
299 | echo -e "nameserver 1.1.1.1\nnameserver 8.8.8.8" > /etc/resolv.conf || {
300 | error "$(get_string "warp_native_dns_failed")"
301 | exit 1
302 | }
303 | success "$(get_string "warp_native_dns_ok")"
304 | echo ""
305 |
306 | info "$(get_string "warp_native_download_wgcf")"
307 | WGCF_RELEASE_URL="https://api.github.com/repos/ViRb3/wgcf/releases/latest"
308 | WGCF_VERSION=$(curl -s "$WGCF_RELEASE_URL" | grep tag_name | cut -d '"' -f 4)
309 |
310 | if [ -z "$WGCF_VERSION" ]; then
311 | error "$(get_string "warp_native_wgcf_version_failed")"
312 | exit 1
313 | fi
314 |
315 | ARCH=$(uname -m)
316 | case $ARCH in
317 | x86_64) WGCF_ARCH="amd64" ;;
318 | aarch64|arm64) WGCF_ARCH="arm64" ;;
319 | armv7l) WGCF_ARCH="armv7" ;;
320 | *) WGCF_ARCH="amd64" ;;
321 | esac
322 |
323 | info "$(get_string "warp_native_arch_detected") $ARCH -> $WGCF_ARCH"
324 |
325 | WGCF_DOWNLOAD_URL="https://github.com/ViRb3/wgcf/releases/download/${WGCF_VERSION}/wgcf_${WGCF_VERSION#v}_linux_${WGCF_ARCH}"
326 | WGCF_BINARY_NAME="wgcf_${WGCF_VERSION#v}_linux_${WGCF_ARCH}"
327 |
328 | wget -q "$WGCF_DOWNLOAD_URL" -O "$WGCF_BINARY_NAME" || {
329 | error "$(get_string "warp_native_wgcf_download_failed")"
330 | exit 1
331 | }
332 |
333 | chmod +x "$WGCF_BINARY_NAME" || {
334 | error "$(get_string "warp_native_wgcf_chmod_failed")"
335 | exit 1
336 | }
337 | mv "$WGCF_BINARY_NAME" /usr/local/bin/wgcf || {
338 | error "$(get_string "warp_native_wgcf_move_failed")"
339 | exit 1
340 | }
341 | success "wgcf $WGCF_VERSION $(get_string "warp_native_wgcf_installed")"
342 | echo ""
343 |
344 | info "$(get_string "warp_native_register_wgcf")"
345 |
346 | if [[ -f wgcf-account.toml ]]; then
347 | info "$(get_string "warp_native_account_exists")"
348 | else
349 | info "$(get_string "warp_native_registering")"
350 |
351 | info "$(get_string "warp_native_wgcf_binary_check")"
352 | if ! wgcf --help &>/dev/null; then
353 | warn "$(get_string "warp_native_wgcf_not_executable")"
354 | chmod +x /usr/local/bin/wgcf
355 | if ! wgcf --help &>/dev/null; then
356 | error "$(get_string "warp_native_wgcf_not_executable")"
357 | exit 1
358 | fi
359 | fi
360 |
361 | output=$(timeout 60 bash -c 'yes | wgcf register' 2>&1)
362 | ret=$?
363 |
364 | if [[ $ret -ne 0 ]]; then
365 | warn "$(get_string "warp_native_register_error") $ret."
366 |
367 | if [[ $ret -eq 126 ]]; then
368 | warn "$(get_string "warp_native_wgcf_not_executable")"
369 | elif [[ $ret -eq 124 ]]; then
370 | warn "Registration timed out after 60 seconds."
371 | elif [[ "$output" == *"500 Internal Server Error"* ]]; then
372 | warn "$(get_string "warp_native_cf_500_detected")"
373 | info "$(get_string "warp_native_known_behavior")"
374 | elif [[ "$output" == *"429"* || "$output" == *"Too Many Requests"* ]]; then
375 | warn "$(get_string "warp_native_cf_rate_limited")"
376 | elif [[ "$output" == *"403"* || "$output" == *"Forbidden"* ]]; then
377 | warn "$(get_string "warp_native_cf_forbidden")"
378 | elif [[ "$output" == *"network"* || "$output" == *"connection"* ]]; then
379 | warn "$(get_string "warp_native_network_issue")"
380 | else
381 | warn "$(get_string "warp_native_unknown_error")"
382 | echo "$output"
383 | fi
384 |
385 | info "$(get_string "warp_native_trying_alternative")"
386 | echo | wgcf register &>/dev/null || true
387 |
388 | sleep 2
389 | fi
390 |
391 | if [[ ! -f wgcf-account.toml ]]; then
392 | error "$(get_string "warp_native_registration_failed")"
393 | exit 1
394 | fi
395 |
396 | success "$(get_string "warp_native_account_created")"
397 | fi
398 |
399 | wgcf generate &>/dev/null || {
400 | error "$(get_string "warp_native_config_gen_failed")"
401 | exit 1
402 | }
403 | success "$(get_string "warp_native_config_generated")"
404 | echo ""
405 |
406 | info "$(get_string "warp_native_edit_config")"
407 | WGCF_CONF_FILE="wgcf-profile.conf"
408 |
409 | if [ ! -f "$WGCF_CONF_FILE" ]; then
410 | error "$(get_string "warp_native_config_not_found" | sed "s/не найден/Файл $WGCF_CONF_FILE не найден/" | sed "s/not found/File $WGCF_CONF_FILE not found/")"
411 | exit 1
412 | fi
413 |
414 | sed -i '/^DNS =/d' "$WGCF_CONF_FILE" || {
415 | error "$(get_string "warp_native_dns_removed")"
416 | exit 1
417 | }
418 |
419 | if ! grep -q "Table = off" "$WGCF_CONF_FILE"; then
420 | sed -i '/^MTU =/aTable = off' "$WGCF_CONF_FILE" || {
421 | error "$(get_string "warp_native_table_off_failed")"
422 | exit 1
423 | }
424 | fi
425 |
426 | if ! grep -q "PersistentKeepalive = 25" "$WGCF_CONF_FILE"; then
427 | sed -i '/^Endpoint =/aPersistentKeepalive = 25' "$WGCF_CONF_FILE" || {
428 | error "$(get_string "warp_native_keepalive_failed")"
429 | exit 1
430 | }
431 | fi
432 |
433 | mkdir -p /etc/wireguard || {
434 | error "$(get_string "warp_native_wireguard_dir_failed")"
435 | exit 1
436 | }
437 | mv "$WGCF_CONF_FILE" /etc/wireguard/warp.conf || {
438 | error "$(get_string "warp_native_config_move_failed")"
439 | exit 1
440 | }
441 | success "$(get_string "warp_native_config_saved")"
442 | echo ""
443 |
444 | info "$(get_string "warp_native_check_ipv6")"
445 |
446 | is_ipv6_enabled() {
447 | sysctl net.ipv6.conf.all.disable_ipv6 2>/dev/null | grep -q ' = 0' || return 1
448 | sysctl net.ipv6.conf.default.disable_ipv6 2>/dev/null | grep -q ' = 0' || return 1
449 | ip -6 addr show scope global | grep -qv 'inet6 .*fe80::' || return 1
450 | return 0
451 | }
452 |
453 | if is_ipv6_enabled; then
454 | success "$(get_string "warp_native_ipv6_enabled")"
455 | else
456 | warn "$(get_string "warp_native_ipv6_disabled")"
457 | sed -i 's/,\s*[0-9a-fA-F:]\+\/128//' /etc/wireguard/warp.conf
458 | sed -i '/Address = [0-9a-fA-F:]\+\/128/d' /etc/wireguard/warp.conf
459 | success "$(get_string "warp_native_ipv6_removed")"
460 | fi
461 | echo ""
462 |
463 | info "$(get_string "warp_native_connect_warp")"
464 | systemctl start wg-quick@warp &>/dev/null || {
465 | error "$(get_string "warp_native_connect_failed")"
466 | exit 1
467 | }
468 | success "$(get_string "warp_native_warp_connected")"
469 | echo ""
470 |
471 | info "$(get_string "warp_native_check_status")"
472 |
473 | if ! wg show warp &>/dev/null; then
474 | error "$(get_string "warp_native_warp_not_found")"
475 | exit 1
476 | fi
477 |
478 | for i in {1..10}; do
479 | handshake=$(wg show warp | grep "latest handshake" | awk -F': ' '{print $2}')
480 | if [[ "$handshake" == *"second"* || "$handshake" == *"minute"* ]]; then
481 | success "$(get_string "warp_native_handshake_received") $handshake"
482 | success "$(get_string "warp_native_warp_active")"
483 | break
484 | fi
485 | sleep 1
486 | done
487 |
488 | if [[ -z "$handshake" || "$handshake" == "0 seconds ago" ]]; then
489 | warn "$(get_string "warp_native_handshake_failed")"
490 | fi
491 |
492 | curl_result=$(curl -s --interface warp https://www.cloudflare.com/cdn-cgi/trace | grep "warp=" | cut -d= -f2)
493 |
494 | if [[ "$curl_result" == "on" ]]; then
495 | success "$(get_string "warp_native_cf_response")"
496 | else
497 | warn "$(get_string "warp_native_cf_not_confirmed")"
498 | fi
499 | echo ""
500 |
501 | info "$(get_string "warp_native_enable_autostart")"
502 | systemctl enable wg-quick@warp &>/dev/null || {
503 | error "$(get_string "warp_native_autostart_failed")"
504 | exit 1
505 | }
506 | success "$(get_string "warp_native_autostart_enabled")"
507 | echo ""
508 |
509 | restore_dns
510 | success "$(get_string "warp_native_installation_complete")"
511 | }
512 |
513 | install_bbr() {
514 | info "$(get_string "install_full_node_installing_bbr")"
515 | sudo sh -c 'modprobe tcp_bbr && sysctl net.ipv4.tcp_available_congestion_control && sysctl -w net.ipv4.tcp_congestion_control=bbr && echo "net.ipv4.tcp_congestion_control=bbr" >> /etc/sysctl.conf && sysctl -p'
516 | success "$(get_string "install_full_node_bbr_installed_success")"
517 | }
518 |
519 | setup_logs_and_logrotate() {
520 | info "$(get_string "install_full_node_setup_logs")"
521 |
522 | if [ ! -d "/var/log/remnanode" ]; then
523 | sudo mkdir -p /var/log/remnanode
524 | sudo chmod -R 777 /var/log/remnanode
525 | info "$(get_string "install_full_node_logs_dir_created")"
526 | else
527 | info "$(get_string "install_full_node_logs_dir_exists")"
528 | fi
529 |
530 | if ! command -v logrotate >/dev/null 2>&1; then
531 | sudo apt update -y && sudo apt install logrotate -y
532 | fi
533 |
534 | if [ ! -f "/etc/logrotate.d/remnanode" ] || ! grep -q "copytruncate" /etc/logrotate.d/remnanode; then
535 | sudo tee /etc/logrotate.d/remnanode > /dev/null <\n " /var/www/site/index.html
578 | sudo sed -i "s/ .env
599 | echo "$SSL_CERT_FULL" >> .env
600 |
601 | info "$(get_string "install_full_node_using_standard_compose")"
602 | cp "/opt/remnasetup/data/docker/node-compose.yml" docker-compose.yml
603 |
604 | sudo docker compose up -d || {
605 | error "$(get_string "install_full_node_remnanode_error")"
606 | exit 1
607 | }
608 | success "$(get_string "install_full_node_remnanode_installed_success")"
609 | }
610 |
611 | main() {
612 | trap restore_dns EXIT
613 |
614 | info "$(get_string "install_full_node_start")"
615 |
616 | check_components
617 | request_data
618 |
619 | info "$(get_string "install_full_node_updating_packages")"
620 | sudo apt update -y
621 |
622 | if ! check_docker; then
623 | install_docker
624 | fi
625 |
626 | if [[ "$SKIP_WARP" != "true" ]]; then
627 | if command -v wgcf >/dev/null 2>&1 && [ -f "/etc/wireguard/warp.conf" ]; then
628 | uninstall_warp_native
629 | echo ""
630 | fi
631 | install_warp
632 | fi
633 |
634 | if [[ "$SKIP_BBR" != "true" ]]; then
635 | install_bbr
636 | fi
637 |
638 | if [[ "$SKIP_CADDY" != "true" ]]; then
639 | if [[ "$UPDATE_CADDY" == "true" ]]; then
640 | sudo systemctl stop caddy
641 | sudo rm -f /etc/caddy/Caddyfile
642 | fi
643 | install_caddy
644 | fi
645 |
646 | setup_logs_and_logrotate
647 |
648 | if [[ "$SKIP_REMNANODE" != "true" ]]; then
649 | if [[ "$UPDATE_REMNANODE" == "true" ]]; then
650 | cd /opt/remnanode
651 | sudo docker compose down
652 | rm -f docker-compose.yml
653 | fi
654 | install_remnanode
655 | fi
656 |
657 | success "$(get_string "install_full_node_complete")"
658 |
659 | if [[ "$SKIP_WARP" != "true" ]]; then
660 | echo ""
661 | echo -e "${BOLD_CYAN}➤ $(get_string "warp_native_check_service"):${RESET} systemctl status wg-quick@warp"
662 | echo -e "${BOLD_CYAN}➤ $(get_string "warp_native_show_info"):${RESET} wg show warp"
663 | echo -e "${BOLD_CYAN}➤ $(get_string "warp_native_stop_interface"):${RESET} systemctl stop wg-quick@warp"
664 | echo -e "${BOLD_CYAN}➤ $(get_string "warp_native_start_interface"):${RESET} systemctl start wg-quick@warp"
665 | echo -e "${BOLD_CYAN}➤ $(get_string "warp_native_restart_interface"):${RESET} systemctl restart wg-quick@warp"
666 | echo -e "${BOLD_CYAN}➤ $(get_string "warp_native_disable_autostart"):${RESET} systemctl disable wg-quick@warp"
667 | echo -e "${BOLD_CYAN}➤ $(get_string "warp_native_enable_autostart_cmd"):${RESET} systemctl enable wg-quick@warp"
668 | echo ""
669 | fi
670 |
671 | read -n 1 -s -r -p "$(get_string "install_full_node_press_key")"
672 | exit 0
673 | }
674 |
675 | main
676 |
--------------------------------------------------------------------------------
/scripts/remnanode/install-ipv6.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | source "/opt/remnasetup/scripts/common/colors.sh"
4 | source "/opt/remnasetup/scripts/common/functions.sh"
5 | source "/opt/remnasetup/scripts/common/languages.sh"
6 |
7 | check_ipv6_status() {
8 | if sysctl net.ipv6.conf.all.disable_ipv6 | grep -q "= 1"; then
9 | return 1
10 | else
11 | return 0
12 | fi
13 | }
14 |
15 | disable_ipv6() {
16 | info "$(get_string "ipv6_disabling")"
17 |
18 | sudo tee -a /etc/sysctl.conf > /dev/null << EOF
19 |
20 | # IPv6 Disable
21 | net.ipv6.conf.all.disable_ipv6 = 1
22 | net.ipv6.conf.default.disable_ipv6 = 1
23 | net.ipv6.conf.lo.disable_ipv6 = 1
24 | EOF
25 |
26 | if [ -d "/proc/sys/net/ipv6/conf/tun0" ]; then
27 | echo "net.ipv6.conf.tun0.disable_ipv6 = 1" | sudo tee -a /etc/sysctl.conf
28 | fi
29 |
30 | sudo sysctl -w net.ipv6.conf.all.disable_ipv6=1
31 | sudo sysctl -w net.ipv6.conf.default.disable_ipv6=1
32 | sudo sysctl -w net.ipv6.conf.lo.disable_ipv6=1
33 |
34 | if [ -d "/proc/sys/net/ipv6/conf/tun0" ]; then
35 | sudo sysctl -w net.ipv6.conf.tun0.disable_ipv6=1
36 | fi
37 |
38 | success "$(get_string "ipv6_disabled_success")"
39 | }
40 |
41 | enable_ipv6() {
42 | info "$(get_string "ipv6_enabling")"
43 |
44 | sudo sed -i '/^# IPv6 Disable$/,/^net\.ipv6\.conf\..*\.disable_ipv6 = 1$/d' /etc/sysctl.conf
45 |
46 | sudo sysctl -w net.ipv6.conf.all.disable_ipv6=0
47 | sudo sysctl -w net.ipv6.conf.default.disable_ipv6=0
48 | sudo sysctl -w net.ipv6.conf.lo.disable_ipv6=0
49 |
50 | if [ -d "/proc/sys/net/ipv6/conf/tun0" ]; then
51 | sudo sysctl -w net.ipv6.conf.tun0.disable_ipv6=0
52 | fi
53 |
54 | success "$(get_string "ipv6_enabled_success")"
55 | }
56 |
57 | main() {
58 | if check_ipv6_status; then
59 | echo -e "${GREEN}[INFO] $(get_string "ipv6_status_enabled")${RESET}"
60 | while true; do
61 | question "$(get_string "ipv6_disable_confirm")"
62 | if [[ "$REPLY" == "y" || "$REPLY" == "Y" ]]; then
63 | disable_ipv6
64 | break
65 | elif [[ "$REPLY" == "n" || "$REPLY" == "N" ]]; then
66 | info "$(get_string "ipv6_operation_cancelled")"
67 | break
68 | else
69 | warn "$(get_string "install_full_node_please_enter_yn")"
70 | fi
71 | done
72 | else
73 | echo -e "${RED}[INFO] $(get_string "ipv6_status_disabled")${RESET}"
74 | while true; do
75 | question "$(get_string "ipv6_enable_confirm")"
76 | if [[ "$REPLY" == "y" || "$REPLY" == "Y" ]]; then
77 | enable_ipv6
78 | break
79 | elif [[ "$REPLY" == "n" || "$REPLY" == "N" ]]; then
80 | info "$(get_string "ipv6_operation_cancelled")"
81 | break
82 | else
83 | warn "$(get_string "install_full_node_please_enter_yn")"
84 | fi
85 | done
86 | fi
87 |
88 | read -n 1 -s -r -p "$(get_string "press_any_key")"
89 | exit 0
90 | }
91 |
92 | main
--------------------------------------------------------------------------------
/scripts/remnanode/install-node.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | source "/opt/remnasetup/scripts/common/colors.sh"
4 | source "/opt/remnasetup/scripts/common/functions.sh"
5 | source "/opt/remnasetup/scripts/common/languages.sh"
6 |
7 | check_docker() {
8 | if command -v docker >/dev/null 2>&1; then
9 | info "$(get_string "install_node_docker_installed")"
10 | return 0
11 | else
12 | return 1
13 | fi
14 | }
15 |
16 | install_docker() {
17 | info "$(get_string "install_node_installing_docker")"
18 | sudo curl -fsSL https://get.docker.com | sh || {
19 | error "$(get_string "install_node_docker_error")"
20 | exit 1
21 | }
22 | success "$(get_string "install_node_docker_success")"
23 | }
24 |
25 | setup_logs_and_logrotate() {
26 | info "$(get_string "install_node_setup_logs")"
27 |
28 | if [ ! -d "/var/log/remnanode" ]; then
29 | sudo mkdir -p /var/log/remnanode
30 | sudo chmod -R 777 /var/log/remnanode
31 | info "$(get_string "install_node_logs_dir_created")"
32 | else
33 | info "$(get_string "install_node_logs_dir_exists")"
34 | fi
35 |
36 | if ! command -v logrotate >/dev/null 2>&1; then
37 | sudo apt update -y && sudo apt install logrotate -y
38 | fi
39 |
40 | if [ ! -f "/etc/logrotate.d/remnanode" ] || ! grep -q "copytruncate" /etc/logrotate.d/remnanode; then
41 | sudo tee /etc/logrotate.d/remnanode > /dev/null < .env
86 | echo "$SSL_CERT_FULL" >> .env
87 |
88 | cp "/opt/remnasetup/data/docker/node-compose.yml" docker-compose.yml
89 |
90 | sudo docker compose up -d || {
91 | error "$(get_string "install_node_error")"
92 | exit 1
93 | }
94 | success "$(get_string "install_node_success")"
95 | }
96 |
97 | main() {
98 | if check_remnanode; then
99 | cd /opt/remnanode
100 | sudo docker compose down
101 | fi
102 |
103 | while true; do
104 | question "$(get_string "install_node_enter_app_port")"
105 | APP_PORT="$REPLY"
106 | APP_PORT=${APP_PORT:-3001}
107 | if [[ "$APP_PORT" =~ ^[0-9]+$ ]]; then
108 | break
109 | fi
110 | warn "$(get_string "install_node_port_must_be_number")"
111 | done
112 |
113 | while true; do
114 | question "$(get_string "install_node_enter_ssl_cert")"
115 | SSL_CERT_FULL="$REPLY"
116 | if [[ -n "$SSL_CERT_FULL" ]]; then
117 | break
118 | fi
119 | warn "$(get_string "install_node_ssl_cert_empty")"
120 | done
121 |
122 | if ! check_docker; then
123 | install_docker
124 | fi
125 |
126 | setup_logs_and_logrotate
127 |
128 | install_remnanode
129 |
130 | success "$(get_string "install_node_complete")"
131 | read -n 1 -s -r -p "$(get_string "install_node_press_key")"
132 | exit 0
133 | }
134 |
135 | main
--------------------------------------------------------------------------------
/scripts/remnanode/install-warp.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | source "/opt/remnasetup/scripts/common/colors.sh"
4 | source "/opt/remnasetup/scripts/common/functions.sh"
5 | source "/opt/remnasetup/scripts/common/languages.sh"
6 |
7 | RESTORE_DNS_REQUIRED=false
8 |
9 | restore_dns() {
10 | if [[ "$RESTORE_DNS_REQUIRED" == true && -f /etc/resolv.conf.backup ]]; then
11 | cp /etc/resolv.conf.backup /etc/resolv.conf
12 | success "$(get_string "warp_native_dns_restored")"
13 | RESTORE_DNS_REQUIRED=false
14 | fi
15 | }
16 |
17 | trap restore_dns EXIT
18 |
19 | check_warp_native() {
20 | if command -v wgcf >/dev/null 2>&1 && [ -f "/etc/wireguard/warp.conf" ]; then
21 | info "$(get_string "warp_native_already_installed")"
22 | while true; do
23 | question "$(get_string "warp_native_reconfigure")"
24 | RECONFIGURE="$REPLY"
25 | if [[ "$RECONFIGURE" == "y" || "$RECONFIGURE" == "Y" ]]; then
26 | return 0
27 | elif [[ "$RECONFIGURE" == "n" || "$RECONFIGURE" == "N" ]]; then
28 | info "$(get_string "warp_native_skip_installation")"
29 | read -n 1 -s -r -p "$(get_string "warp_native_press_key")"
30 | exit 0
31 | else
32 | warn "$(get_string "warp_native_please_enter_yn")"
33 | fi
34 | done
35 | fi
36 | return 0
37 | }
38 |
39 | uninstall_warp_native() {
40 | info "$(get_string "warp_native_stopping_warp")"
41 |
42 | if ip link show warp &>/dev/null; then
43 | wg-quick down warp &>/dev/null || true
44 | fi
45 |
46 | systemctl disable wg-quick@warp &>/dev/null || true
47 |
48 | rm -f /etc/wireguard/warp.conf &>/dev/null
49 | rm -rf /etc/wireguard &>/dev/null
50 | rm -f /usr/local/bin/wgcf &>/dev/null
51 | rm -f wgcf-account.toml wgcf-profile.conf &>/dev/null
52 |
53 | info "$(get_string "warp_native_removing_packages")"
54 | DEBIAN_FRONTEND=noninteractive apt remove --purge -y wireguard &>/dev/null || true
55 | DEBIAN_FRONTEND=noninteractive apt autoremove -y &>/dev/null || true
56 |
57 | success "$(get_string "warp_native_uninstall_complete")"
58 | }
59 |
60 | install_warp_native() {
61 | info "$(get_string "warp_native_start_install")"
62 | echo ""
63 |
64 | info "$(get_string "warp_native_install_wireguard")"
65 | apt update -qq &>/dev/null || {
66 | error "$(get_string "warp_native_update_failed")"
67 | exit 1
68 | }
69 | apt install wireguard -y &>/dev/null || {
70 | error "$(get_string "warp_native_wireguard_failed")"
71 | exit 1
72 | }
73 | success "$(get_string "warp_native_wireguard_ok")"
74 | echo ""
75 |
76 | info "$(get_string "warp_native_temp_dns")"
77 | cp /etc/resolv.conf /etc/resolv.conf.backup
78 | RESTORE_DNS_REQUIRED=true
79 | echo -e "nameserver 1.1.1.1\nnameserver 8.8.8.8" > /etc/resolv.conf || {
80 | error "$(get_string "warp_native_dns_failed")"
81 | exit 1
82 | }
83 | success "$(get_string "warp_native_dns_ok")"
84 | echo ""
85 |
86 | info "$(get_string "warp_native_download_wgcf")"
87 | WGCF_RELEASE_URL="https://api.github.com/repos/ViRb3/wgcf/releases/latest"
88 | WGCF_VERSION=$(curl -s "$WGCF_RELEASE_URL" | grep tag_name | cut -d '"' -f 4)
89 |
90 | if [ -z "$WGCF_VERSION" ]; then
91 | error "$(get_string "warp_native_wgcf_version_failed")"
92 | exit 1
93 | fi
94 |
95 | ARCH=$(uname -m)
96 | case $ARCH in
97 | x86_64) WGCF_ARCH="amd64" ;;
98 | aarch64|arm64) WGCF_ARCH="arm64" ;;
99 | armv7l) WGCF_ARCH="armv7" ;;
100 | *) WGCF_ARCH="amd64" ;;
101 | esac
102 |
103 | info "$(get_string "warp_native_arch_detected") $ARCH -> $WGCF_ARCH"
104 |
105 | WGCF_DOWNLOAD_URL="https://github.com/ViRb3/wgcf/releases/download/${WGCF_VERSION}/wgcf_${WGCF_VERSION#v}_linux_${WGCF_ARCH}"
106 | WGCF_BINARY_NAME="wgcf_${WGCF_VERSION#v}_linux_${WGCF_ARCH}"
107 |
108 | wget -q "$WGCF_DOWNLOAD_URL" -O "$WGCF_BINARY_NAME" || {
109 | error "$(get_string "warp_native_wgcf_download_failed")"
110 | exit 1
111 | }
112 |
113 | chmod +x "$WGCF_BINARY_NAME" || {
114 | error "$(get_string "warp_native_wgcf_chmod_failed")"
115 | exit 1
116 | }
117 | mv "$WGCF_BINARY_NAME" /usr/local/bin/wgcf || {
118 | error "$(get_string "warp_native_wgcf_move_failed")"
119 | exit 1
120 | }
121 | success "wgcf $WGCF_VERSION $(get_string "warp_native_wgcf_installed")"
122 | echo ""
123 |
124 | info "$(get_string "warp_native_register_wgcf")"
125 |
126 | if [[ -f wgcf-account.toml ]]; then
127 | info "$(get_string "warp_native_account_exists")"
128 | else
129 | info "$(get_string "warp_native_registering")"
130 |
131 | info "$(get_string "warp_native_wgcf_binary_check")"
132 | if ! wgcf --help &>/dev/null; then
133 | warn "$(get_string "warp_native_wgcf_not_executable")"
134 | chmod +x /usr/local/bin/wgcf
135 | if ! wgcf --help &>/dev/null; then
136 | error "$(get_string "warp_native_wgcf_not_executable")"
137 | exit 1
138 | fi
139 | fi
140 |
141 | output=$(timeout 60 bash -c 'yes | wgcf register' 2>&1)
142 | ret=$?
143 |
144 | if [[ $ret -ne 0 ]]; then
145 | warn "$(get_string "warp_native_register_error") $ret."
146 |
147 | if [[ $ret -eq 126 ]]; then
148 | warn "$(get_string "warp_native_wgcf_not_executable")"
149 | elif [[ $ret -eq 124 ]]; then
150 | warn "Registration timed out after 60 seconds."
151 | elif [[ "$output" == *"500 Internal Server Error"* ]]; then
152 | warn "$(get_string "warp_native_cf_500_detected")"
153 | info "$(get_string "warp_native_known_behavior")"
154 | elif [[ "$output" == *"429"* || "$output" == *"Too Many Requests"* ]]; then
155 | warn "$(get_string "warp_native_cf_rate_limited")"
156 | elif [[ "$output" == *"403"* || "$output" == *"Forbidden"* ]]; then
157 | warn "$(get_string "warp_native_cf_forbidden")"
158 | elif [[ "$output" == *"network"* || "$output" == *"connection"* ]]; then
159 | warn "$(get_string "warp_native_network_issue")"
160 | else
161 | warn "$(get_string "warp_native_unknown_error")"
162 | echo "$output"
163 | fi
164 |
165 | info "$(get_string "warp_native_trying_alternative")"
166 | echo | wgcf register &>/dev/null || true
167 |
168 | sleep 2
169 | fi
170 |
171 | if [[ ! -f wgcf-account.toml ]]; then
172 | error "$(get_string "warp_native_registration_failed")"
173 | exit 1
174 | fi
175 |
176 | success "$(get_string "warp_native_account_created")"
177 | fi
178 |
179 | wgcf generate &>/dev/null || {
180 | error "$(get_string "warp_native_config_gen_failed")"
181 | exit 1
182 | }
183 | success "$(get_string "warp_native_config_generated")"
184 | echo ""
185 |
186 | info "$(get_string "warp_native_edit_config")"
187 | WGCF_CONF_FILE="wgcf-profile.conf"
188 |
189 | if [ ! -f "$WGCF_CONF_FILE" ]; then
190 | error "$(get_string "warp_native_config_not_found" | sed "s/не найден/Файл $WGCF_CONF_FILE не найден/" | sed "s/not found/File $WGCF_CONF_FILE not found/")"
191 | exit 1
192 | fi
193 |
194 | sed -i '/^DNS =/d' "$WGCF_CONF_FILE" || {
195 | error "$(get_string "warp_native_dns_removed")"
196 | exit 1
197 | }
198 |
199 | if ! grep -q "Table = off" "$WGCF_CONF_FILE"; then
200 | sed -i '/^MTU =/aTable = off' "$WGCF_CONF_FILE" || {
201 | error "$(get_string "warp_native_table_off_failed")"
202 | exit 1
203 | }
204 | fi
205 |
206 | if ! grep -q "PersistentKeepalive = 25" "$WGCF_CONF_FILE"; then
207 | sed -i '/^Endpoint =/aPersistentKeepalive = 25' "$WGCF_CONF_FILE" || {
208 | error "$(get_string "warp_native_keepalive_failed")"
209 | exit 1
210 | }
211 | fi
212 |
213 | mkdir -p /etc/wireguard || {
214 | error "$(get_string "warp_native_wireguard_dir_failed")"
215 | exit 1
216 | }
217 | mv "$WGCF_CONF_FILE" /etc/wireguard/warp.conf || {
218 | error "$(get_string "warp_native_config_move_failed")"
219 | exit 1
220 | }
221 | success "$(get_string "warp_native_config_saved")"
222 | echo ""
223 |
224 | info "$(get_string "warp_native_check_ipv6")"
225 |
226 | is_ipv6_enabled() {
227 | sysctl net.ipv6.conf.all.disable_ipv6 2>/dev/null | grep -q ' = 0' || return 1
228 | sysctl net.ipv6.conf.default.disable_ipv6 2>/dev/null | grep -q ' = 0' || return 1
229 | ip -6 addr show scope global | grep -qv 'inet6 .*fe80::' || return 1
230 | return 0
231 | }
232 |
233 | if is_ipv6_enabled; then
234 | success "$(get_string "warp_native_ipv6_enabled")"
235 | else
236 | warn "$(get_string "warp_native_ipv6_disabled")"
237 | sed -i 's/,\s*[0-9a-fA-F:]\+\/128//' /etc/wireguard/warp.conf
238 | sed -i '/Address = [0-9a-fA-F:]\+\/128/d' /etc/wireguard/warp.conf
239 | success "$(get_string "warp_native_ipv6_removed")"
240 | fi
241 | echo ""
242 |
243 | info "$(get_string "warp_native_connect_warp")"
244 | systemctl start wg-quick@warp &>/dev/null || {
245 | error "$(get_string "warp_native_connect_failed")"
246 | exit 1
247 | }
248 | success "$(get_string "warp_native_warp_connected")"
249 | echo ""
250 |
251 | info "$(get_string "warp_native_check_status")"
252 |
253 | if ! wg show warp &>/dev/null; then
254 | error "$(get_string "warp_native_warp_not_found")"
255 | exit 1
256 | fi
257 |
258 | for i in {1..10}; do
259 | handshake=$(wg show warp | grep "latest handshake" | awk -F': ' '{print $2}')
260 | if [[ "$handshake" == *"second"* || "$handshake" == *"minute"* ]]; then
261 | success "$(get_string "warp_native_handshake_received") $handshake"
262 | success "$(get_string "warp_native_warp_active")"
263 | break
264 | fi
265 | sleep 1
266 | done
267 |
268 | if [[ -z "$handshake" || "$handshake" == "0 seconds ago" ]]; then
269 | warn "$(get_string "warp_native_handshake_failed")"
270 | fi
271 |
272 | curl_result=$(curl -s --interface warp https://www.cloudflare.com/cdn-cgi/trace | grep "warp=" | cut -d= -f2)
273 |
274 | if [[ "$curl_result" == "on" ]]; then
275 | success "$(get_string "warp_native_cf_response")"
276 | else
277 | warn "$(get_string "warp_native_cf_not_confirmed")"
278 | fi
279 | echo ""
280 |
281 | info "$(get_string "warp_native_enable_autostart")"
282 | systemctl enable wg-quick@warp &>/dev/null || {
283 | error "$(get_string "warp_native_autostart_failed")"
284 | exit 1
285 | }
286 | success "$(get_string "warp_native_autostart_enabled")"
287 | echo ""
288 |
289 | restore_dns
290 | success "$(get_string "warp_native_installation_complete")"
291 | echo ""
292 | echo -e "${BOLD_CYAN}➤ $(get_string "warp_native_check_service"):${RESET} systemctl status wg-quick@warp"
293 | echo -e "${BOLD_CYAN}➤ $(get_string "warp_native_show_info"):${RESET} wg show warp"
294 | echo -e "${BOLD_CYAN}➤ $(get_string "warp_native_stop_interface"):${RESET} systemctl stop wg-quick@warp"
295 | echo -e "${BOLD_CYAN}➤ $(get_string "warp_native_start_interface"):${RESET} systemctl start wg-quick@warp"
296 | echo -e "${BOLD_CYAN}➤ $(get_string "warp_native_restart_interface"):${RESET} systemctl restart wg-quick@warp"
297 | echo -e "${BOLD_CYAN}➤ $(get_string "warp_native_disable_autostart"):${RESET} systemctl disable wg-quick@warp"
298 | echo -e "${BOLD_CYAN}➤ $(get_string "warp_native_enable_autostart_cmd"):${RESET} systemctl enable wg-quick@warp"
299 | echo ""
300 | }
301 |
302 | main() {
303 | if ! check_warp_native; then
304 | return 0
305 | fi
306 |
307 | if command -v wgcf >/dev/null 2>&1 && [ -f "/etc/wireguard/warp.conf" ]; then
308 | uninstall_warp_native
309 | echo ""
310 | fi
311 |
312 | install_warp_native
313 | read -n 1 -s -r -p "$(get_string "warp_native_press_key")"
314 | exit 0
315 | }
316 |
317 | main
318 |
--------------------------------------------------------------------------------
/scripts/remnanode/update.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | source "/opt/remnasetup/scripts/common/colors.sh"
4 | source "/opt/remnasetup/scripts/common/functions.sh"
5 | source "/opt/remnasetup/scripts/common/languages.sh"
6 |
7 | update_panel() {
8 | info "$(get_string "update_node_updating")"
9 | cd /opt/remnanode
10 | docker compose pull
11 | docker compose up -d
12 | }
13 |
14 | main() {
15 | update_panel
16 | success "$(get_string "update_node_complete")"
17 | read -n 1 -s -r -p "$(get_string "update_node_press_key")"
18 | exit 0
19 | }
20 |
21 | main
22 |
--------------------------------------------------------------------------------
/scripts/remnawave/install-caddy.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | source "/opt/remnasetup/scripts/common/colors.sh"
4 | source "/opt/remnasetup/scripts/common/functions.sh"
5 | source "/opt/remnasetup/scripts/common/languages.sh"
6 |
7 | REINSTALL_CADDY=false
8 |
9 | check_component() {
10 | if [ -f "/opt/remnawave/caddy/docker-compose.yml" ] || [ -f "/opt/remnawave/caddy/Caddyfile" ]; then
11 | info "$(get_string "install_caddy_detected")"
12 | while true; do
13 | question "$(get_string "install_caddy_reinstall")"
14 | REINSTALL="$REPLY"
15 | if [[ "$REINSTALL" == "y" || "$REINSTALL" == "Y" ]]; then
16 | warn "$(get_string "install_caddy_stopping")"
17 | if [ -f "/opt/remnawave/caddy/docker-compose.yml" ]; then
18 | cd /opt/remnawave/caddy && docker compose down
19 | fi
20 | if docker ps -a --format '{{.Names}}' | grep -q "remnawave-caddy\|caddy"; then
21 | if [ "$NEED_PROTECTION" = "y" ]; then
22 | docker rmi remnawave/caddy-with-auth:latest 2>/dev/null || true
23 | else
24 | docker rmi caddy:2.9 2>/dev/null || true
25 | fi
26 | fi
27 | rm -f /opt/remnawave/caddy/Caddyfile
28 | rm -f /opt/remnawave/caddy/docker-compose.yml
29 | REINSTALL_CADDY=true
30 | break
31 | elif [[ "$REINSTALL" == "n" || "$REINSTALL" == "N" ]]; then
32 | info "$(get_string "install_caddy_reinstall_denied")"
33 | read -n 1 -s -r -p "$(get_string "install_caddy_press_key")"
34 | exit 0
35 | else
36 | warn "$(get_string "install_caddy_please_enter_yn")"
37 | fi
38 | done
39 | else
40 | REINSTALL_CADDY=true
41 | fi
42 | }
43 |
44 | install_docker() {
45 | if ! command -v docker &> /dev/null; then
46 | info "$(get_string "install_caddy_installing")"
47 | sudo curl -fsSL https://get.docker.com | sh
48 | fi
49 | }
50 |
51 | install_without_protection() {
52 | if [ "$REINSTALL_CADDY" = true ]; then
53 | info "$(get_string "install_caddy_installing")"
54 | mkdir -p /opt/remnawave/caddy
55 | cd /opt/remnawave/caddy
56 |
57 | cp "/opt/remnasetup/data/caddy/caddyfile" Caddyfile
58 | cp "/opt/remnasetup/data/docker/caddy-compose.yml" docker-compose.yml
59 |
60 | sed -i "s|\$PANEL_DOMAIN|$PANEL_DOMAIN|g" Caddyfile
61 | sed -i "s|\$SUB_DOMAIN|$SUB_DOMAIN|g" Caddyfile
62 | sed -i "s|\$PANEL_PORT|$PANEL_PORT|g" Caddyfile
63 | sed -i "s|\$SUB_PORT|$SUB_PORT|g" Caddyfile
64 |
65 | cd /opt/remnawave
66 | if [ -f ".env" ]; then
67 | sed -i "s|PANEL_DOMAIN=.*|PANEL_DOMAIN=$PANEL_DOMAIN|g" .env
68 | sed -i "s|SUB_DOMAIN=.*|SUB_DOMAIN=$SUB_DOMAIN|g" .env
69 | sed -i "s|PANEL_PORT=.*|PANEL_PORT=$PANEL_PORT|g" .env
70 | fi
71 |
72 | cd /opt/remnawave/subscription
73 | if [ -f "docker-compose.yml" ]; then
74 | sed -i "s|PANEL_DOMAIN=.*|PANEL_DOMAIN=$PANEL_DOMAIN|g" docker-compose.yml
75 | sed -i "s|SUB_PORT=.*|SUB_PORT=$SUB_PORT|g" docker-compose.yml
76 | fi
77 |
78 | cd /opt/remnawave && docker compose down && docker compose up -d
79 | cd /opt/remnawave/subscription && docker compose down && docker compose up -d
80 | cd /opt/remnawave/caddy && docker compose up -d
81 | fi
82 | }
83 |
84 | install_with_protection() {
85 | if [ "$REINSTALL_CADDY" = true ]; then
86 | info "$(get_string "install_caddy_installing")"
87 | mkdir -p /opt/remnawave/caddy
88 | cd /opt/remnawave/caddy
89 |
90 | cp "/opt/remnasetup/data/caddy/caddyfile-protection" Caddyfile
91 | cp "/opt/remnasetup/data/docker/caddy-protection-compose.yml" docker-compose.yml
92 |
93 | sed -i "s|\$PANEL_DOMAIN|$PANEL_DOMAIN|g" docker-compose.yml
94 | sed -i "s|\$CUSTOM_LOGIN_ROUTE|$CUSTOM_LOGIN_ROUTE|g" docker-compose.yml
95 | sed -i "s|\$LOGIN_USERNAME|$LOGIN_USERNAME|g" docker-compose.yml
96 | sed -i "s|\$LOGIN_EMAIL|$LOGIN_EMAIL|g" docker-compose.yml
97 | sed -i "s|\$LOGIN_PASSWORD|$LOGIN_PASSWORD|g" docker-compose.yml
98 |
99 | sed -i "s|\$PANEL_PORT|$PANEL_PORT|g" Caddyfile
100 | sed -i "s|\$SUB_DOMAIN|$SUB_DOMAIN|g" Caddyfile
101 | sed -i "s|\$SUB_PORT|$SUB_PORT|g" Caddyfile
102 |
103 | cd /opt/remnawave
104 | if [ -f ".env" ]; then
105 | sed -i "s|PANEL_DOMAIN=.*|PANEL_DOMAIN=$PANEL_DOMAIN|g" .env
106 | sed -i "s|SUB_DOMAIN=.*|SUB_DOMAIN=$SUB_DOMAIN|g" .env
107 | sed -i "s|PANEL_PORT=.*|PANEL_PORT=$PANEL_PORT|g" .env
108 | fi
109 |
110 | cd /opt/remnawave/subscription
111 | docker compose down
112 | rm -f docker-compose.yml
113 | cp "/opt/remnasetup/data/docker/subscription-protection-compose.yml" docker-compose.yml
114 |
115 | sed -i "s|\$PANEL_PORT|$PANEL_PORT|g" docker-compose.yml
116 | sed -i "s|\$SUB_PORT|$SUB_PORT|g" docker-compose.yml
117 | sed -i "s|\$PROJECT_NAME|$PROJECT_NAME|g" docker-compose.yml
118 | sed -i "s|\$PROJECT_DESCRIPTION|$PROJECT_DESCRIPTION|g" docker-compose.yml
119 |
120 | cd /opt/remnawave && docker compose down && docker compose up -d
121 | cd /opt/remnawave/subscription && docker compose up -d
122 | cd /opt/remnawave/caddy && docker compose up -d
123 | fi
124 | }
125 |
126 | check_docker() {
127 | if command -v docker >/dev/null 2>&1; then
128 | info "$(get_string "install_caddy_detected")"
129 | return 0
130 | else
131 | return 1
132 | fi
133 | }
134 |
135 | main() {
136 | check_component
137 |
138 | while true; do
139 | question "$(get_string "install_caddy_need_protection")"
140 | NEED_PROTECTION="$REPLY"
141 | if [[ "$NEED_PROTECTION" == "y" || "$NEED_PROTECTION" == "Y" || "$NEED_PROTECTION" == "n" || "$NEED_PROTECTION" == "N" ]]; then
142 | break
143 | fi
144 | warn "$(get_string "install_caddy_please_enter_yn")"
145 | done
146 |
147 | while true; do
148 | question "$(get_string "install_caddy_enter_panel_domain")"
149 | PANEL_DOMAIN="$REPLY"
150 | if [[ -n "$PANEL_DOMAIN" ]]; then
151 | break
152 | fi
153 | warn "$(get_string "install_caddy_domain_empty")"
154 | done
155 |
156 | while true; do
157 | question "$(get_string "install_caddy_enter_sub_domain")"
158 | SUB_DOMAIN="$REPLY"
159 | if [[ -n "$SUB_DOMAIN" ]]; then
160 | break
161 | fi
162 | warn "$(get_string "install_caddy_domain_empty")"
163 | done
164 |
165 | question "$(get_string "install_caddy_enter_panel_port")"
166 | PANEL_PORT="$REPLY"
167 | PANEL_PORT=${PANEL_PORT:-3000}
168 |
169 | question "$(get_string "install_caddy_enter_sub_port")"
170 | SUB_PORT="$REPLY"
171 | SUB_PORT=${SUB_PORT:-3010}
172 |
173 | if [ "$NEED_PROTECTION" = "y" ]; then
174 | while true; do
175 | question "$(get_string "install_caddy_enter_project_name")"
176 | PROJECT_NAME="$REPLY"
177 | if [[ -n "$PROJECT_NAME" ]]; then
178 | break
179 | fi
180 | warn "$(get_string "install_caddy_project_name_empty")"
181 | done
182 |
183 | while true; do
184 | question "$(get_string "install_caddy_enter_project_description")"
185 | PROJECT_DESCRIPTION="$REPLY"
186 | if [[ -n "$PROJECT_DESCRIPTION" ]]; then
187 | break
188 | fi
189 | warn "$(get_string "install_caddy_project_description_empty")"
190 | done
191 |
192 | while true; do
193 | question "$(get_string "install_caddy_enter_login_route")"
194 | CUSTOM_LOGIN_ROUTE="$REPLY"
195 | if [[ -n "$CUSTOM_LOGIN_ROUTE" ]]; then
196 | break
197 | fi
198 | warn "$(get_string "install_caddy_login_route_empty")"
199 | done
200 |
201 | while true; do
202 | question "$(get_string "install_caddy_enter_admin_login")"
203 | LOGIN_USERNAME="$REPLY"
204 | if [[ -n "$LOGIN_USERNAME" ]]; then
205 | break
206 | fi
207 | warn "$(get_string "install_caddy_admin_login_empty")"
208 | done
209 |
210 | while true; do
211 | question "$(get_string "install_caddy_enter_admin_email")"
212 | LOGIN_EMAIL="$REPLY"
213 | if [[ -n "$LOGIN_EMAIL" ]]; then
214 | break
215 | fi
216 | warn "$(get_string "install_caddy_admin_email_empty")"
217 | done
218 |
219 | while true; do
220 | question "$(get_string "install_caddy_enter_admin_password")"
221 | LOGIN_PASSWORD="$REPLY"
222 | if [[ ${#LOGIN_PASSWORD} -lt 8 ]]; then
223 | warn "$(get_string "install_caddy_password_short")"
224 | continue
225 | fi
226 | if ! [[ "$LOGIN_PASSWORD" =~ [A-Z] ]]; then
227 | warn "$(get_string "install_caddy_password_uppercase")"
228 | continue
229 | fi
230 | if ! [[ "$LOGIN_PASSWORD" =~ [a-z] ]]; then
231 | warn "$(get_string "install_caddy_password_lowercase")"
232 | continue
233 | fi
234 | if ! [[ "$LOGIN_PASSWORD" =~ [0-9] ]]; then
235 | warn "$(get_string "install_caddy_password_number")"
236 | continue
237 | fi
238 | if ! [[ "$LOGIN_PASSWORD" =~ [^a-zA-Z0-9] ]]; then
239 | warn "$(get_string "install_caddy_password_special")"
240 | continue
241 | fi
242 | break
243 | done
244 | fi
245 |
246 | if ! check_docker; then
247 | install_docker
248 | fi
249 | if [ "$NEED_PROTECTION" = "y" ]; then
250 | install_with_protection
251 | else
252 | install_without_protection
253 | fi
254 |
255 | success "$(get_string "install_caddy_complete")"
256 | read -n 1 -s -r -p "$(get_string "install_caddy_press_key")"
257 | exit 0
258 | }
259 |
260 | main
261 |
--------------------------------------------------------------------------------
/scripts/remnawave/install-full.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | source "/opt/remnasetup/scripts/common/colors.sh"
4 | source "/opt/remnasetup/scripts/common/functions.sh"
5 | source "/opt/remnasetup/scripts/common/languages.sh"
6 |
7 | REINSTALL_PANEL=false
8 | REINSTALL_SUBSCRIPTION=false
9 | REINSTALL_CADDY=false
10 |
11 | check_component() {
12 | local component=$1
13 | local path=$2
14 | local env_file=$3
15 |
16 | case $component in
17 | "panel")
18 | if [ -f "$path/docker-compose.yml" ] && (cd "$path" && docker compose ps -q | grep -q "remnawave") || [ -f "$env_file" ]; then
19 | info "$(get_string "install_full_detected")"
20 | while true; do
21 | question "$(get_string "install_full_reinstall")"
22 | REINSTALL="$REPLY"
23 | if [[ "$REINSTALL" == "y" || "$REINSTALL" == "Y" ]]; then
24 | warn "$(get_string "install_full_stopping")"
25 | cd "$path" && docker compose down
26 | docker rmi remnawave/panel:latest 2>/dev/null || true
27 | docker rmi remnawave/redis:latest 2>/dev/null || true
28 | docker rmi remnawave/postgres:latest 2>/dev/null || true
29 | docker volume rm remnawave-db-data remnawave-redis-data 2>/dev/null || true
30 | rm -f "$env_file"
31 | rm -f "$path/docker-compose.yml"
32 | REINSTALL_PANEL=true
33 | break
34 | elif [[ "$REINSTALL" == "n" || "$REINSTALL" == "N" ]]; then
35 | info "$(get_string "install_full_reinstall_denied")"
36 | REINSTALL_PANEL=false
37 | break
38 | else
39 | warn "$(get_string "install_full_please_enter_yn")"
40 | fi
41 | done
42 | else
43 | REINSTALL_PANEL=true
44 | fi
45 | ;;
46 | "subscription")
47 | if [ -f "$path/docker-compose.yml" ] && (cd "$path" && docker compose ps -q | grep -q "remnawave-subscription-page") || [ -f "$path/app-config.json" ]; then
48 | info "$(get_string "install_full_subscription_detected")"
49 | while true; do
50 | question "$(get_string "install_full_subscription_reinstall")"
51 | REINSTALL="$REPLY"
52 | if [[ "$REINSTALL" == "y" || "$REINSTALL" == "Y" ]]; then
53 | warn "$(get_string "install_full_stopping")"
54 | cd "$path" && docker compose down
55 | docker rmi remnawave/subscription-page:latest 2>/dev/null || true
56 | rm -f "$path/app-config.json"
57 | rm -f "$path/docker-compose.yml"
58 | REINSTALL_SUBSCRIPTION=true
59 | break
60 | elif [[ "$REINSTALL" == "n" || "$REINSTALL" == "N" ]]; then
61 | info "$(get_string "install_full_subscription_reinstall_denied")"
62 | REINSTALL_SUBSCRIPTION=false
63 | break
64 | else
65 | warn "$(get_string "install_full_please_enter_yn")"
66 | fi
67 | done
68 | else
69 | REINSTALL_SUBSCRIPTION=true
70 | fi
71 | ;;
72 | "caddy")
73 | if [ -f "$path/docker-compose.yml" ] || [ -f "$path/Caddyfile" ]; then
74 | info "$(get_string "install_full_caddy_detected")"
75 | while true; do
76 | question "$(get_string "install_full_caddy_reinstall")"
77 | REINSTALL="$REPLY"
78 | if [[ "$REINSTALL" == "y" || "$REINSTALL" == "Y" ]]; then
79 | warn "$(get_string "install_full_stopping")"
80 | if [ -f "$path/docker-compose.yml" ]; then
81 | cd "$path" && docker compose down
82 | fi
83 | if docker ps -a --format '{{.Names}}' | grep -q "remnawave-caddy\|caddy"; then
84 | if [ "$NEED_PROTECTION" = "y" ]; then
85 | docker rmi remnawave/caddy-with-auth:latest 2>/dev/null || true
86 | else
87 | docker rmi caddy:2.9 2>/dev/null || true
88 | fi
89 | fi
90 | rm -f "$path/Caddyfile"
91 | rm -f "$path/docker-compose.yml"
92 | REINSTALL_CADDY=true
93 | break
94 | elif [[ "$REINSTALL" == "n" || "$REINSTALL" == "N" ]]; then
95 | info "$(get_string "install_full_caddy_reinstall_denied")"
96 | REINSTALL_CADDY=false
97 | break
98 | else
99 | warn "$(get_string "install_full_please_enter_yn")"
100 | fi
101 | done
102 | else
103 | REINSTALL_CADDY=true
104 | fi
105 | ;;
106 | esac
107 | }
108 |
109 | install_docker() {
110 | if ! command -v docker &> /dev/null; then
111 | info "$(get_string "install_full_installing_docker")"
112 | sudo curl -fsSL https://get.docker.com | sh
113 | fi
114 | }
115 |
116 | generate_64() {
117 | openssl rand -hex 64
118 | }
119 |
120 | generate_24() {
121 | openssl rand -hex 24
122 | }
123 |
124 | generate_login() {
125 | tr -dc 'a-zA-Z' < /dev/urandom | head -c 15
126 | }
127 |
128 |
129 |
130 | install_without_protection() {
131 | if [ "$REINSTALL_PANEL" = true ]; then
132 | info "$(get_string "install_full_installing")"
133 | mkdir -p /opt/remnawave
134 | cd /opt/remnawave
135 |
136 | cp "/opt/remnasetup/data/docker/panel.env" .env
137 | cp "/opt/remnasetup/data/docker/panel-compose.yml" docker-compose.yml
138 |
139 | JWT_AUTH_SECRET=$(generate_64)
140 | JWT_API_TOKENS_SECRET=$(generate_64)
141 | METRICS_USER=$(generate_login)
142 | METRICS_PASS=$(generate_64)
143 | WEBHOOK_SECRET_HEADER=$(generate_64)
144 | DB_USER=$(generate_login)
145 | DB_PASSWORD=$(generate_24)
146 |
147 | export DB_USER
148 | export DB_PASSWORD
149 |
150 | sed -i "s|\$PANEL_DOMAIN|$PANEL_DOMAIN|g" .env
151 | sed -i "s|\$PANEL_PORT|$PANEL_PORT|g" .env
152 | sed -i "s|\$METRICS_USER|$METRICS_USER|g" .env
153 | sed -i "s|\$METRICS_PASS|$METRICS_PASS|g" .env
154 | sed -i "s|\$DB_USER|$DB_USER|g" .env
155 | sed -i "s|\$DB_PASSWORD|$DB_PASSWORD|g" .env
156 | sed -i "s|\$JWT_AUTH_SECRET|$JWT_AUTH_SECRET|g" .env
157 | sed -i "s|\$JWT_API_TOKENS_SECRET|$JWT_API_TOKENS_SECRET|g" .env
158 | sed -i "s|\$SUB_DOMAIN|$SUB_DOMAIN|g" .env
159 | sed -i "s|\$WEBHOOK_SECRET_HEADER|$WEBHOOK_SECRET_HEADER|g" .env
160 |
161 | sed -i "s|\$PANEL_PORT|$PANEL_PORT|g" docker-compose.yml
162 |
163 | docker compose up -d
164 | fi
165 |
166 | if [ "$REINSTALL_SUBSCRIPTION" = true ]; then
167 | info "$(get_string "install_full_installing_subscription")"
168 | mkdir -p /opt/remnawave/subscription
169 | cd /opt/remnawave/subscription
170 |
171 | cp "/opt/remnasetup/data/app-config.json" app-config.json
172 | cp "/opt/remnasetup/data/docker/subscription-compose.yml" docker-compose.yml
173 |
174 | sed -i "s|\$PANEL_DOMAIN|$PANEL_DOMAIN|g" docker-compose.yml
175 | sed -i "s|\$SUB_PORT|$SUB_PORT|g" docker-compose.yml
176 | sed -i "s|\$PROJECT_NAME|$PROJECT_NAME|g" docker-compose.yml
177 | sed -i "s|\$PROJECT_DESCRIPTION|$PROJECT_DESCRIPTION|g" docker-compose.yml
178 |
179 | docker compose up -d
180 | fi
181 |
182 | if [ "$REINSTALL_CADDY" = true ]; then
183 | info "$(get_string "install_full_installing_caddy")"
184 | mkdir -p /opt/remnawave/caddy
185 | cd /opt/remnawave/caddy
186 |
187 | cp "/opt/remnasetup/data/caddy/caddyfile" Caddyfile
188 | cp "/opt/remnasetup/data/docker/caddy-compose.yml" docker-compose.yml
189 |
190 | sed -i "s|\$PANEL_DOMAIN|$PANEL_DOMAIN|g" Caddyfile
191 | sed -i "s|\$SUB_DOMAIN|$SUB_DOMAIN|g" Caddyfile
192 | sed -i "s|\$PANEL_PORT|$PANEL_PORT|g" Caddyfile
193 | sed -i "s|\$SUB_PORT|$SUB_PORT|g" Caddyfile
194 |
195 | docker compose up -d
196 | fi
197 | }
198 |
199 | install_with_protection() {
200 | if [ "$REINSTALL_PANEL" = true ]; then
201 | info "$(get_string "install_full_installing_with_protection")"
202 | mkdir -p /opt/remnawave
203 | cd /opt/remnawave
204 |
205 | cp "/opt/remnasetup/data/docker/panel.env" .env
206 | cp "/opt/remnasetup/data/docker/panel-compose.yml" docker-compose.yml
207 |
208 | JWT_AUTH_SECRET=$(generate_64)
209 | JWT_API_TOKENS_SECRET=$(generate_64)
210 | METRICS_USER=$(generate_login)
211 | METRICS_PASS=$(generate_64)
212 | WEBHOOK_SECRET_HEADER=$(generate_64)
213 | DB_USER=$(generate_login)
214 | DB_PASSWORD=$(generate_24)
215 |
216 | export DB_USER
217 | export DB_PASSWORD
218 |
219 | sed -i "s|\$PANEL_DOMAIN|$PANEL_DOMAIN|g" .env
220 | sed -i "s|\$PANEL_PORT|$PANEL_PORT|g" .env
221 | sed -i "s|\$METRICS_USER|$METRICS_USER|g" .env
222 | sed -i "s|\$METRICS_PASS|$METRICS_PASS|g" .env
223 | sed -i "s|\$DB_USER|$DB_USER|g" .env
224 | sed -i "s|\$DB_PASSWORD|$DB_PASSWORD|g" .env
225 | sed -i "s|\$JWT_AUTH_SECRET|$JWT_AUTH_SECRET|g" .env
226 | sed -i "s|\$JWT_API_TOKENS_SECRET|$JWT_API_TOKENS_SECRET|g" .env
227 | sed -i "s|\$SUB_DOMAIN|$SUB_DOMAIN|g" .env
228 | sed -i "s|\$WEBHOOK_SECRET_HEADER|$WEBHOOK_SECRET_HEADER|g" .env
229 |
230 | sed -i "s|\$PANEL_PORT|$PANEL_PORT|g" docker-compose.yml
231 |
232 | docker compose up -d
233 | fi
234 |
235 | if [ "$REINSTALL_SUBSCRIPTION" = true ]; then
236 | info "$(get_string "install_full_installing_subscription_with_protection")"
237 | mkdir -p /opt/remnawave/subscription
238 | cd /opt/remnawave/subscription
239 |
240 | cp "/opt/remnasetup/data/app-config.json" app-config.json
241 | cp "/opt/remnasetup/data/docker/subscription-protection-compose.yml" docker-compose.yml
242 |
243 | sed -i "s|\$PANEL_PORT|$PANEL_PORT|g" docker-compose.yml
244 | sed -i "s|\$SUB_PORT|$SUB_PORT|g" docker-compose.yml
245 | sed -i "s|\$PROJECT_NAME|$PROJECT_NAME|g" docker-compose.yml
246 | sed -i "s|\$PROJECT_DESCRIPTION|$PROJECT_DESCRIPTION|g" docker-compose.yml
247 |
248 | docker compose up -d
249 | fi
250 |
251 | if [ "$REINSTALL_CADDY" = true ]; then
252 | info "$(get_string "install_full_installing_caddy_with_protection")"
253 | mkdir -p /opt/remnawave/caddy
254 | cd /opt/remnawave/caddy
255 |
256 | cp "/opt/remnasetup/data/caddy/caddyfile-protection" Caddyfile
257 | cp "/opt/remnasetup/data/docker/caddy-protection-compose.yml" docker-compose.yml
258 |
259 | sed -i "s|\$PANEL_PORT|$PANEL_PORT|g" Caddyfile
260 | sed -i "s|\$SUB_DOMAIN|$SUB_DOMAIN|g" Caddyfile
261 | sed -i "s|\$SUB_PORT|$SUB_PORT|g" Caddyfile
262 |
263 | sed -i "s|\$PANEL_DOMAIN|$PANEL_DOMAIN|g" docker-compose.yml
264 | sed -i "s|\$CUSTOM_LOGIN_ROUTE|$CUSTOM_LOGIN_ROUTE|g" docker-compose.yml
265 | sed -i "s|\$LOGIN_USERNAME|$LOGIN_USERNAME|g" docker-compose.yml
266 | sed -i "s|\$LOGIN_EMAIL|$LOGIN_EMAIL|g" docker-compose.yml
267 | sed -i "s|\$LOGIN_PASSWORD|$LOGIN_PASSWORD|g" docker-compose.yml
268 |
269 | docker compose up -d
270 | fi
271 | }
272 |
273 | check_docker() {
274 | if command -v docker >/dev/null 2>&1; then
275 | info "$(get_string "install_full_docker_already_installed")"
276 | return 0
277 | else
278 | return 1
279 | fi
280 | }
281 |
282 |
283 |
284 | show_panel_info() {
285 | echo ""
286 | echo -e "${MAGENTA}────────────────────────────────────────────────────────────${RESET}"
287 | echo -e "${BOLD_CYAN}$(get_string "install_full_panel_info_header")${RESET}"
288 | echo -e "${MAGENTA}────────────────────────────────────────────────────────────${RESET}"
289 |
290 | if [ "$NEED_PROTECTION" = "y" ]; then
291 | echo -e "${BOLD_GREEN}$(get_string "install_full_panel_url")${RESET} ${BLUE}https://${PANEL_DOMAIN}/${CUSTOM_LOGIN_ROUTE}${RESET}"
292 | else
293 | echo -e "${BOLD_GREEN}$(get_string "install_full_panel_url")${RESET} ${BLUE}https://${PANEL_DOMAIN}${RESET}"
294 | fi
295 |
296 | if [ "$NEED_PROTECTION" = "y" ]; then
297 | echo -e "${BOLD_GREEN}$(get_string "install_full_email")${RESET} ${YELLOW}${LOGIN_EMAIL}${RESET}"
298 | echo -e "${BOLD_GREEN}$(get_string "install_full_username")${RESET} ${YELLOW}${LOGIN_USERNAME}${RESET}"
299 | echo -e "${BOLD_GREEN}$(get_string "install_full_password")${RESET} ${YELLOW}${LOGIN_PASSWORD}${RESET}"
300 | fi
301 |
302 | echo -e "${MAGENTA}────────────────────────────────────────────────────────────${RESET}"
303 | echo ""
304 | }
305 |
306 | main() {
307 | check_component "panel" "/opt/remnawave" "/opt/remnawave/.env"
308 | check_component "subscription" "/opt/remnawave/subscription" "/opt/remnawave/subscription/.env"
309 | check_component "caddy" "/opt/remnawave/caddy" "/opt/remnawave/caddy/.env"
310 |
311 | if [ "$REINSTALL_PANEL" = false ] && [ "$REINSTALL_SUBSCRIPTION" = false ] && [ "$REINSTALL_CADDY" = false ]; then
312 | info "$(get_string "install_full_no_components")"
313 | read -n 1 -s -r -p "$(get_string "install_full_press_key")"
314 | exit 0
315 | fi
316 |
317 | while true; do
318 | question "$(get_string "install_full_need_protection")"
319 | NEED_PROTECTION="$REPLY"
320 | if [[ "$NEED_PROTECTION" == "y" || "$NEED_PROTECTION" == "Y" ]]; then
321 | break
322 | elif [[ "$NEED_PROTECTION" == "n" || "$NEED_PROTECTION" == "N" ]]; then
323 | break
324 | else
325 | warn "$(get_string "install_full_please_enter_yn")"
326 | fi
327 | done
328 |
329 | while true; do
330 | question "$(get_string "install_full_enter_panel_domain")"
331 | PANEL_DOMAIN="$REPLY"
332 | if [[ -n "$PANEL_DOMAIN" ]]; then
333 | break
334 | fi
335 | warn "$(get_string "install_full_domain_empty")"
336 | done
337 |
338 | while true; do
339 | question "$(get_string "install_full_enter_sub_domain")"
340 | SUB_DOMAIN="$REPLY"
341 | if [[ -n "$SUB_DOMAIN" ]]; then
342 | break
343 | fi
344 | warn "$(get_string "install_full_domain_empty")"
345 | done
346 |
347 | question "$(get_string "install_full_enter_panel_port")"
348 | PANEL_PORT="$REPLY"
349 | PANEL_PORT=${PANEL_PORT:-3000}
350 |
351 | question "$(get_string "install_full_enter_sub_port")"
352 | SUB_PORT="$REPLY"
353 | SUB_PORT=${SUB_PORT:-3010}
354 |
355 | while true; do
356 | question "$(get_string "install_full_enter_project_name")"
357 | PROJECT_NAME="$REPLY"
358 | if [[ -n "$PROJECT_NAME" ]]; then
359 | break
360 | fi
361 | warn "$(get_string "install_full_project_name_empty")"
362 | done
363 |
364 | while true; do
365 | question "$(get_string "install_full_enter_project_description")"
366 | PROJECT_DESCRIPTION="$REPLY"
367 | if [[ -n "$PROJECT_DESCRIPTION" ]]; then
368 | break
369 | fi
370 | warn "$(get_string "install_full_project_description_empty")"
371 | done
372 |
373 | if [ "$NEED_PROTECTION" = "y" ]; then
374 | while true; do
375 | question "$(get_string "install_full_enter_login_route")"
376 | CUSTOM_LOGIN_ROUTE="$REPLY"
377 | if [[ -n "$CUSTOM_LOGIN_ROUTE" ]]; then
378 | break
379 | fi
380 | warn "$(get_string "install_full_login_route_empty")"
381 | done
382 |
383 | while true; do
384 | question "$(get_string "install_full_enter_admin_login")"
385 | LOGIN_USERNAME="$REPLY"
386 | if [[ -n "$LOGIN_USERNAME" ]]; then
387 | break
388 | fi
389 | warn "$(get_string "install_full_admin_login_empty")"
390 | done
391 |
392 | while true; do
393 | question "$(get_string "install_full_enter_admin_email")"
394 | LOGIN_EMAIL="$REPLY"
395 | if [[ -n "$LOGIN_EMAIL" ]]; then
396 | break
397 | fi
398 | warn "$(get_string "install_full_admin_email_empty")"
399 | done
400 |
401 | while true; do
402 | question "$(get_string "install_full_enter_admin_password")"
403 | LOGIN_PASSWORD="$REPLY"
404 | if [[ ${#LOGIN_PASSWORD} -lt 8 ]]; then
405 | warn "$(get_string "install_full_password_short")"
406 | continue
407 | fi
408 | if ! [[ "$LOGIN_PASSWORD" =~ [A-Z] ]]; then
409 | warn "$(get_string "install_full_password_uppercase")"
410 | continue
411 | fi
412 | if ! [[ "$LOGIN_PASSWORD" =~ [a-z] ]]; then
413 | warn "$(get_string "install_full_password_lowercase")"
414 | continue
415 | fi
416 | if ! [[ "$LOGIN_PASSWORD" =~ [0-9] ]]; then
417 | warn "$(get_string "install_full_password_number")"
418 | continue
419 | fi
420 | if ! [[ "$LOGIN_PASSWORD" =~ [^a-zA-Z0-9] ]]; then
421 | warn "$(get_string "install_full_password_special")"
422 | continue
423 | fi
424 | break
425 | done
426 | fi
427 |
428 | if ! check_docker; then
429 | install_docker
430 | fi
431 | if [ "$NEED_PROTECTION" = "y" ]; then
432 | install_with_protection
433 | else
434 | install_without_protection
435 | fi
436 |
437 | success "$(get_string "install_full_complete")"
438 |
439 | show_panel_info
440 |
441 | read -n 1 -s -r -p "$(get_string "install_full_press_key")"
442 | exit 0
443 | }
444 |
445 | main
446 |
--------------------------------------------------------------------------------
/scripts/remnawave/install-panel.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | source "/opt/remnasetup/scripts/common/languages.sh"
4 | source "/opt/remnasetup/scripts/common/colors.sh"
5 | source "/opt/remnasetup/scripts/common/functions.sh"
6 |
7 | REINSTALL_PANEL=false
8 |
9 | check_component() {
10 | if [ -f "/opt/remnawave/docker-compose.yml" ] && (cd /opt/remnawave && docker compose ps -q | grep -q "remnawave\|remnawave-db\|remnawave-redis") || [ -f "/opt/remnawave/.env" ]; then
11 | info "$(get_string "install_panel_detected")"
12 | while true; do
13 | question "$(get_string "install_panel_reinstall")"
14 | REINSTALL="$REPLY"
15 | if [[ "$REINSTALL" == "y" || "$REINSTALL" == "Y" ]]; then
16 | warn "$(get_string "install_panel_stopping")"
17 | cd /opt/remnawave && docker compose down
18 | docker rmi remnawave/backend:latest postgres:17 valkey/valkey:8.0.2-alpine 2>/dev/null || true
19 | docker volume rm remnawave-db-data remnawave-redis-data 2>/dev/null || true
20 | rm -f /opt/remnawave/.env
21 | rm -f /opt/remnawave/docker-compose.yml
22 | REINSTALL_PANEL=true
23 | break
24 | elif [[ "$REINSTALL" == "n" || "$REINSTALL" == "N" ]]; then
25 | info "$(get_string "install_panel_reinstall_denied")"
26 | read -n 1 -s -r -p "$(get_string "install_panel_press_key")"
27 | exit 1
28 | else
29 | warn "$(get_string "install_panel_please_enter_yn")"
30 | fi
31 | done
32 | else
33 | REINSTALL_PANEL=true
34 | fi
35 | }
36 |
37 | install_docker() {
38 | if ! command -v docker &> /dev/null; then
39 | info "$(get_string "install_panel_installing_docker")"
40 | curl -fsSL https://get.docker.com -o get-docker.sh
41 | sh get-docker.sh
42 | rm get-docker.sh
43 | fi
44 | }
45 |
46 | generate_64() {
47 | openssl rand -hex 64
48 | }
49 |
50 | generate_24() {
51 | openssl rand -hex 24
52 | }
53 |
54 | generate_login() {
55 | tr -dc 'a-zA-Z' < /dev/urandom | head -c 15
56 | }
57 |
58 | install_panel() {
59 | if [ "$REINSTALL_PANEL" = true ]; then
60 | info "$(get_string "install_panel_installing")"
61 | mkdir -p /opt/remnawave
62 | cd /opt/remnawave
63 |
64 | cp "/opt/remnasetup/data/docker/panel.env" .env
65 | cp "/opt/remnasetup/data/docker/panel-compose.yml" docker-compose.yml
66 |
67 | JWT_AUTH_SECRET=$(generate_64)
68 | JWT_API_TOKENS_SECRET=$(generate_64)
69 | METRICS_USER=$(generate_login)
70 | METRICS_PASS=$(generate_64)
71 | WEBHOOK_SECRET_HEADER=$(generate_64)
72 | DB_USER=$(generate_login)
73 | DB_PASSWORD=$(generate_24)
74 |
75 | sed -i "s|\$PANEL_DOMAIN|$PANEL_DOMAIN|g" .env
76 | sed -i "s|\$PANEL_PORT|$PANEL_PORT|g" .env
77 | sed -i "s|\$DB_USER|$DB_USER|g" .env
78 | sed -i "s|\$DB_PASSWORD|$DB_PASSWORD|g" .env
79 | sed -i "s|\$JWT_AUTH_SECRET|$JWT_AUTH_SECRET|g" .env
80 | sed -i "s|\$JWT_API_TOKENS_SECRET|$JWT_API_TOKENS_SECRET|g" .env
81 | sed -i "s|\$SUB_DOMAIN|$SUB_DOMAIN|g" .env
82 | sed -i "s|\$METRICS_USER|$METRICS_USER|g" .env
83 | sed -i "s|\$METRICS_PASS|$METRICS_PASS|g" .env
84 | sed -i "s|\$WEBHOOK_SECRET_HEADER|$WEBHOOK_SECRET_HEADER|g" .env
85 |
86 | sed -i "s|\$PANEL_PORT|$PANEL_PORT|g" docker-compose.yml
87 |
88 | docker compose up -d
89 | fi
90 | }
91 |
92 | check_docker() {
93 | if command -v docker >/dev/null 2>&1; then
94 | info "$(get_string "install_panel_docker_installed")"
95 | return 0
96 | else
97 | return 1
98 | fi
99 | }
100 |
101 | main() {
102 | check_component
103 |
104 | while true; do
105 | question "$(get_string "install_panel_enter_panel_domain")"
106 | PANEL_DOMAIN="$REPLY"
107 | if [[ -n "$PANEL_DOMAIN" ]]; then
108 | break
109 | fi
110 | warn "$(get_string "install_panel_domain_empty")"
111 | done
112 |
113 | while true; do
114 | question "$(get_string "install_panel_enter_sub_domain")"
115 | SUB_DOMAIN="$REPLY"
116 | if [[ -n "$SUB_DOMAIN" ]]; then
117 | break
118 | fi
119 | warn "$(get_string "install_panel_domain_empty")"
120 | done
121 |
122 | question "$(get_string "install_panel_enter_panel_port")"
123 | PANEL_PORT="$REPLY"
124 | PANEL_PORT=${PANEL_PORT:-3000}
125 |
126 | if ! check_docker; then
127 | install_docker
128 | fi
129 | install_panel
130 |
131 | success "$(get_string "install_panel_complete")"
132 | read -n 1 -s -r -p "$(get_string "install_panel_press_key")"
133 | exit 0
134 | }
135 |
136 | main
137 |
--------------------------------------------------------------------------------
/scripts/remnawave/install-subscription.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | source "/opt/remnasetup/scripts/common/colors.sh"
4 | source "/opt/remnasetup/scripts/common/functions.sh"
5 | source "/opt/remnasetup/scripts/common/languages.sh"
6 |
7 | REINSTALL_SUBSCRIPTION=false
8 |
9 | check_component() {
10 | if [ -f "/opt/remnawave/subscription/docker-compose.yml" ] && (cd /opt/remnawave/subscription && docker compose ps -q | grep -q "remnawave-subscription-page") || [ -f "/opt/remnawave/subscription/app-config.json" ]; then
11 | info "$(get_string install_subscription_detected)"
12 | while true; do
13 | question "$(get_string install_subscription_reinstall)"
14 | REINSTALL="$REPLY"
15 | if [[ "$REINSTALL" == "y" || "$REINSTALL" == "Y" ]]; then
16 | warn "$(get_string install_subscription_stopping)"
17 | cd /opt/remnawave/subscription && docker compose down
18 | docker rmi remnawave/subscription-page:latest 2>/dev/null || true
19 | rm -f /opt/remnawave/subscription/app-config.json
20 | rm -f /opt/remnawave/subscription/docker-compose.yml
21 | REINSTALL_SUBSCRIPTION=true
22 | break
23 | elif [[ "$REINSTALL" == "n" || "$REINSTALL" == "N" ]]; then
24 | info "$(get_string install_subscription_reinstall_denied)"
25 | read -n 1 -s -r -p "$(get_string install_subscription_press_key)"
26 | exit 0
27 | else
28 | warn "$(get_string install_subscription_please_enter_yn)"
29 | fi
30 | done
31 | else
32 | REINSTALL_SUBSCRIPTION=true
33 | fi
34 | }
35 |
36 | install_docker() {
37 | if ! command -v docker &> /dev/null; then
38 | info "$(get_string install_subscription_installing_docker)"
39 | sudo curl -fsSL https://get.docker.com | sh
40 | fi
41 | }
42 |
43 | install_subscription() {
44 | if [ "$REINSTALL_SUBSCRIPTION" = true ]; then
45 | info "$(get_string install_subscription_installing)"
46 | mkdir -p /opt/remnawave/subscription
47 | cd /opt/remnawave/subscription
48 |
49 | cp "/opt/remnasetup/data/app-config.json" app-config.json
50 | cp "/opt/remnasetup/data/docker/subscription-compose.yml" docker-compose.yml
51 |
52 | sed -i "s|\$PANEL_DOMAIN|$PANEL_DOMAIN|g" docker-compose.yml
53 | sed -i "s|\$SUB_PORT|$SUB_PORT|g" docker-compose.yml
54 | sed -i "s|\$PROJECT_NAME|$PROJECT_NAME|g" docker-compose.yml
55 | sed -i "s|\$PROJECT_DESCRIPTION|$PROJECT_DESCRIPTION|g" docker-compose.yml
56 |
57 | cd /opt/remnawave
58 | if [ -f ".env" ]; then
59 | sed -i "s|SUB_DOMAIN=.*|SUB_DOMAIN=$SUB_DOMAIN|g" .env
60 | fi
61 |
62 | docker compose down && docker compose up -d
63 | cd /opt/remnawave/subscription
64 | docker compose down && docker compose up -d
65 | fi
66 | }
67 |
68 | check_docker() {
69 | if command -v docker >/dev/null 2>&1; then
70 | info "$(get_string install_subscription_docker_installed)"
71 | return 0
72 | else
73 | return 1
74 | fi
75 | }
76 |
77 | main() {
78 | check_component
79 |
80 | while true; do
81 | question "$(get_string install_subscription_enter_panel_domain)"
82 | PANEL_DOMAIN="$REPLY"
83 | if [[ -n "$PANEL_DOMAIN" ]]; then
84 | break
85 | fi
86 | warn "$(get_string install_subscription_domain_empty)"
87 | done
88 |
89 | while true; do
90 | question "$(get_string install_subscription_enter_sub_domain)"
91 | SUB_DOMAIN="$REPLY"
92 | if [[ -n "$SUB_DOMAIN" ]]; then
93 | break
94 | fi
95 | warn "$(get_string install_subscription_domain_empty)"
96 | done
97 |
98 | question "$(get_string install_subscription_enter_sub_port)"
99 | SUB_PORT="$REPLY"
100 | SUB_PORT=${SUB_PORT:-3010}
101 |
102 | while true; do
103 | question "$(get_string install_subscription_enter_project_name)"
104 | PROJECT_NAME="$REPLY"
105 | if [[ -n "$PROJECT_NAME" ]]; then
106 | break
107 | fi
108 | warn "$(get_string install_subscription_project_name_empty)"
109 | done
110 |
111 | while true; do
112 | question "$(get_string install_subscription_enter_project_description)"
113 | PROJECT_DESCRIPTION="$REPLY"
114 | if [[ -n "$PROJECT_DESCRIPTION" ]]; then
115 | break
116 | fi
117 | warn "$(get_string install_subscription_project_description_empty)"
118 | done
119 |
120 | if ! check_docker; then
121 | install_docker
122 | fi
123 | install_subscription
124 |
125 | success "$(get_string install_subscription_complete)"
126 | read -n 1 -s -r -p "$(get_string install_subscription_press_key)"
127 | exit 0
128 | }
129 |
130 | main
131 |
--------------------------------------------------------------------------------
/scripts/remnawave/update-full.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | source "/opt/remnasetup/scripts/common/colors.sh"
4 | source "/opt/remnasetup/scripts/common/functions.sh"
5 | source "/opt/remnasetup/scripts/common/languages.sh"
6 |
7 | update_all() {
8 | info "$(get_string update_full_updating)"
9 |
10 | cd /opt/remnawave
11 | docker compose pull
12 | docker compose up -d
13 |
14 | cd /opt/remnawave/subscription
15 | docker compose pull
16 | docker compose up -d
17 |
18 | success "$(get_string update_full_complete)"
19 | }
20 |
21 | main() {
22 | update_all
23 | read -n 1 -s -r -p "$(get_string update_full_press_key)"
24 | exit 0
25 | }
26 |
27 | main
28 |
--------------------------------------------------------------------------------
/scripts/remnawave/update-panel.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | source "/opt/remnasetup/scripts/common/colors.sh"
4 | source "/opt/remnasetup/scripts/common/functions.sh"
5 | source "/opt/remnasetup/scripts/common/languages.sh"
6 |
7 | update_panel() {
8 | info "$(get_string update_panel_updating)"
9 | cd /opt/remnawave
10 | docker compose pull
11 | docker compose up -d
12 | success "$(get_string update_panel_complete)"
13 | }
14 |
15 | main() {
16 | update_panel
17 | read -n 1 -s -r -p "$(get_string update_panel_press_key)"
18 | exit 0
19 | }
20 |
21 | main
22 |
--------------------------------------------------------------------------------
/scripts/remnawave/update-subscription.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | source "/opt/remnasetup/scripts/common/colors.sh"
4 | source "/opt/remnasetup/scripts/common/functions.sh"
5 | source "/opt/remnasetup/scripts/common/languages.sh"
6 |
7 | update_subscription() {
8 | info "$(get_string update_subscription_updating)"
9 | cd /opt/remnawave/subscription
10 | docker compose pull
11 | docker compose up -d
12 | success "$(get_string update_subscription_complete)"
13 | }
14 |
15 | main() {
16 | update_subscription
17 | read -n 1 -s -r -p "$(get_string update_subscription_press_key)"
18 | exit 0
19 | }
20 |
21 | main
22 |
--------------------------------------------------------------------------------