Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c857979a55 | ||
|
|
71856b15e9 | ||
|
|
80c1d75d2f | ||
|
|
cb0649016c | ||
|
|
4cc10b264b | ||
|
|
0144e83f05 | ||
|
|
968d047f87 |
@@ -1,8 +1,11 @@
|
|||||||
# загрузки пользователей и временные файлы панели
|
# Anything holding a licence key or a password
|
||||||
panel/tmp/avatar/*
|
server.cfg
|
||||||
panel/tmp/mods/*
|
secrets.cfg
|
||||||
panel/tmp/tickets_img/*
|
*.env
|
||||||
!panel/tmp/**/.gitkeep
|
|
||||||
# локальные креды — никогда не коммитим
|
# Runtime state
|
||||||
*.credentials
|
cache/
|
||||||
.redl-*
|
logs/
|
||||||
|
*.log
|
||||||
|
server/
|
||||||
|
server-data/
|
||||||
|
|||||||
@@ -1,248 +0,0 @@
|
|||||||
[Русский](ИЗМЕНЕНИЯ.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.**
|
|
||||||
@@ -1,411 +0,0 @@
|
|||||||
[Русский](ИНСТРУКЦИЯ.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 2–5 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 1–2 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 5–15 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.**
|
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
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.
|
||||||
@@ -1,230 +1,131 @@
|
|||||||
<p align="right">
|
# FiveRP
|
||||||
<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 · Game Hosting Control Panel · REDL build
|
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.
|
||||||
|
|
||||||
**Base: HostinPL 5.6** (original by Samir Shelenko and Alexander Zemlyanoy, written for Debian 9 /
|
> 🇷🇺 Русская версия — ниже / Russian version below.
|
||||||
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.
|
|
||||||
|
|
||||||
> 🇷🇺 **[Русская версия этого описания → README.ru.md](README.ru.md)**
|
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.
|
||||||
|
|
||||||
A control panel for selling and managing game servers: SA-MP, CRMP, MTA, Minecraft, CS 1.6, CS:S, RAGE:MP.
|
## What it does today
|
||||||
Client area, ticket system, payment gateways, automated game server deployment onto game nodes.
|
|
||||||
|
|
||||||
**Maintained and developed with [REDL.IO](https://redl.io) — AI-powered hosting.**
|
- **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.
|
||||||
|
|
||||||
This build is a modernisation of the HostinPL 5.6 panel. The original was written for Debian 9 and PHP 7.0
|
| Loading | Sign in | Register | Identity |
|
||||||
(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.
|
|  |  |  |  |
|
||||||
|
|
||||||
---
|
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).
|
||||||
|
|
||||||
## ⚠️ Legal status — read this before installing
|
## Install
|
||||||
|
|
||||||
The original **HostinPL 5.6 is a commercial, proprietary product**. Its authors are Samir Shelenko and
|
Ubuntu 22.04 / 24.04 or Debian 12, and a free server key from [keymaster.fivem.net](https://keymaster.fivem.net).
|
||||||
Alexander Zemlyanoy. The file `panel/LICENSE` is their licence, and it grants no permission to
|
|
||||||
redistribute or modify the software.
|
|
||||||
|
|
||||||
The sources this build is based on were circulated as "nulled" — a cracked copy of a paid product,
|
```sh
|
||||||
published without the authors' consent. We are not hiding that or rewriting history: the authors'
|
git clone https://github.com/RedlHosting/redl-fivem-rp.git
|
||||||
copyright headers are preserved throughout the code and were deliberately not removed.
|
cd redl-fivem-rp
|
||||||
|
sudo sh install.sh --licence cfxk_your_key_here
|
||||||
What this means in practice:
|
|
||||||
|
|
||||||
* 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.
|
|
||||||
|
|
||||||
Responsibility for use rests with whoever installs it.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## What is in this repository
|
|
||||||
|
|
||||||
| 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:
|
|
||||||
|
|
||||||
| | |
|
|
||||||
|---|---|
|
|
||||||
| [](docs/screenshots/en/00-login.png)<br>Login page | [](docs/screenshots/en/01-cabinet.png)<br>Client area |
|
|
||||||
| [](docs/screenshots/en/03-server-order.png)<br>Ordering a game server | [](docs/screenshots/en/11-admin-dashboard.png)<br>Admin dashboard |
|
|
||||||
| [](docs/screenshots/en/24-admin-statistics.png)<br>Statistics | [](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 | 50–1000 | 7777–9999 |
|
|
||||||
| Criminal Russia: Multiplayer 0.3e | `crmp` | samp | 50–500 | 3335–7000 |
|
|
||||||
| Criminal Russia: Multiplayer 0.3.7 | `crmp037` | samp | 50–500 | 3335–7000 |
|
|
||||||
| United Multiplayer | `unit` | samp | 50–500 | 7777–9999 |
|
|
||||||
| Multi Theft Auto | `mta` | mtasa | 50–1000 | 25020–80520 |
|
|
||||||
| Minecraft (Java) | `mine` | mine | 10–100 | 12410–55641 |
|
|
||||||
| Minecraft: Pocket Edition | `mcpe` | mine | 10–100 | 12410–55641 |
|
|
||||||
| Counter-Strike 1.6 | `cs` | valve | 6–32 | 27016–30550 |
|
|
||||||
| Counter-Strike: Source | `css` | valve | 6–32 | 27016–30550 |
|
|
||||||
| Counter-Strike: Global Offensive | `csgo` | valve | 6–32 | 27016–30550 |
|
|
||||||
| GTA V: RAGE MP | `ragemp` | ragemp | 50–500 | 22000–25000 |
|
|
||||||
|
|
||||||
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 1–2 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
|
|
||||||
```
|
```
|
||||||
|
|
||||||
The script asks only for the panel address, the administrator e-mail and password — everything else
|
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`.
|
||||||
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.
|
|
||||||
|
|
||||||
To run the panel on a non-standard port, set it via an environment variable:
|
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`.
|
||||||
|
|
||||||
```bash
|
Day to day:
|
||||||
sudo PORT=8095 bash install-panel.sh
|
|
||||||
|
```sh
|
||||||
|
service fiverp start|stop|restart|status # systemd boxes: systemctl ...
|
||||||
|
rcon status # command into the live console
|
||||||
|
tail -f /opt/fivem/logs/server.log
|
||||||
```
|
```
|
||||||
|
|
||||||
### Game node
|
## Layout
|
||||||
|
|
||||||
```bash
|
resources/[local]/fiverp-auth registration and login (NUI + scrypt + oxmysql)
|
||||||
git clone https://github.com/RedlHosting/redl-gamepanel.git
|
resources/[local]/fiverp-characters identity, character load and spawn
|
||||||
cd redl-gamepanel
|
resources/[local]/fiverp-loadscreen loading screen
|
||||||
sudo bash install-node.sh
|
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.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# FiveRP (RU)
|
||||||
|
|
||||||
|
Открытая **ролевая база для FiveM** — регистрация аккаунта, вход и личность персонажа, написанные ИИ-агентом на живом сервере, в кадре, и выложенные бесплатно для всех.
|
||||||
|
|
||||||
|
Это не форк фреймворка: `fiverp-auth`, `fiverp-characters` и `fiverp-loadscreen` написаны под этот сервер. Никакого ESX и QBCore. Извне берутся только штатные ресурсы Cfx (`mapmanager`, `chat`, `spawnmanager`, `sessionmanager`, `hardcap`) и `oxmysql` — установщик скачивает их сам, поэтому репозиторий остаётся маленьким и читаемым.
|
||||||
|
|
||||||
|
## Что уже работает
|
||||||
|
|
||||||
|
- **Регистрация в два шага** — сначала аккаунт (почта, логин, пароль), потом личность (имя и фамилия, выдаются один раз и печатаются на паспорте).
|
||||||
|
- **Вход**, который возвращает к своему персонажу — данные поднимаются из базы.
|
||||||
|
- **Загрузочный экран** с настоящим прогрессом из событий стриминга игры.
|
||||||
|
- **Пароли на scrypt**, соль на каждый пароль, хранение в виде `scrypt$соль$хеш`. Клиент ничего не решает: регистрация, вход и спавн — на сервере.
|
||||||
|
- **`basic-gamemode` намеренно выключен** — он делает принудительный респавн при старте карты и выбрасывал бы игрока в мир за экраном авторизации. Спавном занимается `fiverp-auth`.
|
||||||
|
|
||||||
|
Весь интерфейс живёт по одной дизайн-системе — светлая «стеклянная» тема на шрифте **Unbounded**, город виден за панелями. Каждый цвет, радиус и шрифт — токен; правила в [`docs/DESIGN.md`](docs/DESIGN.md), шрифты лежат внутри ресурсов (у страницы NUI нет гарантированного интернета).
|
||||||
|
|
||||||
|
## Установка
|
||||||
|
|
||||||
|
Ubuntu 22.04 / 24.04 или Debian 12 и бесплатный ключ сервера с [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_ваш_ключ
|
||||||
```
|
```
|
||||||
|
|
||||||
At the end the script prints the IP, SSH login and password — enter those in the panel under
|
Установщик скачает рекомендованную сборку FXServer, склонирует `cfx-server-data`, поставит `oxmysql`, поднимет MariaDB со схемой и сгенерированным паролем, положит ресурсы FiveRP, напишет `server.cfg`, пропишет автозапуск и дождётся регистрации сервера в Cfx. Дальше — `connect <ваш ip>:30120`.
|
||||||
**Admin → Locations → Add location**. See [GUIDE.en.md](GUIDE.en.md) for details.
|
|
||||||
|
|
||||||
---
|
Параметры: `--dir`, `--hostname`, `--port`, `--name` (второй сервер на той же машине), `--db-name`, `--db-user`, `--db-pass`, `--build`, `--no-start`. Список — `sh install.sh --help`.
|
||||||
|
|
||||||
## What already works
|
Повседневное:
|
||||||
|
|
||||||
* Login, registration, password recovery — **without a captcha** (it is enabled from the admin area, see below)
|
```sh
|
||||||
* Client area: balance, invoices, transfers, bonuses, promo codes, referral system
|
service fiverp start|stop|restart|status # на машинах с systemd — systemctl ...
|
||||||
* Ticket system with categories and attachments
|
rcon status # команда в живую консоль
|
||||||
* Admin area: users, servers, locations, games, news, statistics, promo codes, settings
|
tail -f /opt/fivem/logs/server.log
|
||||||
* 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))
|
|
||||||
|
|
||||||
### 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
|
resources/[local]/fiverp-auth регистрация и вход (NUI + scrypt + oxmysql)
|
||||||
does not exist. When you need it:
|
resources/[local]/fiverp-characters личность, загрузка персонажа и спавн
|
||||||
|
resources/[local]/fiverp-loadscreen загрузочный экран
|
||||||
|
sql/schema.sql accounts, characters
|
||||||
|
server.cfg.example основной конфиг
|
||||||
|
bin/ супервизор, rcon, init-скрипт
|
||||||
|
docs/DESIGN.md дизайн-система, по которой сделаны все экраны
|
||||||
|
|
||||||
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
|
Всего, что после спавна: чат, HUD, инвентарь, деньги, работы, транспорт. В таблице `characters` уже есть модель, внешность, позиция и поворот — геймплею есть куда писать.
|
||||||
"ERROR for site owner: Invalid site key" and nobody can log in.
|
|
||||||
|
|
||||||
## What does not work
|
Issues и pull request'ы приветствуются — это сделано, чтобы этим пользовались.
|
||||||
|
|
||||||
* **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.
|
|
||||||
|
|
||||||
---
|
Собрано на VDS [REDL](https://redl.io), где ИИ-агент живёт прямо на сервере с root-доступом. Задача ставится обычными словами, агент пишет код, чинит свои же ошибки и перезапускает сервис. Весь процесс записан — включая моменты, где всё ломалось.
|
||||||
|
|
||||||
## Planned next
|
## Лицензия
|
||||||
|
|
||||||
1. **More games — by request.** The eleven games above are the ones the panel came with. Adding a
|
MIT на код FiveRP. У `cfx-server-data` и `oxmysql` свои лицензии; Unbounded — под SIL Open Font License.
|
||||||
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.**
|
|
||||||
|
|||||||
@@ -1,227 +0,0 @@
|
|||||||
<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)<br>Страница входа | [](docs/screenshots/ru/01-cabinet.png)<br>Личный кабинет |
|
|
||||||
| [](docs/screenshots/ru/03-server-order.png)<br>Заказ игрового сервера | [](docs/screenshots/ru/11-admin-dashboard.png)<br>Главная админки |
|
|
||||||
| [](docs/screenshots/ru/24-admin-statistics.png)<br>Статистика | [](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 | 50–1000 | 7777–9999 |
|
|
||||||
| Criminal Russia: Multiplayer 0.3e | `crmp` | samp | 50–500 | 3335–7000 |
|
|
||||||
| Criminal Russia: Multiplayer 0.3.7 | `crmp037` | samp | 50–500 | 3335–7000 |
|
|
||||||
| United Multiplayer | `unit` | samp | 50–500 | 7777–9999 |
|
|
||||||
| Multi Theft Auto | `mta` | mtasa | 50–1000 | 25020–80520 |
|
|
||||||
| Minecraft (Java) | `mine` | mine | 10–100 | 12410–55641 |
|
|
||||||
| Minecraft: Pocket Edition | `mcpe` | mine | 10–100 | 12410–55641 |
|
|
||||||
| Counter-Strike 1.6 | `cs` | valve | 6–32 | 27016–30550 |
|
|
||||||
| Counter-Strike: Source | `css` | valve | 6–32 | 27016–30550 |
|
|
||||||
| Counter-Strike: Global Offensive | `csgo` | valve | 6–32 | 27016–30550 |
|
|
||||||
| GTA V: RAGE MP | `ragemp` | ragemp | 50–500 | 22000–25000 |
|
|
||||||
|
|
||||||
Ядро сервера клиент меняет в один клик, не заказывая новый сервер:
|
|
||||||
|
|
||||||
* **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) — Хостинг с искусственным интеллектом.**
|
|
||||||
@@ -1,243 +0,0 @@
|
|||||||
[Русский](БЕЗОПАСНОСТЬ.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.**
|
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
#!/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
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
#!/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"
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
#!/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
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
# Образ для игровых серверов.
|
|
||||||
# Поддерживается и развивается с помощью 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"]
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
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
|
|
||||||
@@ -0,0 +1,303 @@
|
|||||||
|
# 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 (секция) | 18–20px | 600 | `-0.03em` |
|
||||||
|
| Кнопка / линк | 14px | 600 | `-0.01em` |
|
||||||
|
| Body / инпут | 14–15px | 400 | `-0.005em` |
|
||||||
|
| Лейбл поля | 11px | 600 | `+0.09em`, `uppercase` |
|
||||||
|
| Eyebrow / надзаголовок | 10px | 600 | `+0.20em`, `uppercase` |
|
||||||
|
| Метаданные | 11–12px | 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 и **посмотреть глазами** до выката.
|
||||||
|
After Width: | Height: | Size: 823 KiB |
|
After Width: | Height: | Size: 645 KiB |
|
After Width: | Height: | Size: 790 KiB |
|
After Width: | Height: | Size: 669 KiB |
@@ -1,58 +0,0 @@
|
|||||||
<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.**
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
<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) — Хостинг с искусственным интеллектом.**
|
|
||||||
|
Before Width: | Height: | Size: 382 KiB |
|
Before Width: | Height: | Size: 377 KiB |
|
Before Width: | Height: | Size: 361 KiB |
|
Before Width: | Height: | Size: 60 KiB |
|
Before Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 88 KiB |
|
Before Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 40 KiB |
|
Before Width: | Height: | Size: 42 KiB |
|
Before Width: | Height: | Size: 106 KiB |
|
Before Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 68 KiB |
|
Before Width: | Height: | Size: 74 KiB |
|
Before Width: | Height: | Size: 50 KiB |
|
Before Width: | Height: | Size: 37 KiB |
|
Before Width: | Height: | Size: 136 KiB |
|
Before Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 120 KiB |
|
Before Width: | Height: | Size: 45 KiB |
|
Before Width: | Height: | Size: 51 KiB |
|
Before Width: | Height: | Size: 53 KiB |
|
Before Width: | Height: | Size: 37 KiB |
|
Before Width: | Height: | Size: 45 KiB |
|
Before Width: | Height: | Size: 43 KiB |
|
Before Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 264 KiB |
|
Before Width: | Height: | Size: 93 KiB |
|
Before Width: | Height: | Size: 205 KiB |
|
Before Width: | Height: | Size: 122 KiB |
|
Before Width: | Height: | Size: 69 KiB |
|
Before Width: | Height: | Size: 2.4 KiB |
|
Before Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 37 KiB |
|
Before Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 40 KiB |
|
Before Width: | Height: | Size: 380 KiB |
|
Before Width: | Height: | Size: 376 KiB |
|
Before Width: | Height: | Size: 362 KiB |
|
Before Width: | Height: | Size: 63 KiB |
|
Before Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 91 KiB |
|
Before Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 37 KiB |
|
Before Width: | Height: | Size: 40 KiB |
|
Before Width: | Height: | Size: 43 KiB |
|
Before Width: | Height: | Size: 103 KiB |
|
Before Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 74 KiB |
|
Before Width: | Height: | Size: 78 KiB |
|
Before Width: | Height: | Size: 54 KiB |
|
Before Width: | Height: | Size: 39 KiB |
|
Before Width: | Height: | Size: 137 KiB |
|
Before Width: | Height: | Size: 46 KiB |
|
Before Width: | Height: | Size: 122 KiB |
|
Before Width: | Height: | Size: 47 KiB |
|
Before Width: | Height: | Size: 52 KiB |
|
Before Width: | Height: | Size: 54 KiB |
|
Before Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 46 KiB |
|
Before Width: | Height: | Size: 45 KiB |
|
Before Width: | Height: | Size: 45 KiB |
|
Before Width: | Height: | Size: 49 KiB |
|
Before Width: | Height: | Size: 274 KiB |
|
Before Width: | Height: | Size: 97 KiB |
|
Before Width: | Height: | Size: 209 KiB |
|
Before Width: | Height: | Size: 123 KiB |
|
Before Width: | Height: | Size: 70 KiB |
|
Before Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 39 KiB |
|
Before Width: | Height: | Size: 47 KiB |
|
Before Width: | Height: | Size: 45 KiB |
|
Before Width: | Height: | Size: 42 KiB |
@@ -1,252 +0,0 @@
|
|||||||
#!/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'
|
|
||||||
@@ -1,256 +0,0 @@
|
|||||||
#!/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'
|
|
||||||
@@ -0,0 +1,241 @@
|
|||||||
|
#!/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"
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
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]
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
Copyright (c) 21.11.2020 HOSTINPL Автор: Samir Shelenko and Alexander Zemlyanoy (https://vk.com/id00v / https://vk.com/mrsasha082) «HOSTINPL 5.6»
|
|
||||||
|
|
||||||
ДАННОЕ ПРОГРАММНОЕ ОБЕСПЕЧЕНИЕ ПРЕДОСТАВЛЯЕТСЯ «КАК ЕСТЬ», БЕЗ КАКИХ-ЛИБО ГАРАНТИЙ, ЯВНО
|
|
||||||
ВЫРАЖЕННЫХ ИЛИ ПОДРАЗУМЕВАЕМЫХ, ВКЛЮЧАЯ, НО НЕ ОГРАНИЧИВАЯСЬ ГАРАНТИЯМИ ТОВАРНОЙ ПРИГОДНОСТИ,
|
|
||||||
СООТВЕТСТВИЯ ПО ЕГО КОНКРЕТНОМУ НАЗНАЧЕНИЮ И ОТСУТСТВИЯ НАРУШЕНИЙ ПРАВ. НИ В КАКОМ СЛУЧАЕ
|
|
||||||
АВТОРЫ ИЛИ ПРАВООБЛАДАТЕЛИ НЕ НЕСУТ ОТВЕТСТВЕННОСТИ ПО ИСКАМ О ВОЗМЕЩЕНИИ УЩЕРБА, УБЫТКОВ
|
|
||||||
ИЛИ ДРУГИХ ТРЕБОВАНИЙ ПО ДЕЙСТВУЮЩИМ КОНТРАКТАМ, ДЕЛИКТАМ ИЛИ ИНОМУ, ВОЗНИКШИМ ИЗ, ИМЕЮЩИМ
|
|
||||||
ПРИЧИНОЙ ИЛИ СВЯЗАННЫМ С ПРОГРАММНЫМ ОБЕСПЕЧЕНИЕМ ИЛИ ИСПОЛЬЗОВАНИЕМ ПРОГРАММНОГО ОБЕСПЕЧЕНИЯ
|
|
||||||
ИЛИ ИНЫМИ ДЕЙСТВИЯМИ С ПРОГРАММНЫМ ОБЕСПЕЧЕНИЕМ.
|
|
||||||