- In-app notifications: a bell in the top bar backed by Laravel's database
notification channel, alongside existing e-mail notifications (same
per-trigger toggle drives both; ticket links now correctly point into the
recipient's own area instead of always linking to the client view).
- Drag-and-drop attachments on every upload form, plus inline image
thumbnails in the message thread instead of a plain download link.
- Customer satisfaction (CSAT) rating: clients rate a closed ticket 1-5 stars
with an optional comment; shown read-only to operators, surfaced as a KPI
on the stats dashboard, and linked from the "ticket closed" e-mail.
- Saved queue views: operators can save/apply/delete named filter+sort+
column presets in the ticket queue and mark one as their default.
- Full-text search (MySQL FULLTEXT, portable LIKE fallback) across ticket
subject/body and reply message bodies, now also on the client's own ticket
list.
- Stats CSV export for the currently filtered ticket set.
- Optional BookStack knowledge-base integration (off by default): suggests
articles by category/subcategory while creating a ticket and in a separate
sidebar for operators on an existing ticket (with a copy-link button).
Configurable connection/SSL bypass/search-type filter, plus two
independent per-shelf allow-lists so nothing is ever searched until an
admin opts specific shelves in.
- Closed tickets no longer show in "Moje zgłoszenia"/"Nieprzypisane"/team
queue tabs, only under "Zamknięte" (matching how "Otwarte" already worked).
- Wired up the Admin > About "Wersja" field to config('app.version')/VERSION
in .env instead of a stale hardcoded string.
- Fixed: TicketService::setStatus() now checks a status's stage rather than
the literal key 'closed' to decide whether to fire the "ticket closed"
notification/stop the timer.
- Updated README/ARCHITECTURE/CHANGELOG/install/SECURITY docs and all three
wiki/ role guides for the above; documented a root-vs-www-data file
ownership gotcha in CLAUDE.md (running artisan commands via a plain
`docker exec` can leave root-owned Blade cache files that later break
recompilation for the www-data Apache process).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
428 lines
15 KiB
Markdown
428 lines
15 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 `servicedesk`
|
|
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.1.0 # rezerwa na przyszłość, jeszcze nigdzie nie wyświetlane
|
|
|
|
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 i SMTP konfiguruje się wygodniej z poziomu **Admin > Konfiguracja**
|
|
w samej aplikacji (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.4. Instalacja aplikacji wewnątrz kontenera
|
|
|
|
```bash
|
|
docker compose exec servicedesk composer install --no-dev --optimize-autoloader
|
|
docker compose exec servicedesk php artisan key:generate
|
|
docker compose exec servicedesk php artisan migrate --seed
|
|
docker compose exec servicedesk 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 `servicedesk` 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) i kolejka
|
|
|
|
`routes/console.php` planuje `tickets:check-sla-breaches` co 15 minut, ale **obraz
|
|
Dockera nie ma wbudowanego cron/supervisora** — bez dodatkowego kroku to zadanie
|
|
nigdy się nie uruchomi. Najprościej dodać wpis crona **na hoście**:
|
|
|
|
```cron
|
|
* * * * * cd /ścieżka/do/repo && docker compose exec -T servicedesk php artisan schedule:run >> /dev/null 2>&1
|
|
```
|
|
|
|
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 > Konfiguracja pole **host LDAP** albo **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 > Konfiguracja** i wpisz prawdziwe dane LDAP/SMTP (albo wyczyść
|
|
pole hosta LDAP, żeby wrócić do wartości z `.env`).
|
|
3. Użyj przycisków **„Testuj połączenie”** przy obu sekcjach, zanim zaczniesz
|
|
polegać na logowaniu przez katalog.
|
|
|
|
### Integracje opcjonalne (BookStack)
|
|
|
|
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 > Konfiguracja**: adres instancji,
|
|
Token ID/Secret (rola/użytkownik właściciela tokenu musi mieć w BookStacku
|
|
uprawnienie „Access System API”), 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.
|
|
|
|
---
|
|
|
|
## 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.1.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 > Konfiguracja 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) i kolejka
|
|
|
|
Crontab użytkownika, pod którym stoi aplikacja (np. `www-data`):
|
|
|
|
```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ść.
|
|
|
|
### 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/SMTP w Admin > Konfiguracja (placeholdery z seeda
|
|
inaczej realnie próbują łączyć się z fałszywymi adresami), przetestuj oba
|
|
połączenia przyciskiem „Testuj połączenie”.
|
|
|
|
### 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.
|