EmpireRP UCP — cPanel დეპლოის გზამკვლევი

ეს გზამკვლევი დეტალურად აღწერს, როგორ ავტვირთოთ ხელახლა აშენებული Laravel + Inertia/React UCP (ucp-laravel/) სტანდარტულ cPanel ჰოსტინგზე — კოდის ატვირთვიდან, ბაზების შექმნიდან, frontend-ის აწყობიდან, Discord OAuth-ის კონფიგურაციიდან, თქვენს ცოცხალ FiveM/QBCore სერვერთან დაკავშირებამდე.

1მიმოხილვა და არქიტექტურა

აპლიკაცია იყენებს ორ ცალკე მონაცემთა ბაზის კავშირს:

კავშირიდანიშნულებაEnv პრეფიქსი
mysqlUCP-ის საკუთარი მონაცემები: მომხმარებლები, როლები, სიახლეები, რულეტკა, აუდიტ-ლოგები, პარამეტრები, სერვერის ტოკენებიDB_*
fivemთქვენი ცოცხალი FiveM სერვერის ბაზა (QBCore/Qbox/ESX სქემა) — მოთამაშეები, მანქანები, სახლები, ბანებიFIVEM_DB_*

ეს ორივე შეიძლება იყოს იმავე MySQL სერვერზე (მაშინაც კი, ერთსა და იმავე ფიზიკურ ბაზაში), თუ მოწოდებულ დაშვებებს (credentials) აქვთ წვდომა ორივე ცხრილთა ნაკრებზე — ისინი არ საჭიროებენ ფიზიკურად ცალკე ბაზებს, მხოლოდ ლოგიკურად ცალკე Eloquent კავშირებს.

2cPanel-ის მოთხოვნები

დაწყებამდე შეამოწმეთ ეს პუნქტები cPanel-ში:

  • PHP 8.3+ — cPanel → MultiPHP Manager → აირჩიეთ თქვენი დომენი → აირჩიეთ 8.3 (ან უფრო ახალი).
  • PHP გაფართოებები: pdo_mysql, mbstring, openssl, tokenizer, xml, ctype, json, bcmath, fileinfo, gd — ჩართეთ ნაკლული გაფართოებები MultiPHP INI Editor-ში ან მიმართეთ ჰოსტინგ-პროვაიდერს.
  • Composer — cPanel → SoftwareComposer (თუ ჰოსტინგზე უკვე დაყენებულია), ან SSH/Terminal წვდომა ხელით დასაყენებლად.
  • Terminal / SSH წვდომა — cPanel → AdvancedTerminal. საჭიროა artisan ბრძანებების გასაშვებად. თუ ეს არ არის ხელმისაწვდომი, მიმართეთ ჰოსტინგის მხარდაჭერას SSH-ის ჩასართავად.
  • Node.js — cPanel → SoftwareSetup Node.js App (CloudLinux NodeJS Selector). გამოიყენება ერთხელ, npm run build-ის გასაშვებად. თუ არ არის ხელმისაწვდომი, frontend ააწყვეთ თქვენს კომპიუტერზე და ატვირთეთ მხოლოდ მიღებული public/build საქაღალდე (იხილეთ ნაბიჯი 9).
  • MySQL ბაზები — cPanel → MySQL Database Wizard.

3აპლიკაციის ატვირთვა

ატვირთეთ მთელი ucp-laravel/ საქაღალდე public_html-ის გარეთ — მაგ. ~/empirerp-ucp-ში — და არა პირდაპირ public_html-ში. მხოლოდ აპლიკაციის საკუთარმა public/ ქვესაქაღალდემ უნდა იყოს ვებ-წვდომადი (იხილეთ ნაბიჯი 7).

# თქვენს კომპიუტერზე დაარქივეთ აპლიკაცია (vendor/node_modules ჯერ არც არსებობს, ასე რომ არ სჭირდება გამორიცხვა)
cd ucp-laravel
zip -r ../ucp-laravel.zip . -x ".git/*"

# cPanel File Manager-ში: ატვირთეთ ucp-laravel.zip ~/empirerp-ucp-ში, შემდეგ Extract

ალტერნატივად გამოიყენეთ cPanel-ის Git Version Control, თუ კოდი Git რეპოზიტორიაშია, ან SFTP კლიენტი (FileZilla, WinSCP).

4მონაცემთა ბაზების შექმნა

cPanel → MySQL Database Wizard:

  1. შექმენით ბაზა თავად UCP-სთვის, მაგ. cpaneluser_ucp.
  2. შექმენით ბაზის მომხმარებელი, მაგ. cpaneluser_ucpapp, ძლიერი, გენერირებული პაროლით.
  3. დაამატეთ მომხმარებელი ბაზას All Privileges-ით.
  4. თუ თქვენი FiveM სერვერის ბაზა იმავე MySQL სერვერზეა, ჩაინიშნეთ მისი არსებული ბაზის სახელი — სწორედ მასზე მიუთითებთ FIVEM_DB_*-ს. თუ ის სხვა ჰოსტზეა, გაარკვიეთ FiveM ჰოსტინგ-პროვაიდერთან, შესაძლებელია თუ არა დისტანციური (remote) MySQL წვდომა, და დაუშვით (whitelist) ამ cPanel სერვერის IP მისამართი იქ.

მინიმალური პრივილეგიები: შექმენით ცალკე MySQL მომხმარებელი fivem კავშირისთვის (მაგ. cpaneluser_fivem), წვდომით მხოლოდ თამაშის სერვერის ბაზაზე, განსხვავებული UCP-ის საკუთარი ბაზის მომხმარებლისგან. ასე ერთი კავშირის ბაგი ან კომპრომეტაცია ვერ შეეხება მეორე ბაზის მონაცემებს.

5PHP დამოკიდებულებების დაყენება

გახსენით cPanel Terminal (ან SSH-ით შედით):

cd ~/empirerp-ucp
composer install --no-dev --optimize-autoloader

თუ composer თქვენს PATH-ში არ არის, cPanel-ის ჩაშენებული ვერსია ჩვეულებრივ გამოიძახება როგორც php ~/composer.phar install --no-dev --optimize-autoloader — ზუსტი ბრძანებისთვის შეამოწმეთ Software → Composer.

6.env-ის კონფიგურაცია

cp .env.example .env
php artisan key:generate

დაარედაქტირეთ .env (File Manager-ის კოდის რედაქტორით, ან nano .env Terminal-ში) და შეავსეთ:

ცვლადიმნიშვნელობა
APP_URLთქვენი რეალური დომენი, მაგ. https://ucp.ticcix.com
APP_ENVproduction
APP_DEBUGfalse — არასდროს true production-ში, რადგან ეს ავლენს stack trace-ებს
DB_DATABASE / DB_USERNAME / DB_PASSWORDUCP ბაზის დაშვებები მე-4 ნაბიჯიდან
FIVEM_DB_DATABASE / FIVEM_DB_USERNAME / FIVEM_DB_PASSWORDFiveM სერვერის ბაზის დაშვებები მე-4 ნაბიჯიდან
FIVEM_FRAMEWORKqbcore, qbox ან esx
DISCORD_CLIENT_ID / DISCORD_CLIENT_SECRETDiscord Developer Portal-იდან — იხილეთ ნაბიჯი 10
DISCORD_BOT_TOKEN / DISCORD_GUILD_IDროლების სინქისთვის — იხილეთ ნაბიჯი 10

7დომენის მიმართვა /public-ზე

Laravel-მა არასდროს არ უნდა უპასუხოს მოთხოვნებს პროექტის root-იდან — მხოლოდ ucp-laravel/public-იდან. აირჩიეთ ერთ-ერთი მიდგომა:

ვარიანტი A — ცალკე სუბდომეინი (რეკომენდებული)

  1. cPanel → Domains → შექმენით სუბდომეინი, მაგ. ucp.ticcix.com.
  2. როცა გკითხავენ document root-ს, დააყენეთ პირდაპირ ~/empirerp-ucp/public-ზე.

ვარიანტი B — მთავარი დომენი / ქვესაქაღალდე

თუ თქვენი ჰოსტინგი არ იძლევა სუბდომეინისთვის თვითნებური document root-ის დაყენების საშუალებას, შექმენით სიმლინკი public_html-იდან (ან მისი ქვესაქაღალდედან) აპლიკაციის public საქაღალდეზე:

cd ~/public_html
rm -rf ucp   # მხოლოდ იმ შემთხვევაში, თუ ეს ბილიკი ცარიელია/გამოუყენებელია
ln -s ~/empirerp-ucp/public ucp

თქვენი საიტი მაშინ ხელმისაწვდომი იქნება https://yourdomain.com/ucp-ზე. დააყენეთ APP_URL .env-ში, რომ ზუსტად ემთხვეოდეს.

არასდროს ატვირთოთ ucp-laravel საქაღალდის შიგთავსი პირდაპირ public_html-ში — ეს გამოააშკარავებდა .env-ს (თქვენი ბაზის პაროლები), app/-ს და ყველა სხვა სერვერის მხარის ფაილს პირდაპირ HTTP-ზე.

8მიგრაციები და სიდერები

php artisan migrate --force
php artisan db:seed --force

ეს ქმნის ყველა UCP-საკუთარ ცხრილს (მომხმარებლები, როლები, სიახლეები, რულეტკა, აუდიტ-ლოგები, ბანები, გაფრთხილებები, სერვერის სტატუსის ცხრილები და ა.შ.) და ავსებს ნაგულისხმევ როლებს/უფლებებს (superadmin, admin, moderator, player) და საიტის ნაგულისხმევ პარამეტრებს. ის არ ეხება თქვენი FiveM სერვერის საკუთარ ცხრილებს (players, player_vehicles, …) — ისინი იკითხება/იწერება ადგილზე, არასდროს მიგრირდება.

9Frontend-ის აწყობა

თუ ჰოსტინგს აქვს Node.js App ფუნქცია

# cPanel → Setup Node.js App → შექმენით აპლიკაცია, მიმართული ~/empirerp-ucp-ზე
# შემდეგ, იმ აპლიკაციის "Run NPM Install" / ტერმინალში:
npm install
npm run build

თუ ჰოსტინგს არ აქვს Node.js მხარდაჭერა

ააწყვეთ ლოკალურად და ატვირთეთ მხოლოდ კომპილირებული შედეგი:

# თქვენს კომპიუტერზე, ucp-laravel/-ის ასლში
npm install
npm run build
# ეს აწარმოებს ucp-laravel/public/build/-ს — ატვირთეთ ეს ერთი საქაღალდე
# იმავე ბილიკზე სერვერზე (public/build/).

npm run build საჭიროა ხელახლა გაშვება მხოლოდ მაშინ, როცა იცვლება frontend კოდი (resources/js, resources/css) — არა ყოველი დეპლოის დროს.

10Discord OAuth და ბოტის კონფიგურაცია

  1. გადადით Discord Developer Portal-ზე და შექმენით აპლიკაცია.
  2. OAuth2 → General: დააკოპირეთ Client ID და Client Secret DISCORD_CLIENT_ID / DISCORD_CLIENT_SECRET-ში.
  3. OAuth2 → Redirects: დაამატეთ https://ucp.ticcix.com/auth/discord/callback (უნდა ემთხვეოდეს .env-ის DISCORD_REDIRECT_URI-ს ზუსტად, სქემის ჩათვლით).
  4. Bot ტაბი: შექმენით ბოტი, დააკოპირეთ მისი ტოკენი DISCORD_BOT_TOKEN-ში.
  5. დაპატიჟეთ ბოტი თქვენს Discord სერვერზე, სულ მცირე View Server Members უფლებით (საჭიროა როლების სინქისთვის, წევრის როლების წასაკითხად).
  6. დააკოპირეთ თქვენი Discord სერვერის ID (მარჯვენა კლიკი სერვერის აიქონზე → Copy Server ID, Developer Mode-ის ჩართვით Discord-ში) DISCORD_GUILD_ID-ში.
  7. ადმინ პანელში, მისი გაშვების შემდეგ, მომავალი "Discord როლების მიბმის" ეკრანიდან (ან პირდაპირ discord_role_maps ცხრილში ჩანაწერების დამატებით) დააკავშირეთ კონკრეტული Discord როლის ID-ები admin / moderator და ა.შ.-სთან.

11პირველი სუპერადმინის შექმნა

შედით ერთხელ Discord-ით (რომ თქვენი ანგარიშის ჩანაწერი შეიქმნას), შემდეგ დაიწინაურეთ თავად Terminal-ით:

php artisan tinker
>>> $user = App\Models\User::where('email', 'you@example.com')->first();
>>> $user->assignRole('superadmin');
>>> exit

2FA ავტომატურად ხდება სავალდებულო შემდეგივე შესვლისას superadmin/admin/moderator ანგარიშისთვის — გადამისამართდებით მის დასაყენებლად, ნებისმიერ გვერდზე მისვლამდე.

12Cron: სქედულერი და queue

cPanel → Cron Jobs → დაამატეთ ახალი დავალება, ყოველ წუთს გასაშვები:

* * * * * cd ~/empirerp-ucp && php artisan schedule:run >> /dev/null 2>&1

ეს ატარებს საათობრივ Discord როლების ხელახალ სინქრონიზაციას (routes/console.php).

Queue worker

უმეტესი გაზიარებული (shared) cPanel ჰოსტინგი არ იძლევა ხანგრძლივად მომუშავე ფონური პროცესების საშუალებას, ასე რომ მუდმივი php artisan queue:work დემონი ჩვეულებრივ შეუძლებელია. ორი ვარიანტია:

მიდგომაროდის გამოვიყენოთ
QUEUE_CONNECTION=sync .env-ში ყველაზე მარტივი — დავალებები (Discord როლის სინქი შესვლისას) სრულდება დაუყოვნებლივ, request-ის ფარგლებში. კარგია დაბალი/საშუალო ტრაფიკისთვის.
Cron-ით მართული queue დამუშავება დაამატეთ მეორე cron ჩანაწერი: * * * * * cd ~/empirerp-ucp && php artisan queue:work --stop-when-empty >> /dev/null 2>&1. ამუშავებს queue-ში დავალებებს ყოველ წუთს, მუდმივი პროცესის გარეშე. საჭიროებს QUEUE_CONNECTION=database-ს (რაც უკვე ნაგულისხმევია).

თუ თქვენი ჰოსტინგი VPS/საკუთარი cPanel ინსტალაციაა, სადაც გაქვთ root და პროცესის მენეჯერი (systemd, Supervisor), ნამდვილი მუდმივი queue:work worker უფრო კარგი გრძელვადიანი გამოსავალია — უბრალოდ აქ ნაგულისხმევი არ არის, რადგან უმეტესი cPanel გაზიარებული ჰოსტინგი ამას არ უჭერს მხარს.

13FiveM სერვერის და API ტოკენის რეგისტრაცია

REST API (routes/api.php) აუთენტიფიცირებს FiveM Lua რესურსებს ცალკე Sanctum ტოკენით თითოეული სერვერისთვის — განცალკევებული ნებისმიერი მოთამაშის/ადმინის დაშბორდის შესვლისგან. შექმენით ერთი ჯერჯერობით Tinker-ით:

php artisan tinker
>>> $server = App\Models\FiveMServer::create(['name' => 'EmpireRP Main']);
>>> $token = $server->createToken('main-server')->plainTextToken;
>>> echo $token;
>>> exit

დააკოპირეთ ეს ტოკენი ახლავე — ის მხოლოდ ერთხელ ჩანს. შეინახეთ ის, როგორც convar ან კონფიგურაციის მნიშვნელობა FiveM სერვერზე, არასდროს დააკომიტოთ საჯარო რესურსის რეპოზიტორიაში.

14FiveM/Lua ინტეგრაციის მაგალითები

მაგალითი server-side Lua კოდისა API-სთან დასაკავშირებლად (მოარგეთ URL/ტოკენის მიღება თქვენს კონვენციას):

local UCP_URL = 'https://ucp.ticcix.com/api/v1'
local UCP_TOKEN = GetConvar('ucp_api_token', '')

local function UcpRequest(method, path, body, cb)
    PerformHttpRequest(UCP_URL .. path, function(status, responseText, headers)
        cb(status, json.decode(responseText or '{}'))
    end, method, body and json.encode(body) or '', {
        ['Content-Type'] = 'application/json',
        ['Authorization'] = 'Bearer ' .. UCP_TOKEN,
    })
end

-- ბანის შემოწმება დაკავშირებისას
AddEventHandler('playerConnecting', function(name, setKickReason, deferrals)
    local src = source
    deferrals.defer()
    local license = GetPlayerIdentifierByType(src, 'license')

    UcpRequest('GET', '/players/' .. license .. '/ban-check', nil, function(status, data)
        if data and data.banned then
            deferrals.done(('You are banned: %s'):format(data.reason or 'No reason given'))
        else
            deferrals.done()
        end
    end)
end)

-- სერვერის heartbeat ყოველ 60 წამში
CreateThread(function()
    while true do
        Wait(60000)
        local players = {}
        for _, id in ipairs(GetPlayers()) do
            table.insert(players, { source_id = tonumber(id), name = GetPlayerName(id) })
        end
        UcpRequest('POST', '/server/heartbeat', {
            player_count = #GetPlayers(),
            max_players = GetConvarInt('sv_maxclients', 48),
            players = players,
        }, function() end)
    end
end)

15HTTPS / SSL

cPanel → SSL/TLS Status → გაუშვით AutoSSL თქვენი სუბდომეინისთვის (Let's Encrypt, უფასო, ავტომატურად განახლებადი). გაცემის შემდეგ დარწმუნდით, რომ APP_URL იყენებს https://-ს და SESSION_SECURE_COOKIE=true რჩება ჩართული .env-ში — სესიის cookie-ები არ გაიგზავნება უბრალო HTTP-ზე ამის ჩართვისას, რაც განზრახულია.

16უსაფრთხოების საბოლოო ჩეკლისტი

პუნქტისტატუსი
APP_DEBUG=falseსავალდებულო
.env ვებ-root-ის გარეთაა (მხოლოდ public/ შიგნითაა)დაცულია საქაღალდის სტრუქტურით
ცალკე MySQL მომხმარებლები mysql და fivem კავშირებისთვისთქვენი პასუხისმგებლობა — იხილეთ ნაბიჯი 4
2FA სავალდებულოა admin/moderator/superadmin როლებისთვისჩაშენებულია — EnsureTwoFactorIsConfirmed middleware
CSRF დაცვაLaravel-ის ნაგულისხმევი, ჩართული ყველა web როუტისთვის
Rate limiting login/2FA/API-ზეჩაშენებულია
პაროლები დაჰეშილია bcrypt/argon2-ითLaravel-ის ნაგულისხმევი hasher — არასდროს MD5/plaintext
FiveM API ტოკენების როტაცია/გაუქმება სერვერის გათიშვისასთქვენი პასუხისმგებლობა
რეგულარული composer update / npm update უსაფრთხოების პაჩებისთვისთქვენი პასუხისმგებლობა

17ძველი PHP საიტის გამორთვა

ორიგინალი engine/, view/, public/admin/app-assets, base.sql და root-ის index.php/.htaccess ფაილები დარჩა ადგილზე დისკზე და არ შეცვლილა და არ წაშლილა ამ გადაწერის დროს — ისინი შეიცავდნენ არაავტორიზებული ადმინ-ჩაწერის bypass-ს და SQL injection-ის პრობლემებს, დოკუმენტირებულს docs/database-analysis.md-ში, ასე რომ არ დატოვოთ ისინი საჯაროდ განლაგებული ახალი UCP-ის ამუშავების შემდეგ.

მას შემდეგ, რაც დაადასტურებთ ახალი UCP-ის მუშაობას production-ში დასაწყისიდან ბოლომდე (შესვლა, სიახლეები, რულეტკა, ადმინ პანელები, API გამოძახებები თქვენი FiveM სერვერიდან — ყველაფერი მუშაობს):

  1. მიმართეთ თქვენი დომენი/სუბდომეინი მთლიანად ახალ ucp-laravel/public საქაღალდეზე (ნაბიჯი 7).
  2. გააკეთეთ საბოლოო სარეზერვო ასლი ძველი საიტის ფაილებისა და მისი base.sql ბაზის დამპისა, იმ შემთხვევისთვის, თუ დაგჭირდებათ ძველი სიახლეების/რულეტკის შემცველობის ნახვა.
  3. წაშალეთ ძველი engine/, view/, public/ (ძველი, არა-Laravel-ის ვერსია), index.php და .htaccess პროექტის root-იდან.
  4. წაშალეთ ძველი ucp_admin, ucp_news, ucp_settings, ucp_category_roulette, ucp_item_roulette, ucp_drop_roulette ცხრილები ძველი ბაზიდან, მას შემდეგ, რაც დაადასტურებთ, რომ მათ აღარაფერი კითხულობს.

18ხშირი პრობლემები

სიმპტომისავარაუდო მიზეზი
500 შეცდომა, ცარიელი გვერდიდროებით დააყენეთ APP_DEBUG=true და შეამოწმეთ storage/logs/laravel.log; შემდეგ დააბრუნეთ false-ზე.
"vendor/autoload.php not found"composer install არ გაშვებულა, ან გაშვებულია არასწორ საქაღალდეში.
Assets 404 / დაუსტილებელი გვერდიnpm run build არ გაშვებულა, ან public/build არ აიტვირთა.
Discord შესვლა გადამისამართდება შეცდომაზეRedirect URI არ ემთხვევა — უნდა ემთხვეოდეს Developer Portal-ის ჩანაწერს ზუსტად, https://-ის ჩათვლით.
"SQLSTATE[HY000] [1045] Access denied"არასწორი ბაზის დაშვებები .env-ში, ან MySQL მომხმარებელი არ არის დამატებული იმ ბაზაზე cPanel-ში.
FiveM API გამოძახებები აბრუნებს 401-სტოკენი არასწორად გაიცა, ან Authorization: Bearer … header არ იგზავნება Lua რესურსიდან.