Files
servicedesk/install.md
Kacper 4e8f17189a
All checks were successful
Build and push image / build (push) Successful in 1m24s
v1.0.2
- Fix CI registry login (unauthorized): use dedicated REGISTRY_TOKEN secret
  instead of GITHUB_TOKEN, fail fast with a clear error if it's unset.
- Pause ticket work-timer while a ticket is closed (won't auto-start on open
  or manual resume; stops on close via status change, quick action, API, or
  merge).
- Reject empty/blank login submissions client- and server-side instead of
  passing them straight to the auth provider.
- Closing a ticket now sends only the "ticket closed" notification instead
  of also sending a duplicate "status changed" one.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-22 08:50:03 +02:00

418 lines
14 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.0.2 # 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.
---
## 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.0.2
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.