Przejdź do treści
Q
QuoteNode

Instalacja

Rozwiązywanie problemów z instalacją

Typowe problemy z wdrażaniem QuoteNode związane z linkami, CORS, SMTP, DNS, kontrolami stanu Docker oraz utratą kluczy tajnych.

Rozwiązywanie problemów z instalacją

Większość problemów z instalacją wynika z niezgodności adresu URL, braku SMTP, zablokowanych portów lub utraty kluczy tajnych. Zacznij od widocznego adresu URL w przeglądarce i pracuj w głąb systemu.

Publiczne linki otwierają się na moim komputerze, ale nie u klientów

Sprawdź, jaki adres URL został skopiowany lub wysłany.

  • http://localhost/... działa tylko na tym samym komputerze.
  • http://192.168.x.x/... działa tylko w sieci LAN lub VPN. VPN może być prawidłowym rozwiązaniem dla zespołów wewnętrznych, ale nie stanowi strategii publicznych linków dla klientów.
  • https://crm.example.com/... działa publicznie tylko wtedy, gdy DNS, HTTPS, proxy i firewall są skonfigurowane poprawnie.

W przypadku publicznych linków dla klientów, wdrażaj za pomocą publicznej domeny, używając publicznego serwera Ubuntu lub Coolify.

Przeglądarka wyświetla błędy CORS

CORS_ALLOWED_ORIGINS musi dokładnie zgadzać się z originem przeglądarki. Poniższe adresy to różne originy:

https://crm.example.com
https://www.crm.example.com
http://crm.example.com
https://crm.example.com:8443

Popraw zmienną, ponownie wdroż aplikację i spróbuj ponownie w nowej sesji przeglądarki.

Wiadomości e-mail nie są dostarczane

Najpierw sprawdź:

  • SMTP_HOST i SMTP_PORT są poprawne.
  • SMTP_AUTH=true, gdy Twój dostawca wymaga uwierzytelniania.
  • SMTP_STARTTLS=true dla portu 587.
  • SMTP_USERNAME i SMTP_PASSWORD są prawidłowe.
  • Dostawca zezwala na logowanie SMTP/hasło aplikacji.
  • NOTIFICATIONS_EMAIL_ENABLED=true, gdy wiadomości e-mail z powiadomieniami powinny być wysyłane.

Ręczne udostępnianie linków publicznych i pobieranie PDF mogą nadal działać bez SMTP.

Linki do preferencji powiadomień są nieprawidłowe

Ustaw:

NOTIFICATIONS_PUBLIC_PREFERENCES_URL=https://<frontend-domain>/notifications/preferences

Nie zostawiaj go wskazującego na localhost w publicznym wdrożeniu.

Kontenery Docker są w stanie unhealthy

Uruchom:

docker compose ps
docker compose logs -f backend
docker compose logs -f frontend
docker compose logs -f gotenberg

Typowe przyczyny:

  • PostgreSQL wciąż się uruchamia.
  • DB_PASSWORD różni się między PostgreSQL a backendem.
  • DB_ENCRYPTION_KEY jest brakujący lub ma nieprawidłowy format.
  • Gotenberg nie jest w stanie healthy, więc generowanie PDF nie może się rozpocząć.
  • Porty hosta 80/443 są już używane przez inną usługę.

Po naprawieniu konfiguracji, zrestartuj dotkniętą usługę lub cały stos:

docker compose restart backend
docker compose up -d

Moje IP jest zablokowane — nie mogę się zalogować

Są dwie typowe przyczyny: lista dozwolonych IP zablokowała dostęp lub ochrona przed brute-force zablokowała IP po zbyt wielu nieudanych próbach logowania.

Blokada przez listę dozwolonych IP

QuoteNode posiada listę dozwolonych adresów IP konfigurowalną w Ustawienia > Bezpieczeństwo > Lista dozwolonych IP (lub przez zmienną SECURITY_IP_WHITELIST). Po włączeniu obejmuje ona całą powierzchnię administracyjną — łącznie z ekranami logowania i 2FA — więc wykluczony adres dostaje 403 IP_NOT_ALLOWED i nie może się nawet zalogować. (Health-checki oraz publiczne linki do ofert klientów pozostają dostępne.)

