Instalação
Instalar num Portátil Local
Corre o QuoteNode localmente para avaliação sem o expor a clientes.
Instalar num Portátil Local
Usa este caminho quando queres experimentar o QuoteNode num computador. É a forma mais rápida de avaliar a aplicação, mas não é um deployment orientado a clientes.
Quando usar
- Queres uma instância de demo ou avaliação.
- Estás a testar criação de propostas, PDFs, templates, produtos e definições.
- Não precisas que os clientes abram links públicos de propostas a partir de outro dispositivo.
O que não vai funcionar
- Links públicos de propostas gerados como
http://localhost/...funcionam apenas no mesmo portátil. - Links de email normalmente não são úteis porque os destinatários não conseguem abrir
localhosta partir das suas máquinas. - HTTPS normalmente não está configurado para esta topologia.
- O portátil deve estar acordado e o Docker deve estar a correr.
Pré-requisitos
- Docker Desktop ou Docker Engine com Docker Compose v2.
- Pelo menos 2,5 GB de RAM disponíveis para a stack.
- Pelo menos 10 GB de espaço livre em disco.
Verifica a tua configuração:
docker --version # Should show 24.0+
docker compose version # Should show v2.x
Passo 1 — Criar uma diretoria de projeto
mkdir quotenode && cd quotenode
Passo 2 — Criar o ficheiro de configuração
Cria um ficheiro chamado .env com as definições mínimas necessárias. Gera os teus próprios segredos — não uses os valores de substituição em produção:
# 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
# O IP do cliente é detetado automaticamente atrás de proxies internos e CDNs (incluindo Cloudflare) — deixe vazio.
# Defina apenas para um proxy invulgar cujo par de transporte seja um endereço PÚBLICO a confiar (IP/CIDR ou nome de host).
SECURITY_TRUSTED_PROXIES=
# Application
LOG_LEVEL=INFO
SPRING_PROFILES_ACTIVE=prod
Guarda o teu ficheiro
.envimediatamente. ODB_ENCRYPTION_KEYcifra dados sensíveis em repouso. Se o perderes, os campos cifrados (códigos MFA, password SMTP, chaves API) tornam-se permanentemente ilegíveis. Copia este ficheiro para um local seguro.
Passo 3 — Criar o ficheiro Docker Compose
Cria um ficheiro chamado 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:
Passo 4 — Iniciar o QuoteNode
docker compose up -d
O Docker vai descarregar as imagens dos containers (isto demora alguns minutos na primeira execução). Acompanha o progresso do arranque:
docker compose logs -f backend
Aguarda até ver Started QuoteNodeApplication — isso significa que o backend está pronto.
Passo 5 — Abrir o QuoteNode
Navega para http://localhost no teu browser.
Credenciais de acesso predefinidas:
| Campo | Valor |
|---|---|
[email protected] | |
| Password | Admin123! |
Altera a password predefinida imediatamente após iniciar sessão (Definições > O meu perfil > Alterar password).
O que está a correr?
Após docker compose up, tens 4 containers:
| Container | O que faz | Uso de RAM |
|---|---|---|
postgres | Armazena todos os teus dados | ~256 MB |
backend | Servidor API Java, lógica de negócio | ~512 MB – 1,5 GB |
gotenberg | Converte HTML para PDF (baseado em Chromium) | ~200 MB |
frontend | Serve a interface web + reverse proxy | ~20 MB |
Total: aproximadamente 1,5 – 2,5 GB de RAM.
Comandos habituais
# 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
Manter os teus segredos seguros
O teu ficheiro .env contém segredos criptográficos únicos para a tua instalação:
| Segredo | O que protege | Se perdido |
|---|---|---|
DB_PASSWORD | Acesso à base de dados | Recuperável — reiniciar via ferramentas de administração PostgreSQL |
DB_ENCRYPTION_KEY | Códigos MFA, password SMTP, chave API FX e todos os campos PII se ENCRYPT_PII=true | Permanentemente ilegível — não pode ser desencriptado sem a chave original |
TIMING_TOKEN_SECRET | Tokens anti-timing-attack | Regenerar — tokens existentes invalidados, utilizadores re-autenticam-se |
PUBLIC_LINK_PASSWORD_SESSION_SECRET | Sessões de links públicos | Regenerar — sessões ativas expiram, os links continuam a funcionar |
O segredo crítico é o
DB_ENCRYPTION_KEY. Todos os outros segredos podem ser regenerados com um pequeno incómodo. Mas oDB_ENCRYPTION_KEYcifra dados em repouso — perdê-lo significa que esses campos ficam perdidos para sempre.
Recuperação se .env for perdido
Se perderes o teu ficheiro .env mas tiveres uma cópia de segurança da base de dados não cifrada (o modo de cópia padrão):
- Cria um novo
.envcom novos segredos. - Restaura a base de dados a partir da tua cópia de segurança.
- Todos os dados de negócio (clientes, propostas, produtos, utilizadores) serão totalmente acessíveis.
- Apenas alguns campos são perdidos: códigos MFA (utilizadores re-inscrevem-se), password SMTP (reinserir nas definições), chave API FX (reinserir nas definições). Se
ENCRYPT_PII=trueestava ativo, os dados de clientes cifrados também são perdidos.
Cópias de segurança
Esta configuração de contentor único executa as cópias de segurança agendadas dentro do processo do backend (JOBS_MODE: all), por isso não precisas de um contentor backup-worker separado. A própria cópia é executada como um pipeline de shell (pg_dump → compressão → cifragem) fora da JVM, pelo que o seu custo de memória é pequeno — é por isso que o backend no portátil está dimensionado em 1280m em vez de 1g.
Isto é aceitável para um portátil ou uma instância de avaliação. Para uma implementação em produção, executa um contentor worker dedicado backup-only (JOBS_MODE: backup-only) ao lado de um backend web (JOBS_MODE: web) para que as cópias de segurança nunca compitam por memória com o tráfego web. Consulta Cópia de segurança e recuperação.
Mudar do portátil para servidor
Não publiques links de portátil para clientes. Quando estiveres pronto para um deployment partilhado ou público, escolhe um destes caminhos: