Compare commits

..
5 Commits
Author SHA1 Message Date
redlandClaude Fable 5 cbaa9b38b9 Убран VK-app ID оригинального автора из шаблона конфигурации
В config.php оставался vk_id прежнего владельца панели (7611069) и включённая
VK-авторизация. Перед публикацией репозитория заменено на пустой плейсхолдер,
vk_stat выключен: свой ID вписывается в админке.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-01 05:46:02 +00:00
redlandClaude Fable 5 b6c93514e3 HostinPL 5.6 указан в заголовке каждого документа и в описании репозитория
Из названий было не видно, какая панель лежит в основе. Теперь версия стоит
в H1 всех документов и галерей, а в начале README (обоих) добавлена строка об
оригинале: HostinPL 5.6, авторы Samir Shelenko и Alexander Zemlyanoy, писалась
под Debian 9 / PHP 7.0. Нумерация версии оставлена от оригинала.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-01 05:42:58 +00:00
redlandClaude Fable 5 2e0eacbcea Перевод панели доведён до конца, скриншоты всех разделов, английское описание главным
Перевод
* Полный обход исходников скриптом, повторяющим логику подстановки: было
  видно 681 строку, которая осталась бы русской. Переведены все — словарь
  вырос с 640 до 1304 записей.
* Покрыты разделы, которых раньше не было в проверке: консоль сервера с
  описанием всех RCON-команд, планировщик задач, файрвол, базы MySQL, FTP,
  автоустановка модов, совладельцы, статистика сервера, страница аккаунта,
  сообщения планировщика и все письма.
* Убран ключ-предлог 'в' => 'at': он подменялся ВНУТРИ ещё не переведённых
  предложений и давал «at поле имя пользователя». Служебные слова переводятся
  только вместе с фразой.
* Формат даты 'd.m.Y в H:i' содержал русское «в» между датой и временем —
  теперь проходит через t() (31 вызов date() в 20 файлах), в английском
  режиме получается 30.07.2026 14:22.
* Абзацы FAQ разбиты по строкам вёрстки, атрибуты placeholder и tooltip
  добавлены отдельными ключами — раньше они не попадали в разбор.
* Живой обход 45 страниц в английском режиме: русскими остались 4 места, и
  все намеренно — подпись «Русский» у переключателя, коды языков игрового
  сервера в FAQ и регулярное выражение проверки имени.

Скриншоты
* docs/screenshots/{ru,en} — по 38 кадров: вход, регистрация, восстановление
  пароля, кабинет, заказ сервера, новости, статус, тикеты, FAQ, веб-хостинг и
  вся админка вместе со скрытыми вкладками настроек и переключателем капчи.
* docs/screenshots/README.md и README.ru.md — галереи, где каждый раздел
  показан на двух языках рядом.

Документация
* README.md теперь английский (главное описание), русский переехал в
  README.ru.md; в начале каждого — кнопка на другой язык.
* Добавлен раздел о доступных играх: 11 игр каталога с кодами,
  query-протоколами, диапазонами слотов и портов, плюс список ядер, которые
  клиент переключает в один клик.
* Добавлен раздел планов: больше игр по просьбам людей и современные
  платёжные системы — PayPal, Stripe, Lava.

Поддерживается и развивается с помощью REDL.IO.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-30 03:48:33 +00:00
redl e0b96d3501 Двуязычный интерфейс (RU/EN) с автоопределением + документация на двух языках
Панель:
- engine/main/lang.php: класс Lang, автоопределение языка (?lang= -> cookie -> Accept-Language -> конфиг)
- перевод применяется к готовому ответу через ob_start(): покрывает всю панель, админку и письма,
  не требуя правки 200 файлов шаблонов; отсутствующая фраза остаётся русской
- замена только на границах слов, иначе короткий ключ портил длинные слова (Модуль -> Modуль)
- AJAX-ответы переводятся отдельно (json_encode экранирует кириллицу в \uXXXX)
- application/lang/en.php: 657 переводов; ru.php как точка расширения
- переключатель RU/EN в шапке кабинета, админки и в подвале страницы входа
- 'lang' в config.php — язык по умолчанию
- проверено обходом 20 разделов: 0 непереведённых фраз, 0 мешанины языков, 0 фаталов

Документация — теперь на русском и английском:
- README.en.md, CHANGES.en.md, SECURITY.en.md, GUIDE.en.md
- переключатели языка в начале каждого документа
- раздел «Язык интерфейса» в инструкции: как работает, как добавить свой язык
2026-07-30 02:25:34 +00:00
redl 6ee2744e3a Сборка REDL: панель на PHP 8, капча-тумблер, закрытые уязвимости, установщики в один клик
- панель работает на PHP 8.4 (оригинал под PHP 7.0): elFinder utf8_encode, apache_get_modules, warnings
- капча выключена и управляется из админки (флаг captcha_enable + блок настроек)
- закрыты две SQL-инъекции без авторизации (createAuthLog: пароль из POST и CF-Connecting-IP)
- убрано хранение паролей в открытом виде в authlog, XSS в поле ref
- установщики install-panel.sh и install-node.sh (оригинальный затирал sources.list репозиториями Debian 9)
- современный Dockerfile (оригинальный на debian:stretch больше не собирается)
- планировщик и автозапуск работают без systemd
- брендинг REDL.IO, год 2026, ссылки на redl.io
- документация на русском: ИЗМЕНЕНИЯ, БЕЗОПАСНОСТЬ, ИНСТРУКЦИЯ
2026-07-30 02:00:01 +00:00
612 changed files with 357281 additions and 4172 deletions
+8 -11
View File
@@ -1,11 +1,8 @@
# Anything holding a licence key or a password
server.cfg
secrets.cfg
*.env
# Runtime state
cache/
logs/
*.log
server/
server-data/
# загрузки пользователей и временные файлы панели
panel/tmp/avatar/*
panel/tmp/mods/*
panel/tmp/tickets_img/*
!panel/tmp/**/.gitkeep
# локальные креды — никогда не коммитим
*.credentials
.redl-*
+248
View File
@@ -0,0 +1,248 @@
[Русский](ИЗМЕНЕНИЯ.md) · **English**
# HostinPL 5.6 · What changed compared to the original
Original: **HostinPL 5.6** as a "nulled" build (the `Xopowblu-4EJlOBEK/HostinPL-5.6` fork),
written for Debian 9 and PHP 7.0.
Below is everything we changed, with file names and line numbers. Panel code changes live in `panel/`.
**Maintained and developed with [REDL.IO](https://redl.io) — AI-powered hosting.**
---
## 1. The panel now runs on modern PHP
The original targets PHP 7.0. On PHP 8 parts of it died outright. It now runs on **PHP 8.4**:
walking all 21 sections (home, servers, news, status, tickets, web hosting and the whole admin area)
returns **200 on every one and zero errors in the log**.
| File | Before | After |
|------|--------|-------|
| `engine/engine_ftp/elFinder.class.php:4497` | `utf8_encode()` — removed in PHP 8.2, the file manager died with a fatal error | Wrapped in `function_exists()`, falls back to `mb_convert_encoding($str,'UTF-8','ISO-8859-1')` |
| `application/views/admin/checksys/index.php:22` | `apache_get_modules()` — only exists under Apache with mod_php, so the "System check" page returned 500 under nginx | Wrapped in `function_exists()`, otherwise checked via `REQUEST_URI` |
| `application/views/admin/index.php:215` | `$item['invoice_ammount']` used after the `foreach`, producing an Undefined variable when there were no invoices | `isset()` check |
| `application/models/users.php:199-206` | `$city[1]`, `$country[1]`, `$countryCode[1]` used without checking that the regex matched | `isset()` checks |
| `application/controllers/common/loginheader.php:35` | `$_GET['ref']` without `isset` | `isset()` plus a cast to `(int)` |
### What turned out to be a false alarm
The bundled **phpseclib 1.x** uses `create_function()`, which PHP 8 removed. It looked like a blocker,
but checking showed that **phpseclib is not included anywhere in the panel** — it is dead code.
Communication with game nodes goes through the native `php-ssh2` extension
(`engine/libs/ssh2.php``ssh2_exec`). Nothing needed fixing.
`get_magic_quotes_gpc()` in `elFinderConnector.class.php:320` is equally harmless: it sits behind
`version_compare(PHP_VERSION,'5.4','<') && ...`, so on PHP 8 it is never reached.
---
## 2. The captcha is off and managed from the admin area
The captcha used to be hard-wired: without valid Google keys **it was impossible to log in**,
and the form displayed "ERROR for site owner: Invalid site key". There is now a `captcha_enable`
flag in `application/config.php`, defaulting to `0` (off).
**Backend.** All four validators received the line
`if($this->config->captcha_enable != '1') return $result;` before the captcha check:
* `application/controllers/account/login.php` — login
* `application/controllers/common/loginheader.php` — registration and the contact form (2 places)
* `application/controllers/tickets/create.php` — ticket creation
**Markup.** The five captcha widgets (4 in `views/common/loginheader.php`, 1 in
`views/tickets/create.php`) are wrapped in `<?php if(@$captcha_enable == '1'): ?>`. Google's `api.js`
is only loaded when the captcha is on. Every `grecaptcha.reset(...)` call became
`window.grecaptcha && grecaptcha.reset(...)` — otherwise, with the captcha off, JavaScript threw
inside the error handlers and the forms stopped responding.
**Admin area.** `views/admin/settings.php`, the "Other settings" tab, gained a
"Bot protection (reCAPTCHA v2)" block: an Off/On selector plus Site key and Secret key fields.
Verified both ways: with the captcha off, login and registration succeed and the user is really
created in the database; with it on, a fresh session gets "Confirm that you are not a robot!"
and the widget returns to the page.
> **Careful when adding your own settings.** The settings writer matches configuration lines
> **by substring** (`strpos`). That is why the flag is called `captcha_enable` and not `captcha`:
> the string `captcha` occurs inside `recaptcha` and `secret_recaptcha`, and saving would have
> overwritten the wrong parameter. New keys must not be substrings of existing ones.
---
## 3. Vulnerabilities fixed
In brief (full detail with code in [SECURITY.en.md](SECURITY.en.md)):
* **Two pre-authentication SQL injections** in `application/models/users.php` (`createAuthLog()`):
the login journal received the password from the form and the IP from the `CF-Connecting-IP`
header — which was not validated at all — without escaping. Every value now goes through
`$this->db->escape()` or a cast to `(int)`, and the header is validated with
`filter_var(..., FILTER_VALIDATE_IP)`.
* **Plaintext passwords**: `login.php` wrote the real password into the `authlog` table on every
login attempt, including failed ones. Removed.
* **XSS** in the hidden `ref` field of the registration form — `htmlspecialchars()` added.
* The request to the `ip-api.com` geolocation service now only runs for a valid IP and uses `urlencode()`.
---
## 4. The installer was rewritten from scratch
The original `install` was dangerous on any current system:
* `echo "deb ... stretch main" > /etc/apt/sources.list`**wiped the repository list** and replaced
it with Debian 9, after which apt was broken
* installed `php7.0` and hard-coded edits to `/etc/php/7.0/apache2/php.ini`
* switched MariaDB to `bind-address = 0.0.0.0` without a word of warning
* only ran when `/etc/issue.net` said `Debian9`, and otherwise refused to start
* generated passwords and tokens but saved them nowhere — you could only copy them off the screen
* carried on after any failure: every command ended in `> /dev/null 2>&1`
The new scripts:
**`install-panel.sh`** — nginx, PHP 8.x (version detected automatically), MariaDB, a database with a
random password, configuration, scheduler, autostart watchdog, administrator creation, and a final
check that the login page really is served with its form. Idempotent: running it again does not wipe
a database that is already loaded. Credentials are written to `/root/.redl-panel-credentials`
(chmod 600). With `set -euo pipefail` the script stops on error instead of pretending all is well.
**`install-node.sh`** — Docker from the official repository, image build, the
`/home/cp/gameservers/files` layout, the `gameservers` group, MariaDB for game server databases,
SteamCMD, ProFTPD, and sshd configuration that rolls back if `sshd -t` fails. **Before doing anything
it checks whether Docker can work on this machine at all** (`unshare -Ur`) and says so plainly if it
cannot. At the end it prints ready-to-use location details and firewall commands.
Neither script touches `/etc/apt/sources.list`.
---
## 5. The game server image was rebuilt
The original `docker/Dockerfile.original-stretch` is based on `debian:stretch`, and the Debian 9
repositories were shut down in 2023 — **that image no longer builds**, `apt-get update` inside it fails.
The new `docker/Dockerfile` is based on Debian 12 (bookworm) but **must still be tagged
`debian:stretch`**: that name is hard-coded in the panel
(`application/models/servers.php:642`, `docker create ... debian:stretch`) and cannot be changed
without editing the panel.
What is inside: 32-bit libraries (SA-MP, CRMP, MTA and older CS builds are i386), `screen` for
consoles, Java for Minecraft, Node.js 20 for RAGE:MP, and `gdb` for crash analysis.
Node.js comes from NodeSource **over HTTPS with repository key verification**, unlike before.
---
## 6. Working without systemd
The panel assumes systemd is present. Container VPSes do not have it, so watchdogs were added on
cron: `/usr/local/bin/hostinpl-guard` (panel) and `/usr/local/bin/gamenode-guard` (node).
Once a minute (every 2 minutes on a node) they check MariaDB, PHP-FPM, nginx, Docker and cron and
restart whatever died, plus an `@reboot` job to bring everything up after a restart.
Panel liveness is determined by an **HTTP request** to the login page rather than by searching for a
process name — a pattern search would have matched the watchdog's own command line.
---
## 7. Scheduler
The original pointed its 9 jobs at the panel's public domain. They now go to `127.0.0.1`
(independent of DNS and external reachability) and carry `-m` timeouts so that a hung request does
not pile up processes.
> **Token pitfall.** You cannot extract the scheduler token from the config with a plain
> `grep "'token'"` — the line `'yk_password1' => 'token'` matches the same pattern, two values end
> up in the URL separated by a newline, and the jobs then fail silently. The installer writes the
> token directly when it generates the configuration.
---
## 8. Interface languages: Russian and English
The original was Russian-only, with every string hard-coded in the templates. The build now has a
translation layer:
* `engine/main/lang.php` — the `Lang` class and the global `t()` helper
* `application/lang/ru.php`, `application/lang/en.php` — dictionaries
* Language selection order: `?lang=` parameter → `lang` cookie → **the browser's `Accept-Language`
header** → panel default
* A switcher (RU / EN) in the header of the client area and on the login page
* Anything not yet translated falls back to the original Russian text, so nothing can disappear
from the interface
Translation is applied to the finished response via `ob_start()`, so none of the 200 template files
had to be rewritten and the coverage extends to the admin area and the e-mails at once. Replacement
only happens on word boundaries — without that a short key corrupts longer words ("Мод" turned
"Модуль" into "Modуль").
### Translation coverage
The dictionary holds **1304 phrases** — everything the panel shows a human: the client area,
ordering a server, server management (the console with every RCON command described, FTP, MySQL
databases, the firewall, the task scheduler, mod auto-install), tickets, the whole FAQ, the entire
admin area, the e-mails and the scheduler's status messages.
Coverage is not eyeballed: a script replays the replacement logic against the sources and lists
what would stay Russian. By that measure **four** items are uncovered, and all four should be:
* "Русский" — the label of the language switcher itself;
* `rus => Pyccĸий` and `ukr => Українська` in the FAQ — these are game server language codes, not UI;
* the name validation regular expression `/^([А-ЯЁ])([а-яё]{1,15})$/u` — code, not text.
Two traps that produced mixed-language output were fixed separately:
* the preposition "в" was its own dictionary entry and got substituted inside sentences that were not
translated yet, producing "at поле имя пользователя". Function words like that are only translated
together with the phrase they belong to;
* the date format `d.m.Y в H:i` contains the Russian "в" between the date and the time. It now goes
through `t()` (31 `date()` calls across 20 files), so English mode renders `30.07.2026 14:22`.
Verified by a live crawl of 45 pages in English mode, including the creation forms and the hidden
settings tabs. Details: see [GUIDE.en.md](GUIDE.en.md), the "Interface language" section.
---
## 9. Branding and dates
* The year in footers: `2020©``2026©` (`views/common/footer.php`, `views/common/loginheader.php`)
* Name and description are REDL.IO: `application/config.php` (`description`, `keywords`,
`mail_sender`, `mail_from`), footers, the "System check" page
* Links to the previous build owner's sites (`hostinpl.ru`, `osmp.ga`) replaced with `redl.io`
* "VK community" links pointing at somebody else's community replaced with `redl.io`
(`footer.php`, `views/main/index.php`, `views/offline/index.php`)
* All 15 e-mail templates: a "REDL.IO / AI-powered hosting" header and the signature
"Sincerely, the REDL.IO team"
**The authors' copyright headers were not removed** — they remain in every file that had them.
---
## 10. phpMyAdmin
The panel links to `/phpmyadmin` from the admin area. The original installer set phpMyAdmin up
through Apache, which did not work under nginx. It is now installed from the distribution repository
and served by nginx itself.
> **Reverse proxy pitfall.** For the address `/phpmyadmin` (without a trailing slash) nginx replies
> with a redirect and by default puts **its own port** into it, for example
> `http://panel.example.com:8095/phpmyadmin/`. If the panel sits behind a reverse proxy that address
> is unreachable from outside and the admin link does not open. The cure is
> `absolute_redirect off; port_in_redirect off;`, already present in the configuration the installer
> generates.
---
## What we did NOT do
* We did not rewrite password hashing. The panel stores passwords as **unsalted MD5**
(`md5($password)`, a `varchar(32)` column). Moving to `password_hash()` affects login,
registration, recovery and password changes, and requires migrating existing users.
* We did not move game server creation away from Docker.
* We did not delete `panel/application/public/js/proxy/proxy.php` — the file is defanged but kept as
evidence (see [SECURITY.en.md](SECURITY.en.md)).
* We did not test the payment gateways with real payments, nor VK login.
* We did not verify the game servers themselves: that needs a node with Docker.
---
**Maintained and developed with [REDL.IO](https://redl.io) — AI-powered hosting.**
+411
View File
@@ -0,0 +1,411 @@
[Русский](ИНСТРУКЦИЯ.md) · **English**
# HostinPL 5.6 · Complete guide
From an empty server to a hosting service that can take clients.
**Maintained and developed with [REDL.IO](https://redl.io) — AI-powered hosting.**
Contents:
1. [How it all fits together](#1-how-it-all-fits-together)
2. [Installing the panel](#2-installing-the-panel)
3. [Installing a game node](#3-installing-a-game-node)
4. [Connecting the node to the panel](#4-connecting-the-node-to-the-panel)
5. [Game server files](#5-game-server-files)
6. [Configuring the panel](#6-configuring-the-panel)
7. [Interface language](#7-interface-language)
8. [Captcha](#8-captcha)
9. [HTTPS and domain](#9-https-and-domain)
10. [Maintenance](#10-maintenance)
11. [Troubleshooting](#11-troubleshooting)
---
## 1. How it all fits together
Two roles, usually on separate machines:
**The panel** — the website, the database, client areas, tickets, payments. It does not need Docker
and runs happily on a cheap VPS.
**A game node (location)** — where the game servers themselves run. The panel connects to it
**over SSH** and creates a separate Docker container for every ordered server:
```
docker create --tty --rm --name=gs<ID> --network=host \
--cpus="<cores>" --memory=<RAM>M \
--volume="/home/gs<ID>/:/home/container/" \
--workdir=/home/container debian:stretch
```
Hence two hard requirements for a node: **working Docker** and **a local image tagged
`debian:stretch`** (the name is hard-coded in the panel). Server files live on the node in
`/home/gs<ID>` and appear inside the container as `/home/container`.
You can have several nodes — they are added to the panel as separate locations, and the client
picks one when ordering.
> **Important about nodes.** Docker does not start inside container-based VPSes (LXC, OpenVZ and
> similar) because the kernel forbids nested namespaces. A node needs **a dedicated server or KVM**.
> The installer checks this first and warns you.
---
## 2. Installing the panel
Ubuntu 24.04 / 22.04 or Debian 12 / 13. Minimum: 1 core, 1 GB RAM, 10 GB disk.
```bash
apt-get update && apt-get install -y git
git clone https://github.com/RedlHosting/redl-gamepanel.git
cd redl-gamepanel
sudo bash install-panel.sh
```
The script asks three things:
* **the panel's domain or IP** — it goes into the configuration as the base address, and all links
and e-mails are built from it;
* **the administrator e-mail** — also the login;
* **the administrator password** — at least 6 characters.
Everything after that is automatic: packages, a database with a random password, the schema
(27 tables), configuration, permissions, PHP, nginx, the scheduler, an autostart watchdog, an
administrator with full access, and a final check that the login page opens.
It takes 25 minutes. The panel address and login are printed at the end.
**A non-standard port** (if 80 is taken, or the panel sits behind a reverse proxy):
```bash
sudo PORT=8095 bash install-panel.sh
```
**Where things end up:**
| What | Path |
|------|------|
| Panel code | `/var/www/hostinpl` |
| Configuration | `/var/www/hostinpl/application/config.php` |
| Database password and scheduler token | `/root/.redl-panel-credentials` |
| nginx configuration | `/etc/nginx/sites-available/hostinpl` |
| Scheduler | `/etc/cron.d/hostinpl` |
| Autostart watchdog | `/usr/local/bin/hostinpl-guard` |
| Database | MariaDB, database `hostin` |
**phpMyAdmin** (optional; the panel links to it from the admin area):
```bash
sudo apt-get install -y phpmyadmin # do NOT let it configure a web server, decline
sudo ln -sfn /usr/share/phpmyadmin /var/www/hostinpl/phpmyadmin
```
Log in with the database user from `/root/.redl-panel-credentials`. Remember that phpMyAdmin will be
reachable by anyone who knows the address: either restrict it by IP in nginx, or do not install it.
---
## 3. Installing a game node
You need **a dedicated server or KVM** (not LXC/OpenVZ). Minimum: 2 cores, 2 GB RAM, 100 GB disk,
counting 12 GB per game server.
```bash
apt-get update && apt-get install -y git
git clone https://github.com/RedlHosting/redl-gamepanel.git
cd redl-gamepanel
sudo bash install-node.sh
```
The script asks:
* **this node's IP** — how the panel will reach it;
* **whether to allow root SSH login with a password** — the panel can only connect with a login and
password, and its commands require root. Answer `yes` and the script generates a strong root
password and shows it at the end.
What it does: checks that the machine is suitable, installs Docker from the official repository,
builds the `debian:stretch` image (Debian 12 inside, plus 32-bit libraries, Java, Node.js and
screen), creates `/home/cp/gameservers/files` and the `gameservers` group, installs MariaDB for game
server databases, SteamCMD and ProFTPD, configures sshd, and installs a Docker watchdog.
The first run takes 515 minutes, mostly building the image.
**Close the ports immediately afterwards.** The script prints ready-made commands; substitute the
panel's address:
```bash
ufw allow from PANEL_IP to any port 22 proto tcp
ufw allow from PANEL_IP to any port 3306 proto tcp
ufw deny 3306
# leave the game ports open: 7777 (SA-MP), 22005 (RAGE:MP), 25565 (Minecraft) and so on
```
If you also connect to the node over SSH yourself, **allow your own IP first** or you will lock
yourself out:
```bash
ufw allow from YOUR_IP to any port 22 proto tcp
```
---
## 4. Connecting the node to the panel
1. Sign in to the panel as an administrator
2. Go to **Admin → Locations → Add location**
3. Fill in:
| Field | What to enter |
|-------|---------------|
| Name | Anything meaningful: "Frankfurt", "Helsinki" |
| IP | The node's IP |
| User | `root` |
| Password | The root password printed by `install-node.sh` |
| Games | Tick the games available on this node |
4. Save and open **Admin → Locations** — if the panel connected, the location shows its real cores,
RAM and disk (refreshed hourly by the scheduler).
If it does not connect, check from the panel machine:
```bash
ssh root@NODE_IP # does the password work?
php -m | grep ssh2 # is the php-ssh2 extension installed?
```
If the extension is missing: `apt-get install -y php-ssh2`, then restart PHP-FPM.
---
## 5. Game server files
**They are not included.** The original installer downloaded them from third-party sites over plain
HTTP with no signature checking, so we removed that (details in [SECURITY.en.md](SECURITY.en.md)).
Lay the files out on the node like this:
```
/home/cp/gameservers/files/<game_code>/
```
Game codes are listed under **Admin → Games** (for example `samp`, `crmp`, `mta`, `minecraft`,
`cs`, `css`, `ragemp`). When a server is created, the panel copies the contents of that directory
into `/home/gs<ID>/`.
Only take builds from primary sources: SA-MP from `sa-mp.com`, MTA from `mtasa.com`,
Minecraft (Paper) from `papermc.io`, RAGE:MP from `rage.mp`, Steam games via SteamCMD
(already installed in `/root/steamcmd`).
Then test the whole chain: order a server as a test client and confirm that it is created and starts.
---
## 6. Configuring the panel
**Admin → Settings**, four tabs:
* **General settings** — name, description, contacts, logo
* **Payment gateways** — Unitpay, Enot, AnyPay, LiteKassa, RoboKassa, FreeKassa, YooKassa, QIWI.
You need your own merchant accounts. We never tested a gateway with a real payment — test each one
with your own money before taking clients.
* **Other settings** — trial period, e-mail confirmation, **captcha**, maintenance mode, VK login
* **Information and bonuses** — top-up bonuses, referral percentages
Plans and games live under **Admin → Games** (versions, RAM and core limits, prices).
> When you save settings the panel rewrites `config.php` entirely. It matches configuration lines
> **by substring**, so do not create parameters whose names are contained in other names.
> After editing the file by hand, restart PHP-FPM **fully** (`restart`, not `reload`) — otherwise a
> cached copy is served.
---
## 7. Interface language
The panel speaks **Russian and English**. The language is chosen in this order:
1. `?lang=en` or `?lang=ru` in the URL — an explicit choice, remembered in a cookie for a year
2. the `lang` cookie — the previous choice
3. **the browser's `Accept-Language` header** — automatic detection
4. the `lang` value in `application/config.php` (default `ru`)
Automatic detection deliberately keeps Russian for Russian-speaking locales (ru, uk, be, kk and
others) and switches to English for everything else, so a visitor from Germany or Brazil gets the
English interface without touching anything.
**The switcher** sits in the top bar of the client area and the admin area (the `RU` / `EN` buttons
next to the balance) and in the footer of the login page.
**How it is built.** The panel was written with Russian text directly in the templates — about
1500 strings across 200 files. Instead of rewriting all of them, translation happens on the finished
response: `index.php` wraps the output in `ob_start()`, and before the page is sent to the browser
the strings are replaced using a dictionary (`engine/main/lang.php`). This covers the whole panel at
once, including the admin area and e-mails, and anything missing from the dictionary simply stays
Russian — nothing can vanish from the screen.
Replacement only happens on word boundaries, so a short dictionary key cannot corrupt a longer word.
**Adding your own language**, for example German:
```bash
cp panel/application/lang/en.php panel/application/lang/de.php
# translate the values in de.php, then add 'de' to Lang::AVAILABLE
# in panel/engine/main/lang.php
```
**Coverage.** The dictionary holds 1304 phrases and covers everything a person sees: the client area,
ordering, server management (console with all RCON commands, FTP, MySQL, firewall, scheduler,
mod auto-install), tickets, the whole FAQ, the entire admin area, and the e-mail templates. Coverage
is measured, not assumed — a script replays the replacement logic against the sources and reports
what would stay Russian; a live crawl of 45 pages in English mode confirms it. Four items remain
Russian on purpose: the "Русский" label of the switcher itself, the two game-server language codes
in the FAQ (`rus => Pyccĸий`, `ukr => Українська`), and a name-validation regular expression.
If you do spot an untranslated phrase, add it to `application/lang/en.php` — the key is the Russian
text exactly as it appears in the output. Two rules worth knowing:
* **never add a standalone function word** (a preposition or conjunction such as `в`). It gets
substituted inside sentences that are not translated yet and produces mixed-language nonsense
like "at поле имя пользователя". Translate such words as part of the whole phrase.
* if the phrase contains a PHP variable, use the static fragments around it as separate keys — the
rendered text has real values in place of the variable and the full string would never match.
See the [screenshot gallery](docs/screenshots/README.md) for every section in both languages.
Translation only runs when the language is not Russian; in Russian mode the dictionary is not even
loaded, so there is no overhead.
---
## 8. Captcha
By default it is **off** — registration and login work without it, and the field is simply absent.
To enable it:
1. Get **reCAPTCHA v2 "I'm not a robot"** keys at
[google.com/recaptcha/admin](https://www.google.com/recaptcha/admin) and specify the panel's domain
2. Go to **Admin → Settings → Other settings → "Bot protection (reCAPTCHA v2)"**
3. Paste the **Site key** and the **Secret key**
4. Switch to "Enabled" and save
**The order matters.** Enable the captcha without keys and the form will show
"ERROR for site owner: Invalid site key", locking everyone out. If that happens, open
`/var/www/hostinpl/application/config.php`, set `'captcha_enable' => '0'` and restart PHP-FPM:
```bash
sudo sed -i "s/'captcha_enable' => '1'/'captcha_enable' => '0'/" /var/www/hostinpl/application/config.php
sudo service php8.4-fpm restart # use your PHP version
```
The captcha covers login, registration, password recovery and ticket creation — all four forms at once.
---
## 9. HTTPS and domain
The panel is installed over HTTP. For a certificate:
```bash
sudo apt-get install -y certbot python3-certbot-nginx
sudo certbot --nginx -d panel.example.com
```
After the certificate is issued, update the address in the configuration, otherwise links in
e-mails and redirects will stay on `http://`:
```bash
sudo sed -i "s|'url' => 'http://|'url' => 'https://|" /var/www/hostinpl/application/config.php
sudo service php8.4-fpm restart
```
If the panel sits behind a reverse proxy (Caddy, nginx, Cloudflare), its nginx configuration already
contains `absolute_redirect off; port_in_redirect off;` — without them redirects point at the
internal port and links such as `/phpmyadmin` do not open from outside.
---
## 10. Maintenance
**Backups.** Keep the database and the configuration:
```bash
mysqldump hostin > /root/hostin-$(date +%F).sql
cp /var/www/hostinpl/application/config.php /root/config-$(date +%F).php
```
Game server files live on the node in `/home/gs<ID>` — back those up on the node.
**Logs:**
| What to look at | Where |
|-----------------|-------|
| PHP and nginx errors | `/var/log/nginx/error.log` |
| Scheduler hits | `grep 'main/cron' /var/log/nginx/access.log` |
| Login journal | the `authlog` table |
| Node image build | `/tmp/redl-docker-build.log` |
**Health check:** **Admin → System check** — PHP extensions, versions, scheduler links. Every item
should be green.
**Updating the code** from the repository:
```bash
cd redl-gamepanel && git pull
sudo cp -a panel/application panel/engine /var/www/hostinpl/ # config.php is not overwritten
sudo chown -R www-data:www-data /var/www/hostinpl
sudo service php8.4-fpm restart
```
---
## 11. Troubleshooting
**"Error: could not load the controller …"** — the panel found no controller for that address.
Usually it is a link to something that does not exist, for example `/phpmyadmin` when phpMyAdmin is
not installed (see section 2). If the section really should exist, check that the files are in place
and owned by `www-data`, then restart PHP-FPM **fully**: opcache may have cached a file that was
caught mid-write.
**The login page is blank or has no form** — look at `/var/log/nginx/error.log`. A common cause is
`short_open_tag = Off`. The panel uses short `<? ?>` tags; the installer turns the option on, but you
have to restore it if you reinstall PHP.
**Cannot log in, the form shows "Invalid site key"** — the captcha is on without keys, see section 8.
**The scheduler is not running** (statistics not updating, expired servers not suspended):
```bash
grep -c 'main/cron' /var/log/nginx/access.log # should grow
pgrep -a cron # is the daemon running?
cat /etc/cron.d/hostinpl # every job must be on ONE line
```
If a job is split across two lines, the token got into the file with a newline — rewrite the file,
taking the token from `/root/.redl-panel-credentials`.
**A server is not created on the node** — check on the node:
```bash
docker info # is the daemon up?
docker image inspect debian:stretch # is the image there?
docker run --rm debian:stretch echo ok # does a container start?
ls /home/cp/gameservers/files/ # are the game files in place?
```
**The server is created but dies immediately** — look at the container log on the node:
`docker logs gs<ID>`. Most often the 32-bit libraries are missing (rebuild the image from our
`docker/Dockerfile`) or the game build is damaged.
**Everything worked until a reboot** — on machines without systemd, the watchdogs on cron bring
things back. Check `pgrep -a cron` and run `/usr/local/bin/hostinpl-guard` by hand
(`gamenode-guard` on a node).
**A phrase in the interface is still Russian in English mode** — add it to
`panel/application/lang/en.php`; the key is the Russian text exactly as it appears on the page.
---
**Maintained and developed with [REDL.IO](https://redl.io) — AI-powered hosting.**
-21
View File
@@ -1,21 +0,0 @@
MIT License
Copyright (c) 2026 REDL
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+199 -100
View File
@@ -1,131 +1,230 @@
# FiveRP
<p align="right">
<a href="README.ru.md"><img alt="Читать по-русски" src="https://img.shields.io/badge/%F0%9F%87%B7%F0%9F%87%BA%20%D0%A7%D0%B8%D1%82%D0%B0%D1%82%D1%8C%20%D0%BF%D0%BE--%D1%80%D1%83%D1%81%D1%81%D0%BA%D0%B8-2867c4?style=for-the-badge"></a>
</p>
An open **FiveM roleplay base** — account registration, login and character identity, written by an AI agent on a live server, on camera, and free for anyone to take and build on.
# HostinPL 5.6 · Game Hosting Control Panel · REDL build
> 🇷🇺 Русская версия — ниже / Russian version below.
**Base: HostinPL 5.6** (original by Samir Shelenko and Alexander Zemlyanoy, written for Debian 9 /
PHP 7.0) — brought back to life on a current stack, audited and documented. Version numbering follows
the original: this is 5.6, our changes sit on top of it.
Not a framework fork: `fiverp-auth`, `fiverp-characters` and `fiverp-loadscreen` are written for this server. No ESX, no QBCore. The only things that come from outside are the stock Cfx resources (`mapmanager`, `chat`, `spawnmanager`, `sessionmanager`, `hardcap`) and `oxmysql` — the installer fetches both, so this repository stays small and readable.
> 🇷🇺 **[Русская версия этого описания → README.ru.md](README.ru.md)**
## What it does today
A control panel for selling and managing game servers: SA-MP, CRMP, MTA, Minecraft, CS 1.6, CS:S, RAGE:MP.
Client area, ticket system, payment gateways, automated game server deployment onto game nodes.
- **Two-step registration** — account first (email, username, password), identity second (first and last name, issued once, printed onto a passport).
- **Login** that returns you to your resident, with the character loaded from the database.
- **Loading screen** with real progress from the game's own streaming events.
- **Passwords hashed with scrypt**, per-password salt, stored as `scrypt$salt$hash`. The client never decides anything: registration, login and spawning are all server side.
- **`basic-gamemode` is deliberately off** — it force-respawns on map start, which would drop a player into the world behind the auth screen. `fiverp-auth` owns spawning instead.
**Maintained and developed with [REDL.IO](https://redl.io) — AI-powered hosting.**
| Loading | Sign in | Register | Identity |
|---|---|---|---|
| ![Loading](docs/screens/loading.png) | ![Sign in](docs/screens/signin.png) | ![Register](docs/screens/register.png) | ![Identity](docs/screens/identity.png) |
The whole interface follows one design system — a light, glassy theme on **Unbounded**, with the city visible behind the panels. Every colour, radius and font is a token; the rules are in [`docs/DESIGN.md`](docs/DESIGN.md), and the fonts ship with the resources (a NUI page has no guaranteed internet).
## Install
Ubuntu 22.04 / 24.04 or Debian 12, and a free server key from [keymaster.fivem.net](https://keymaster.fivem.net).
```sh
git clone https://github.com/RedlHosting/redl-fivem-rp.git
cd redl-fivem-rp
sudo sh install.sh --licence cfxk_your_key_here
```
The installer fetches the recommended FXServer build, clones `cfx-server-data`, installs `oxmysql`, sets up MariaDB with the schema and a generated password, copies the FiveRP resources in, writes `server.cfg`, adds a boot entry and waits until the server has registered with Cfx. Then connect to `<your ip>:30120`.
Options: `--dir`, `--hostname`, `--port`, `--name` (a second server on the same box), `--db-name`, `--db-user`, `--db-pass`, `--build`, `--no-start`. Run `sh install.sh --help`.
Day to day:
```sh
service fiverp start|stop|restart|status # systemd boxes: systemctl ...
rcon status # command into the live console
tail -f /opt/fivem/logs/server.log
```
## Layout
resources/[local]/fiverp-auth registration and login (NUI + scrypt + oxmysql)
resources/[local]/fiverp-characters identity, character load and spawn
resources/[local]/fiverp-loadscreen loading screen
sql/schema.sql accounts, characters
server.cfg.example main config
bin/ keep-alive supervisor, rcon, init script
docs/DESIGN.md the design system every screen follows
## What's not built yet
Everything after the spawn: chat, HUD, inventory, money, jobs, vehicles. The `characters` table already carries model, appearance, position and heading, so gameplay has somewhere to write.
Issues and pull requests welcome — this is meant to be used, not admired.
## How it was made
Built on a [REDL](https://redl.io) VDS, where an AI agent lives on the server itself with root access. The task is given in plain words, the agent writes the code, fixes its own mistakes and restarts the service. The whole process is recorded — including the parts where it broke.
## Licence
MIT for the FiveRP code. `cfx-server-data` and `oxmysql` keep their own licences; Unbounded is under the SIL Open Font License.
This build is a modernisation of the HostinPL 5.6 panel. The original was written for Debian 9 and PHP 7.0
(both end-of-life since 2022) and simply would not start on a current server. Here it runs on an up-to-date
stack, the vulnerabilities we found are fixed, and one-click installers are included.
---
# FiveRP (RU)
## ⚠️ Legal status — read this before installing
Открытая **ролевая база для FiveM** — регистрация аккаунта, вход и личность персонажа, написанные ИИ-агентом на живом сервере, в кадре, и выложенные бесплатно для всех.
The original **HostinPL 5.6 is a commercial, proprietary product**. Its authors are Samir Shelenko and
Alexander Zemlyanoy. The file `panel/LICENSE` is their licence, and it grants no permission to
redistribute or modify the software.
Это не форк фреймворка: `fiverp-auth`, `fiverp-characters` и `fiverp-loadscreen` написаны под этот сервер. Никакого ESX и QBCore. Извне берутся только штатные ресурсы Cfx (`mapmanager`, `chat`, `spawnmanager`, `sessionmanager`, `hardcap`) и `oxmysql` — установщик скачивает их сам, поэтому репозиторий остаётся маленьким и читаемым.
The sources this build is based on were circulated as "nulled" — a cracked copy of a paid product,
published without the authors' consent. We are not hiding that or rewriting history: the authors'
copyright headers are preserved throughout the code and were deliberately not removed.
## Что уже работает
What this means in practice:
- **Регистрация в два шага** — сначала аккаунт (почта, логин, пароль), потом личность (имя и фамилия, выдаются один раз и печатаются на паспорте).
- **Вход**, который возвращает к своему персонажу — данные поднимаются из базы.
- **Загрузочный экран** с настоящим прогрессом из событий стриминга игры.
- **Пароли на scrypt**, соль на каждый пароль, хранение в виде `scrypt$соль$хеш`. Клиент ничего не решает: регистрация, вход и спавн — на сервере.
- **`basic-gamemode` намеренно выключен** — он делает принудительный респавн при старте карты и выбрасывал бы игрока в мир за экраном авторизации. Спавном занимается `fiverp-auth`.
* using it for commercial hosting infringes copyright, with all the risks that follow;
* the legal route is to buy a licence from the authors, or use an open-source alternative
(Pterodactyl, Pelican);
* this repository is only useful for studying the code, auditing it, and internal experiments.
Весь интерфейс живёт по одной дизайн-системе — светлая «стеклянная» тема на шрифте **Unbounded**, город виден за панелями. Каждый цвет, радиус и шрифт — токен; правила в [`docs/DESIGN.md`](docs/DESIGN.md), шрифты лежат внутри ресурсов (у страницы NUI нет гарантированного интернета).
Responsibility for use rests with whoever installs it.
## Установка
---
Ubuntu 22.04 / 24.04 или Debian 12 и бесплатный ключ сервера с [keymaster.fivem.net](https://keymaster.fivem.net).
## What is in this repository
```sh
git clone https://github.com/RedlHosting/redl-fivem-rp.git
cd redl-fivem-rp
sudo sh install.sh --licence cfxk_ваш_ключ
| Path | What it is |
|------|------------|
| `install-panel.sh` | One-click panel install (nginx + PHP 8 + MariaDB + scheduler + admin user) |
| `install-node.sh` | One-click game node install (Docker + image + directories + SSH/FTP + SteamCMD) |
| `panel/` | Panel source code with all of our changes |
| `docker/Dockerfile` | Modern game server image (Debian 12; the original stretch-based one no longer builds) |
| `docker/Dockerfile.original-stretch` | The original Dockerfile, kept for comparison |
| [`CHANGES.en.md`](CHANGES.en.md) | Full list of differences from the original |
| [`SECURITY.en.md`](SECURITY.en.md) | Audit results: vulnerabilities, backdoors, what was fixed |
| [`GUIDE.en.md`](GUIDE.en.md) | Complete guide: install, connecting a node, configuration, maintenance |
---
## Screenshots
Every section of the panel, captured in both languages — 38 screenshots per language:
| | |
|---|---|
| [![Login](docs/screenshots/en/00-login.png)](docs/screenshots/en/00-login.png)<br>Login page | [![Client area](docs/screenshots/en/01-cabinet.png)](docs/screenshots/en/01-cabinet.png)<br>Client area |
| [![Order a server](docs/screenshots/en/03-server-order.png)](docs/screenshots/en/03-server-order.png)<br>Ordering a game server | [![Admin dashboard](docs/screenshots/en/11-admin-dashboard.png)](docs/screenshots/en/11-admin-dashboard.png)<br>Admin dashboard |
| [![Statistics](docs/screenshots/en/24-admin-statistics.png)](docs/screenshots/en/24-admin-statistics.png)<br>Statistics | [![Captcha switch](docs/screenshots/en/28-admin-settings-other-captcha.png)](docs/screenshots/en/28-admin-settings-other-captcha.png)<br>The captcha switch in the admin area |
**Full galleries:** [🇬🇧 English](docs/screenshots/README.md) · [🇷🇺 Russian](docs/screenshots/README.ru.md)
---
## Supported games
Eleven games ship in the panel's catalogue. Each one has its own query driver (so the panel can read
online player counts and the map), its own port and slot ranges, and its own default resource limits.
| Game | Code | Query protocol | Slots | Port range |
|------|------|----------------|-------|------------|
| San Andreas: Multiplayer 0.3.7 | `samp` | samp | 501000 | 77779999 |
| Criminal Russia: Multiplayer 0.3e | `crmp` | samp | 50500 | 33357000 |
| Criminal Russia: Multiplayer 0.3.7 | `crmp037` | samp | 50500 | 33357000 |
| United Multiplayer | `unit` | samp | 50500 | 77779999 |
| Multi Theft Auto | `mta` | mtasa | 501000 | 2502080520 |
| Minecraft (Java) | `mine` | mine | 10100 | 1241055641 |
| Minecraft: Pocket Edition | `mcpe` | mine | 10100 | 1241055641 |
| Counter-Strike 1.6 | `cs` | valve | 632 | 2701630550 |
| Counter-Strike: Source | `css` | valve | 632 | 2701630550 |
| Counter-Strike: Global Offensive | `csgo` | valve | 632 | 2701630550 |
| GTA V: RAGE MP | `ragemp` | ragemp | 50500 | 2200025000 |
The client can switch the server core in one click, without ordering a new server:
* **Minecraft (Java)** — CraftBukkit 1.7.10 / 1.8 / 1.13.2, Spigot 1.7.10 / 1.9.4 / 1.16.1, Vanilla 1.7.10 / 1.15.1
* **Minecraft: PE** — Genisys and Nukkit builds
* **Counter-Strike** — a clean build from SteamCMD, plus the VAC anti-cheat toggle and FastDL
* **RAGE:MP** — NodeJS modules are installed from the server management page
Every game is **disabled** in a fresh install (`Admin → Games → status`) — switch on the ones you
have a node and server files for. The game files themselves are not shipped, see
"What does not work" below.
---
## Requirements
**For the panel** (the website and database — Docker is not needed):
* Ubuntu 24.04 / 22.04 or Debian 12 / 13
* 1 core, 1 GB RAM, 10 GB disk
* Any VPS will do, including container-based ones (LXC/OpenVZ)
**For a game node** (this is where the game servers actually run):
* Ubuntu 24.04 / 22.04 or Debian 12 / 13
* 2 cores, 2 GB RAM, 100 GB disk (budget 12 GB per game server)
* **Full kernel access is mandatory: a dedicated server or KVM.**
The panel creates a separate Docker container for every game server, and Docker does not start
inside a container-based VPS (LXC/OpenVZ) — the kernel forbids nested namespaces. The installer
checks for this and tells you plainly if the machine is unsuitable.
The panel and a node can share one machine, but separating them is better: keep the website on a
cheap VPS and put the games on real hardware.
---
## One-click install
### Panel
```bash
apt-get update && apt-get install -y git
git clone https://github.com/RedlHosting/redl-gamepanel.git
cd redl-gamepanel
sudo bash install-panel.sh
```
Установщик скачает рекомендованную сборку FXServer, склонирует `cfx-server-data`, поставит `oxmysql`, поднимет MariaDB со схемой и сгенерированным паролем, положит ресурсы FiveRP, напишет `server.cfg`, пропишет автозапуск и дождётся регистрации сервера в Cfx. Дальше — `connect <ваш ip>:30120`.
The script asks only for the panel address, the administrator e-mail and password — everything else
is automatic: it installs nginx, PHP 8 and MariaDB, creates a database with a random password, loads
the schema, writes the configuration and the scheduler, sets up an autostart watchdog, creates the
administrator, and verifies that the login page actually opens.
Параметры: `--dir`, `--hostname`, `--port`, `--name` (второй сервер на той же машине), `--db-name`, `--db-user`, `--db-pass`, `--build`, `--no-start`. Список — `sh install.sh --help`.
To run the panel on a non-standard port, set it via an environment variable:
Повседневное:
```sh
service fiverp start|stop|restart|status # на машинах с systemd — systemctl ...
rcon status # команда в живую консоль
tail -f /opt/fivem/logs/server.log
```bash
sudo PORT=8095 bash install-panel.sh
```
## Что где лежит
### Game node
resources/[local]/fiverp-auth регистрация и вход (NUI + scrypt + oxmysql)
resources/[local]/fiverp-characters личность, загрузка персонажа и спавн
resources/[local]/fiverp-loadscreen загрузочный экран
sql/schema.sql accounts, characters
server.cfg.example основной конфиг
bin/ супервизор, rcon, init-скрипт
docs/DESIGN.md дизайн-система, по которой сделаны все экраны
```bash
git clone https://github.com/RedlHosting/redl-gamepanel.git
cd redl-gamepanel
sudo bash install-node.sh
```
## Чего ещё нет
At the end the script prints the IP, SSH login and password — enter those in the panel under
**Admin → Locations → Add location**. See [GUIDE.en.md](GUIDE.en.md) for details.
Всего, что после спавна: чат, HUD, инвентарь, деньги, работы, транспорт. В таблице `characters` уже есть модель, внешность, позиция и поворот — геймплею есть куда писать.
---
Issues и pull request'ы приветствуются — это сделано, чтобы этим пользовались.
## What already works
## Как это сделано
* Login, registration, password recovery — **without a captcha** (it is enabled from the admin area, see below)
* Client area: balance, invoices, transfers, bonuses, promo codes, referral system
* Ticket system with categories and attachments
* Admin area: users, servers, locations, games, news, statistics, promo codes, settings
* System check (Admin → System check) — every item green
* Payment gateways: Unitpay, Enot, AnyPay, LiteKassa, RoboKassa, FreeKassa, YooKassa, QIWI
(credentials are entered in the admin area; we did not test any gateway with a real payment)
* Web hosting (the WEB section) — via ISPmanager on a separate machine
* Scheduler: statistics, tasks, automatic suspension of expired servers
* **Two interface languages: Russian and English**, with automatic detection from the browser
and a switcher in the header (see [GUIDE.en.md](GUIDE.en.md))
Собрано на VDS [REDL](https://redl.io), где ИИ-агент живёт прямо на сервере с root-доступом. Задача ставится обычными словами, агент пишет код, чинит свои же ошибки и перезапускает сервис. Весь процесс записан — включая моменты, где всё ломалось.
### The captcha is off and is enabled with a single switch
## Лицензия
By default there is no captcha anywhere — not on login, registration or tickets; the field simply
does not exist. When you need it:
MIT на код FiveRP. У `cfx-server-data` и `oxmysql` свои лицензии; Unbounded — под SIL Open Font License.
1. Get reCAPTCHA v2 ("I'm not a robot") keys at [google.com/recaptcha](https://www.google.com/recaptcha/admin)
2. Go to **Admin → Settings → Other settings → "Bot protection (reCAPTCHA v2)"**
3. Paste the Site key and the Secret key, switch it to "Enabled", save
The order matters: keys first, switch second — otherwise the form shows
"ERROR for site owner: Invalid site key" and nobody can log in.
## What does not work
* **Creating game servers on a container-based VPS.** You need a node with real Docker
(dedicated or KVM) — see Requirements for why.
* **Game server files are not included.** The original installer downloaded them from third-party
sites over plain HTTP with no signature checking, so we removed that (see [SECURITY.en.md](SECURITY.en.md)).
You need to place the files into `/home/cp/gameservers/files/<game_code>/` yourself.
* **Payments and VK login** require your own keys and merchant accounts.
---
## Planned next
1. **More games — by request.** The eleven games above are the ones the panel came with. Adding a
game means one row in the catalogue plus a query driver and server files, so the list is meant to
grow: tell us which game you want and it goes into the queue. Rust, ARK, Terraria, Valheim,
Garry's Mod, Team Fortress 2, FiveM and Minecraft: Bedrock are the obvious candidates.
2. **Modern payment gateways: PayPal, Stripe and Lava.** Right now the panel only has the Russian
gateways of its original author (Unitpay, Enot, AnyPay, LiteKassa, RoboKassa, FreeKassa, YooKassa,
QIWI). PayPal and Stripe open up card payments worldwide, Lava covers Russia and the CIS —
together they let the panel take money outside a single region.
Have a different priority? Open an issue in the repository.
---
## Differences from the original, in brief
* Runs on **PHP 8.4** instead of PHP 7.0 — a walk through all 21 sections of the panel produces
zero errors in the log
* **The installer was rewritten from scratch**: the original overwrote `/etc/apt/sources.list` with
Debian 9 repositories and broke apt on any current system
* **Two pre-authentication SQL injections were fixed**, and plaintext password storage was removed
* The captcha became manageable from the admin area
* Russian and English interface with automatic language detection
* The scheduler and autostart work even where systemd is absent
The full list is in [CHANGES.en.md](CHANGES.en.md) and [SECURITY.en.md](SECURITY.en.md).
---
**Maintained and developed with [REDL.IO](https://redl.io) — AI-powered hosting.**
+227
View File
@@ -0,0 +1,227 @@
<p align="right">
<a href="README.md"><img alt="Read in English" src="https://img.shields.io/badge/%F0%9F%87%AC%F0%9F%87%A7%20Read%20in%20English-2867c4?style=for-the-badge"></a>
</p>
# HostinPL 5.6 · Панель управления игровым хостингом · сборка REDL
**Основа: HostinPL 5.6** (оригинал — Samir Shelenko и Alexander Zemlyanoy, написан под Debian 9 /
PHP 7.0) — приведён в рабочее состояние на современном стеке, проверен и описан. Нумерация версии
взята от оригинала: это 5.6, наши правки лежат поверх неё.
> 🇬🇧 **[English version of this page → README.md](README.md)**
Панель для продажи и управления игровыми серверами: SA-MP, CRMP, MTA, Minecraft, CS 1.6, CS:S, RAGE:MP.
Личный кабинет клиента, тикеты, платёжные системы, автоматическое создание серверов на игровых нодах.
**Поддерживается и развивается с помощью [REDL.IO](https://redl.io) — Хостинг с искусственным интеллектом.**
Эта сборка — модернизация панели HostinPL 5.6: оригинал написан под Debian 9 и PHP 7.0 (сняты с поддержки в 2022 году)
и на современном сервере просто не запускался. Здесь он приведён в рабочее состояние на актуальном стеке,
закрыты найденные уязвимости и добавлены установщики в один клик.
---
## ⚠️ Правовой статус — прочитайте до установки
Оригинальная панель **HostinPL 5.6 — коммерческий проприетарный продукт**. Авторы: Samir Shelenko и
Alexander Zemlyanoy. Файл `panel/LICENSE` — их лицензия, и разрешения на распространение или изменение
она не даёт.
Исходники, лежащие в основе этой сборки, распространялись как «nulled» — то есть это взломанная копия
платного продукта, выложенная без согласия авторов. Мы этого не скрываем и не переписываем историю:
авторские копирайт-заголовки в коде сохранены как есть, они специально не удалялись.
Что это значит на практике:
* использование в коммерческом хостинге — нарушение авторских прав со всеми вытекающими рисками;
* легальный путь — купить лицензию у авторов либо взять открытую альтернативу (Pterodactyl, Pelican);
* этот репозиторий имеет смысл только для изучения кода, аудита и внутренних экспериментов.
Ответственность за использование лежит на том, кто устанавливает.
---
## Что в репозитории
| Путь | Что это |
|------|---------|
| `install-panel.sh` | Установка панели в один клик (nginx + PHP 8 + MariaDB + планировщик + админ) |
| `install-node.sh` | Установка игровой ноды в один клик (Docker + образ + каталоги + SSH/FTP + SteamCMD) |
| `panel/` | Код панели со всеми нашими правками |
| `docker/Dockerfile` | Современный образ игровых серверов (Debian 12; оригинальный на stretch больше не собирается) |
| `docker/Dockerfile.original-stretch` | Оригинальный Dockerfile — оставлен для сравнения |
| [`ИЗМЕНЕНИЯ.md`](ИЗМЕНЕНИЯ.md) | Полный список отличий от оригинала |
| [`БЕЗОПАСНОСТЬ.md`](БЕЗОПАСНОСТЬ.md) | Что нашли при аудите: уязвимости, закладки, что исправлено |
| [`ИНСТРУКЦИЯ.md`](ИНСТРУКЦИЯ.md) | Полная инструкция: установка, подключение ноды, настройка, обслуживание |
---
## Скриншоты
Каждый раздел панели снят на двух языках — по 38 кадров на язык:
| | |
|---|---|
| [![Вход](docs/screenshots/ru/00-login.png)](docs/screenshots/ru/00-login.png)<br>Страница входа | [![Кабинет](docs/screenshots/ru/01-cabinet.png)](docs/screenshots/ru/01-cabinet.png)<br>Личный кабинет |
| [![Заказ сервера](docs/screenshots/ru/03-server-order.png)](docs/screenshots/ru/03-server-order.png)<br>Заказ игрового сервера | [![Админка](docs/screenshots/ru/11-admin-dashboard.png)](docs/screenshots/ru/11-admin-dashboard.png)<br>Главная админки |
| [![Статистика](docs/screenshots/ru/24-admin-statistics.png)](docs/screenshots/ru/24-admin-statistics.png)<br>Статистика | [![Переключатель капчи](docs/screenshots/ru/28-admin-settings-other-captcha.png)](docs/screenshots/ru/28-admin-settings-other-captcha.png)<br>Переключатель капчи в админке |
**Полные галереи:** [🇷🇺 русская](docs/screenshots/README.ru.md) · [🇬🇧 английская](docs/screenshots/README.md)
---
## Какие игры доступны
В каталоге панели одиннадцать игр. У каждой свой query-драйвер (панель сама читает онлайн игроков
и карту), свои диапазоны портов и слотов и свои лимиты ресурсов по умолчанию.
| Игра | Код | Query-протокол | Слоты | Порты |
|------|-----|----------------|-------|-------|
| San Andreas: Multiplayer 0.3.7 | `samp` | samp | 501000 | 77779999 |
| Criminal Russia: Multiplayer 0.3e | `crmp` | samp | 50500 | 33357000 |
| Criminal Russia: Multiplayer 0.3.7 | `crmp037` | samp | 50500 | 33357000 |
| United Multiplayer | `unit` | samp | 50500 | 77779999 |
| Multi Theft Auto | `mta` | mtasa | 501000 | 2502080520 |
| Minecraft (Java) | `mine` | mine | 10100 | 1241055641 |
| Minecraft: Pocket Edition | `mcpe` | mine | 10100 | 1241055641 |
| Counter-Strike 1.6 | `cs` | valve | 632 | 2701630550 |
| Counter-Strike: Source | `css` | valve | 632 | 2701630550 |
| Counter-Strike: Global Offensive | `csgo` | valve | 632 | 2701630550 |
| GTA V: RAGE MP | `ragemp` | ragemp | 50500 | 2200025000 |
Ядро сервера клиент меняет в один клик, не заказывая новый сервер:
* **Minecraft (Java)** — CraftBukkit 1.7.10 / 1.8 / 1.13.2, Spigot 1.7.10 / 1.9.4 / 1.16.1, Vanilla 1.7.10 / 1.15.1
* **Minecraft: PE** — сборки Genisys и Nukkit
* **Counter-Strike** — чистая сборка из SteamCMD, плюс переключатель античита VAC и FastDL
* **RAGE:MP** — NodeJS-модули ставятся со страницы управления сервером
В свежей установке все игры **выключены** (`Админка → Игры → статус`) — включайте те, под которые
у вас есть нода и сборки. Сами сборки игр в комплект не входят, см. «Что не работает» ниже.
---
## Требования
**Для панели** (сам сайт и база — Docker не нужен):
* Ubuntu 24.04 / 22.04 или Debian 12 / 13
* 1 ядро, 1 ГБ ОЗУ, 10 ГБ диска
* Подойдёт любой VPS, включая контейнерный (LXC/OpenVZ)
**Для игровой ноды** (там крутятся сами игровые сервера):
* Ubuntu 24.04 / 22.04 или Debian 12 / 13
* 2 ядра, 2 ГБ ОЗУ, 100 ГБ диска (считайте 1–2 ГБ на игровой сервер)
* **Обязательно полный доступ к ядру: выделенный сервер или KVM.**
Панель создаёт на каждый игровой сервер отдельный Docker-контейнер, а в контейнерном VPS
(LXC/OpenVZ) Docker не запускается — ядро запрещает вложенные namespace. Установщик это проверяет
и честно предупредит.
Панель и нода могут быть одной машиной, но лучше разными: сайт можно держать на дешёвом VPS,
а под игры взять железо.
---
## Установка в один клик
### Панель
```bash
apt-get update && apt-get install -y git
git clone https://github.com/RedlHosting/redl-gamepanel.git
cd redl-gamepanel
sudo bash install-panel.sh
```
Скрипт спросит только адрес панели, e-mail и пароль администратора — дальше всё сам:
поставит nginx, PHP 8 и MariaDB, создаст базу со случайным паролем, загрузит схему, настроит
конфиг и планировщик, поднимет сторож автозапуска, создаст администратора и проверит,
что страница входа реально открывается.
Панель работает на нестандартном порту, если задать его переменной:
```bash
sudo PORT=8095 bash install-panel.sh
```
### Игровая нода
```bash
git clone https://github.com/RedlHosting/redl-gamepanel.git
cd redl-gamepanel
sudo bash install-node.sh
```
В конце скрипт напечатает IP, логин и пароль SSH — их нужно вбить в панели:
**Админка → Локации → Добавить локацию**. Подробнее — в [ИНСТРУКЦИЯ.md](ИНСТРУКЦИЯ.md).
---
## Что уже работает
* Вход, регистрация, восстановление пароля — **без капчи** (включается из админки, см. ниже)
* Личный кабинет: баланс, счета, переводы, бонусы, промокоды, реферальная система
* Тикет-система с категориями и вложениями
* Админка: пользователи, сервера, локации, игры, новости, статистика, промокоды, настройки
* Проверка системы (Админка → Проверка системы) — все пункты зелёные
* Платёжные системы: Unitpay, Enot, AnyPay, LiteKassa, RoboKassa, FreeKassa, Юkassa, QIWI
(реквизиты вписываются в админке; ни один шлюз мы не тестировали живыми платежами)
* Веб-хостинг (раздел WEB) — через ISPmanager на отдельной машине
* Планировщик: статистика, задачи, автоотключение просроченных серверов
* **Два языка интерфейса: русский и английский**, с автоопределением по браузеру
и переключателем в шапке (см. [ИНСТРУКЦИЯ.md](ИНСТРУКЦИЯ.md))
### Капча — выключена, включается одним переключателем
По умолчанию капчи нет нигде: ни при входе, ни при регистрации, ни в тикетах — поля просто
не существует. Когда понадобится:
1. Получите ключи reCAPTCHA v2 («Я не робот») на [google.com/recaptcha](https://www.google.com/recaptcha/admin)
2. **Админка → Настройки → Прочие настройки → «Защита от ботов (reCAPTCHA v2)»**
3. Вставьте Site key и Secret key, переключите на «Включена», сохраните
Порядок важен: сначала ключи, потом переключатель — иначе на форме появится
«ERROR for site owner: Invalid site key» и войти будет нельзя.
## Что не работает
* **Создание игровых серверов на контейнерном VPS.** Нужна нода с настоящим Docker
(дедик или KVM) — причина описана в требованиях.
* **Сборки игр в комплект не входят.** В оригинальном установщике они качались с посторонних
сайтов по обычному HTTP без проверки подписи — мы этот способ убрали (см. [БЕЗОПАСНОСТЬ.md](БЕЗОПАСНОСТЬ.md)).
Сборки нужно положить в `/home/cp/gameservers/files/<код_игры>/` самостоятельно.
* **Платежи и VK-авторизация** требуют ваших собственных ключей и мерчант-аккаунтов.
---
## Что планируем дальше
1. **Больше игр — по просьбам людей.** Одиннадцать игр выше — это то, с чем панель пришла.
Добавить игру = строка в каталоге плюс query-драйвер и сборки, так что список задуман
расширяемым: скажите, какую игру хотите, и она встанет в очередь. Очевидные кандидаты —
Rust, ARK, Terraria, Valheim, Garry's Mod, Team Fortress 2, FiveM и Minecraft: Bedrock.
2. **Современные платёжные системы: PayPal, Stripe и Lava.** Сейчас в панели только российские
шлюзы её первоначального автора (Unitpay, Enot, AnyPay, LiteKassa, RoboKassa, FreeKassa,
YooKassa, QIWI). PayPal и Stripe открывают приём карт по всему миру, Lava закрывает Россию
и СНГ — вместе они позволяют принимать деньги не только в одном регионе.
Приоритеты другие? Откройте issue в репозитории.
---
## Коротко об отличиях от оригинала
* Работает на **PHP 8.4** вместо PHP 7.0 — обход всех 21 раздела панели даёт ноль ошибок в логе
* **Установщик переписан с нуля**: оригинальный затирал `/etc/apt/sources.list` репозиториями
Debian 9 и ломал apt на любой современной системе
* **Закрыты две SQL-инъекции, доступные без авторизации**, и убрано хранение паролей в открытом виде
* Капча стала управляемой из админки
* Русский и английский интерфейс с автоопределением языка
* Планировщик и автозапуск работают и там, где нет systemd
Полностью — в [ИЗМЕНЕНИЯ.md](ИЗМЕНЕНИЯ.md) и [БЕЗОПАСНОСТЬ.md](БЕЗОПАСНОСТЬ.md).
---
**Поддерживается и развивается с помощью [REDL.IO](https://redl.io) — Хостинг с искусственным интеллектом.**
+243
View File
@@ -0,0 +1,243 @@
[Русский](БЕЗОПАСНОСТЬ.md) · **English**
# HostinPL 5.6 · Security audit: what we found and what we fixed
An audit of the HostinPL 5.6 build (a "nulled" copy). We looked for hidden backdoors and webshells,
obfuscation, data being sent to third-party hosts, SQL injections, RCE, authorisation bypasses,
password storage, and problems in the installer and the Dockerfile.
**Maintained and developed with [REDL.IO](https://redl.io) — AI-powered hosting.**
---
## The short version
The fork claimed: "all shells/holes have been cut out of the panel". Our check shows that claim is
**only partly true**. We found no working backdoors, and there is no obfuscation in the panel code.
But we did find:
* **proof that webshells really were present in this lineage** — one was found, already defanged;
* **two SQL injections reachable without authentication** — not mentioned anywhere in the fork's description;
* **user passwords written to the database in plaintext**;
* code being installed onto game nodes **over plain HTTP with no signature verification**.
We fixed the first three. Details below.
---
## 1. A webshell in the build's lineage — defanged
**File:** `panel/application/public/js/proxy/proxy.php` — all 7 lines of it:
```php
<?php
if(isset($_GET['cmd'])){
print('Backdoor fixed by Xopowblu-4EJlOBEK aka Und3X (und3x.ru)');
}else{
header("Location: /");
}
?>
```
The file takes a `?cmd=` parameter — the classic signature of a command-accepting webshell. Here the
body has been replaced with a message, so **this particular shell is defanged**. But the implication
is unambiguous: backdoors were planted deliberately in this build's distribution chain, and one of
them sat in a scripts folder nobody normally looks at.
**Our decision:** the file is kept as evidence. It executes nothing. If you do not want it:
`rm panel/application/public/js/proxy/proxy.php`.
**Takeaway:** any other copy of this panel obtained anywhere but this repository may contain a
*working* shell. Before installing someone else's build, check at least this much:
```bash
grep -rIn --include=*.php -E "eval\(|assert\(|base64_decode|gzinflate|\\\$_REQUEST\[" .
find . -path ./engine/libs -prune -o -name '*.php' -print | grep -E '(assets|public|tmp)/'
```
---
## 2. What we did not find (the good news)
Checked specifically, result negative:
* **No obfuscation in the panel code.** Zero occurrences of `eval`, `assert`, `create_function`,
`gzinflate`, `$$` or `$_REQUEST` outside vendor libraries. Not a single long base64 blob.
* **No stray PHP files** in the asset, upload or temporary directories — apart from the `proxy.php`
described above.
* **No data leaking to the author's hosts.** `vipadmin.club` appeared only in the keywords meta tag
and the mail sender address; `osmp.ga` was a footer link. Nothing sends passwords, licence data or
database credentials anywhere.
* **The admin gate is intact.** Every controller under `application/controllers/admin/**` performs a
`getAccessLevel()` check (level 2 for the admin area, 3 for games and locations). Not one file
without it.
* **The panel never invokes a shell directly.** A naive `grep` reports 54 `exec` and 8 `system`, but
all of them are inside vendor libraries: in phpseclib, `exec()` is an SSH method rather than a
process launcher, and the rest is in elFinder. The panel's own code calls no shell.
---
## 3. Two pre-authentication SQL injections — FIXED
The most serious finding, and one the fork's description says nothing about.
**File:** `panel/application/models/users.php`, function `createAuthLog()`.
The function records login attempts. It used to interpolate values straight into SQL:
```php
$query=$this->db->query("INSERT INTO `authlog` (... ,`ip`, ... ,`password`)
VALUES (NULL, '".$userid."', '".$ip."', ... , '".$password."');");
```
**Vector 1 — via the login form.** `$password` is the raw password from `$_POST`, unescaped. It is
called from `account/login.php` on **every** login attempt, including failed ones — that is, **before
any authentication**. Anybody off the street could inject SQL through the login form.
**Vector 2 — via a forged header.** `$ip` came from `getRealIpAdress()` (`engine/main/user.php`),
which returned the `CF-Connecting-IP` header **with no validation at all** — the early `return` sat
before the `filter_var` block:
```php
if (!empty($_SERVER["HTTP_CF_CONNECTING_IP"])) {
return $_SERVER["HTTP_CF_CONNECTING_IP"]; // as-is, unvalidated
}
```
That header is supplied by the client, so an attacker fully controlled the value reaching SQL.
The same unvalidated IP was also sent to the external `ip-api.com` service.
**What we did:**
* every value in the `INSERT` now goes through `$this->db->escape()` or is cast to `(int)`;
* `CF-Connecting-IP` is validated with `filter_var($ip, FILTER_VALIDATE_IP)` and otherwise ignored
in favour of `REMOTE_ADDR`;
* the `ip-api.com` request only runs for a valid IP and uses `urlencode()`.
**Verified on a live installation:** injection attempts through the password field and through a
forged header no longer get through, the `authlog` table is intact, and all 27 tables are present.
---
## 4. Plaintext passwords — FIXED
`account/login.php` passed the **real password** from the form into `createAuthLog()`:
```php
$this->usersModel->createAuthLog($userid['user_id'], $ip, '1', $password); // successful login
$this->usersModel->createAuthLog($userid['user_id'], $ip, '0', $password); // and failed ones too
```
So the `authlog` table accumulated every user's password in plaintext — including typos and
passwords for other services that people enter by accident. Anyone with database access could read
them, and given the injection in section 3, so could an outsider.
**What we did:** the password is no longer passed; an empty string is stored instead.
Verified: after both a successful and a failed login, the column is empty.
---
## 5. XSS in the registration form — FIXED
`panel/application/views/common/loginheader.php`: the `?ref=` parameter (referral code) was echoed
into a hidden form field without escaping — `<?echo $_GET['ref']?>`.
`htmlspecialchars(..., ENT_QUOTES, 'UTF-8')` was added, and the controller casts the value to `(int)`.
---
## 6. Passwords stored as unsalted MD5 — NOT fixed
`login.php:114``md5($password)`, column `user_password varchar(32)`.
Unsalted MD5 is brute-forced at billions of hashes per second on a consumer GPU, and rainbow tables
for common passwords are freely available. If the database leaks, treat every user password as known.
We **did not change this**: moving to `password_hash()` touches login, registration, recovery and
password changes, and requires migrating existing users — that is a project, not a one-line edit.
**What to do:** if the panel is used with real people, migrate to `password_hash()` transparently —
when a user logs in successfully against the old MD5 hash, re-save it with the new algorithm.
---
## 7. Code installed onto game nodes over HTTP — source removed
`panel/engine/games/game_settings.php:54-69` — 18 Node.js modules for RAGE:MP are downloaded
**over plain HTTP** from a third-party host, `mc.hostinpl.ru`, as zip archives with no signature and
no checksum verification.
Any intermediary on the path — or the owner of that host — can replace the contents, and the code
then runs on your game nodes. The original installer likewise pulled game server builds from
`dl.und3x.ru` and `vipadmin.club`.
**What we did:** our node installer no longer downloads builds from those hosts — you place the
files into `/home/cp/gameservers/files/<game_code>/` yourself. The lines in `game_settings.php` were
left alone (that is panel functionality), but you should not rely on them: either replace the URLs
with your own over HTTPS, or deploy the modules by hand.
Node.js in our `docker/Dockerfile` is installed from NodeSource **over HTTPS with repository key
verification**.
---
## 8. The panel connects to nodes with the root password — by design
The `locations` table stores `location_user` and `location_password` (`varchar(32)`) **in plaintext**,
and the connection uses `ssh2_auth_password()`. The panel's commands (`useradd`, `docker`,
`chown /home`) require root, so in practice the panel logs into the node as root with a password.
This cannot be changed without reworking the panel: it supports neither keys nor `sudo`. Measures
that reduce the risk:
```bash
# on the node: SSH and MySQL reachable only from the panel's address
ufw allow from PANEL_IP to any port 22 proto tcp
ufw allow from PANEL_IP to any port 3306 proto tcp
ufw deny 3306
```
The node installer prints these commands when it finishes. Additionally: use a separate password per
node, and keep access to the panel's database to a minimum — whoever reads `locations` gets root on
every node.
---
## 9. MariaDB on the node listens on all interfaces — the panel requires it
The panel creates game server databases on the node itself and connects to them over the network,
so `bind-address = 0.0.0.0` is mandatory. Our installer does that but **prints a warning** and the
firewall commands — unlike the original, which changed the setting silently.
---
## 10. The registration e-mail contains the password
`panel/application/views/mail/account/register.php` sends the new user their password in plaintext by
e-mail. That is the original design; we did not change it so as not to break the registration and
activation flow. Bear in mind the message stays in the mailbox and on intermediate servers.
---
## Overall assessment
| Item | Status |
|------|--------|
| Working backdoors in the panel code | none found |
| Webshell in the build's lineage | found already defanged, kept as evidence |
| Obfuscation, data exfiltration | none found |
| Admin authorisation bypass | none found |
| Pre-auth SQL injections (2) | **fixed** |
| Plaintext passwords in the database | **fixed** |
| XSS in the registration form | **fixed** |
| Unsalted MD5 | remains, needs separate work |
| Code installed over HTTP onto nodes | source removed from the installer |
| Root password used for node access | by design, mitigate with a firewall |
| Password inside the registration e-mail | remains |
**This panel should not be used for commercial hosting with real clients and payments** — because of
its legal status (see the README), because of the MD5 passwords, and because the provenance of the
code does not deserve trust. For study, internal tasks and auditing, it is fine.
---
**Maintained and developed with [REDL.IO](https://redl.io) — AI-powered hosting.**
-50
View File
@@ -1,50 +0,0 @@
#!/bin/sh
### BEGIN INIT INFO
# Provides: fiverp
# Required-Start: $network mariadb
# Default-Start: 2 3 4 5
# Short-Description: FiveRP (FXServer)
### END INIT INFO
NAME=fiverp
DIR=/opt/fivem
LOG=/opt/fivem/logs/server.log
SUPPID=/run/$NAME.sup.pid
CHILDPID=/run/$NAME.child.pid
status() {
if [ -f $CHILDPID ] && kill -0 "$(cat $CHILDPID)" 2>/dev/null; then
echo "$NAME running (pid $(cat $CHILDPID))"; return 0
fi
echo "$NAME not running"; return 3
}
start() {
if status >/dev/null 2>&1; then echo "$NAME already running"; return 0; fi
service mariadb start >/dev/null 2>&1
i=0
while ! mysqladmin ping >/dev/null 2>&1; do
i=$((i+1)); [ $i -gt 30 ] && echo "warning: mariadb did not come up" && break
sleep 1
done
setsid $DIR/bin/run-server.sh $NAME $DIR $LOG >/dev/null 2>&1 < /dev/null &
sleep 2
echo "$NAME started"
}
stop() {
[ -f $SUPPID ] && kill -TERM "$(cat $SUPPID)" 2>/dev/null
[ -f $CHILDPID ] && kill -TERM "$(cat $CHILDPID)" 2>/dev/null
sleep 2
[ -f $CHILDPID ] && kill -9 "$(cat $CHILDPID)" 2>/dev/null
rm -f $SUPPID $CHILDPID
echo "$NAME stopped"
}
case "$1" in
start) start ;;
stop) stop ;;
restart) stop; start ;;
status) status ;;
*) echo "usage: $0 {start|stop|restart|status}"; exit 1 ;;
esac
-8
View File
@@ -1,8 +0,0 @@
#!/bin/sh
# Send a command to the running FXServer console, e.g. rcon status
FIFO=${FIFO:-/run/fiverp.stdin}
if [ ! -p "$FIFO" ]; then
echo "server is not running (no console pipe at $FIFO)" >&2
exit 1
fi
printf '%s\n' "$*" > "$FIFO"
-43
View File
@@ -1,43 +0,0 @@
#!/bin/sh
# ---------------------------------------------------------------------------
# run-server.sh <name> <dir> <logfile>
#
# Keeps FXServer alive and gives it a console you can talk to.
#
# * FXServer reads its console from stdin and quits the moment stdin hits
# EOF, so it gets a FIFO whose write end this script holds open. That FIFO
# doubles as the command channel used by bin/rcon.
# * A server that dies instantly is usually failing for a reason that will
# not fix itself (bad licence key, database down). The restart delay backs
# off to 5 minutes so a broken server does not hammer Cfx, and resets once
# it has stayed up for a while.
# ---------------------------------------------------------------------------
NAME="$1"; DIR="$2"; LOG="$3"
FIFO="/run/$NAME.stdin"
MIN=5; MAX=300; HEALTHY=120
mkdir -p "$(dirname "$LOG")"
echo $$ > "/run/$NAME.sup.pid"
[ -p "$FIFO" ] || { rm -f "$FIFO"; mkfifo "$FIFO"; }
exec 9<> "$FIFO"
cleanup() { kill "$CHILD" 2>/dev/null; rm -f "/run/$NAME.sup.pid" "/run/$NAME.child.pid"; exit 0; }
trap cleanup TERM INT
delay=$MIN
while true; do
if [ -f "$LOG" ] && [ "$(stat -c %s "$LOG")" -gt 209715200 ]; then mv "$LOG" "$LOG.1"; fi
echo "=== $(date -Is) starting $NAME ===" >> "$LOG"
started=$(date +%s)
( cd "$DIR/server-data" && exec "$DIR/server/run.sh" +exec server.cfg ) >> "$LOG" 2>&1 <&9 &
CHILD=$!
echo $CHILD > "/run/$NAME.child.pid"
wait $CHILD
RC=$?
ran=$(( $(date +%s) - started ))
if [ "$ran" -ge "$HEALTHY" ]; then delay=$MIN; else delay=$(( delay * 2 )); [ "$delay" -gt "$MAX" ] && delay=$MAX; fi
echo "=== $(date -Is) $NAME exited (rc=$RC) after ${ran}s, restarting in ${delay}s ===" >> "$LOG"
sleep "$delay"
done
+48
View File
@@ -0,0 +1,48 @@
# Образ для игровых серверов.
# Поддерживается и развивается с помощью REDL.IO — Хостинг с искусственным интеллектом.
#
# ВАЖНО: образ обязательно собирать с тегом debian:stretch —
# имя жёстко прописано в коде панели (application/models/servers.php,
# команда `docker create ... debian:stretch`). Внутри это современный
# Debian 12, а не мёртвый stretch: репозитории stretch отключены с 2023 года,
# оригинальный Dockerfile из-за этого больше не собирается.
#
# docker build -t debian:stretch -f Dockerfile .
#
FROM debian:bookworm-slim
ENV DEBIAN_FRONTEND=noninteractive
# 32-битные библиотеки: SA-MP, CRMP, MTA и старые сборки CS собраны под i386
RUN dpkg --add-architecture i386 \
&& apt-get update \
&& apt-get install -y --no-install-recommends \
libstdc++6:i386 libgcc-s1:i386 zlib1g:i386 libncurses6:i386 libtinfo6:i386 \
lib32stdc++6 lib32gcc-s1 \
&& rm -rf /var/lib/apt/lists/*
# базовое окружение игровых серверов: screen для консолей, gdb для крашдампов
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
screen curl wget ca-certificates gnupg unzip tar xz-utils \
procps net-tools iproute2 tzdata locales \
gdb libreadline8 libbabeltrace1 libdw1 libc6-dbg \
gcc g++ make python3 \
&& rm -rf /var/lib/apt/lists/*
# Java для Minecraft (Paper, Spigot, Nukkit)
RUN apt-get update \
&& apt-get install -y --no-install-recommends default-jre-headless \
&& rm -rf /var/lib/apt/lists/*
# Node.js LTS для RAGE:MP и MTA-скриптов
RUN curl -fsSL https://deb.nodesource.com/setup_20.x | bash - \
&& apt-get install -y --no-install-recommends nodejs \
&& rm -rf /var/lib/apt/lists/*
# каталог, куда панель монтирует файлы сервера (/home/gs<id> → /home/container)
RUN mkdir -p /home/container
WORKDIR /home/container
ENV TZ=Europe/Moscow
CMD ["/bin/bash"]
+19
View File
@@ -0,0 +1,19 @@
FROM debian:stretch
RUN echo "deb http://deb.debian.org/debian stretch main" > /etc/apt/sources.list \
&& echo "deb-src http://deb.debian.org/debian stretch main" >> /etc/apt/sources.list \
&& echo "deb http://security.debian.org/debian-security stretch/updates main" >> /etc/apt/sources.list \
&& echo "deb-src http://security.debian.org/debian-security stretch/updates main" >> /etc/apt/sources.list \
&& echo "deb http://deb.debian.org/debian stretch-updates main" >> /etc/apt/sources.list \
&& echo "deb-src http://deb.debian.org/debian stretch-updates main" >> /etc/apt/sources.list
RUN dpkg --add-architecture i386
RUN apt-get update && apt-get install -y libstdc++6:i386 libgcc1:i386 zlib1g:i386 libncurses5:i386
RUN apt-get install -y libbabeltrace1 libc6-dbg libdw1 lib32stdc++6 libreadline5 gdb-minimal
RUN apt-get update && apt-get install -y gnupg screen apt-transport-https curl
RUN echo 'deb https://deb.nodesource.com/node_14.x stretch main' > /etc/apt/sources.list.d/nodesource.list \
&& echo 'deb-src https://deb.nodesource.com/node_14.x stretch main' >> /etc/apt/sources.list.d/nodesource.list \
&& curl -sLf -o /dev/null 'https://deb.nodesource.com/node_14.x/dists/stretch/Release' \
&& curl -s https://deb.nodesource.com/gpgkey/nodesource.gpg.key | apt-key add - \
&& curl -sL https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - \
&& echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list
RUN apt-get update
RUN apt-get install -y gcc g++ make nodejs yarn
-303
View File
@@ -1,303 +0,0 @@
# FiveRP — Server Design System
Единый стиль для **всего** сервера: loading screen, авторизация, будущие меню
(инвентарь, телефон, банк, HUD). Основан на белой «стеклянной» теме Filmorum
(`fiveM_design.md`), адаптирован под CEF/NUI FiveM.
> Правило номер один: **ни один компонент не хардкодит цвет, радиус или шрифт.**
> Только `var(--…)` из блока токенов ниже. Смена темы = одна строка.
---
## 0. Шрифт — Unbounded
**Unbounded — основной шрифт всего сервера.** Геометрический дисплейный гротеск,
переменная ось веса 300–800. Никаких других сансов в интерфейсе.
Файлы лежат в каждом ресурсе локально (`html/fonts/`), а НЕ грузятся с
`fonts.googleapis.com`: у части игроков CEF стартует раньше сети, и веб-шрифт
успевает не приехать — интерфейс мигает системным шрифтом. Локальный woff2
рисуется с первого кадра.
```css
@font-face {
font-family: 'Unbounded';
font-style: normal;
font-weight: 300 800; /* вариативный: один файл на все веса */
font-display: block; /* block, не swap — подмены шрифта быть не должно */
src: url('fonts/unbounded-latin.woff2') format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6,
U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F,
U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* + unbounded-latin-ext.woff2 и unbounded-cyrillic.woff2 теми же блоками */
```
```css
--sans: 'Unbounded', -apple-system, BlinkMacSystemFont, 'Segoe UI',
Roboto, 'Helvetica Neue', Arial, sans-serif;
--mono: ui-monospace, 'SF Mono', SFMono-Regular, Menlo, Consolas,
'Liberation Mono', 'DejaVu Sans Mono', monospace;
```
**Как набирать Unbounded.** Шрифт широкий и характерный — он требует другой
типографики, чем SF Pro:
| Роль | Размер | Вес | Трекинг |
|---|---|---|---|
| Display (лого, экран загрузки) | 44–72px | 800 | `-0.04em` |
| H1 (заголовок панели) | 26–30px | 700 | `-0.035em` |
| H2 (секция) | 1820px | 600 | `-0.03em` |
| Кнопка / линк | 14px | 600 | `-0.01em` |
| Body / инпут | 1415px | 400 | `-0.005em` |
| Лейбл поля | 11px | 600 | `+0.09em`, `uppercase` |
| Eyebrow / надзаголовок | 10px | 600 | `+0.20em`, `uppercase` |
| Метаданные | 1112px | 400 | `0` |
Правила:
- **Крупный кегль = минусовой трекинг.** У Unbounded широкие апроши; без
`letter-spacing: -0.03em` заголовок расползается.
- **Мелкий кегль = плюсовой трекинг + uppercase.** Ниже 12px строчные буквы
Unbounded слипаются — все подписи только капслоком с разрядкой.
- **Веса 300 и 800 — крайности**, в интерфейсе живут 400/500/600/700.
- **Не набирать Unbounded длинный текст.** Абзац больше двух строк — читать
тяжело. Для многострочных описаний кегль 13px/1.65 и вес 400, не больше.
- **Цифры документов, ID, MRZ — только `--mono`.** Unbounded для них слишком
«дизайнерский», а нужен вид машинного считывания.
---
## 1. Философия
- **Air, light, frosted** — полупрозрачные стеклянные панели поверх живой игры.
- **Кислород** — между карточками 12px, между секциями 40–48px.
- **Один акцент** — синий `#0071e3`, дозированно: кнопка, ссылка, фокус.
- **Сдержанное движение** — fade и подъём на 4px. Анимации ради анимации нет.
- **Один герой на экран** — крупный объект (паспорт, лого), остальное тихое.
---
## 2. Токены
```css
:root {
--bg: #f5f5f7;
--bg-deep: #e8e8ed;
--surface: rgba(255, 255, 255, 0.72);
--surface-solid:#ffffff;
--panel: rgba(255, 255, 255, 0.80);
--header-bg: rgba(255, 255, 255, 0.70);
--field-bg: rgba(0, 0, 0, 0.04);
--field-bg-focus:#ffffff;
--fg: #1d1d1f;
--fg-secondary: #6e6e73;
--fg-tertiary: #a1a1a6;
--accent: #0071e3;
--accent-strong:#0077ed;
--accent-soft: rgba(0, 113, 227, 0.08);
--ring: rgba(0, 113, 227, 0.18);
--line: rgba(0, 0, 0, 0.06);
--line-strong: rgba(0, 0, 0, 0.10);
--hover: rgba(0, 0, 0, 0.04);
--success: #34c759;
--danger: #ff3b30;
--radius-lg: 1.5rem; /* 24px — крупные панели */
--radius-md: 1rem; /* 16px — карточки, инпуты */
--radius-sm: 0.7rem; /* 11px — мелочь */
--ease: cubic-bezier(0.25, 0.8, 0.25, 1);
color-scheme: light;
}
```
Тёмная и бежевая темы — через `data-theme` на `html`, переопределением тех же
токенов (`--bg`, `--surface`, `--fg`, `--accent`, `--success`, `--danger`).
---
## 3. КРИТИЧНО: особенности CEF во FiveM
Это не «хорошо бы», а условия, при нарушении которых интерфейс ломается
в игре, оставаясь идеальным в браузере.
1. **`backdrop-filter` и `box-shadow` не живут на одном элементе.**
Композитор CEF рисует тень такого элемента непрозрачным прямоугольником —
в игре под панелью появляется чёрный квадрат. Если нужны и блюр, и тень —
тень вешать на элемент-обёртку, а блюр на внутренний. По умолчанию
**у стеклянных панелей тени нет вообще**: их отделяет от игры блюр и
светлая граница, этого достаточно.
2. **`html, body { background: transparent }`** — иначе NUI закрашивает игру.
3. **Никаких затемняющих подложек.** Экран за панелью — это игра, её видно
и она красивая. Контраст добирается блюром и белой заливкой стекла, а не
`rgba(0,0,0,…)` поверх кадра. Допустим только **осветляющий** вуаль:
`radial-gradient(…, rgba(255,255,255,0.28), transparent)`.
4. **Шрифты — локальные woff2.** См. раздел 0.
5. **`prefers-reduced-motion`** — обязательный блок с самого начала.
6. **Курсор и фокус**`SetNuiFocus(true, true)` только когда панель открыта,
и обязательно `SetNuiFocus(false, false)` при закрытии, иначе игрок
остаётся без управления.
---
## 4. Стеклянная панель
```css
.glass {
background: var(--surface);
backdrop-filter: saturate(180%) blur(24px);
-webkit-backdrop-filter: saturate(180%) blur(24px);
border: 1px solid rgba(255, 255, 255, 0.55);
border-radius: var(--radius-lg);
/* без box-shadow — см. раздел 3.1 */
}
```
Тень разрешена только элементам **без** `backdrop-filter` (карточки на
непрозрачном фоне, экран загрузки):
```css
.shadow-soft { box-shadow: 0 1px 2px rgba(0,0,0,.03), 0 12px 40px -12px rgba(0,0,0,.12); }
.shadow-lift { box-shadow: 0 2px 4px rgba(0,0,0,.04), 0 24px 50px -16px rgba(0,0,0,.20); }
```
Ховер кликабельной карточки: `transform: translateY(-4px)`, 0.4s `--ease`.
---
## 5. Кнопки
```css
.btn-primary {
border-radius: 999px;
padding: 12px 24px;
font: 600 14px var(--sans);
letter-spacing: -0.01em;
color: #fff;
background-image: linear-gradient(180deg, var(--accent-strong), var(--accent));
box-shadow: 0 1px 1px rgba(0,0,0,.10), inset 0 1px 0 rgba(255,255,255,.20);
}
.btn-primary:hover { filter: brightness(1.08); }
.btn-primary:active { transform: scale(0.97); }
.btn-primary:disabled { opacity: .5; pointer-events: none; }
.btn-ghost {
border-radius: 999px;
padding: 10px 20px;
font: 600 14px var(--sans);
color: var(--accent);
background: var(--accent-soft);
}
.btn-ghost:hover { background: rgba(0,113,227,.14); }
```
Ровно два стиля кнопок на весь сервер. Третий не заводить.
---
## 6. Поля ввода
```css
.field {
width: 100%;
border-radius: var(--radius-md);
padding: 13px 14px;
font: 400 15px var(--sans);
letter-spacing: -0.005em;
color: var(--fg);
background: var(--field-bg);
border: 1px solid transparent;
outline: none;
transition: background-color .2s, border-color .2s, box-shadow .2s;
}
.field::placeholder { color: var(--fg-tertiary); }
.field:focus {
background: var(--field-bg-focus);
border-color: var(--accent);
box-shadow: 0 0 0 4px var(--ring); /* можно: у инпута нет backdrop-filter */
}
.field.is-bad { border-color: var(--danger); box-shadow: 0 0 0 4px rgba(255,59,48,.14); }
```
Лейбл над полем — 11px/600/`+0.09em`/uppercase/`--fg-secondary`.
---
## 7. Компоновка
- Панель по центру экрана, ширина `min(940px, 92vw)`, радиус `--radius-lg`.
- Внутренние отступы панели 32–40px.
- Между полями формы 16px, между группами 24px.
- Двухколоночная схема «форма + герой»: `grid-template-columns: minmax(0,1fr) 420px`.
- Сворачивание в одну колонку — анимацией `grid-template-columns` и `width`,
620ms `--ease`.
---
## 8. Motion
```css
.fade-up { animation: fadeUp .6s cubic-bezier(.22,1,.36,1) both; }
.delay-1 { animation-delay: .06s } .delay-2 { animation-delay: .12s }
.delay-3 { animation-delay: .18s } .delay-4 { animation-delay: .24s }
@keyframes fadeUp {
from { opacity: 0; transform: translateY(18px) scale(.995); filter: blur(6px); }
to { opacity: 1; transform: none; filter: blur(0); }
}
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: .001ms !important;
transition-duration: .001ms !important;
}
}
```
---
## 9. Фокус и доступность
```css
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 6px; }
::selection { background: var(--accent-soft); color: var(--accent-strong); }
```
Контраст текста к стеклу — не ниже 4.5:1. `--fg-tertiary` только для
плейсхолдеров и декоративных подписей, никогда для смысловых.
---
## 10. Существующие интерфейсы
| Ресурс | Что это | Файлы |
|---|---|---|
| `fiverp-loadscreen` | экран загрузки сервера | `html/index.html`, `style.css`, `app.js` |
| `fiverp-auth` | регистрация и вход, паспорт США | `html/index.html`, `style.css`, `app.js` |
`fiverp-auth` использует один «герой» — разворот паспорта США (страница с
данными: фото, поля, MRZ). Паспорт — единственный насыщенный цветом объект
на экране; форма рядом с ним остаётся полностью нейтральной.
---
## 11. Чек-лист нового интерфейса
1. Скопировать `html/fonts/` и блок `:root` из `fiverp-auth`.
2. `html, body { background: transparent }`.
3. Панель — `.glass`, **без** `box-shadow`.
4. Ни одного затемняющего слоя поверх игры.
5. Шрифт Unbounded, трекинг по таблице из раздела 0.
6. Акцент только `--accent`; кнопки только `.btn-primary` / `.btn-ghost`.
7. Подключить `prefers-reduced-motion`.
8. `SetNuiFocus(false, false)` на закрытии — проверить руками.
9. Отрендерить в headless Chrome и **посмотреть глазами** до выката.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 823 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 645 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 790 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 669 KiB

+58
View File
@@ -0,0 +1,58 @@
<p align="right">
<a href="README.ru.md"><img alt="Читать по-русски" src="https://img.shields.io/badge/%F0%9F%87%B7%F0%9F%87%BA%20%D0%A7%D0%B8%D1%82%D0%B0%D1%82%D1%8C%20%D0%BF%D0%BE--%D1%80%D1%83%D1%81%D1%81%D0%BA%D0%B8-2867c4?style=for-the-badge"></a>
</p>
# HostinPL 5.6 · Panel screenshots
Every section of the panel, captured on a live install — one shot per section, in both languages.
The English interface is on the left and the same screen in Russian on the right: the language is
switched with a button in the header and detected from the browser automatically.
Shots are 1600 pixels wide; click an image to open it full size.
[← back to the README](../../README.md)
| English | Russian |
|---|---|
| **Panel login**<br>[<img src="en/00-login.png" width="470">](en/00-login.png) | **Panel login**<br>[<img src="ru/00-login.png" width="470">](ru/00-login.png) |
| **Registration — no captcha**<br>[<img src="en/00b-register.png" width="470">](en/00b-register.png) | **Registration — no captcha**<br>[<img src="ru/00b-register.png" width="470">](ru/00b-register.png) |
| **Password recovery**<br>[<img src="en/00c-password-reset.png" width="470">](en/00c-password-reset.png) | **Password recovery**<br>[<img src="ru/00c-password-reset.png" width="470">](ru/00c-password-reset.png) |
| **Client area**<br>[<img src="en/01-cabinet.png" width="470">](en/01-cabinet.png) | **Client area**<br>[<img src="ru/01-cabinet.png" width="470">](ru/01-cabinet.png) |
| **My game servers**<br>[<img src="en/02-servers.png" width="470">](en/02-servers.png) | **My game servers**<br>[<img src="ru/02-servers.png" width="470">](ru/02-servers.png) |
| **Ordering a game server**<br>[<img src="en/03-server-order.png" width="470">](en/03-server-order.png) | **Ordering a game server**<br>[<img src="ru/03-server-order.png" width="470">](ru/03-server-order.png) |
| **Hosting news**<br>[<img src="en/04-news.png" width="470">](en/04-news.png) | **Hosting news**<br>[<img src="ru/04-news.png" width="470">](ru/04-news.png) |
| **Server status**<br>[<img src="en/05-status.png" width="470">](en/05-status.png) | **Server status**<br>[<img src="ru/05-status.png" width="470">](ru/05-status.png) |
| **Support tickets**<br>[<img src="en/06-tickets.png" width="470">](en/06-tickets.png) | **Support tickets**<br>[<img src="ru/06-tickets.png" width="470">](ru/06-tickets.png) |
| **Creating a ticket**<br>[<img src="en/07-ticket-create.png" width="470">](en/07-ticket-create.png) | **Creating a ticket**<br>[<img src="ru/07-ticket-create.png" width="470">](ru/07-ticket-create.png) |
| **FAQ — client help**<br>[<img src="en/08-faq.png" width="470">](en/08-faq.png) | **FAQ — client help**<br>[<img src="ru/08-faq.png" width="470">](ru/08-faq.png) |
| **Web hosting**<br>[<img src="en/09-webhosting.png" width="470">](en/09-webhosting.png) | **Web hosting**<br>[<img src="ru/09-webhosting.png" width="470">](ru/09-webhosting.png) |
| **Ordering web hosting**<br>[<img src="en/10-webhosting-order.png" width="470">](en/10-webhosting-order.png) | **Ordering web hosting**<br>[<img src="ru/10-webhosting-order.png" width="470">](ru/10-webhosting-order.png) |
| **Admin — dashboard**<br>[<img src="en/11-admin-dashboard.png" width="470">](en/11-admin-dashboard.png) | **Admin — dashboard**<br>[<img src="ru/11-admin-dashboard.png" width="470">](ru/11-admin-dashboard.png) |
| **Admin — users**<br>[<img src="en/12-admin-users.png" width="470">](en/12-admin-users.png) | **Admin — users**<br>[<img src="ru/12-admin-users.png" width="470">](ru/12-admin-users.png) |
| **Admin — game servers**<br>[<img src="en/13-admin-servers.png" width="470">](en/13-admin-servers.png) | **Admin — game servers**<br>[<img src="ru/13-admin-servers.png" width="470">](ru/13-admin-servers.png) |
| **Admin — game catalogue**<br>[<img src="en/14-admin-games.png" width="470">](en/14-admin-games.png) | **Admin — game catalogue**<br>[<img src="ru/14-admin-games.png" width="470">](ru/14-admin-games.png) |
| **Admin — game locations (nodes)**<br>[<img src="en/15-admin-locations.png" width="470">](en/15-admin-locations.png) | **Admin — game locations (nodes)**<br>[<img src="ru/15-admin-locations.png" width="470">](ru/15-admin-locations.png) |
| **Admin — mods**<br>[<img src="en/16-admin-mods.png" width="470">](en/16-admin-mods.png) | **Admin — mods**<br>[<img src="ru/16-admin-mods.png" width="470">](ru/16-admin-mods.png) |
| **Admin — file repository**<br>[<img src="en/17-admin-repo.png" width="470">](en/17-admin-repo.png) | **Admin — file repository**<br>[<img src="ru/17-admin-repo.png" width="470">](ru/17-admin-repo.png) |
| **Admin — tickets**<br>[<img src="en/18-admin-tickets.png" width="470">](en/18-admin-tickets.png) | **Admin — tickets**<br>[<img src="ru/18-admin-tickets.png" width="470">](ru/18-admin-tickets.png) |
| **Admin — ticket categories**<br>[<img src="en/19-admin-ticket-categories.png" width="470">](en/19-admin-ticket-categories.png) | **Admin — ticket categories**<br>[<img src="ru/19-admin-ticket-categories.png" width="470">](ru/19-admin-ticket-categories.png) |
| **Admin — payments**<br>[<img src="en/20-admin-invoices.png" width="470">](en/20-admin-invoices.png) | **Admin — payments**<br>[<img src="ru/20-admin-invoices.png" width="470">](ru/20-admin-invoices.png) |
| **Admin — promo codes**<br>[<img src="en/21-admin-promo.png" width="470">](en/21-admin-promo.png) | **Admin — promo codes**<br>[<img src="ru/21-admin-promo.png" width="470">](ru/21-admin-promo.png) |
| **Admin — news**<br>[<img src="en/22-admin-news.png" width="470">](en/22-admin-news.png) | **Admin — news**<br>[<img src="ru/22-admin-news.png" width="470">](ru/22-admin-news.png) |
| **Admin — site enquiries**<br>[<img src="en/23-admin-infobox.png" width="470">](en/23-admin-infobox.png) | **Admin — site enquiries**<br>[<img src="ru/23-admin-infobox.png" width="470">](ru/23-admin-infobox.png) |
| **Admin — statistics and charts**<br>[<img src="en/24-admin-statistics.png" width="470">](en/24-admin-statistics.png) | **Admin — statistics and charts**<br>[<img src="ru/24-admin-statistics.png" width="470">](ru/24-admin-statistics.png) |
| **Admin — system check**<br>[<img src="en/25-admin-checksys.png" width="470">](en/25-admin-checksys.png) | **Admin — system check**<br>[<img src="ru/25-admin-checksys.png" width="470">](ru/25-admin-checksys.png) |
| **Settings — general**<br>[<img src="en/26-admin-settings.png" width="470">](en/26-admin-settings.png) | **Settings — general**<br>[<img src="ru/26-admin-settings.png" width="470">](ru/26-admin-settings.png) |
| **Settings — payment gateways**<br>[<img src="en/27-admin-settings-payments.png" width="470">](en/27-admin-settings-payments.png) | **Settings — payment gateways**<br>[<img src="ru/27-admin-settings-payments.png" width="470">](ru/27-admin-settings-payments.png) |
| **Settings — the captcha switch**<br>[<img src="en/28-admin-settings-other-captcha.png" width="470">](en/28-admin-settings-other-captcha.png) | **Settings — the captcha switch**<br>[<img src="ru/28-admin-settings-other-captcha.png" width="470">](ru/28-admin-settings-other-captcha.png) |
| **Settings — information and bonuses**<br>[<img src="en/29-admin-settings-info.png" width="470">](en/29-admin-settings-info.png) | **Settings — information and bonuses**<br>[<img src="ru/29-admin-settings-info.png" width="470">](ru/29-admin-settings-info.png) |
| **The language switcher in the header**<br>[<img src="en/30-language-switcher.png" width="470">](en/30-language-switcher.png) | **The language switcher in the header**<br>[<img src="ru/30-language-switcher.png" width="470">](ru/30-language-switcher.png) |
| **Admin — balance transactions**<br>[<img src="en/31-admin-waste.png" width="470">](en/31-admin-waste.png) | **Admin — balance transactions**<br>[<img src="ru/31-admin-waste.png" width="470">](ru/31-admin-waste.png) |
| **Admin — web hosting accounts**<br>[<img src="en/32-admin-webhosting.png" width="470">](en/32-admin-webhosting.png) | **Admin — web hosting accounts**<br>[<img src="ru/32-admin-webhosting.png" width="470">](ru/32-admin-webhosting.png) |
| **Admin — web locations**<br>[<img src="en/33-admin-web-locations.png" width="470">](en/33-admin-web-locations.png) | **Admin — web locations**<br>[<img src="ru/33-admin-web-locations.png" width="470">](ru/33-admin-web-locations.png) |
| **Admin — web hosting plans**<br>[<img src="en/34-admin-web-plans.png" width="470">](en/34-admin-web-plans.png) | **Admin — web hosting plans**<br>[<img src="ru/34-admin-web-plans.png" width="470">](ru/34-admin-web-plans.png) |
| **Admin — e-mail templates**<br>[<img src="en/35-admin-mail-templates.png" width="470">](en/35-admin-mail-templates.png) | **Admin — e-mail templates**<br>[<img src="ru/35-admin-mail-templates.png" width="470">](ru/35-admin-mail-templates.png) |
---
**Maintained and developed with [REDL.IO](https://redl.io) — AI-powered hosting.**
+58
View File
@@ -0,0 +1,58 @@
<p align="right">
<a href="README.md"><img alt="Read in English" src="https://img.shields.io/badge/%F0%9F%87%AC%F0%9F%87%A7%20Read%20in%20English-2867c4?style=for-the-badge"></a>
</p>
# HostinPL 5.6 · Скриншоты панели
Все разделы панели, снятые на живой установке — по одному кадру на раздел, на двух языках.
Слева русский интерфейс, справа тот же экран на английском: язык переключается кнопкой в шапке
и определяется по браузеру автоматически.
Кадры сняты в разрешении 1600 пикселей по ширине; нажмите на картинку, чтобы открыть её целиком.
[← вернуться к описанию](../../README.ru.md)
| Русский | English |
|---|---|
| **Вход в панель**<br>[<img src="ru/00-login.png" width="470">](ru/00-login.png) | **Вход в панель**<br>[<img src="en/00-login.png" width="470">](en/00-login.png) |
| **Регистрация — без капчи**<br>[<img src="ru/00b-register.png" width="470">](ru/00b-register.png) | **Регистрация — без капчи**<br>[<img src="en/00b-register.png" width="470">](en/00b-register.png) |
| **Восстановление пароля**<br>[<img src="ru/00c-password-reset.png" width="470">](ru/00c-password-reset.png) | **Восстановление пароля**<br>[<img src="en/00c-password-reset.png" width="470">](en/00c-password-reset.png) |
| **Личный кабинет**<br>[<img src="ru/01-cabinet.png" width="470">](ru/01-cabinet.png) | **Личный кабинет**<br>[<img src="en/01-cabinet.png" width="470">](en/01-cabinet.png) |
| **Мои игровые серверы**<br>[<img src="ru/02-servers.png" width="470">](ru/02-servers.png) | **Мои игровые серверы**<br>[<img src="en/02-servers.png" width="470">](en/02-servers.png) |
| **Заказ игрового сервера**<br>[<img src="ru/03-server-order.png" width="470">](ru/03-server-order.png) | **Заказ игрового сервера**<br>[<img src="en/03-server-order.png" width="470">](en/03-server-order.png) |
| **Новости хостинга**<br>[<img src="ru/04-news.png" width="470">](ru/04-news.png) | **Новости хостинга**<br>[<img src="en/04-news.png" width="470">](en/04-news.png) |
| **Статус серверов**<br>[<img src="ru/05-status.png" width="470">](ru/05-status.png) | **Статус серверов**<br>[<img src="en/05-status.png" width="470">](en/05-status.png) |
| **Тикеты поддержки**<br>[<img src="ru/06-tickets.png" width="470">](ru/06-tickets.png) | **Тикеты поддержки**<br>[<img src="en/06-tickets.png" width="470">](en/06-tickets.png) |
| **Создание тикета**<br>[<img src="ru/07-ticket-create.png" width="470">](ru/07-ticket-create.png) | **Создание тикета**<br>[<img src="en/07-ticket-create.png" width="470">](en/07-ticket-create.png) |
| **FAQ — справка для клиентов**<br>[<img src="ru/08-faq.png" width="470">](ru/08-faq.png) | **FAQ — справка для клиентов**<br>[<img src="en/08-faq.png" width="470">](en/08-faq.png) |
| **Веб-хостинг**<br>[<img src="ru/09-webhosting.png" width="470">](ru/09-webhosting.png) | **Веб-хостинг**<br>[<img src="en/09-webhosting.png" width="470">](en/09-webhosting.png) |
| **Заказ веб-хостинга**<br>[<img src="ru/10-webhosting-order.png" width="470">](ru/10-webhosting-order.png) | **Заказ веб-хостинга**<br>[<img src="en/10-webhosting-order.png" width="470">](en/10-webhosting-order.png) |
| **Админка — главная**<br>[<img src="ru/11-admin-dashboard.png" width="470">](ru/11-admin-dashboard.png) | **Админка — главная**<br>[<img src="en/11-admin-dashboard.png" width="470">](en/11-admin-dashboard.png) |
| **Админка — пользователи**<br>[<img src="ru/12-admin-users.png" width="470">](ru/12-admin-users.png) | **Админка — пользователи**<br>[<img src="en/12-admin-users.png" width="470">](en/12-admin-users.png) |
| **Админка — игровые серверы**<br>[<img src="ru/13-admin-servers.png" width="470">](ru/13-admin-servers.png) | **Админка — игровые серверы**<br>[<img src="en/13-admin-servers.png" width="470">](en/13-admin-servers.png) |
| **Админка — каталог игр**<br>[<img src="ru/14-admin-games.png" width="470">](ru/14-admin-games.png) | **Админка — каталог игр**<br>[<img src="en/14-admin-games.png" width="470">](en/14-admin-games.png) |
| **Админка — игровые локации (ноды)**<br>[<img src="ru/15-admin-locations.png" width="470">](ru/15-admin-locations.png) | **Админка — игровые локации (ноды)**<br>[<img src="en/15-admin-locations.png" width="470">](en/15-admin-locations.png) |
| **Админка — моды**<br>[<img src="ru/16-admin-mods.png" width="470">](ru/16-admin-mods.png) | **Админка — моды**<br>[<img src="en/16-admin-mods.png" width="470">](en/16-admin-mods.png) |
| **Админка — репозиторий файлов**<br>[<img src="ru/17-admin-repo.png" width="470">](ru/17-admin-repo.png) | **Админка — репозиторий файлов**<br>[<img src="en/17-admin-repo.png" width="470">](en/17-admin-repo.png) |
| **Админка — тикеты**<br>[<img src="ru/18-admin-tickets.png" width="470">](ru/18-admin-tickets.png) | **Админка — тикеты**<br>[<img src="en/18-admin-tickets.png" width="470">](en/18-admin-tickets.png) |
| **Админка — категории тикетов**<br>[<img src="ru/19-admin-ticket-categories.png" width="470">](ru/19-admin-ticket-categories.png) | **Админка — категории тикетов**<br>[<img src="en/19-admin-ticket-categories.png" width="470">](en/19-admin-ticket-categories.png) |
| **Админка — платежи**<br>[<img src="ru/20-admin-invoices.png" width="470">](ru/20-admin-invoices.png) | **Админка — платежи**<br>[<img src="en/20-admin-invoices.png" width="470">](en/20-admin-invoices.png) |
| **Админка — промокоды**<br>[<img src="ru/21-admin-promo.png" width="470">](ru/21-admin-promo.png) | **Админка — промокоды**<br>[<img src="en/21-admin-promo.png" width="470">](en/21-admin-promo.png) |
| **Админка — новости**<br>[<img src="ru/22-admin-news.png" width="470">](ru/22-admin-news.png) | **Админка — новости**<br>[<img src="en/22-admin-news.png" width="470">](en/22-admin-news.png) |
| **Админка — обращения с сайта**<br>[<img src="ru/23-admin-infobox.png" width="470">](ru/23-admin-infobox.png) | **Админка — обращения с сайта**<br>[<img src="en/23-admin-infobox.png" width="470">](en/23-admin-infobox.png) |
| **Админка — статистика и графики**<br>[<img src="ru/24-admin-statistics.png" width="470">](ru/24-admin-statistics.png) | **Админка — статистика и графики**<br>[<img src="en/24-admin-statistics.png" width="470">](en/24-admin-statistics.png) |
| **Админка — проверка системы**<br>[<img src="ru/25-admin-checksys.png" width="470">](ru/25-admin-checksys.png) | **Админка — проверка системы**<br>[<img src="en/25-admin-checksys.png" width="470">](en/25-admin-checksys.png) |
| **Настройки — общие**<br>[<img src="ru/26-admin-settings.png" width="470">](ru/26-admin-settings.png) | **Настройки — общие**<br>[<img src="en/26-admin-settings.png" width="470">](en/26-admin-settings.png) |
| **Настройки — платёжные системы**<br>[<img src="ru/27-admin-settings-payments.png" width="470">](ru/27-admin-settings-payments.png) | **Настройки — платёжные системы**<br>[<img src="en/27-admin-settings-payments.png" width="470">](en/27-admin-settings-payments.png) |
| **Настройки — переключатель капчи**<br>[<img src="ru/28-admin-settings-other-captcha.png" width="470">](ru/28-admin-settings-other-captcha.png) | **Настройки — переключатель капчи**<br>[<img src="en/28-admin-settings-other-captcha.png" width="470">](en/28-admin-settings-other-captcha.png) |
| **Настройки — информация и бонусы**<br>[<img src="ru/29-admin-settings-info.png" width="470">](ru/29-admin-settings-info.png) | **Настройки — информация и бонусы**<br>[<img src="en/29-admin-settings-info.png" width="470">](en/29-admin-settings-info.png) |
| **Переключатель языка в шапке**<br>[<img src="ru/30-language-switcher.png" width="470">](ru/30-language-switcher.png) | **Переключатель языка в шапке**<br>[<img src="en/30-language-switcher.png" width="470">](en/30-language-switcher.png) |
| **Админка — операции по балансу**<br>[<img src="ru/31-admin-waste.png" width="470">](ru/31-admin-waste.png) | **Админка — операции по балансу**<br>[<img src="en/31-admin-waste.png" width="470">](en/31-admin-waste.png) |
| **Админка — веб-хостинги**<br>[<img src="ru/32-admin-webhosting.png" width="470">](ru/32-admin-webhosting.png) | **Админка — веб-хостинги**<br>[<img src="en/32-admin-webhosting.png" width="470">](en/32-admin-webhosting.png) |
| **Админка — веб-локации**<br>[<img src="ru/33-admin-web-locations.png" width="470">](ru/33-admin-web-locations.png) | **Админка — веб-локации**<br>[<img src="en/33-admin-web-locations.png" width="470">](en/33-admin-web-locations.png) |
| **Админка — тарифы веб-хостинга**<br>[<img src="ru/34-admin-web-plans.png" width="470">](ru/34-admin-web-plans.png) | **Админка — тарифы веб-хостинга**<br>[<img src="en/34-admin-web-plans.png" width="470">](en/34-admin-web-plans.png) |
| **Админка — шаблоны писем**<br>[<img src="ru/35-admin-mail-templates.png" width="470">](ru/35-admin-mail-templates.png) | **Админка — шаблоны писем**<br>[<img src="en/35-admin-mail-templates.png" width="470">](en/35-admin-mail-templates.png) |
---
**Поддерживается и развивается с помощью [REDL.IO](https://redl.io) — Хостинг с искусственным интеллектом.**
Binary file not shown.

After

Width:  |  Height:  |  Size: 382 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 377 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 361 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 264 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 205 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 380 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 376 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 362 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 137 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 274 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 209 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 123 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

+252
View File
@@ -0,0 +1,252 @@
#!/usr/bin/env bash
#
# Установка игровой ноды (локации) в один клик.
# Поддерживается и развивается с помощью REDL.IO — Хостинг с искусственным интеллектом.
#
# Нода — это машина, на которой реально крутятся игровые сервера.
# Панель подключается к ней по SSH и создаёт docker-контейнер на каждый сервер.
#
# Что делает скрипт:
# 1. Проверяет, что docker здесь вообще может работать (в контейнерных VPS — не может)
# 2. Ставит docker, собирает образ debian:stretch (имя ждёт панель)
# 3. Создаёт раскладку /home/cp/gameservers/files и группу gameservers
# 4. Ставит MariaDB для баз игровых серверов и SteamCMD
# 5. Настраивает SSH и ProFTPD так, как ожидает панель
# 6. Печатает данные для подключения локации в админке
#
# Запуск: sudo bash install-node.sh
#
set -euo pipefail
C_OK=$'\033[1;32m'; C_ERR=$'\033[1;31m'; C_INF=$'\033[1;36m'; C_W=$'\033[1;33m'; C_0=$'\033[0m'
step() { printf '%s»%s %s\n' "$C_INF" "$C_0" "$1"; }
ok() { printf ' %s✓%s %s\n' "$C_OK" "$C_0" "$1"; }
warn() { printf ' %s!%s %s\n' "$C_W" "$C_0" "$1"; }
die() { printf '%s✗ %s%s\n' "$C_ERR" "$1" "$C_0" >&2; exit 1; }
[[ $EUID -eq 0 ]] || die "Запустите скрипт от root: sudo bash $0"
SRC="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
printf '\n%s=== Игровая нода · установка ===%s\n' "$C_INF" "$C_0"
printf 'Поддерживается и развивается с помощью REDL.IO — Хостинг с искусственным интеллектом\n\n'
# ── 0. проверка пригодности машины ───────────────────────────────────────────
step "Проверяю, может ли эта машина запускать docker…"
CAPFAIL=0
if ! unshare -Ur true 2>/dev/null; then
CAPFAIL=1
warn "Ядро запрещает вложенные namespace (unshare не работает)"
fi
if [[ ! -e /dev/fuse && $CAPFAIL -eq 1 ]]; then
warn "Нет /dev/fuse"
fi
if [[ $CAPFAIL -eq 1 ]]; then
printf '\n%sЭТА МАШИНА НЕ ПОДХОДИТ ПОД ИГРОВУЮ НОДУ.%s\n' "$C_ERR" "$C_0"
printf 'Панель разворачивает каждый игровой сервер отдельным docker-контейнером,\n'
printf 'а здесь docker не запустится: это контейнерный VPS (LXC/OpenVZ или\n'
printf 'аналог) без CAP_SYS_ADMIN. Rootless podman тоже не поможет — ядро\n'
printf 'запрещает вложенные user namespace.\n\n'
printf 'Нужен сервер с полным доступом к ядру: выделенный сервер (dedicated)\n'
printf 'или KVM/VMware-виртуализация. Панель при этом может жить где угодно,\n'
printf 'включая контейнерный VPS — ей docker не нужен.\n\n'
read -rp "Всё равно продолжить установку? (yes/no): " GO
[[ "$GO" == "yes" ]] || exit 1
else
ok "Namespace доступны, docker будет работать"
fi
read -rp "IP этой ноды (как её увидит панель): " NODEIP
[[ -n "$NODEIP" ]] || die "IP не может быть пустым"
read -rp "Разрешить панели вход по SSH под root с паролем? (yes/no): " SSHROOT
. /etc/os-release 2>/dev/null || true
CODENAME="${VERSION_CODENAME:-bookworm}"
DISTRO_ID="${ID:-debian}"
step "Система: ${PRETTY_NAME:-неизвестна}"
export DEBIAN_FRONTEND=noninteractive
# ── 1. базовые пакеты ────────────────────────────────────────────────────────
step "Ставлю базовые пакеты…"
apt-get update -qq
apt-get install -y -qq curl wget ca-certificates gnupg unzip tar sudo pwgen htop \
openssh-server mariadb-server proftpd-basic lib32stdc++6 >/dev/null 2>&1 || \
apt-get install -y -qq curl wget ca-certificates gnupg unzip tar sudo pwgen htop \
openssh-server mariadb-server proftpd lib32stdc++6 >/dev/null
ok "Пакеты установлены"
# ── 2. docker ────────────────────────────────────────────────────────────────
step "Ставлю Docker…"
if command -v docker >/dev/null 2>&1; then
ok "Docker уже установлен: $(docker --version 2>/dev/null | head -1)"
else
install -m 0755 -d /etc/apt/keyrings
curl -fsSL "https://download.docker.com/linux/$DISTRO_ID/gpg" -o /etc/apt/keyrings/docker.asc
chmod a+r /etc/apt/keyrings/docker.asc
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/$DISTRO_ID $CODENAME stable" \
> /etc/apt/sources.list.d/docker.list
apt-get update -qq
apt-get install -y -qq docker-ce docker-ce-cli containerd.io >/dev/null
ok "Docker установлен из официального репозитория"
fi
service docker start >/dev/null 2>&1 || /etc/init.d/docker start >/dev/null 2>&1 || true
sleep 3
docker info >/dev/null 2>&1 || die "Docker-демон не поднялся. Без него нода не заработает"
ok "Docker-демон отвечает"
# ── 3. образ игровых серверов ────────────────────────────────────────────────
step "Собираю образ для игровых серверов (тег debian:stretch — его ждёт панель)…"
if docker image inspect debian:stretch >/dev/null 2>&1; then
ok "Образ debian:stretch уже собран"
else
DF="$SRC/docker/Dockerfile"
[[ -f "$DF" ]] || die "Не найден $DF — распакуйте репозиторий целиком"
docker build -t debian:stretch -f "$DF" "$SRC/docker" >/tmp/redl-docker-build.log 2>&1 \
|| { tail -25 /tmp/redl-docker-build.log; die "Сборка образа не удалась, полный лог: /tmp/redl-docker-build.log"; }
ok "Образ собран (Debian 12 + 32-битные библиотеки + Java + Node.js + screen)"
fi
# ── 4. раскладка каталогов ───────────────────────────────────────────────────
step "Создаю раскладку каталогов…"
groupadd -f gameservers
mkdir -p /home/cp/backups /home/cp/gameservers/files
chown -R root:root /home
chmod 755 /home /home/cp /home/cp/gameservers /home/cp/gameservers/files
chmod 700 /home/cp/backups
ok "/home/cp/gameservers/files — сюда кладутся сборки игр, /home/gs<id> — сами сервера"
# ── 5. база для игровых серверов ─────────────────────────────────────────────
step "Настраиваю MariaDB для баз игровых серверов…"
mkdir -p /run/mysqld && chown mysql:mysql /run/mysqld
if ! mysqladmin ping >/dev/null 2>&1; then
service mariadb start >/dev/null 2>&1 || /etc/init.d/mariadb start >/dev/null 2>&1 || true
for _ in $(seq 1 45); do mysqladmin ping >/dev/null 2>&1 && break; sleep 2; done
fi
mysqladmin ping >/dev/null 2>&1 || die "MariaDB не поднялась"
SQLCNF=/etc/mysql/mariadb.conf.d/50-server.cnf
if [[ -f "$SQLCNF" ]]; then
sed -i 's/^bind-address.*/bind-address = 0.0.0.0/' "$SQLCNF"
grep -q '^max_connections' "$SQLCNF" || sed -i '/^\[mysqld\]/a max_connections = 1000' "$SQLCNF"
fi
DBADMPASS="$(pwgen -cns -1 20)"
mysql -e "CREATE USER IF NOT EXISTS 'gsadmin'@'%' IDENTIFIED BY '$DBADMPASS';
ALTER USER 'gsadmin'@'%' IDENTIFIED BY '$DBADMPASS';
GRANT ALL PRIVILEGES ON *.* TO 'gsadmin'@'%' WITH GRANT OPTION;
FLUSH PRIVILEGES;"
service mariadb restart >/dev/null 2>&1 || /etc/init.d/mariadb restart >/dev/null 2>&1 || true
sleep 3
umask 077; printf 'mysql_user=gsadmin\nmysql_pass=%s\n' "$DBADMPASS" > /root/.redl-node-credentials
warn "MariaDB слушает 0.0.0.0:3306 — так требует панель. ОБЯЗАТЕЛЬНО ограничьте порт файрволом (см. конец вывода)"
ok "Пользователь gsadmin создан, пароль в /root/.redl-node-credentials"
# ── 6. SSH ───────────────────────────────────────────────────────────────────
step "Настраиваю SSH…"
SSHD=/etc/ssh/sshd_config
cp -n "$SSHD" "$SSHD.bak-redl" 2>/dev/null || true
grep -q '^DenyGroups gameservers' "$SSHD" || echo 'DenyGroups gameservers' >> "$SSHD"
if [[ "$SSHROOT" == "yes" ]]; then
sed -i 's/^#\?PermitRootLogin.*/PermitRootLogin yes/' "$SSHD"
sed -i 's/^#\?PasswordAuthentication.*/PasswordAuthentication yes/' "$SSHD"
ROOTPASS="$(pwgen -cns -1 24)"
echo "root:$ROOTPASS" | chpasswd
printf 'ssh_user=root\nssh_pass=%s\n' "$ROOTPASS" >> /root/.redl-node-credentials
ok "Вход root по паролю разрешён, пароль сгенерирован (в /root/.redl-node-credentials)"
warn "Панель хранит этот пароль в своей базе в открытом виде — таково её устройство"
else
ROOTPASS=""
warn "Вход root по паролю не менялся. Панель умеет подключаться ТОЛЬКО по логину и паролю,"
warn "и её команды требуют прав root — иначе создание серверов не заработает"
fi
if ! sshd -t 2>/dev/null; then
cp "$SSHD.bak-redl" "$SSHD"
die "Конфиг sshd не прошёл проверку — откатил, ничего не сломано"
fi
service ssh restart >/dev/null 2>&1 || service sshd restart >/dev/null 2>&1 || /etc/init.d/ssh restart >/dev/null 2>&1 || true
ok "Группе gameservers вход по SSH запрещён (игровые пользователи не зайдут в шелл)"
# ── 7. FTP ───────────────────────────────────────────────────────────────────
step "Настраиваю ProFTPD (доступ клиента к файлам сервера)…"
PFTP=/etc/proftpd/proftpd.conf
if [[ -f "$PFTP" ]]; then
grep -q '^DefaultRoot ~' "$PFTP" || echo 'DefaultRoot ~' >> "$PFTP"
grep -q '^RequireValidShell off' "$PFTP" || echo 'RequireValidShell off' >> "$PFTP"
service proftpd restart >/dev/null 2>&1 || /etc/init.d/proftpd restart >/dev/null 2>&1 || true
ok "Клиент видит только свой каталог (DefaultRoot ~)"
else
warn "Конфиг ProFTPD не найден — FTP не настроен, файловый менеджер панели работать не будет"
fi
# ── 8. SteamCMD ──────────────────────────────────────────────────────────────
step "Ставлю SteamCMD (нужен для CS 1.6 / CS:S и других игр Steam)…"
if [[ -x /root/steamcmd/steamcmd.sh ]]; then
ok "SteamCMD уже установлен"
else
mkdir -p /root/steamcmd && cd /root/steamcmd
if curl -fsSL -o steamcmd_linux.tar.gz https://media.steampowered.com/client/steamcmd_linux.tar.gz; then
tar xzf steamcmd_linux.tar.gz && rm -f steamcmd_linux.tar.gz
ok "SteamCMD в /root/steamcmd"
else
warn "Не удалось скачать SteamCMD — поставьте позже вручную"
fi
cd "$SRC"
fi
# ── 9. сторож docker ─────────────────────────────────────────────────────────
step "Ставлю сторож docker…"
cat > /usr/local/bin/gamenode-guard <<'GUARD'
#!/bin/sh
# Поднимает docker и MariaDB, если упали или машина перезагрузилась.
docker info >/dev/null 2>&1 || service docker start >/dev/null 2>&1
mysqladmin ping >/dev/null 2>&1 || service mariadb start >/dev/null 2>&1
# запускаем контейнеры серверов, помеченных как работающие
for c in $(docker ps -a --filter "name=^gs" --filter "status=exited" --format '{{.Names}}' 2>/dev/null); do
docker start "$c" >/dev/null 2>&1
done
GUARD
chmod +x /usr/local/bin/gamenode-guard
cat > /etc/cron.d/gamenode-guard <<'G'
SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
*/2 * * * * root /usr/local/bin/gamenode-guard >/dev/null 2>&1
@reboot root sleep 30; /usr/local/bin/gamenode-guard >/dev/null 2>&1
G
chmod 644 /etc/cron.d/gamenode-guard
service cron start >/dev/null 2>&1 || /etc/init.d/cron start >/dev/null 2>&1 || true
ok "Проверка раз в 2 минуты + подъём после перезагрузки"
# ── 10. проверка ─────────────────────────────────────────────────────────────
step "Финальная проверка…"
docker image inspect debian:stretch >/dev/null 2>&1 && ok "образ debian:stretch на месте" || warn "образа debian:stretch НЕТ"
[[ -d /home/cp/gameservers/files ]] && ok "каталоги на месте"
getent group gameservers >/dev/null && ok "группа gameservers создана"
mysqladmin ping >/dev/null 2>&1 && ok "MariaDB отвечает"
# проверяем, что контейнер реально создаётся и стартует
if docker run --rm debian:stretch /bin/echo test >/dev/null 2>&1; then
ok "Тестовый контейнер запускается — нода готова принимать игровые сервера"
else
warn "Тестовый контейнер НЕ запустился — проверьте docker вручную"
fi
CPU="$(nproc)"; RAM="$(free -m | awk '/^Mem:/{print $2}')"; HDD="$(df -BG --output=size / | tail -1 | tr -dc '0-9')"
printf '\n%s=== Нода установлена ===%s\n\n' "$C_OK" "$C_0"
printf '%sПодключение локации в панели (Админка → Локации → Добавить):%s\n' "$C_INF" "$C_0"
printf ' IP : %s\n' "$NODEIP"
printf ' Пользователь SSH: root\n'
if [[ -n "$ROOTPASS" ]]; then
printf ' Пароль SSH : %s\n' "$ROOTPASS"
else
printf ' Пароль SSH : (вы отказались менять — укажите действующий пароль root)\n'
fi
printf ' Ресурсы : %s ядер, %s МБ ОЗУ, %s ГБ диска\n\n' "$CPU" "$RAM" "$HDD"
printf ' Данные MySQL для баз игровых серверов — в /root/.redl-node-credentials\n\n'
printf '%sОБЯЗАТЕЛЬНО закройте порты файрволом (замените IP_ПАНЕЛИ на адрес панели):%s\n' "$C_W" "$C_0"
printf ' ufw allow from IP_ПАНЕЛИ to any port 22 proto tcp\n'
printf ' ufw allow from IP_ПАНЕЛИ to any port 3306 proto tcp\n'
printf ' ufw deny 3306\n'
printf ' # порты игровых серверов (например 7777, 22005, 25565) оставьте открытыми\n\n'
printf '%sСборки игр%s кладите в /home/cp/gameservers/files/<код_игры>/\n' "$C_INF" "$C_0"
printf ' Коды игр берите в Админка → Игры. Оригинальные ссылки на сборки в\n'
printf ' установщике вели на сторонние сайты по HTTP — мы их не используем,\n'
printf ' подробности в БЕЗОПАСНОСТЬ.md\n\n'
printf 'Поддерживается и развивается с помощью REDL.IO — Хостинг с искусственным интеллектом\n\n'
+256
View File
@@ -0,0 +1,256 @@
#!/usr/bin/env bash
#
# Установка панели игрового хостинга в один клик.
# Поддерживается и развивается с помощью REDL.IO — Хостинг с искусственным интеллектом.
#
# Что делает скрипт:
# 1. Ставит nginx, PHP 8.x (FPM), MariaDB
# 2. Разворачивает код панели в /var/www/hostinpl
# 3. Создаёт базу, пользователя и загружает схему
# 4. Прописывает конфиг (пароль БД, адрес, токен планировщика)
# 5. Настраивает планировщик (9 заданий) и сторож автозапуска
# 6. Создаёт администратора панели
#
# Запуск: sudo bash install-panel.sh
#
set -euo pipefail
C_OK=$'\033[1;32m'; C_ERR=$'\033[1;31m'; C_INF=$'\033[1;36m'; C_W=$'\033[1;33m'; C_0=$'\033[0m'
step() { printf '%s»%s %s\n' "$C_INF" "$C_0" "$1"; }
ok() { printf ' %s✓%s %s\n' "$C_OK" "$C_0" "$1"; }
warn() { printf ' %s!%s %s\n' "$C_W" "$C_0" "$1"; }
die() { printf '%s✗ %s%s\n' "$C_ERR" "$1" "$C_0" >&2; exit 1; }
[[ $EUID -eq 0 ]] || die "Запустите скрипт от root: sudo bash $0"
SRC="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
[[ -d "$SRC/panel" ]] || die "Рядом со скриптом нет каталога panel/ — распакуйте репозиторий целиком"
WEBROOT=/var/www/hostinpl
DBNAME=hostin
DBUSER=hostinpl
PORT="${PORT:-80}"
printf '\n%s=== Панель игрового хостинга · установка ===%s\n' "$C_INF" "$C_0"
printf 'Поддерживается и развивается с помощью REDL.IO — Хостинг с искусственным интеллектом\n\n'
# ── 0. вопросы ───────────────────────────────────────────────────────────────
read -rp "Домен или IP панели (например panel.example.com): " DOMAIN
[[ -n "$DOMAIN" ]] || die "Адрес не может быть пустым"
read -rp "E-Mail администратора (он же логин в панель): " ADMMAIL
[[ "$ADMMAIL" == *@*.* ]] || die "Похоже, это не e-mail"
read -rsp "Пароль администратора (минимум 6 символов): " ADMPASS; echo
[[ ${#ADMPASS} -ge 6 ]] || die "Пароль короче 6 символов"
if [[ -e "$WEBROOT" ]]; then
read -rp "Каталог $WEBROOT уже существует. Перезаписать код панели? (yes/no): " OW
[[ "$OW" == "yes" ]] || die "Отменено пользователем"
fi
. /etc/os-release 2>/dev/null || true
step "Система: ${PRETTY_NAME:-неизвестна}"
# ── 1. пакеты ────────────────────────────────────────────────────────────────
step "Устанавливаю пакеты (nginx, PHP-FPM, MariaDB)…"
export DEBIAN_FRONTEND=noninteractive
apt-get update -qq
apt-get install -y -qq nginx mariadb-server unzip curl cron pwgen \
php-fpm php-mysql php-mbstring php-curl php-gd php-xml php-zip php-ssh2 >/dev/null
PHPV="$(ls -1 /etc/php/ 2>/dev/null | sort -V | tail -1)"
[[ -n "$PHPV" ]] || die "PHP не установился"
ok "PHP $PHPV, $(nginx -v 2>&1 | sed 's/.*nginx\///')"
# ── 2. службы (в контейнерах systemd может отсутствовать) ────────────────────
step "Запускаю MariaDB…"
mkdir -p /run/mysqld && chown mysql:mysql /run/mysqld
if ! mysqladmin ping >/dev/null 2>&1; then
service mariadb start >/dev/null 2>&1 || /etc/init.d/mariadb start >/dev/null 2>&1 || true
for _ in $(seq 1 45); do mysqladmin ping >/dev/null 2>&1 && break; sleep 2; done
fi
mysqladmin ping >/dev/null 2>&1 || die "MariaDB не поднялась — проверьте /var/log/mysql/"
ok "MariaDB отвечает"
# ── 3. база ──────────────────────────────────────────────────────────────────
step "Создаю базу данных…"
DBPASS="$(pwgen -cns -1 20)"
mysql -e "CREATE DATABASE IF NOT EXISTS \`$DBNAME\` DEFAULT CHARSET utf8;"
mysql -e "CREATE USER IF NOT EXISTS '$DBUSER'@'localhost' IDENTIFIED BY '$DBPASS';
ALTER USER '$DBUSER'@'localhost' IDENTIFIED BY '$DBPASS';
GRANT ALL PRIVILEGES ON \`$DBNAME\`.* TO '$DBUSER'@'localhost';
FLUSH PRIVILEGES;"
ok "База $DBNAME, пользователь $DBUSER (права только на свою базу)"
# ── 4. файлы ─────────────────────────────────────────────────────────────────
step "Копирую код панели в $WEBROOT"
mkdir -p "$WEBROOT"
cp -a "$SRC/panel/." "$WEBROOT/"
if [[ -f "$WEBROOT/hostinpl.sql" ]]; then
TABLES="$(mysql -N -B -e "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema='$DBNAME'")"
if [[ "$TABLES" -lt 5 ]]; then
mysql "$DBNAME" < "$WEBROOT/hostinpl.sql"
ok "Схема загружена: $(mysql -N -B -e "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema='$DBNAME'") таблиц"
else
warn "В базе уже $TABLES таблиц — схему не перезаписываю"
fi
rm -f "$WEBROOT/hostinpl.sql"
fi
# ── 5. конфиг ────────────────────────────────────────────────────────────────
step "Настраиваю конфигурацию…"
CRONTOKEN="$(pwgen -cns -1 24)"
CFG="$WEBROOT/application/config.php"
PROTO=http; [[ "$PORT" == "443" ]] && PROTO=https
URL="$PROTO://$DOMAIN/"; [[ "$PORT" != "80" && "$PORT" != "443" ]] && URL="http://$DOMAIN:$PORT/"
# Значения подставляются через callback, а не строкой замены: иначе символы вида $1
# в сгенерированном пароле PHP принял бы за обратную ссылку и записал бы мусор.
php -r '
$f=$argv[1];$s=file_get_contents($f);
$set=function($s,$key,$val,$anchor=false){
$re="~".($anchor?"^":"")."\x27".preg_quote($key,"~")."\x27(\s*)=>(\s*)\x27[^\x27]*\x27~u".($anchor?"m":"");
return preg_replace_callback($re,function($m)use($key,$val){
return "\x27".$key."\x27".$m[1]."=>".$m[2]."\x27".$val."\x27";
},$s,1);
};
$s=$set($s,"db_username",$argv[2]);
$s=$set($s,"db_password",$argv[3]);
$s=$set($s,"url",$argv[4]);
$s=$set($s,"token",$argv[5],true);
file_put_contents($f,$s);' "$CFG" "$DBUSER" "$DBPASS" "$URL" "$CRONTOKEN"
php -l "$CFG" >/dev/null || die "Конфиг повреждён — установка прервана"
grep -q "'db_password' => *'$DBPASS'" "$CFG" || grep -q "$DBPASS" "$CFG" || die "Пароль БД не попал в конфиг"
umask 077; printf 'db_user=%s\ndb_pass=%s\ncron_token=%s\n' "$DBUSER" "$DBPASS" "$CRONTOKEN" > /root/.redl-panel-credentials
ok "Конфиг записан, креды сохранены в /root/.redl-panel-credentials"
# ── 6. права ─────────────────────────────────────────────────────────────────
step "Выставляю права…"
chown -R www-data:www-data "$WEBROOT"
find "$WEBROOT" -type d -exec chmod 750 {} +
find "$WEBROOT" -type f -exec chmod 640 {} +
chmod -R 770 "$WEBROOT/tmp"
ok "Владелец www-data, конфиг недоступен извне"
# ── 7. PHP ───────────────────────────────────────────────────────────────────
step "Настраиваю PHP $PHPV"
INI="/etc/php/$PHPV/fpm/php.ini"
sed -i 's/^short_open_tag = Off/short_open_tag = On/' "$INI"
sed -i 's/^upload_max_filesize = .*/upload_max_filesize = 90M/' "$INI"
sed -i 's/^post_max_size = .*/post_max_size = 360M/' "$INI"
sed -i 's|^;\?date.timezone =.*|date.timezone = Europe/Moscow|' "$INI"
mkdir -p /run/php
service "php$PHPV-fpm" restart >/dev/null 2>&1 || /etc/init.d/"php$PHPV-fpm" restart >/dev/null 2>&1 || true
sleep 2
[[ -S "/run/php/php$PHPV-fpm.sock" ]] || die "PHP-FPM сокет не создан"
ok "short_open_tag включён (панель использует <? ?>), загрузка до 90 МБ"
# ── 8. nginx ─────────────────────────────────────────────────────────────────
step "Настраиваю nginx (порт $PORT)…"
cat > /etc/nginx/sites-available/hostinpl <<NGINX
server {
listen 0.0.0.0:$PORT default_server;
server_name $DOMAIN _;
root $WEBROOT;
index index.php;
client_max_body_size 360M;
# редиректы без хоста и порта — иначе за обратным прокси уводит на :$PORT
absolute_redirect off;
port_in_redirect off;
location / { try_files \$uri \$uri/ /index.php\$is_args\$args; }
location ~ \.php\$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php$PHPV-fpm.sock;
fastcgi_read_timeout 300;
}
# закрываем служебное
location ~ /\.(ht|git) { deny all; }
location ~* \.(sql|log)\$ { deny all; }
location ^~ /application/config.php { deny all; }
}
NGINX
ln -sfn /etc/nginx/sites-available/hostinpl /etc/nginx/sites-enabled/hostinpl
rm -f /etc/nginx/sites-enabled/default
nginx -t >/dev/null 2>&1 || { nginx -t; die "Конфиг nginx не прошёл проверку"; }
service nginx restart >/dev/null 2>&1 || /etc/init.d/nginx restart >/dev/null 2>&1 || nginx
sleep 1
ok "Сайт слушает 0.0.0.0:$PORT"
# ── 9. планировщик ───────────────────────────────────────────────────────────
step "Настраиваю планировщик…"
B="http://127.0.0.1:$PORT/main/cron"
{
echo 'SHELL=/bin/sh'
echo 'PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin'
echo "0 0 * * * root curl -s -m 300 '$B/index?token=$CRONTOKEN' >/dev/null 2>&1"
echo "* * * * * root curl -s -m 55 '$B/gameServers?token=$CRONTOKEN' >/dev/null 2>&1"
echo "* * * * * root curl -s -m 55 '$B/tasks?token=$CRONTOKEN' >/dev/null 2>&1"
echo "*/10 * * * * root curl -s -m 300 '$B/serverReloader?token=$CRONTOKEN' >/dev/null 2>&1"
echo "*/30 * * * * root curl -s -m 300 '$B/stopServers?token=$CRONTOKEN' >/dev/null 2>&1"
echo "*/30 * * * * root curl -s -m 300 '$B/stopServersQuery?token=$CRONTOKEN' >/dev/null 2>&1"
echo "0 * * * * root curl -s -m 300 '$B/updateStats?token=$CRONTOKEN' >/dev/null 2>&1"
echo "0 * * * * root curl -s -m 300 '$B/updateStatsLocations?token=$CRONTOKEN' >/dev/null 2>&1"
echo "0 3 * * 0 root curl -s -m 300 '$B/clearLogs?token=$CRONTOKEN' >/dev/null 2>&1"
} > /etc/cron.d/hostinpl
chmod 644 /etc/cron.d/hostinpl
service cron start >/dev/null 2>&1 || /etc/init.d/cron start >/dev/null 2>&1 || true
ok "9 заданий панели (статистика, задачи, автоотключение просрочки)"
# ── 10. сторож автозапуска (нужен там, где нет systemd) ──────────────────────
step "Ставлю сторож автозапуска…"
cat > /usr/local/bin/hostinpl-guard <<GUARD
#!/bin/sh
# Поднимает стек панели, если что-то упало или машина перезагрузилась.
pgrep -x mariadbd >/dev/null 2>&1 || pgrep -x mysqld >/dev/null 2>&1 || {
mkdir -p /run/mysqld; chown mysql:mysql /run/mysqld
service mariadb start >/dev/null 2>&1 || /etc/init.d/mariadb start >/dev/null 2>&1
}
pgrep -f "^php-fpm$PHPV: master" >/dev/null 2>&1 || service php$PHPV-fpm start >/dev/null 2>&1
curl -sf -o /dev/null http://127.0.0.1:$PORT/account/login || service nginx start >/dev/null 2>&1
pgrep -x cron >/dev/null 2>&1 || service cron start >/dev/null 2>&1
GUARD
chmod +x /usr/local/bin/hostinpl-guard
cat > /etc/cron.d/hostinpl-guard <<'G'
SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
* * * * * root /usr/local/bin/hostinpl-guard >/dev/null 2>&1
@reboot root sleep 20; /usr/local/bin/hostinpl-guard >/dev/null 2>&1
G
chmod 644 /etc/cron.d/hostinpl-guard
ok "Проверка раз в минуту и подъём после перезагрузки"
# ── 11. администратор ────────────────────────────────────────────────────────
step "Создаю администратора панели…"
HASH="$(php -r 'echo md5($argv[1]);' "$ADMPASS")"
mysql "$DBNAME" <<SQL
DELETE FROM users WHERE user_email='$ADMMAIL';
INSERT INTO users
(user_email,user_password,user_firstname,user_lastname,user_status,user_balance,
user_access_level,user_date_reg,user_online_date,user_promo_date,user_activate,
key_activate,ref,rmoney,bonuses,test_server,user_last_date)
VALUES
('$ADMMAIL','$HASH','Администратор','Панели',1,0.00,3,NOW(),UNIX_TIMESTAMP(),
CURDATE(),1,'',0,0.00,0.00,'2',NOW());
SQL
ok "Логин $ADMMAIL, уровень доступа 3 (полный)"
# ── 12. проверка ─────────────────────────────────────────────────────────────
step "Проверяю работу панели…"
sleep 2
CODE="$(curl -s -o /tmp/redl-check.html -w '%{http_code}' -L "http://127.0.0.1:$PORT/account/login" || true)"
[[ "$CODE" == "200" ]] || die "Страница входа вернула $CODE (ожидался 200). Смотрите /var/log/nginx/error.log"
grep -q 'kt_login_signin_form' /tmp/redl-check.html || die "Страница входа отдалась без формы"
rm -f /tmp/redl-check.html
ok "Страница входа отвечает, форма на месте"
printf '\n%s=== Установка завершена ===%s\n\n' "$C_OK" "$C_0"
printf ' Адрес панели : %s\n' "$URL"
printf ' Логин : %s\n' "$ADMMAIL"
printf ' Пароль : тот, что вы ввели при установке\n'
printf ' Креды БД : /root/.redl-panel-credentials\n\n'
printf '%sДальше:%s\n' "$C_INF" "$C_0"
printf ' 1. Капча выключена. Включить: Админка → Настройки → Прочие настройки → «Защита от ботов»\n'
printf ' (сначала впишите оба ключа с google.com/recaptcha, потом переключайте)\n'
printf ' 2. Платёжные системы: Админка → Настройки → Платёжные системы\n'
printf ' 3. Игровую ноду ставьте скриптом install-node.sh и подключайте в Админка → Локации\n\n'
printf 'Поддерживается и развивается с помощью REDL.IO — Хостинг с искусственным интеллектом\n\n'
-241
View File
@@ -1,241 +0,0 @@
#!/bin/sh
# ---------------------------------------------------------------------------
# FiveRP - installer
#
# Turns a clean Ubuntu/Debian box into this server: FXServer artefacts,
# cfx-server-data, oxmysql, MariaDB with the schema, the FiveRP resources,
# a keep-alive supervisor and a boot entry.
#
# sudo sh install.sh --licence cfxk_xxxxxxxx
#
# Options
# --licence KEY Cfx.re server key from https://keymaster.fivem.net
# --dir PATH install root (default /opt/fivem)
# --hostname NAME server name in the list (default FiveRP)
# --port N game port (default 30120)
# --name NAME service name, one per server (default fiverp)
# --db-name NAME database (default fiverp)
# --db-user USER database user (default fiverp)
# --db-pass PASS password for that user (default: generated)
# --build ID FXServer build (default: latest recommended)
# --no-start install everything, do not start the server
# ---------------------------------------------------------------------------
set -e
DIR=/opt/fivem
LICENCE=""
HOSTNAME_="FiveRP"
BUILD=""
DB_NAME=fiverp
DB_USER=fiverp
DB_PASS=""
PORT=30120
SVC=fiverp
START=1
ARTIFACTS="https://runtime.fivem.net/artifacts/fivem/build_proot_linux/master/"
OXMYSQL="https://github.com/overextended/oxmysql/releases/latest/download/oxmysql.zip"
SERVER_DATA_REPO="https://github.com/citizenfx/cfx-server-data"
SRC=$(cd "$(dirname "$0")" && pwd)
while [ $# -gt 0 ]; do
case "$1" in
--licence|--license) LICENCE="$2"; shift 2 ;;
--dir) DIR="$2"; shift 2 ;;
--hostname) HOSTNAME_="$2"; shift 2 ;;
--build) BUILD="$2"; shift 2 ;;
--db-name) DB_NAME="$2"; shift 2 ;;
--db-user) DB_USER="$2"; shift 2 ;;
--db-pass) DB_PASS="$2"; shift 2 ;;
--port) PORT="$2"; shift 2 ;;
--name) SVC="$2"; shift 2 ;;
--no-start) START=0; shift ;;
-h|--help) sed -n '2,25p' "$0"; exit 0 ;;
*) echo "unknown option: $1" >&2; exit 1 ;;
esac
done
say() { printf '\n\033[1;34m==\033[0m %s\n' "$*"; }
ok() { printf ' \033[32mok\033[0m %s\n' "$*"; }
die() { printf '\n\033[31mfailed:\033[0m %s\n' "$*" >&2; exit 1; }
[ "$(id -u)" = 0 ] || die "run as root (sudo sh install.sh ...)"
[ -f "$SRC/sql/schema.sql" ] || die "run this from a clone of the repository"
command -v apt-get >/dev/null || die "this installer is for Debian/Ubuntu"
# --- 1. packages -----------------------------------------------------------
say "Installing packages"
export DEBIAN_FRONTEND=noninteractive
apt-get update -qq
apt-get install -y -qq curl ca-certificates wget xz-utils git unzip mariadb-server >/dev/null
ok "curl, git, unzip, xz-utils, mariadb-server"
# --- 2. FXServer -----------------------------------------------------------
say "Fetching FXServer"
if [ -n "$BUILD" ]; then
REL=$(curl -fsSL "$ARTIFACTS" | tr '>' '\n' | grep -oE "\./$BUILD-[0-9a-f]+/fx\.tar\.xz" | head -1)
[ -n "$REL" ] || die "build $BUILD not found on $ARTIFACTS"
else
REL=$(curl -fsSL "$ARTIFACTS" | tr '>' '\n' | grep -B4 'LATEST RECOMMENDED' \
| grep -oE '\./[0-9]+-[0-9a-f]+/fx\.tar\.xz' | head -1)
[ -n "$REL" ] || die "could not find the recommended build on $ARTIFACTS"
fi
URL="$ARTIFACTS${REL#./}"
BUILD_ID=$(echo "$REL" | grep -oE '[0-9]+' | head -1)
mkdir -p "$DIR/server" "$DIR/bin" "$DIR/logs"
if [ -x "$DIR/server/run.sh" ] && [ "$(cat "$DIR/server/.build" 2>/dev/null)" = "$BUILD_ID" ]; then
ok "build $BUILD_ID already installed"
else
TMP=$(mktemp -d)
curl -fL# "$URL" -o "$TMP/fx.tar.xz" || die "download failed: $URL"
rm -rf "$DIR/server"; mkdir -p "$DIR/server"
tar xJf "$TMP/fx.tar.xz" -C "$DIR/server"
rm -rf "$TMP"
chmod +x "$DIR/server/run.sh"
echo "$BUILD_ID" > "$DIR/server/.build"
ok "build $BUILD_ID unpacked into $DIR/server"
fi
# --- 3. server-data + oxmysql ---------------------------------------------
say "Fetching the base resources"
if [ -d "$DIR/server-data/resources" ]; then
ok "server-data already present"
else
git clone --depth=1 -q "$SERVER_DATA_REPO" "$DIR/server-data" || die "could not clone cfx-server-data"
ok "cfx-server-data cloned (mapmanager, chat, spawnmanager, sessionmanager, hardcap)"
fi
if [ -f "$DIR/server-data/resources/[system]/oxmysql/fxmanifest.lua" ]; then
ok "oxmysql already present"
else
TMP=$(mktemp -d)
curl -fsSL "$OXMYSQL" -o "$TMP/oxmysql.zip" || die "could not download oxmysql"
mkdir -p "$DIR/server-data/resources/[system]"
unzip -q -o "$TMP/oxmysql.zip" -d "$TMP"
rm -rf "$DIR/server-data/resources/[system]/oxmysql"
mv "$(find "$TMP" -maxdepth 2 -name fxmanifest.lua | head -1 | xargs dirname)" \
"$DIR/server-data/resources/[system]/oxmysql"
rm -rf "$TMP"
ok "oxmysql installed"
fi
# --- 4. database -----------------------------------------------------------
say "Preparing the database"
if [ -d /run/systemd/system ]; then
systemctl enable --now mariadb >/dev/null 2>&1 || true
else
service mariadb start >/dev/null 2>&1 || true
fi
i=0
while ! mysqladmin ping >/dev/null 2>&1; do
i=$((i+1)); [ $i -gt 60 ] && die "mariadb did not start"
sleep 1
done
if [ -z "$DB_PASS" ]; then
if [ -f "$DIR/server-data/server.cfg" ]; then
DB_PASS=$(sed -n "s|^set mysql_connection_string \"mysql://[^:]*:\([^@]*\)@.*|\1|p" "$DIR/server-data/server.cfg" | head -1)
fi
[ -n "$DB_PASS" ] || DB_PASS=$(head -c 24 /dev/urandom | base64 | tr -dc 'A-Za-z0-9' | cut -c1-24)
fi
mysql -e "CREATE DATABASE IF NOT EXISTS \`$DB_NAME\` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
mysql -e "CREATE USER IF NOT EXISTS '$DB_USER'@'localhost' IDENTIFIED BY '$DB_PASS';"
mysql -e "ALTER USER '$DB_USER'@'localhost' IDENTIFIED BY '$DB_PASS';"
mysql -e "GRANT ALL PRIVILEGES ON \`$DB_NAME\`.* TO '$DB_USER'@'localhost'; FLUSH PRIVILEGES;"
mysql "$DB_NAME" < "$SRC/sql/schema.sql"
ok "database $DB_NAME, user $DB_USER@localhost, schema applied"
# --- 5. FiveRP resources and configuration --------------------------------
say "Installing FiveRP"
mkdir -p "$DIR/server-data/resources/[local]"
for r in fiverp-auth fiverp-characters fiverp-loadscreen; do
rm -rf "$DIR/server-data/resources/[local]/$r"
cp -r "$SRC/resources/[local]/$r" "$DIR/server-data/resources/[local]/"
done
ok "fiverp-auth, fiverp-characters, fiverp-loadscreen"
cp "$SRC/bin/run-server.sh" "$DIR/bin/"
sed "s|/run/fiverp.stdin|/run/$SVC.stdin|" "$SRC/bin/rcon" > "$DIR/bin/rcon"
chmod +x "$DIR/bin/run-server.sh" "$DIR/bin/rcon"
ln -sf "$DIR/bin/rcon" "/usr/local/bin/$([ "$SVC" = fiverp ] && echo rcon || echo "rcon-$SVC")"
CFG="$DIR/server-data/server.cfg"
if [ -z "$LICENCE" ] && [ -f "$CFG" ]; then
LICENCE=$(sed -n 's/^sv_licenseKey "\(.*\)"$/\1/p' "$CFG" | head -1)
fi
sed -e "s|0.0.0.0:30120|0.0.0.0:$PORT|g" \
-e "s|^sv_hostname .*|sv_hostname \"$HOSTNAME_\"|" \
-e "s|^sets sv_projectName .*|sets sv_projectName \"$HOSTNAME_\"|" \
-e "s|^sv_licenseKey .*|sv_licenseKey \"${LICENCE:-cfxk_your_key_here}\"|" \
-e "s|^set mysql_connection_string .*|set mysql_connection_string \"mysql://$DB_USER:$DB_PASS@localhost/$DB_NAME?charset=utf8mb4\"|" \
"$SRC/server.cfg.example" > "$CFG"
chmod 600 "$CFG"
ok "server.cfg written (mode 600 - it holds the key and the password)"
# --- 6. boot entry ---------------------------------------------------------
say "Setting up start on boot"
sed -e "s|^NAME=.*|NAME=$SVC|" -e "s|^DIR=.*|DIR=$DIR|" -e "s|^LOG=.*|LOG=$DIR/logs/server.log|" \
"$SRC/bin/fiverp.init" > "/etc/init.d/$SVC"
chmod +x "/etc/init.d/$SVC"
if [ -d /run/systemd/system ]; then
cat > "/etc/systemd/system/$SVC.service" <<EOF
[Unit]
Description=FiveRP (FXServer)
After=network-online.target mariadb.service
Wants=mariadb.service
[Service]
Type=simple
WorkingDirectory=$DIR/server-data
ExecStart=$DIR/bin/run-server.sh $SVC $DIR $DIR/logs/server.log
KillMode=mixed
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable "$SVC" >/dev/null 2>&1
ok "systemd unit $SVC.service"
elif [ -d /etc/redl/enabled ]; then
# REDL VDS: no systemd, the platform starts whatever is marked here
touch "/etc/redl/enabled/$SVC" /etc/redl/enabled/mariadb
ok "marked in /etc/redl/enabled (REDL VDS autostart)"
else
command -v update-rc.d >/dev/null && update-rc.d "$SVC" defaults >/dev/null 2>&1 || true
ok "/etc/init.d/$SVC installed"
fi
# --- 7. start --------------------------------------------------------------
if [ "$START" = 0 ]; then say "Done (not started, --no-start)"; exit 0; fi
if [ -z "$LICENCE" ]; then
say "Done"
echo " No licence key given. Get a free one at https://keymaster.fivem.net,"
echo " put it into $CFG and run: service $SVC start"
exit 0
fi
say "Starting the server"
if [ -d /run/systemd/system ]; then systemctl restart "$SVC"; else service "$SVC" restart >/dev/null; fi
LOG="$DIR/logs/server.log"
i=0
while [ $i -lt 90 ]; do
if grep -q "Authenticated with cfx.re Nucleus" "$LOG" 2>/dev/null; then
ok "server is up and registered with Cfx"
grep -q "fiverp-auth\] server ready" "$LOG" && ok "fiverp-auth ready"
grep -q "Database server connection established" "$LOG" && ok "oxmysql connected to the database"
IP=$(curl -fsS --max-time 5 https://api.ipify.org 2>/dev/null || echo "<server ip>")
printf '\n connect %s:%s\n logs: tail -f %s\n console: rcon status\n\n' "$IP" "$PORT" "$LOG"
exit 0
fi
if grep -qE "Could not authenticate server license key|invalid license key" "$LOG" 2>/dev/null; then
if [ -d /run/systemd/system ]; then systemctl stop "$SVC"; else service "$SVC" stop >/dev/null 2>&1; fi
die "the licence key was refused - get one at https://keymaster.fivem.net, put it into $CFG and run: service $SVC start"
fi
i=$((i+1)); sleep 2
done
die "the server did not come up in 3 minutes - see $LOG"
+7
View File
@@ -0,0 +1,7 @@
AddDefaultCharset utf-8
php_value memory_limit 10000M
Options -Indexes
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php?action=$1 [L,QSA]
+9
View File
@@ -0,0 +1,9 @@
Copyright (c) 21.11.2020 HOSTINPL Автор: Samir Shelenko and Alexander Zemlyanoy (https://vk.com/id00v / https://vk.com/mrsasha082) «HOSTINPL 5.6»
ДАННОЕ ПРОГРАММНОЕ ОБЕСПЕЧЕНИЕ ПРЕДОСТАВЛЯЕТСЯ «КАК ЕСТЬ», БЕЗ КАКИХ-ЛИБО ГАРАНТИЙ, ЯВНО
ВЫРАЖЕННЫХ ИЛИ ПОДРАЗУМЕВАЕМЫХ, ВКЛЮЧАЯ, НО НЕ ОГРАНИЧИВАЯСЬ ГАРАНТИЯМИ ТОВАРНОЙ ПРИГОДНОСТИ,
СООТВЕТСТВИЯ ПО ЕГО КОНКРЕТНОМУ НАЗНАЧЕНИЮ И ОТСУТСТВИЯ НАРУШЕНИЙ ПРАВ. НИ В КАКОМ СЛУЧАЕ
АВТОРЫ ИЛИ ПРАВООБЛАДАТЕЛИ НЕ НЕСУТ ОТВЕТСТВЕННОСТИ ПО ИСКАМ О ВОЗМЕЩЕНИИ УЩЕРБА, УБЫТКОВ
ИЛИ ДРУГИХ ТРЕБОВАНИЙ ПО ДЕЙСТВУЮЩИМ КОНТРАКТАМ, ДЕЛИКТАМ ИЛИ ИНОМУ, ВОЗНИКШИМ ИЗ, ИМЕЮЩИМ
ПРИЧИНОЙ ИЛИ СВЯЗАННЫМ С ПРОГРАММНЫМ ОБЕСПЕЧЕНИЕМ ИЛИ ИСПОЛЬЗОВАНИЕМ ПРОГРАММНОГО ОБЕСПЕЧЕНИЯ
ИЛИ ИНЫМИ ДЕЙСТВИЯМИ С ПРОГРАММНЫМ ОБЕСПЕЧЕНИЕМ.

Some files were not shown because too many files have changed in this diff Show More