Files
servicedesk/install.md
Kacper 03c6ec7cae v1.4.0
Co nowego:
- Wsparcie Active Directory dla LDAP (obok LLDAP/OpenLDAP), przełącznik typu
  katalogu w Admin > Integracje.
- Wyszukiwarka klientów dla operatora (Operator > Klienci).
- Stronicowanie kolejki operatora (50/stronę) i dashboardu klienta (20/stronę).
- Globalna wyszukiwarka zgłoszeń (Ctrl+K/Cmd+K) z operatorami w stylu Gmaila
  (od:, temat:, treść:, numer:), plus przycisk "Szukaj" w panelu bocznym.
- Ostatnio przeglądane zgłoszenia w panelu bocznym operatora.
- Przeprojektowany pasek nawigacji: suwak Klient/Operator/Administrator
  zamiast rozwijanego menu, bogatsze menu profilu (nazwa/e-mail/role),
  dynamiczne tytuły kart przeglądarki na każdej podstronie.
- Narzędzie do jednorazowego importu historii zgłoszeń z Heska 3.x
  (scripts/hesk-import/).
- Poprawka: paginacja pokazywała surowe klucze tłumaczeń zamiast tekstu
  (brakujący lang/pl/pagination.php).

Zaktualizowana dokumentacja: README, CLAUDE.md, install.md, ARCHITECTURE.md,
CHANGELOG.md, wiki/*.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-05 00:58:35 +02:00

575 lines
21 KiB
Markdown

# Instalacja / wdrożenie
Dwa sposoby uruchomienia Servicedesk: przez **Docker Compose** (tak jak działa ten
projekt obecnie) albo **bezpośrednio na serwerze** z Apache/Nginx + PHP-FPM, bez
kontenerów.
Zanim zaczniesz, ważne rozróżnienie — w projekcie są **dwa różne pliki `.env`**:
| Plik | Do czego służy |
|---|---|
| `.env` (w katalogu głównym repo, obok `compose.yaml`) | Zmienne dla **Docker Compose** — hasła do MariaDB, nazwa hosta dla Traefika. Czyta go `compose.yaml`, **nie** czyta go Laravel. |
| `src/.env` (w katalogu aplikacji Laravel) | Cała konfiguracja **aplikacji** — połączenie z bazą, adres URL, klucz szyfrowania, LDAP, SMTP, itd. Czyta go wyłącznie Laravel. |
Oba istnieją niezależnie od siebie (nawet w wersji Docker) — wartości `DB_*` w
`src/.env` muszą **odpowiadać** wartościom `MYSQL_*` z głównego `.env`, ale to dwa
osobne pliki, w dwóch różnych miejscach.
---
## 1. Wdrożenie przez Docker Compose
### Wymagania
- Docker + wtyczka `docker compose`.
- Zewnętrzna sieć Docker `traefik_public`, jeśli używasz Traefika tak jak w
`compose.yaml` (`docker network create traefik_public`, jeśli jeszcze nie
istnieje). Bez Traefika trzeba samodzielnie zmapować porty serwisu `app`
na hosta (`ports: ["8080:80"]`) i obsłużyć TLS inaczej (patrz sekcja 2 niżej, w
razie potrzeby reverse-proxy przed kontenerem).
### 1.1. Pliki `compose.yaml` i `.env` w katalogu głównym
Oba są celowo `.gitignore`'owane (podobnie jak `src/.env`) — kopiujesz je z
szablonów przy pierwszym wdrożeniu, a potem edytujesz lokalnie:
```bash
cp compose.yaml.example compose.yaml
cp .env.example .env
```
`.env`:
```env
HOSTNAME=servicedesk.twoja-domena.pl
MYSQL_ROOT_PASSWORD=wygeneruj-silne-haslo
MYSQL_DATABASE=servicedesk
MYSQL_USER=servicedesk
MYSQL_PASSWORD=wygeneruj-inne-silne-haslo
# IMAGE_TAG=latest
```
- `HOSTNAME` — domena, pod którą Traefik wystawi aplikację (trafia do reguły
`Host(...)` w etykietach `servicedesk`).
- `MYSQL_*` — dane bazy dla kontenera `mariadb`; `MYSQL_USER`/`MYSQL_PASSWORD` to
**te same** wartości, które za chwilę wpiszesz do `src/.env` jako `DB_USERNAME`/
`DB_PASSWORD`.
- `IMAGE_TAG` — który tag obrazu `servicedesk` wdrożyć (patrz sekcja 1.3a); domyślnie
`latest`, jeśli zmienna nie jest ustawiona.
### 1.2. Plik `src/.env` (Laravel)
```bash
cp src/.env.example src/.env
```
Kluczowe wartości do zmiany względem `.env.example`:
```env
APP_NAME=Servicedesk
APP_ENV=production
APP_DEBUG=false
APP_URL=https://servicedesk.twoja-domena.pl # to samo co HOSTNAME wyżej, z https://
APP_LOCALE=pl
APP_FALLBACK_LOCALE=pl
AUTHOR_CONTACT=helpdesk@twoja-domena.pl # widoczne w Admin > O aplikacji
VERSION=1.3.0 # widoczne w Admin > O aplikacji
DB_CONNECTION=mysql
DB_HOST=mariadb # nazwa serwisu z compose.yaml, NIE 127.0.0.1
DB_PORT=3306
DB_DATABASE=servicedesk # = MYSQL_DATABASE z głównego .env
DB_USERNAME=servicedesk # = MYSQL_USER
DB_PASSWORD=wygeneruj-inne-silne-haslo # = MYSQL_PASSWORD
SESSION_DRIVER=database
QUEUE_CONNECTION=database
```
`APP_KEY` wygenerujesz komendą artisan (krok 1.4) — zostaw puste w pliku.
`LDAP_*` i `MAIL_*` w `src/.env` są tylko **wartościami startowymi/awaryjnymi**.
Docelowo LDAP konfiguruje się wygodniej z poziomu **Admin > Integracje**, a SMTP
z **Admin > Poczta** (patrz ramka ostrzegawcza w kroku 1.6) — ale jeśli chcesz mieć
sensowny fallback zanim ktokolwiek się zaloguje do panelu admina, warto je od razu
uzupełnić:
```env
LDAP_HOST=ldap.twoja-domena.pl
LDAP_USERNAME=cn=admin,dc=twoja-domena,dc=pl
LDAP_PASSWORD=haslo-bind-ldap
LDAP_BASE_DN=dc=twoja-domena,dc=pl
LDAP_PORT=389
MAIL_MAILER=smtp
MAIL_HOST=smtp.twoja-domena.pl
MAIL_PORT=587
MAIL_USERNAME=noreply@twoja-domena.pl
MAIL_PASSWORD=haslo-smtp
MAIL_FROM_ADDRESS=noreply@twoja-domena.pl
MAIL_FROM_NAME="${APP_NAME}"
```
### 1.3. Start kontenerów
Obraz `servicedesk` **nie jest budowany lokalnie**`compose.yaml` odwołuje się
do obrazu zbudowanego przez CI i wypchniętego do rejestru kontenerów Gitea (patrz
1.3a). Uruchomienie stacka to więc zawsze:
```bash
docker compose pull
docker compose up -d
```
Przy zwykłych zmianach w kodzie PHP/Blade nic więcej nie trzeba robić — źródło
jest zamontowane z `./src`, Laravel odświeża się natychmiast. `docker compose
pull` uruchamiaj ponownie tylko wtedy, gdy chcesz podnieść nowszy tag obrazu
(np. po zmianie w `Dockerfile` i przebudowie przez CI).
### 1.3a. Automatyczne budowanie obrazu (CI, Gitea Actions)
`.gitea/workflows/build.yml` buduje i wypycha obraz do wbudowanego rejestru
kontenerów Gitea (`gitea.kzbikowski.pl/kzbkowski/servicedesk`) po każdym pushu na
`main`, który zmienia `Dockerfile` (celowo nie odpala się na zwykłe zmiany w
`src/` — obraz nie zawiera kodu aplikacji, tylko PHP/Apache/rozszerzenia, więc
przebudowa dla samego kodu byłaby marnowaniem czasu CI). Wypycha dwa tagi:
`latest` i `<sha commita>`.
Zanim to zadziała, workflow potrzebuje sekretu `REGISTRY_TOKEN` — Gitei **nie**
ufaj domyślnemu `secrets.GITHUB_TOKEN` do logowania w jej własnym rejestrze
kontenerów, w praktyce kończy się to błędem `unauthorized` przy
`docker login`. Zamiast tego:
1. Wygeneruj token: **Ustawienia użytkownika > Aplikacje > Generate New Token**,
z uprawnieniami co najmniej `write:package` i `read:package`.
2. Dodaj go jako sekret repo: **Ustawienia repo > Actions > Secrets**
nazwa `REGISTRY_TOKEN`, wartość = wygenerowany token.
Jeśli ten sekret nie jest ustawiony, workflow celowo przerywa się **przed**
próbą logowania z czytelnym komunikatem błędu (`::error::`), zamiast wysyłać
puste/nieautoryzowane dane do rejestru i kończyć na niejasnym `unauthorized` z
demona Dockera.
To **tylko build + push** — świadomie bez auto-deployu na produkcję. Po tym jak
CI skończy, wdrożenie nowego obrazu na serwerze wciąż jest ręcznym krokiem:
```bash
cd /ścieżka/do/repo
docker compose pull
docker compose up -d
```
Zanim to zadziała po raz pierwszy, potrzebne jest jednorazowe zalogowanie hosta
produkcyjnego do rejestru Gitea (żeby `docker compose pull` miał czym
autoryzować pobranie obrazu, jeśli repozytorium/paczka nie są publiczne):
```bash
docker login gitea.kzbikowski.pl -u <twoja-nazwa-uzytkownika>
```
Jeśli to zupełnie pierwsze wdrożenie (rejestr jeszcze nie ma żadnego wypchniętego
obrazu `servicedesk`) — poczekaj, aż workflow CI przejdzie choć raz (np. przez
push/PR zmieniający `Dockerfile`, albo ręczne odpalenie z zakładki Actions w
Gitea), zanim spróbujesz `docker compose pull` na serwerze.
### 1.3b. Real-time (Laravel Reverb)
Realtime (kolejka operatora, czat na żywo) wymaga trzeciej usługi w
`compose.yaml`, `reverb` — tego samego obrazu `servicedesk`, tylko z innym
`command: php artisan reverb:start --host=0.0.0.0 --port=8080`. Obraz musi
mieć rozszerzenia PHP `pcntl`/`posix` (potrzebne serwerowi Reverb do obsługi
sygnałów) — jeśli `Dockerfile` ich nie instaluje, `reverb` będzie się zapętlać
w restartach z błędem `Undefined constant "...SIGINT"`; dodaj `pcntl posix` do
listy w `docker-php-ext-install` i poczekaj na przebudowanie obrazu przez CI.
Traefik musi kierować ścieżkę websocketu (`/app*`) do `reverb`, a resztę do
`app` — na tej samej domenie, więc bez dodatkowego wpisu DNS/certyfikatu:
```yaml
reverb:
image: gitea.kzbikowski.pl/kzbkowski/servicedesk:${IMAGE_TAG:-latest}
command: php artisan reverb:start --host=0.0.0.0 --port=8080
volumes:
- ./src:/var/www/html
restart: unless-stopped
depends_on:
mariadb:
condition: service_healthy
networks:
- internal
- traefik_public
labels:
- traefik.enable=true
- traefik.docker.network=traefik_public
- traefik.http.routers.reverb.rule=Host(`${HOSTNAME}`) && PathPrefix(`/app`)
- traefik.http.routers.reverb.priority=1000
- traefik.http.routers.reverb.entrypoints=websecure
- traefik.http.routers.reverb.tls=true
- traefik.http.services.reverb.loadbalancer.server.port=8080
```
`priority=1000` jest ważne — Traefik domyślnie liczy priorytet reguły na
podstawie długości jej zapisu, co może dać samemu `Host(...)` z `servicedesk`
wyższy priorytet niż oczekiwano, przez co ścieżkowa reguła `reverb` przegrywa i
nic nie działa mimo poprawnej konfiguracji.
W `src/.env``BROADCAST_CONNECTION=reverb` plus:
```env
REVERB_APP_ID=wygeneruj-losowy-id
REVERB_APP_KEY=wygeneruj-losowy-klucz
REVERB_APP_SECRET=wygeneruj-losowy-sekret
REVERB_HOST=reverb # nazwa usługi compose — ruch serwer-serwer po sieci internal
REVERB_PORT=8080
REVERB_SCHEME=http
VITE_REVERB_APP_KEY="${REVERB_APP_KEY}"
VITE_REVERB_HOST=servicedesk.twoja-domena.pl # publiczna domena — to, z czym łączy się przeglądarka
VITE_REVERB_PORT=443
VITE_REVERB_SCHEME=https
```
`REVERB_HOST` (serwer→serwer, wewnętrzna sieć Docker) i `VITE_REVERB_HOST`
(przeglądarka→Traefik, publiczna domena) to celowo dwie różne wartości —
pomylenie ich to najczęstszy błąd przy pierwszym wdrożeniu tej funkcji.
Po zmianie zmiennych `VITE_REVERB_*` trzeba przebudować front-end (krok 1.5) —
te wartości są wypiekane w zbudowany bundle JS, nie czytane w runtime.
### 1.4. Instalacja aplikacji wewnątrz kontenera
```bash
docker compose exec app composer install --no-dev --optimize-autoloader
docker compose exec app php artisan key:generate
docker compose exec app php artisan migrate --seed
docker compose exec app php artisan storage:link
```
`migrate --seed` (bez `--fresh`) na pustej bazie utworzy wszystkie tabele i
wypełni dane referencyjne: kategorie/podkategorie, pola dodatkowe, statusy,
priorytety/SLA, zespoły, szybkie akcje, szablony odpowiedzi/e-mail, branding oraz
**jedno konto lokalne `admin@example.com` / `admin`** — zmień jego hasło od razu
po pierwszym zalogowaniu (Admin > Użytkownicy).
### 1.5. Zbudowanie zasobów front-endowych (CSS/Tailwind)
Ani host, ani kontener `app` nie mają zainstalowanego Node.js — buduj
przez jednorazowy kontener `node:22` zamiast dorzucać Node do obrazu aplikacji:
```bash
cd /ścieżka/do/repo
docker run --rm -v "$(pwd)/src":/app -w /app node:22 npm ci
docker run --rm -v "$(pwd)/src":/app -w /app node:22 npm run build
```
Powtarzaj drugi krok po każdej zmianie w `resources/css/` lub `resources/js/`.
### 1.6. Zadanie cykliczne (SLA, automatyzacje, poczta IMAP, AI) i kolejka
`routes/console.php` planuje `tickets:check-sla-breaches` i `automation:run-rules`
co 15 minut, oraz `emails:fetch-imap` (odbieranie zgłoszeń/odpowiedzi e-mailem —
patrz Admin > Poczta) i `ai:run-ticket-automation` (opcjonalna automatyczna
kategoryzacja/podsumowania AI zgłoszeń — patrz Admin > Integracje) co 5 minut,
ale **obraz Dockera nie ma wbudowanego cron/supervisora** — bez dodatkowego
kroku żadne z tych zadań nigdy się nie uruchomi (poczta IMAP nadal da się
sprawdzić ręcznie przyciskiem „Pobierz teraz”, ale bez tego nic nie dzieje się
samo). Wszystkie cztery interwały są też konfigurowalne z poziomu **Admin >
Konfiguracja** (bez potrzeby edycji kodu czy restartu — nowa wartość obowiązuje
od najbliższego tyknięcia harmonogramu).
`compose.yaml` rozwiązuje to czwartą usługą, `cron` — tego samego obrazu
`servicedesk`, tylko z innym poleceniem:
```yaml
cron:
image: gitea.kzbikowski.pl/kzbkowski/servicedesk:${IMAGE_TAG:-latest}
command: php artisan schedule:work
volumes:
- ./src:/var/www/html
restart: unless-stopped
depends_on:
mariadb:
condition: service_healthy
networks:
- internal
```
`schedule:work` to własna, pierwszoplanowa pętla harmonogramu Laravela —
odpowiednik odpalania `schedule:run` co minutę, ale bez potrzeby zewnętrznego
triggera. Ten kontener nie musi być widoczny w Traefiku (nie obsługuje ruchu
HTTP), stąd tylko sieć `internal`. Sprawdź, że działa: `docker compose ps cron`
oraz `docker compose logs -f cron` (loguje każde odpalenie zaplanowanego
zadania).
Powiadomienia e-mail wysyłają się synchronicznie (nie trafiają do kolejki), więc
`php artisan queue:work` nie jest obowiązkowy — `QUEUE_CONNECTION=database` w
`.env` wystarcza jako bezpieczny domyślny driver, gdyby coś w przyszłości zaczęło
kolejkować zadania.
### ⚠️ Ważne: LDAP/SMTP z panelu Admina nadpisują `.env` w locie
`AppServiceProvider` na starcie żądania sprawdza tabelę `settings` — jeśli w
Admin > Integracje pole **host LDAP** albo w Admin > Poczta **SMTP włączony +
host** jest ustawione, **te wartości wygrywają z `.env`**, bez potrzeby
restartu czy redeployu.
Po świeżym `migrate --seed` te pola zawierają **przykładowe placeholdery**
(`ldap.example.com`, `smtp.example.com`, `changeme-*-password`) — to znaczy, że
zaraz po instalacji LDAP/SMTP **faktycznie próbują łączyć się z tymi fałszywymi
adresami**, nawet jeśli w `.env` wpisałeś prawdziwe dane! Zanim oddasz system do
użytku:
1. Zaloguj się lokalnym kontem `admin@example.com` / `admin`.
2. Wejdź w **Admin > Integracje** i wpisz prawdziwe dane LDAP (wybierz też
właściwy **Typ katalogu** — LLDAP/OpenLDAP albo Active Directory — jeśli
katalog to nie LLDAP; albo wyczyść pole hosta LDAP, żeby wrócić do
wartości z `.env`), a w **Admin > Poczta** dane SMTP.
3. Użyj przycisku **„Testuj połączenie”** w Integracje i **„Wyślij testową
wiadomość”** w Poczta, zanim zaczniesz polegać na logowaniu przez katalog
albo na powiadomieniach e-mail.
### Integracje opcjonalne (BookStack, AI)
Podpowiedzi artykułów z bazy wiedzy BookStack (przy tworzeniu zgłoszenia i w
panelu operatora) są **domyślnie wyłączone** i nie wymagają żadnej zmiennej w
`.env` — całość konfiguruje się w **Admin > Integracje**: adres instancji,
Token ID/Secret (rola/użytkownik właściciela tokenu musi mieć w BookStacku
uprawnienie „Access System API”), filtr typu treści (książki/strony/rozdziały,
niezależne checkboxy), tryb wyszukiwania (po nazwie / po tagach / oba), oraz
osobne listy dozwolonych półek dla podpowiedzi przy tworzeniu zgłoszenia i dla
panelu operatora — dopóki żadna półka nie jest zaznaczona, wyszukiwanie nic
nie zwraca.
Integracja AI (Admin > Integracje > „Integracja AI”) jest **domyślnie
wyłączona** i tak samo nie wymaga żadnej zmiennej w `.env` — adres API
(dowolny dostawca kompatybilny z OpenAI: Groq, OpenAI, lokalny Ollama),
opcjonalny klucz API, nazwa modelu i przełącznik weryfikacji SSL. Sama w
sobie nic nie robi — dopiero po jej włączeniu można włączyć automatyczne
tagowanie treści BookStack (przyciski przy integracji BookStack) oraz
automatyczną kategoryzację/podsumowania AI zgłoszeń (Admin > Integracje >
„Automatyzacja AI dla zgłoszeń”, wymaga też wpisu crona z kroku 1.6/2.6
powyżej — to ten sam harmonogram co SLA/automatyzacje/IMAP).
### Import historycznych zgłoszeń z Heska (opcjonalnie)
Jeśli migrujesz z helpdesku Hesk 3.x, `scripts/hesk-import/` zawiera
jednorazowe (nie ciągłe) narzędzie migracyjne — importuje zgłoszenia wraz z
pełną historią odpowiedzi/notatek, ograniczone do jednej domeny e-mail, w
trybie dry-run domyślnie. Nie dotyka bazy Heska poza odczytem. Zobacz
`scripts/hesk-import/README.md` po pełną instrukcję.
---
## 2. Wdrożenie bezpośrednio na serwerze (Apache/Nginx, bez Dockera)
### Wymagania
- **PHP 8.3+** z rozszerzeniami: `curl`, `mysqli`, `pdo_mysql`, `ldap`, `zip`,
`mbstring`, `openssl`, `tokenizer`, `xml`, `ctype`, `bcmath`, `fileinfo`.
- **Composer 2**.
- **MariaDB/MySQL** (osobny serwer lub lokalny).
- **Node.js 20+** — tylko do jednorazowej (lub przy update'ach) budowy zasobów
Tailwind/Vite; niepotrzebny w runtime.
- **Apache** z `mod_rewrite` (+ `mod_ssl`) **lub** **Nginx + PHP-FPM**.
### 2.1. Pobranie kodu i zależności
```bash
cd /var/www
git clone <adres-repo> servicedesk
cd servicedesk/src
composer install --no-dev --optimize-autoloader
```
### 2.2. Konfiguracja `.env`
```bash
cp .env.example .env
```
Te same zmienne co w sekcji 1.2, z jedną różnicą — `DB_HOST` wskazuje na
prawdziwy adres serwera bazy, nie na nazwę serwisu Compose:
```env
APP_NAME=Servicedesk
APP_ENV=production
APP_DEBUG=false
APP_URL=https://servicedesk.twoja-domena.pl
APP_LOCALE=pl
APP_FALLBACK_LOCALE=pl
AUTHOR_CONTACT=helpdesk@twoja-domena.pl
VERSION=1.3.0
DB_CONNECTION=mysql
DB_HOST=127.0.0.1 # albo adres IP/hostname prawdziwego serwera DB
DB_PORT=3306
DB_DATABASE=servicedesk
DB_USERNAME=servicedesk
DB_PASSWORD=wygeneruj-silne-haslo
SESSION_DRIVER=database
QUEUE_CONNECTION=database
```
Uzupełnij też `LDAP_*`/`MAIL_*` jak w sekcji 1.2 (to samo ostrzeżenie o
Admin > Integracje/Poczta nadpisującym te wartości w locie dotyczy tu identycznie).
```bash
php artisan key:generate
php artisan migrate --seed
php artisan storage:link
```
### 2.3. Budowa zasobów
```bash
npm ci
npm run build
```
(Na maszynie bez Node.js zbuduj `public/build/` gdzie indziej i skopiuj cały
katalog — to jedyne, co Vite generuje poza źródłem.)
### 2.4. Uprawnienia plików
```bash
chown -R www-data:www-data storage bootstrap/cache
chmod -R 775 storage bootstrap/cache
```
(Zmień `www-data` na użytkownika, pod którym faktycznie działa Apache/PHP-FPM w
Twojej dystrybucji, np. `apache` na RHEL/CentOS.)
### 2.5. Konfiguracja serwera WWW
**DocumentRoot musi wskazywać na `public/`**, nigdy na katalog główny aplikacji.
**Apache** (`/etc/apache2/sites-available/servicedesk.conf`):
```apache
<VirtualHost *:443>
ServerName servicedesk.twoja-domena.pl
DocumentRoot /var/www/servicedesk/src/public
<Directory /var/www/servicedesk/src/public>
AllowOverride All
Require all granted
</Directory>
SSLEngine on
SSLCertificateFile /etc/ssl/certs/servicedesk.crt
SSLCertificateKeyFile /etc/ssl/private/servicedesk.key
ErrorLog ${APACHE_LOG_DIR}/servicedesk-error.log
CustomLog ${APACHE_LOG_DIR}/servicedesk-access.log combined
</VirtualHost>
```
```bash
a2enmod rewrite ssl
a2ensite servicedesk
systemctl reload apache2
```
> Jeśli kiedykolwiek dodasz własny katalog `public/icons/` — nie rób tego. Stockowy
> moduł `mod_alias` Apache definiuje `Alias /icons/ "/usr/share/apache2/icons/"`
> (FancyIndexing), który po cichu przechwytuje `/icons/*` przed dotarciem do
> Laravela. Ten projekt celowo używa `public/pwa-icons/` z tego właśnie powodu.
**Nginx** (+ PHP-FPM):
```nginx
server {
listen 443 ssl http2;
server_name servicedesk.twoja-domena.pl;
root /var/www/servicedesk/src/public;
ssl_certificate /etc/ssl/certs/servicedesk.crt;
ssl_certificate_key /etc/ssl/private/servicedesk.key;
index index.php;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
location ~ /\.(?!well-known).* {
deny all;
}
}
```
### 2.6. Zadanie cykliczne (SLA, automatyzacje, poczta IMAP, AI) i kolejka
Crontab użytkownika, pod którym stoi aplikacja (np. `www-data`) — obsługuje też
`automation:run-rules`, `emails:fetch-imap` i `ai:run-ticket-automation`
(patrz 1.6 wyżej, w tym konfigurowalne interwały w Admin > Konfiguracja):
```cron
* * * * * cd /var/www/servicedesk/src && php artisan schedule:run >> /dev/null 2>&1
```
Jak w wersji Docker — kolejka (`php artisan queue:work`) nie jest obowiązkowa,
skoro powiadomienia wysyłają się synchronicznie; zostaw `QUEUE_CONNECTION=database`
jako bezpieczny domyślny driver na przyszłość.
Realtime (patrz 1.3b) potrzebuje tu **długo działającego procesu**
`php artisan reverb:start` — PHP musi mieć rozszerzenia `pcntl`/`posix`
(standardowo dostępne, ale sprawdź `php -m`). Najprościej pod systemd:
```ini
# /etc/systemd/system/servicedesk-reverb.service
[Unit]
Description=Servicedesk Reverb websocket server
After=network.target
[Service]
User=www-data
WorkingDirectory=/var/www/servicedesk/src
ExecStart=/usr/bin/php artisan reverb:start --host=0.0.0.0 --port=8080
Restart=always
[Install]
WantedBy=multi-user.target
```
```bash
systemctl enable --now servicedesk-reverb
```
Ustaw też te same zmienne `.env` co w 1.3b (`BROADCAST_CONNECTION=reverb`,
`REVERB_*`, `VITE_REVERB_*` — tu `REVERB_HOST` to po prostu `127.0.0.1`, nie
nazwa usługi compose) i skieruj serwer WWW tak, by ścieżka `/app*` trafiała do
portu 8080 zamiast do PHP-FPM/Apache (osobny `location`/`VirtualHost` dla tej
jednej ścieżki, analogicznie do reguły Traefika w 1.3b).
### 2.7. Pierwsze logowanie i dalsza konfiguracja
Identycznie jak w kroku 1.6 — zaloguj się `admin@example.com` / `admin`, zmień
hasło, uzupełnij prawdziwe LDAP w Admin > Integracje i SMTP w Admin > Poczta
(placeholdery z seeda inaczej realnie próbują łączyć się z fałszywymi
adresami), przetestuj oba połączenia.
### 2.8. Aktualizacje (bez przestoju)
```bash
cd /var/www/servicedesk/src
git pull
composer install --no-dev --optimize-autoloader
php artisan migrate --force
npm ci && npm run build
php artisan config:clear # tylko jeśli wcześniej użyto config:cache
```
Nie ma potrzeby restartu Apache/PHP-FPM dla samych zmian w PHP/Blade — restart
przyda się tylko po zmianie w `.env` lub konfiguracji samego serwera WWW.