===============================================================
 FOUNTAINTRADE — TÁVOLI (LINUX) SZERVERES TELEPÍTÉS,
 TÖBB KASSZA/ESZKÖZ EGYIDEJŰ HASZNÁLATÁHOZ
===============================================================

Ez az útmutató attól különbözik az install.txt-től, hogy nem egyetlen
helyi géprakerülő "php -S" fejlesztői szerverről szól, hanem egy
VALÓDI, TÖBB ESZKÖZ ÁLTAL EGYSZERRE HASZNÁLHATÓ szerverfelállásról
Linuxon (pl. bérelt VPS-en). A "php -S" beépített szerver EGYSZERRE
CSAK EGY kérést tud kiszolgálni — ha több pénztári gép/tablet egyszerre
próbálja használni, a kérések sorba állnak, és a program lassúnak vagy
akadozónak tűnik. Erre a célra Nginx + PHP-FPM kell, ami valódi
egyidejű kiszolgálásra való.

FONTOS, EGY MÁSIK "több eszköz" TÖRTÉNET: ez a dokumentum arról szól,
hogyan tegyél EGYETLEN FountainTrade-példányt (Önálló vagy Szerver
node) alkalmassá arra, hogy TÖBB BÖNGÉSZŐ egyszerre, gyorsan elérje —
minden böngésző UGYANAZT a gépet nyitja meg. Az 1.5.0-ban bevezetett
Windows-oldali "Kliens" szerepkör (lásd install.txt "0b. TÖBB-
TERMINÁLOS ÜZEMMÓD" és README.md "Több-terminálos üzemmód: Önálló /
Szerver / Kliens") egy MÁSIK dolog: ott KÜLÖN Windows-gépeknek van
SAJÁT telepítésük, de SAJÁT adatbázisuk nincs — mindent egy Szerver
node-on keresztül, HTTP(S)-en át érnek el. A kettő kombinálható (egy
Szerver node maga is futhat itt lent leírt Nginx+PHP-FPM mögött), de
egymástól teljesen független döntés — ne keverd össze őket.

Célgép feltételezve: Ubuntu vagy Debian szerver, root/sudo joggal.


---------------------------------------------------------------
1. CSOMAGOK TELEPÍTÉSE
---------------------------------------------------------------

    sudo apt update
    sudo apt install nginx php8.3-fpm php8.3-cli php8.3-sqlite3 \
        php8.3-curl php8.3-mbstring php8.3-gd php8.3-xml php8.3-zip \
        certbot python3-certbot-nginx sqlite3

(Ha az Ubuntu/Debian verziód repója nem 8.3-at ad alapból, adhatod
hozzá az ondrej/php PPA-t: `sudo add-apt-repository ppa:ondrej/php`,
vagy állítsd a fenti sorban 8.3 helyett a nálad elérhető, 8.1+
verziószámra.)

CSAK akkor, ha MySQL-t választasz adatbázisnak SQLite helyett (lásd
README "Adatbázis: SQLite vs MySQL" szakasza) — a fenti lista NEM
tartalmazza, enélkül a MySQL-kapcsolat "could not find driver" hibával
elbukik:

    sudo apt install php8.3-mysql

Megjegyzés: a MySQL-ágat ez a telepítés NEM validálta élő MySQL-
szerverrel, csak kódszinten és SQL-rögzítéssel (lásd README).

Ellenőrzés, hogy minden kiterjesztés tényleg bekapcsolt-e:

    php -m | grep -iE "sqlite3|curl|mbstring|gd|xml|zip|fileinfo|openssl|opcache|mysql"

Ha valamelyik hiányzik (pl. fileinfo vagy openssl néha külön
csomagban van disztribúciónként), keresd meg és telepítsd:

    apt search php8.3- | grep -iE "fileinfo|openssl"


---------------------------------------------------------------
2. A PROJEKT FELTÖLTÉSE
---------------------------------------------------------------

Töltsd fel a teljes "stock-manager-app" mappát a szerverre, pl.:

    /var/www/stock-manager-app/

(scp, rsync vagy git — bármelyik jó.)

Jogosultságok — az Nginx/PHP-FPM alapból "www-data" felhasználóként
fut Debian/Ubuntu alatt, neki kell írnia tudnia az adatokba:

    sudo chown -R www-data:www-data /var/www/stock-manager-app/data \
        /var/www/stock-manager-app/invoices \
        /var/www/stock-manager-app/webroot/assets
    sudo chmod -R 775 /var/www/stock-manager-app/data \
        /var/www/stock-manager-app/invoices \
        /var/www/stock-manager-app/webroot/assets


---------------------------------------------------------------
3. NGINX BEÁLLÍTÁSA
---------------------------------------------------------------

FONTOS: a "root" mindig a "webroot" ALMAPPÁRA mutasson, SOHA a projekt
gyökerére! Ha véletlenül a projekt gyökerét szolgálod ki, bárki
letöltheti a config/config.php-t (benne API-kulcsokkal) vagy magát az
SQLite adatbázist. Ugyanezért a "data", "config", "src" mappákat is
kifejezetten tiltsd.

Az app mindig a domain GYÖKERÉBŐL várja magát (a JS mindenhol
"/api/..." útvonalakat hív, nem relatívat) — ne tedd almappába (pl.
példa.hu/kassza/), csak közvetlenül a domain/aldomain gyökerébe.

Hozz létre egy fájlt: /etc/nginx/sites-available/stock-manager

    server {
        listen 80;
        server_name kassza.pelda.hu;   # <- ide a saját domained/aldomained

        root /var/www/stock-manager-app/webroot;
        index index.php;

        client_max_body_size 16m;   # logó/import feltöltéshez elég hely

        location ~ /\.(?!well-known) { deny all; }   # rejtett fájlok tiltása

        # Ugyanazok a biztonsági fejlécek, mint amit az API-válaszok már
        # eddig is kaptak (lásd webroot/api/_bootstrap.php) — itt a statikus
        # HTML-oldalakra is kiterjesztve. Szigorúbb Content-Security-Policy
        # és HSTS SZÁNDÉKOSAN nincs itt beállítva — mindkettő könnyen
        # eltörhet valamit (pl. a TinyMCE-t vagy egy oldal beágyazott
        # <script> blokkját, illetve a HSTS-t visszavonhatatlanul, ha
        # elsietve kerül be) — csak alapos, külön tesztelés után érdemes
        # bevezetni.
        add_header X-Content-Type-Options nosniff always;
        add_header X-Frame-Options DENY always;
        add_header Referrer-Policy same-origin always;

        location / {
            try_files $uri $uri/ =404;
        }

        location ~ \.php$ {
            include snippets/fastcgi-php.conf;
            fastcgi_pass unix:/run/php/php8.3-fpm.sock;
        }
    }

Engedélyezd, és teszteld a konfigurációt:

    sudo ln -s /etc/nginx/sites-available/stock-manager /etc/nginx/sites-enabled/
    sudo nginx -t
    sudo systemctl reload nginx

Ha ADATBÁZIS FÁJLNÉV/PATH miatt "unix:/run/php/php8.3-fpm.sock" nem
létezik nálad, nézd meg a tényleges socket-elérési utat:

    ls /run/php/


---------------------------------------------------------------
4. PHP-FPM ÉS PHP.INI HANGOLÁS
---------------------------------------------------------------

A /etc/php/8.3/fpm/php.ini fájlban ellenőrizd (állítsd, ha kell):

    upload_max_filesize = 16M
    post_max_size = 16M
    opcache.enable=1
    opcache.memory_consumption=64
    opcache.max_accelerated_files=4000
    opcache.revalidate_freq=2
    opcache.validate_timestamps=1

A pool-beállítás (/etc/php/8.3/fpm/pool.d/www.conf) egy néhány
pénztáras kisboltnak alapból (pm = dynamic, pm.max_children = 5-10)
bőven elég — csak akkor kell hangolni, ha sok tíz egyidejű eszközzel
számolsz.

Alkalmazd a változásokat:

    sudo systemctl restart php8.3-fpm


---------------------------------------------------------------
5. HTTPS (Let's Encrypt) — ERŐSEN AJÁNLOTT
---------------------------------------------------------------

Mivel a szerver most már a nyílt interneten (nem csak helyi hálózaton)
érhető el, a bejelentkezési jelszó és a session-süti védelme miatt
mindenképp állíts be HTTPS-t:

    sudo certbot --nginx -d kassza.pelda.hu

Ez automatikusan beállítja a tanúsítványt ÉS a 80 -> 443 átirányítást
is. Az app session-kezelése fel van készítve erre — HTTPS alatt
bekapcsolja a "Secure" sütijelzőt —, DE ehhez a PHP-nek ténylegesen
LÁTNIA kell, hogy a kérés HTTPS-en jött. Mivel az Nginx itt közvetlenül,
FastCGI-n (socketen) keresztül adja át a kérést a PHP-FPM-nek (nem egy
külön HTTP-proxy fejléccel), ehhez a certbot által létrehozott 443-as
szerver-blokkban (nem a 80-asban!) explicit be kell állítani:

    fastcgi_param HTTPS on;

a "location ~ \.php$ { ... }" blokkon belül, a "fastcgi_pass" sor
mellé. A certbot ezt NEM teszi be automatikusan. Enélkül a PHP sose
látja HTTPS-nek a kérést, tehát a session-süti "Secure" jelzője
csendben false marad akkor is, ha a böngésző ténylegesen HTTPS-t
használ. Ellenőrzés mentés után:

    sudo nginx -t && sudo systemctl reload nginx

(Ha egy TÁVOLI, más gépen/konténerben futó reverse proxy vagy CDN áll
ez elé az Nginx elé — pl. Cloudflare —, az alkalmazás a "X-Forwarded-
Proto" fejlécet KIZÁRÓLAG akkor fogadja el megbízhatónak, ha a
közvetlen TCP-kapcsolat (ami ebben a felállásban maga a helyi Nginx)
loopback címről jön — ez itt, ugyanazon a gépen, automatikusan
teljesül. Ha viszont MAGA ez az Nginx is csak egy távolabbi proxy
mögött áll, a lánc minden tagjának a valódi HTTPS-állapotot kell
tovább közvetítenie.)


---------------------------------------------------------------
6. TŰZFAL (alap)
---------------------------------------------------------------

    sudo ufw allow 'Nginx Full'   # 80 + 443
    sudo ufw allow OpenSSH        # ha ufw-t most kapcsolod be, ezt előbb tedd, különben kizárod magad
    sudo ufw enable


---------------------------------------------------------------
7. TELEPÍTŐ VARÁZSLÓ FUTTATÁSA
---------------------------------------------------------------

Nyisd meg https://kassza.pelda.hu — ugyanaz az install.php telepítő
varázsló fut le, mint helyi telepítésnél (lásd install.txt 5. pontja).
SQLite-tal itt is elég egy kattintás.


---------------------------------------------------------------
8. TÖBB KASSZA/ESZKÖZ EGYIDEJŰ HASZNÁLATA — MIT JELENT TECHNIKAILAG
---------------------------------------------------------------

Ehhez az üzemmódhoz a kódban is javítottam egy hiányosságot: a SQLite
adatbázis eddig csak WAL módban futott (ez már jó, egyszerre enged
olvasást és írást), de nem volt beállítva "busy_timeout" — enélkül, ha
két eszköz PONTOSAN egyszerre próbált írni (pl. két kassza egyszerre
zár le egy eladást), a második azonnal "database is locked" hibát
kapott volna, ahelyett hogy pár másodpercet várt volna a sorára. Most
5 másodperces türelmi időt kap minden írás, ami bőven elég egy normál
eladás lezárásához — így a második eszköz kérése egyszerűen egy
pillanattal később fut le, hibaüzenet helyett.

Kis-közepes forgalmú boltnak (néhány kassza) ez a SQLite+WAL+
busy_timeout kombináció megbízhatóan elég — nem kell hozzá külön
adatbázis-szerver. Ha a jövőben sokkal nagyobb, sok egyidejű eladást
bonyolító forgalmad lenne, a config/config.php-ban egy sor
átállítással MySQL-re válthatsz (lásd README.md "Database: SQLite vs
MySQL" szakaszát) — de ehhez a legtöbb kisboltnak nincs is szüksége.

Amit még érdemes tudni több eszköz esetén:

  - Mindegyik eszköz UGYANAZT a webcímet nyissa meg (pl.
    https://kassza.pelda.hu) — nem külön-külön telepítést, hanem
    ugyanazt a szervert böngészőből.
  - Dolgozói PIN-bejelentkezés eszközönként külön történik (mindegyik
    kasszán be kell jelentkezni a dolgozónak), de az alkalmazás-
    jelszó (ha bekapcsolod) böngészőnként/eszközönként külön session.
  - A sikertelen bejelentkezési/PIN-próbálkozások limitje IP-cím
    alapú. Ha több kassza EGY boltban, egy router mögött, egy közös
    nyilvános IP-vel megy ki az internetre, ezek OSZTOZNAK a
    próbálkozás-limiten (egy kasszán elrontott PIN közelebb viheti a
    zároláshoz a többi kasszát is ugyanabban a boltban). Kisboltnak ez
    a gyakorlatban ritkán okoz gondot, de érdemes tudni róla.
  - Nyomtatás: a hálózati (Ethernet/WiFi) hőnyomtató-integráció
    (Beállítások → Nyomtató) a SZERVER gépéről küld ESC/POS parancsot
    a nyomtató IP-jére — ha a szerver távoli VPS, ez csak akkor
    működik, ha a nyomtató is elérhető onnan (VPN vagy publikus IP
    esetén). Ha a nyomtató a boltban van, a szerver pedig távoli
    VPS-en, a böngészős nyomtatás (mindig működik, driver nélkül) a
    megbízhatóbb út helyi nyomtatóhoz.


---------------------------------------------------------------
9. AUTOMATIKUS FELADATOK (cron)
---------------------------------------------------------------

Linuxon a standard crontab-bal, magán a szerveren (mivel a webszerver
is ott fut, a "localhost" cím itt is működik, csak nem a 8000-es
porton, hanem a rendes 80/443-on):

    sudo crontab -e -u www-data

    * * * * *     curl -s -H "X-Cron-Token: IDE_A_CRON_TOKEN" "https://kassza.pelda.hu/api/auto-sync-run.php" > /dev/null
    */15 * * * *  curl -s -H "X-Cron-Token: IDE_A_CRON_TOKEN" "https://kassza.pelda.hu/api/auto-backup-run.php" > /dev/null
    * * * * *     curl -s -H "X-Cron-Token: IDE_A_CRON_TOKEN" "https://kassza.pelda.hu/api/nav-queue-run.php" > /dev/null
    0 * * * *     curl -s -H "X-Cron-Token: IDE_A_CRON_TOKEN" "https://kassza.pelda.hu/api/nav-incoming-sync-run.php" > /dev/null
    */30 * * * *  curl -s -H "X-Cron-Token: IDE_A_CRON_TOKEN" "https://kassza.pelda.hu/api/update-check-run.php" > /dev/null
    * * * * *     curl -s -H "X-Cron-Token: IDE_A_CRON_TOKEN" "https://kassza.pelda.hu/api/wc-queue-run.php" > /dev/null
    */30 * * * *  curl -s -H "X-Cron-Token: IDE_A_CRON_TOKEN" "https://kassza.pelda.hu/api/ai-daily-intelligence-run.php" > /dev/null

(Ez mind a hét, a Windows telepítő által is automatikusan regisztrált
cron-feladat Linux/crontab megfelelője — lásd install.txt "8.
AUTOMATIKUS FELADATOK" szakaszát a táblázatos áttekintésért. A
wc-queue-run.php a helyi eladásokból származó WooCommerce készlet-
KIKÜLDÉST dolgozza fel (a fenti auto-sync-run.php ezzel szemben a
termék/rendelés BEHÚZÁST végzi) — enélkül a helyi eladások miatti
készletcsökkenés sosem jut el a WooCommerce-be. Az ai-daily-
intelligence-run.php csak akkor csinál bármit, ha a Beállítások → AI
asszisztens alatt az "AI napi intelligencia" be van kapcsolva.)

(A token a Beállítások → Mentés alatt beállítható cron titkos
tokennel egyezzen — enélkül ezek a végpontok bejelentkezés nélkül
sose futnának le, cron ugyanis nem tud böngészőben bejelentkezni. A
token KIZÁRÓLAG az X-Cron-Token fejlécben megy, sose URL-ben/
query-stringben — egy URL-be írt titok webszerver-/proxy-naplókba
kerülhetne; egyik végpont sem fogad el query-string tokent. A
Beállítások oldal a "localhost:8000"-es változatot mutatja
példaként, mert az a helyi/fejlesztői telepítésre utal — élesben a
fenti, a saját domained szerinti sort használd, a saját tokeneddel.

A nav-queue-run.php és a nav-incoming-sync-run.php sor a NAV Online
Számla integrációhoz tartozik: előbbi a kimenő (a boltban kiállított)
számlák NAV felé történő tényleges beküldését végzi, utóbbi a
más adózók által kiállított, beérkező számlák szinkronját. Mindkettő
önmagától "skipped"-del tér vissza, ha a NAV integráció nincs
bekapcsolva (Beállítások → Számlázz.hu → "NAV számla queue
háttér-feldolgozás bekapcsolva" / "NAV bejövő számla sync
háttér-feldolgozás bekapcsolva") vagy a NAV hitelesítő adatok még
nincsenek kitöltve — tehát ártalmatlan felvenni akkor is, ha egyelőre
nem használod a NAV-integrációt. DE ha bekapcsolod a NAV Online Számla
közvetlen kiállítást, a nav-queue-run.php cron-belépés attól kezdve
NEM opcionális: enélkül a kiállított NAV-számlák tartósan, látszólag
hiba nélkül "queued" állapotban maradnak, sose kerülnek ténylegesen
beküldésre. Egy kimaradt cron-futás egyik NAV-végpontnál sem okoz
adatvesztést — mindkettő onnan folytatja, ahol a legutóbbi sikeres
futás abbahagyta.)

Az update-check-run.php az önfrissítő rendszer OLCSÓ, csak-ellenőrző
belépési pontja (lásd README "Önfrissítés" szakasza) — csak akkor
kérdezi le ténylegesen a GitHub-ot, ha esedékes a Beállítások →
Frissítések alatt beállított időköz alapján, és csak akkor, ha az
"Automatikus ellenőrzés" be van kapcsolva (alapból KIKAPCSOLVA). Ez a
sor SOSE telepít semmit — csak jelzi, ha van újabb elérhető verzió.

Ha felügyelet nélküli, éjszakai AUTOMATIKUS TELEPÍTÉST is szeretnél
(ez opcionális, alapból KIKAPCSOLVA — lásd README), egy KÜLÖN, CLI-alapú
cron-bejegyzés is szükséges (ez NEM curl-lal hívott HTTP-végpont, mert a
tényleges telepítés percekig tarthat):

    0 4 * * *     www-data /usr/bin/php8.3 /var/www/stock-manager-app/tools/update-install-cli.php >> /var/log/fountaintrade-update.log 2>&1

Ehhez a config/config.php "update" szakaszában a "php_cli_binary" mezőt
a rendszer VALÓDI CLI PHP futtathatójára kell állítani (pl.
"/usr/bin/php8.3") — a php-fpm futtatható NEM alkalmas erre, lásd README.


---------------------------------------------------------------
10. FRISSÍTÉS
---------------------------------------------------------------

Kódfrissítéskor NE írd felül a "data" mappát (abban van az élő
adatbázis, biztonsági mentések) és a "config/installer-generated.php"
fájlt (a telepítő varázsló írta, a bolt-specifikus beállításokat
tartalmazza). Minden más fájl biztonságosan cserélhető.


---------------------------------------------------------------
11. HIBAELHÁRÍTÁS
---------------------------------------------------------------

502 Bad Gateway
  -> A PHP-FPM nem fut, vagy rossz a socket-útvonal az Nginx
     configban. Ellenőrizd: sudo systemctl status php8.3-fpm
     és hogy /run/php/php8.3-fpm.sock létezik-e.

"Permission denied" mentéskor / fájlfeltöltéskor
  -> A "data", "invoices", "webroot/assets" mappák tulajdonosa nem
     www-data, vagy nincs rajtuk írási jog. Ismételd meg a 2. pont
     chown/chmod parancsait.

413 Request Entity Too Large logó/import feltöltésnél
  -> A client_max_body_size az Nginx configban túl alacsony (3. pont),
     VAGY az upload_max_filesize/post_max_size a php.ini-ben (4. pont)
     — mindkettőt állítsd legalább 16M-ra.

"database is locked" hiba MÉG a busy_timeout beállítás UTÁN is
  -> Rendkívül ritkán, nagyon hosszú (5 másodpercnél tovább tartó)
     tranzakció esetén fordulhat elő. Ellenőrizd a data/stock.sqlite
     fájl mellett nincs-e "-wal" és "-shm" fájl felhalmozódva
     rendellenesen nagyra — ha igen, egy VACUUM (Beállítások → Mentés
     → "Mentés most", ami VACUUM INTO-t használ) segíthet.

Az oldal http-n működik, de a bejelentkezés után folyton kidob
  -> HTTPS nélkül ez normális lehet, ha valamiért mégis "Secure"
     sütit próbálna küldeni a böngésző — állítsd be a HTTPS-t (5.
     pont), utána ez magától rendeződik.


---------------------------------------------------------------
Ennyi — jó munkát vele!
---------------------------------------------------------------