Najpierw poczekaj dwie minuty, jeśli właśnie zmieniłeś listę w aplikacji. Włączenie lub zawężenie w aplikacji jest chronione przed blokadą: musi zostać potwierdzone przez Twoją sesję w krótkim oknie (domyślnie 120 s), a jeśli Twój bieżący adres nie jest objęty, lista automatycznie wraca do poprzedniej. Przypadkowa samoblokada z ekranu Bezpieczeństwa sama się więc naprawia — poczekaj i spróbuj ponownie. (Ta siatka bezpieczeństwa nie dotyczy zmiennej SECURITY_IP_WHITELIST, która jest egzekwowana przy starcie.)

Odzyskiwanie (override operatora): jeśli nadal nie masz dostępu — np. operatorska SECURITY_IP_WHITELIST, nieprawidłowa zapisana lista lub nie możesz czekać — dodaj tę zmienną do środowiska kontenera backend (.env lub sekcja environment: w docker-compose) i zrestartuj:

SECURITY_IP_WHITELIST_FORCE_DISABLE=true
docker compose restart backend

To wyłącza listę dozwolonych IP i pozwala się zalogować. Po zalogowaniu popraw listę w Ustawienia > Bezpieczeństwo, a następnie usuń SECURITY_IP_WHITELIST_FORCE_DISABLE i zrestartuj ponownie.

Nie zostawiaj SECURITY_IP_WHITELIST_FORCE_DISABLE=true w produkcji. Wyłącza to całą kontrolę dostępu po IP.

Blokada przez ochronę brute-force

QuoteNode blokuje adresy IP po zbyt wielu nieudanych próbach logowania w ciągu godziny:

Próg Niepowodzenia w 1 h Efekt
Soft 10 Postępujące opóźnienie (1 s na każdą dodatkową próbę, maks. 30 s)
Tymczasowa blokada 20 Zablokowany na 5 minut
Trwała blokada 50 Zablokowany do ręcznego odblokowania

Jak odblokować:

  1. Panel admina — inny administrator otwiera Ustawienia > Panel bezpieczeństwa > Zablokowane IP i klika Odblokuj.
  2. Czekanie — tymczasowe blokady wygasają po SECURITY_TEMP_LOCK_MINUTES (domyślnie 5 minut). Trwałe blokady nie wygasają samoistnie.
  3. Tymczasowe podniesienie progów jeśli jesteś zablokowany i żaden inny admin nie może pomóc. Dodaj do .env, zrestartuj, zaloguj się, a potem usuń:
SECURITY_MAX_FAILED_LOGINS_SOFT=9999
SECURITY_MAX_FAILED_LOGINS_TEMP_LOCK=9999
SECURITY_MAX_FAILED_LOGINS_HARD_LOCK=9999

Przywróć domyślne wartości po odzyskaniu dostępu.

Blokady IP linków publicznych

Rate limiter linków publicznych blokuje adresy IP po przekroczeniu limitów prób. Te blokady są przechowywane w pamięci i wygasają po PUBLIC_RATE_LIMIT_IP_BLOCK_MINUTES (domyślnie 60 minut) lub po restarcie backendu.

Aktualizacja zatrzymuje się na “cannot enforce offer-section ownership”

Po aktualizacji backend nie startuje, a docker compose logs backend pokazuje:

Migration V145 cannot enforce offer-section ownership: 3 offer_items row(s) point at a
section of another offer or tenant.

Co to oznacza

Oferta składa się z sekcji (“Sprzęt”, “Usługi”) i pozycji w nich umieszczonych. Ta wersja zaostrza to powiązanie: pozycja może należeć wyłącznie do sekcji własnej oferty. Kilka starych wierszy w Twojej bazie łamie tę regułę, więc aktualizacja zatrzymuje się i prosi o decyzję, zamiast samodzielnie przepisywać Twoje oferty.

Nic nie jest uszkodzone i sama odmowa niczego nie zmieniła. Ta jedna migracja wycofała się w całości — brak zmian zastosowanych połowicznie i nic do naprawiania w historii migracji. Pamiętaj jednak, że wcześniejsze migracje z tej samej aktualizacji mogły już zostać wykonane: baza stoi tuż przed odrzuconym krokiem, niekoniecznie na wersji, od której zaczynałeś. To znaczy również, że poprzednie kontenery mogą, ale nie muszą z nią działać — zależy to od tego, co zmieniły tamte wcześniejsze migracje. Nie licz na cofnięcie obrazów jako drogę wyjścia.

Wybierz, co ma się stać

Wariant Co robi Co się dla Ciebie zmienia
Sprawdź problem (domyślny) Wypisuje dotknięte oferty i pozycje. Nic. Tylko odczyt.
Odepnij błędne przypisania Czyści sekcję wyłącznie tych pozycji. Pozycje zostają w swojej ofercie z tą samą ceną, ilością i opisem. Tracą nagłówek sekcji i stają się pozycjami bez grupy.
Wybierz sekcję dla każdej pozycji Pyta o każdą pozycję i proponuje wyłącznie sekcje jej własnej oferty. Zachowujesz grupowanie, którego chcesz. Pominięte pozycje pozostają nierozwiązane i aktualizacja znowu się zatrzyma.

Oferty już wysłane do klientów nadal pokazują dokładnie to, co klient zobaczył — treść wysłanej oferty jest zamrażana w momencie wysyłki, więc ani link publiczny, ani PDF się nie zmieniają.

Najpierw znajdź właściwą instalację (Coolify lub kilka stacków na jednym serwerze)

Każde poniższe polecenie musi trafić w Twoją instalację. Jeden serwer może ich mieć kilka, a usługę o nazwie backend może mieć więcej niż jedna, więc najpierw wypisz kandydatów — każda linia to jedna instalacja, z nazwą projektu i plikiem Compose, z którego ją uruchomiono:

docker inspect $(docker ps -aq --filter "label=com.docker.compose.service=backend" \
                             --filter "label=com.docker.compose.oneoff=False") \
  --format '{{index .Config.Labels "com.docker.compose.project"}} | {{index .Config.Labels "com.docker.compose.project.config_files"}}'

Jeśli dostaniesz więcej niż jedną linię, wybierz swoją; nie zgaduj. Następnie każde polecenie uruchamiaj z jawnie podanym projektem i plikiem, zamiast samego docker compose, żeby nie zadziałać na niewłaściwej instalacji:

docker compose -p <project> -f <compose-file> run --rm backend --repair-offer-sections

Podstaw <project> i <compose-file> z Twojej linii i zostaw je w każdym poniższym poleceniu — to właśnie sprawia, że każde z nich działa na Twojej instalacji, a nie na innej. Jeśli linia pokazuje kilka plików rozdzielonych przecinkami, powtórz -f dla każdego, w tej kolejności. Jeśli lista jest pusta, kontenery nie zostały uruchomione przez Docker Compose — użyj katalogu wskazanego przez dostawcę lub instrukcję instalacji albo zapytaj osobę, która konfigurowała serwer. W Coolify uruchom to w terminalu serwera, nie kontenera: terminal kontenera wymaga działającego backendu, a Twój nie działa. (Komunikaty samego narzędzia pokazują skrócone docker compose … bez tych opcji, dla czytelności — kopiując je, dopisz swoje -p i -f.)

Najpierw kopia zapasowa

PostgreSQL działa nawet wtedy, gdy backend nie startuje, więc kopię zapasową wykonasz bez aplikacji. Polecenie odczytuje nazwę bazy i użytkownika z samego kontenera, więc zadziała niezależnie od tego, jak je nazwałeś, a hasło nie pojawia się na ekranie:

docker compose -p <project> -f <compose-file> exec -T postgres sh -c \
  'pg_dump -U "$POSTGRES_USER" -d "$POSTGRES_DB" --format=custom -f /tmp/before-repair.dump \
   && pg_restore --list /tmp/before-repair.dump | head -3'
docker compose -p <project> -f <compose-file> cp postgres:/tmp/before-repair.dump ./before-repair.dump
ls -lh before-repair.dump

Linia pg_restore --list wypisuje pierwsze wpisy archiwum: jeśli je widzisz, a ls -lh pokazuje niepusty plik, kopia jest czytelna. Trzymaj before-repair.dump obok swojego docker-compose.yml — leży na dysku serwera, nie w kontenerze, więc przetrwa wszystko poniżej.

Sprawdź problem

docker compose -p <project> -f <compose-file> run --rm backend --repair-offer-sections

To polecenie tylko czyta. Wypisuje dotknięte oferty, co jest nie tak z każdą pozycją i dostępne warianty:

Found 3 offer item(s) in 2 offer(s) assigned to a section that does not belong to that offer.

  Offer OF-2026-014 [DRAFT]
    item:    Mounting frame
    section: section "Installation" — section belongs to another offer

