Installation
Instalacja na lokalnym laptopie
Uruchom QuoteNode lokalnie w celu ewaluacji, bez udostępniania klientom.
Instalacja na lokalnym laptopie
Wybierz tę ścieżkę, gdy chcesz wypróbować QuoteNode na jednym komputerze. To najszybszy sposób na ewaluację aplikacji, ale nie jest to wdrożenie dostępne dla klientów.
Kiedy użyć
- Chcesz uruchomić demo lub instancję próbną.
- Testujesz tworzenie ofert, PDF-y, szablony, produkty i ustawienia.
- Nie potrzebujesz, aby klienci otwierali publiczne linki do ofert z innego urządzenia.
Co nie zadziała
- Publiczne linki do ofert generowane jako
http://localhost/...działają tylko na tym samym laptopie. - Linki w e-mailach nie będą użyteczne, bo odbiorcy nie mogą otworzyć
localhostze swoich maszyn. - HTTPS nie jest normalnie konfigurowany w tej topologii.
- Laptop musi być włączony, a Docker uruchomiony.
Wymagania
- Docker Desktop lub Docker Engine z Docker Compose v2.
- Co najmniej 2,5 GB RAM dostępne dla stacku.
- Co najmniej 10 GB wolnego miejsca na dysku.
Sprawdź konfigurację:
docker --version # Should show 24.0+
docker compose version # Should show v2.x
Krok 1 — Utwórz katalog projektu
mkdir quotenode && cd quotenode
Krok 2 — Utwórz plik konfiguracyjny
Utwórz plik .env z minimalnymi wymaganymi ustawieniami. Wygeneruj własne sekrety — nie używaj wartości zastępczych w produkcji:
# Database
DB_URL=jdbc:postgresql://postgres:5432/quotenode
DB_USERNAME=quotenode
DB_PASSWORD=change-me-to-something-random-32-chars
DB_NAME=quotenode
# ============================================================
# SECURITY SECRETS — READ THIS BEFORE CONTINUING
# These values are UNIQUE to your installation.
# If you lose this file, you CANNOT regenerate the same secrets.
# SAVE THIS FILE and keep a secure backup.
# ============================================================
DB_ENCRYPTION_KEY=0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
TIMING_TOKEN_SECRET=change-me-timing-secret-min-32-chars
PUBLIC_LINK_PASSWORD_SESSION_SECRET=change-me-session-secret-min-32
# Domain (for CORS and Caddy)
CORS_ALLOWED_ORIGINS=http://localhost
DOMAIN=localhost
# IP klienta jest wykrywane automatycznie za wewnętrznymi proxy i CDN (w tym Cloudflare) — zostaw puste.
# Ustaw tylko dla nietypowego proxy, którego peer transportowy ma adres PUBLICZNY do zaufania (IP/CIDR lub nazwa hosta).
SECURITY_TRUSTED_PROXIES=
# Application
LOG_LEVEL=INFO
SPRING_PROFILES_ACTIVE=prod
Zapisz plik
.envnatychmiast.DB_ENCRYPTION_KEYszyfruje wrażliwe dane. Jeśli go stracisz, zaszyfrowane pola (kody MFA, hasło SMTP, klucze API) staną się trwale nieczytelne. Skopiuj ten plik w bezpieczne miejsce.
Krok 3 — Utwórz plik Docker Compose
Utwórz plik docker-compose.yml:
services:
postgres:
image: postgres:18-alpine
cpus: 2.0
mem_limit: 384m
environment:
POSTGRES_DB: ${DB_NAME}
POSTGRES_USER: ${DB_USERNAME}
POSTGRES_PASSWORD: ${DB_PASSWORD}
PGDATA: /var/lib/postgresql/data # required for the postgres:18 image
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${DB_USERNAME}"]
interval: 10s
timeout: 5s
retries: 10
restart: unless-stopped
gotenberg:
image: gotenberg/gotenberg:8
cpus: 2.0
mem_limit: 384m
environment:
LOG_LEVEL: info
healthcheck:
test: ["CMD", "curl", "-fsS", "http://localhost:3000/health"]
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped
backend:
image: ghcr.io/lesisty7/quotenode/quotenode-api:latest
cpus: 2.0
mem_limit: 1280m
depends_on:
postgres: { condition: service_healthy }
gotenberg: { condition: service_healthy }
env_file: .env
environment:
SPRING_PROFILES_ACTIVE: prod
# Single-container eval (WEB_MAINTENANCE backup profile): the backend runs
# scheduled backups in-process, so no separate backup-worker container is needed.
# For production, split into a web backend + a dedicated backup-only worker.
JOBS_MODE: all
BACKUP_RUNTIME_PROFILE: WEB_MAINTENANCE
PDF_ENABLED: "true"
PDF_GOTENBERG_URL: http://gotenberg:3000
volumes:
- backend_uploads:/app/data/uploads
- backend_pdfs:/app/data/pdfs
healthcheck:
test: ["CMD", "curl", "-fsS", "http://localhost:8080/health"]
interval: 15s
timeout: 10s
retries: 5
start_period: 45s
restart: unless-stopped
frontend:
image: ghcr.io/lesisty7/quotenode/quotenode-frontend:latest
cpus: 1.0
mem_limit: 128m
depends_on:
backend: { condition: service_healthy }
ports:
- "80:80"
- "443:443"
healthcheck:
test: ["CMD", "curl", "-fsS", "http://localhost:80/"]
interval: 15s
timeout: 5s
retries: 3
restart: unless-stopped
volumes:
postgres_data:
backend_uploads:
backend_pdfs:
Krok 4 — Uruchom QuoteNode
docker compose up -d
Docker pobierze obrazy kontenerów (za pierwszym razem zajmie to kilka minut). Obserwuj postęp:
docker compose logs -f backend
Poczekaj aż zobaczysz Started QuoteNodeApplication — to oznacza, że backend jest gotowy.
Krok 5 — Otwórz QuoteNode
Przejdź do http://localhost w przeglądarce.
Domyślne dane logowania:
| Pole | Wartość |
|---|---|
[email protected] | |
| Hasło | Admin123! |
Zmień domyślne hasło natychmiast po zalogowaniu (Settings > My Profile > Change Password).
Co działa?
Po docker compose up masz 4 kontenery:
| Kontener | Funkcja | Użycie RAM |
|---|---|---|
postgres | Przechowuje wszystkie dane | ~256 MB |
backend | Serwer API Java, logika biznesowa | ~512 MB – 1,5 GB |
gotenberg | Konwersja HTML do PDF (oparty na Chromium) | ~200 MB |
frontend | Serwuje UI + reverse proxy | ~20 MB |
Łącznie: około 1,5 – 2,5 GB RAM.
Przydatne polecenia
# Check if all services are running
docker compose ps
# View logs (all services)
docker compose logs -f
# View logs (backend only)
docker compose logs -f backend
# Stop QuoteNode
docker compose down
# Update to the latest version
docker compose pull && docker compose up -d
Bezpieczeństwo sekretów
Plik .env zawiera sekrety kryptograficzne unikalne dla tej instalacji:
| Sekret | Co chroni | Jeśli utracony |
|---|---|---|
DB_PASSWORD | Dostęp do bazy danych | Odzyskiwalny — reset przez narzędzia admin PostgreSQL |
DB_ENCRYPTION_KEY | Kody MFA, hasło SMTP, klucz API FX, pola PII przy ENCRYPT_PII=true | Trwale nieczytelny — nie da się odszyfrować bez oryginalnego klucza |
TIMING_TOKEN_SECRET | Tokeny anty-timing-attack | Regenerowalny — użytkownicy logują się ponownie |
PUBLIC_LINK_PASSWORD_SESSION_SECRET | Sesje linków publicznych | Regenerowalny — aktywne sesje wygasają |
Krytyczny sekret to
DB_ENCRYPTION_KEY. Wszystkie inne sekrety można zregenerować z niewielkim utrudnieniem. AleDB_ENCRYPTION_KEYszyfruje dane — utrata oznacza, że te pola przepadają na zawsze.
Odzyskiwanie po utracie .env
Jeśli stracisz .env, ale masz niezaszyfrowaną kopię zapasową bazy (domyślny tryb):
- Utwórz nowy
.envz nowymi sekretami. - Przywróć bazę z kopii zapasowej.
- Wszystkie dane biznesowe (klienci, oferty, produkty, użytkownicy) będą dostępne.
- Utracone: kody MFA (użytkownicy rejestrują się ponownie), hasło SMTP (wprowadź ponownie), klucz API FX. Jeśli
ENCRYPT_PII=truebyło włączone, dane klientów też przepadają.
Kopie zapasowe
Ta konfiguracja jednokontenerowa wykonuje zaplanowane kopie zapasowe wewnątrz procesu backendu (JOBS_MODE: all), więc nie potrzebujesz osobnego kontenera backup-worker. Sama kopia działa jako potok powłoki (pg_dump → kompresja → szyfrowanie) poza JVM, więc jej koszt pamięciowy jest niewielki — dlatego backend na laptopie ma rozmiar 1280m, a nie 1g.
Jest to akceptowalne dla laptopa lub instancji ewaluacyjnej. We wdrożeniu produkcyjnym uruchom dedykowany kontener backup-only (JOBS_MODE: backup-only) obok backendu webowego (JOBS_MODE: web), aby kopie zapasowe nigdy nie konkurowały o pamięć z ruchem webowym. Zobacz Kopie zapasowe i odzyskiwanie.
Przejście z laptopa na serwer
Nie publikuj linków laptopowych klientom. Gdy będziesz gotowy na współdzielone lub publiczne wdrożenie, wybierz jedną ze ścieżek: