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

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

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

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

კავშირი | დანიშნულება | Env პრეფიქსი
--- | --- | ---
`mysql` | UCP-ის საკუთარი მონაცემები: მომხმარებლები, როლები, სიახლეები, რულეტკა, აუდიტ-ლოგები, პარამეტრები, სერვერის ტოკენები | `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 → Software → Composer (თუ ჰოსტინგზე უკვე დაყენებულია), ან SSH/Terminal წვდომა ხელით დასაყენებლად.
- Terminal / SSH წვდომა — cPanel → Advanced → Terminal. საჭიროა `artisan` ბრძანებების გასაშვებად. თუ ეს არ არის ხელმისაწვდომი, მიმართეთ ჰოსტინგის მხარდაჭერას SSH-ის ჩასართავად.
- Node.js — cPanel → Software → Setup 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_ENV` | `production`
`APP_DEBUG` | `false` — არასდროს `true` production-ში, რადგან ეს ავლენს stack trace-ებს
`DB_DATABASE` / `DB_USERNAME` / `DB_PASSWORD` | UCP ბაზის დაშვებები მე-4 ნაბიჯიდან
`FIVEM_DB_DATABASE` / `FIVEM_DB_USERNAME` / `FIVEM_DB_PASSWORD` | FiveM სერვერის ბაზის დაშვებები მე-4 ნაბიჯიდან
`FIVEM_FRAMEWORK` | `qbcore`, `qbox` ან `esx`
`DISCORD_CLIENT_ID` / `DISCORD_CLIENT_SECRET` | Discord 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 რესურსიდან.