Podgląd, potem wykonanie

Bez --apply=true każde polecenie jest podglądem: pokazuje dokładnie, co by zrobiło, i kończy się, niczego nie zmieniając — w każdym terminalu.

docker compose -p <project> -f <compose-file> run --rm backend --repair-offer-sections --plan=unlink

Dodaj --apply=true --acknowledge-backup=true, aby je wykonać. To jedyna postać, która zapisuje:

docker compose -p <project> -f <compose-file> run --rm backend --repair-offer-sections --plan=unlink --apply=true --acknowledge-backup=true

Aby zdecydować o każdej pozycji samodzielnie, użyj --plan=choose. Narzędzie zadaje jedno pytanie na pozycję i wypisuje sekcje tej oferty, więc nigdy nie wpisujesz identyfikatora z bazy. Bez --apply=true nadal tylko pokazuje plan:

docker compose -p <project> -f <compose-file> run --rm backend --repair-offer-sections --plan=choose --apply=true --acknowledge-backup=true

Dokończ aktualizację

Gdy narzędzie zgłosi, że żadna pozycja nie wskazuje już błędnej sekcji:

docker compose -p <project> -f <compose-file> up -d

Zatrzymana migracja niczego po sobie nie zostawiła, więc przy kolejnym starcie po prostu się wykona. Nie potrzebujesz polecenia flyway repair ani baseline — i nie powinieneś go używać, bo ukryłoby problem z danymi zamiast go rozwiązać.

Zweryfikuj jak zwykle: otwórz /health, a następnie sprawdź numer wersji w Admin > Licencja.

Jeśli komunikat mówi, że dane zmieniły się od podglądu

Narzędzie wykonuje plan, który zatwierdziłeś, a nie to, co zastanie w chwili zapisu. Jeśli podczas potwierdzania pojawiło się nowe błędne przypisanie, jakieś się przesunęło albo wybrana przez Ciebie sekcja została usunięta, narzędzie odmawia wykonania całości i niczego nie zmienia. Uruchom sprawdzenie ponownie, żeby zobaczyć aktualny stan, i wybierz jeszcze raz.

Jeśli komunikat mówi, że naprawa została zapisana albo że nie dało się tego potwierdzić

Naprawa wykonuje się w jednej transakcji, więc albo zapisuje się w całości, albo wcale. Dwa zakończenia wymagają uwagi. Jeśli zawiedzie późniejszy krok — zapis raportu albo kontrolny odczyt potwierdzający wynik — narzędzie pozostawia w komunikacie słowo SAVED: naprawa się udała, więc nie przywracaj kopii zapasowej. Jeśli natomiast pojawi się UNCONFIRMED, baza nie odpowiedziała i nikt nie jest w stanie powiedzieć, jak się skończyło; nie przywracaj kopii i nie powtarzaj naprawy. W obu przypadkach uruchom ponownie sprawdzenie w trybie tylko do odczytu: jeśli zgłosi, że nie ma nic do naprawy, gotowe — możesz uruchomić stack; jeśli nadal wypisuje te same pozycje, nic nie zostało zapisane i możesz powtórzyć swój wybór.

docker compose -p <project> -f <compose-file> run --rm backend --repair-offer-sections

Jedno wymaga uważnego przeczytania: jeśli użyłeś wariantu Wybierz sekcję dla każdej pozycji i część pominąłeś, te pominięte pozycje nadal będą wypisywane przez sprawdzenie. Tak ma być i nie znaczy to, że naprawa się nie udała. Porównaj listę z pozycjami, które faktycznie wybrałeś do naprawy: jeśli zostały wyłącznie te pominięte, naprawa zadziałała, a to, co zostało, to odłożona przez Ciebie decyzja — uruchom narzędzie dla nich, gdy będziesz gotów. Jeśli nadal są wypisywane pozycje, które wybrałeś do naprawy, nic nie zostało zapisane.

Co zostało zmienione i co zrobić, jeśli nadal się nie udaje

Każde uruchomienie, które coś zmienia, zapisuje wykaz identyfikatorów pozycji, ofert i sekcji do /app/data/backups/ w wolumenie backendu. Nie zawiera danych klientów i można go dołączyć do zgłoszenia serwisowego. Jeśli pliku nie da się zapisać, narzędzie wypisuje ten sam wykaz na ekranie — skopiuj go przed zamknięciem terminala:

docker compose -p <project> -f <compose-file> run --rm --entrypoint sh backend \
  -c 'ls -1 /app/data/backups/offer-section-repair-*.json'

Jeśli aktualizacja nadal się zatrzymuje:

  • uruchom sprawdzenie ponownie — pominięte pozycje nadal są wypisywane;
  • jeśli komunikat wskazuje inną migrację, to narzędzie jej nie dotyczy. Naprawia wyłącznie przypisania pozycji do sekcji oferty;
  • jeśli komunikat się nie zmienił, a sprawdzenie nie zgłasza nic do naprawy, zgłoś problem, dołączając log backendu i powyższy wykaz.

Certyfikat HTTPS nie zostaje wystawiony

W przypadku publicznych wdrożeń:

  • DNS musi wskazywać na serwer wdrożeniowy.
  • Porty 80 i 443 muszą być osiągalne z internetu.
  • Tylko jeden proxy powinien zakończyć HTTPS dla tej samej domeny.
  • W Coolify, przypisz domenę do usługi frontend.

Zgubiłem plik .env

Jeśli zgubiłeś plik .env, zatrzymaj się i oceń sytuację przed rotacją wartości.

  • DB_PASSWORD zazwyczaj można zresetować z dostępem administratora PostgreSQL.
  • TIMING_TOKEN_SECRET i PUBLIC_LINK_PASSWORD_SESSION_SECRET mogą zostać zregenerowane, ale istniejące sesje/tokeny mogą wygasnąć.
  • DB_ENCRYPTION_KEY jest krytyczny. Bez oryginalnego klucza zaszyfrowane wartości nie mogą zostać odszyfrowane.
  • Jeśli szyfrowanie PII jest włączone, zaszyfrowane pola klienta/kontaktu zależą od DB_ENCRYPTION_KEY.

Przywróć plik .env z kopii zapasowej, gdy tylko to możliwe.

Odzyskiwanie po utracie .env przy posiadaniu kopii zapasowej bazy danych

  1. Utwórz nowy .env z nowymi sekretami.
  2. Przywróć bazę danych z kopii zapasowej (pg_dump / pg_restore).
  3. Wszystkie dane biznesowe (klienci, oferty, produkty, użytkownicy) będą w pełni dostępne.
  4. Utracone zostaną tylko nieliczne pola: kody MFA (użytkownicy ponownie się rejestrują), hasło SMTP (wpisz ponownie w ustawieniach), klucz FX API (wpisz ponownie). Jeśli ENCRYPT_PII=true było włączone, zaszyfrowane dane klientów również przepadają.

Jeśli kopie zapasowe są szyfrowane age (ustawienie domyślne), potrzebujesz pliku tożsamości age z zestawu odzyskiwania, aby je odszyfrować. Zapoznaj się z przewodnikiem Kopie zapasowe i odzyskiwanie.

Zapomniałem hasła administratora

Użyj niedestrukcyjnej ścieżki odzyskiwania:

  1. Jeśli SMTP jest skonfigurowany, skorzystaj ze strony logowania: Zapomniałeś hasła?
  2. Jeśli inny administrator może się zalogować, otwórz Użytkownicy w panelu administratora i zresetuj hasło.
  3. Jeśli żaden administrator nie może się zalogować, a procedura resetowania przez e-mail jest niedostępna, przywróć dostęp z dobrze działającej kopii zapasowej lub zastosuj wewnętrzną procedurę konserwacyjną.

docker compose down -v nie jest procedurą resetowania hasła. Usuwa wszystkie dane — użytkowników, oferty, klientów, ustawienia i kopie zapasowe. Używaj tylko dla jednorazowych środowisk testowych.

Weryfikacja licencji nie powiodła się

W przypadku rzeczywistych wdrożeń, zachowaj:

LICENSE_SKIP_SIGNATURE_CHECK=false

Jeśli prawidłowa licencja nadal nie działa, sprawdź czy wdrożenie ma oczekiwaną wersję aplikacji i zsynchronizowany z NTP zegar systemowy. Potwierdź, że licencja odpowiada wdrożonej edycji/wersji, a następnie skontaktuj się z pomocą techniczną lub zastąp plik licencji poprzez udokumentowaną ścieżkę operatora. Nie ustawiaj pomijania podpisu na true jako obejścia produkcyjnego.

Last reviewed: Recently