18 KiB
Convite Safari 🦁 — Manual de Instruções
Este documento reúne tudo o que é preciso para instalar, publicar e usar o Convite Safari.
- Parte 1 — Deploy: para quem vai instalar e manter a aplicação no servidor.
- Parte 2 — Uso: para o organizador da festa e para os convidados.
Sumário
Parte 1 — Deploy
- Visão geral da arquitetura
- Pré-requisitos
- Copiar o projeto para o servidor
- Configurar as variáveis de ambiente
- Subir a aplicação
- Verificar se está tudo funcionando
- Publicar via Cloudflare
- Atualizar para uma nova versão
- Backup e restauração
- Operação do dia a dia
- Solução de problemas
Parte 2 — Uso
Parte 1 — Deploy
1. Visão geral da arquitetura
A aplicação roda em dois containers gerenciados pelo Docker Compose:
| Serviço | Imagem | Função |
|---|---|---|
app |
construída pelo Dockerfile do projeto |
Servidor Next.js (páginas, área do organizador, RSVP) na porta 3000 |
db |
postgres:17-alpine |
Banco de dados. Os dados ficam no volume pgdata |
Convidado / Organizador
│
▼
Cloudflare (HTTPS) ──► Servidor :3000 ──► container app ──► container db (Postgres)
└─ volume pgdata
Pontos importantes:
- O banco não fica exposto para fora do servidor; só o container
appo acessa. - As migrations (criação/atualização das tabelas) são aplicadas automaticamente toda vez que o container
appsobe. - Os dados ficam no volume Docker
pgdatae sobrevivem a reinícios e rebuilds dos containers.
2. Pré-requisitos
No servidor (ex.: 192.168.18.97):
-
Docker com o plugin Compose. Para conferir:
docker --version docker compose version -
Acesso à internet durante o build — o build baixa as dependências npm e as fontes do Google.
-
Porta livre para a aplicação (padrão
3000; pode ser alterada emAPP_PORT).
3. Copiar o projeto para o servidor
Copie a pasta do projeto para o servidor sem as pastas node_modules, .next e dist (elas são recriadas no build).
Exemplo com rsync, a partir da máquina onde está o código:
rsync -av --exclude node_modules --exclude .next --exclude dist ./ usuario@192.168.18.97:/opt/convite/
Troque
usuarioe/opt/convite/pelo usuário e pela pasta usados nas outras aplicações do servidor.
Arquivos essenciais que precisam estar no servidor:
Dockerfile
compose.yml
.dockerignore
.env.example
package.json
package-lock.json
next.config.ts
tsconfig.json
postcss.config.mjs
drizzle.config.ts
drizzle/ (migrations SQL)
scripts/ (script de migration)
src/
public/
4. Configurar as variáveis de ambiente
No servidor, dentro da pasta do projeto:
cd /opt/convite
cp .env.example .env
nano .env
Variáveis
| Variável | Obrigatória | Exemplo | Descrição |
|---|---|---|---|
POSTGRES_DB |
não | convite |
Nome do banco |
POSTGRES_USER |
não | convite |
Usuário do banco |
POSTGRES_PASSWORD |
sim | Xk29fLq81 |
Senha do banco. Só letras e números |
DATABASE_URL |
não* | — | Usada apenas fora do Docker. No compose é montada automaticamente |
APP_PORT |
não | 3000 |
Porta publicada no servidor |
ORGANIZER_PASSWORD |
sim | — | Senha de acesso à área do organizador |
SESSION_SECRET |
sim | — | Chave de 32+ caracteres para assinar a sessão |
COOKIE_SECURE |
sim | false |
false para acesso por http://IP; true atrás do Cloudflare (HTTPS) |
RUN_MIGRATIONS |
não | true |
false desliga a migration automática na subida |
* Não precisa alterar para o deploy com Docker.
Gerar o SESSION_SECRET
Em qualquer máquina com Node.js:
node -e "console.log(require('crypto').randomBytes(32).toString('base64url'))"
Ou no próprio servidor, sem Node:
openssl rand -base64 32 | tr -d '/+='
Cole o resultado em SESSION_SECRET=.
Exemplo de .env preenchido
POSTGRES_DB=convite
POSTGRES_USER=convite
POSTGRES_PASSWORD=Xk29fLq81mPz
APP_PORT=3000
ORGANIZER_PASSWORD=uma-senha-forte-para-o-admin
SESSION_SECRET=3qD0x9bN1pY7sKcVb2T8wM4rH6jLz5aEoUfGiQ
COOKIE_SECURE=false
🔒 O
.envcontém senhas. Não envie esse arquivo por e-mail/chat e restrinja o acesso:chmod 600 .env.
5. Subir a aplicação
docker compose up -d --build
O que acontece, em ordem:
- A imagem do
appé construída (pode levar alguns minutos na primeira vez). - O Postgres sobe; o
appaguarda o banco ficar saudável. - O
appaplica as migrations pendentes. - O servidor Next.js inicia na porta
APP_PORT.
6. Verificar se está tudo funcionando
Status dos containers — ambos devem estar running (e o db como healthy):
docker compose ps
Logs do app:
docker compose logs -f app
Saída esperada (resumida):
Migrations aplicadas.
▲ Next.js 16.x
- Local: http://0.0.0.0:3000
✓ Ready
No navegador:
| Endereço | Deve mostrar |
|---|---|
http://192.168.18.97:3000/ |
Página inicial "Convites Digitais de Aniversário" |
http://192.168.18.97:3000/admin |
Tela de login do organizador |
Entre com a ORGANIZER_PASSWORD. Se cair na lista de eventos (vazia), o deploy está concluído.
7. Publicar via Cloudflare
Quando o Cloudflare for apontar para a aplicação (mesmo padrão das outras aplicações do servidor):
-
Configure o domínio/túnel no Cloudflare apontando para
http://<servidor>:<APP_PORT>. -
No
.env, altere:COOKIE_SECURE=true -
Recrie o container do app:
docker compose up -d app -
Acesse
https://<seu-domínio>/admine faça login para confirmar.
⚠️ Depois de
COOKIE_SECURE=true, o login pelo IP em HTTP deixa de funcionar — isso é esperado. Use sempre o domínio HTTPS.
Os links de convite são gerados a partir do endereço em que o organizador está navegando. Por isso, depois da publicação, gere/copie os links acessando o /admin pelo domínio, e não pelo IP.
8. Atualizar para uma nova versão
-
Copie o código novo para o servidor (mesmo comando
rsyncdo passo 3). O.envdo servidor não é sobrescrito, pois não está na pasta de origem — confira antes. -
Faça um backup do banco (ver seção 9) — recomendado sempre que houver mudança no banco.
-
Reconstrua e suba o app:
docker compose up -d --build app -
Verifique os logs (
docker compose logs -f app). Novas migrations são aplicadas automaticamente.
Os dados não são perdidos em atualizações.
9. Backup e restauração
Backup
docker compose exec -T db sh -c 'pg_dump -U "$POSTGRES_USER" "$POSTGRES_DB"' > backup-$(date +%F).sql
Gera um arquivo como backup-2026-11-10.sql na pasta atual. Guarde uma cópia fora do servidor.
Backup automático diário (opcional) — adicione ao crontab -e do servidor:
0 3 * * * cd /opt/convite && docker compose exec -T db sh -c 'pg_dump -U "$POSTGRES_USER" "$POSTGRES_DB"' > /opt/convite/backups/backup-$(date +\%F).sql
(crie a pasta antes: mkdir -p /opt/convite/backups)
Restauração
A restauração deve ser feita em um banco vazio. Para recriar o banco do zero:
docker compose down
docker volume rm convite_pgdata # ⚠️ APAGA todos os dados atuais
docker compose up -d db
docker compose exec -T db sh -c 'psql -U "$POSTGRES_USER" "$POSTGRES_DB"' < backup-AAAA-MM-DD.sql
docker compose up -d app
⚠️ Nunca use
docker compose down -vsem querer apagar o banco — o-vremove o volumepgdata.
10. Operação do dia a dia
| Tarefa | Comando |
|---|---|
| Ver status | docker compose ps |
| Ver logs do app | docker compose logs -f app |
| Ver logs do banco | docker compose logs -f db |
| Reiniciar o app | docker compose restart app |
| Parar tudo (mantém dados) | docker compose down |
| Subir novamente | docker compose up -d |
| Console SQL | docker compose exec db sh -c 'psql -U "$POSTGRES_USER" "$POSTGRES_DB"' |
| Trocar a senha do organizador | Edite ORGANIZER_PASSWORD no .env e rode docker compose up -d app |
| Derrubar todas as sessões abertas | Troque o SESSION_SECRET no .env e rode docker compose up -d app |
Os containers estão com restart: unless-stopped: voltam sozinhos se o servidor reiniciar.
11. Solução de problemas
| Sintoma | Causa provável | Solução |
|---|---|---|
defina POSTGRES_PASSWORD no .env ao subir |
Variável ausente | Preencha POSTGRES_PASSWORD no .env |
App reiniciando em loop; log com Falha ao aplicar migrations |
Banco inacessível ou senha errada | Confira docker compose logs db e as variáveis POSTGRES_* |
Erro de senha no Postgres após trocar POSTGRES_PASSWORD |
A senha só é definida na primeira criação do volume | Volte a senha antiga no .env ou altere no banco: ALTER USER convite PASSWORD 'nova'; |
Login do /admin sempre volta para a tela de login |
COOKIE_SECURE=true com acesso via HTTP |
Use false no acesso por IP, ou acesse pelo domínio HTTPS |
Erro SESSION_SECRET não definida no log |
Variável vazia ou curta | Gere um valor de 32+ caracteres (seção 4) |
| "Senha incorreta." no login | ORGANIZER_PASSWORD diferente da digitada |
Confira o .env e recrie o app após alterar |
| Build falha baixando fontes/pacotes | Servidor sem internet no build | Libere o acesso à internet durante o docker compose build |
| Porta 3000 em uso | Outra aplicação na mesma porta | Altere APP_PORT no .env |
| Link do convite abre com o IP em vez do domínio | Link copiado acessando o admin pelo IP | Acesse o /admin pelo domínio e copie de novo |
Parte 2 — Uso
12. Conceitos
| Termo | Significado |
|---|---|
| Evento | A festa: nome, data, horário, local e endereço. Ex.: "1º Aniversário do Arthur" |
| Convite | Um link enviado para uma família/grupo. Tem um nome exibido (ex.: "Família Silva") e uma lista de convidados |
| Convidado | Cada pessoa do convite, marcada como Adulto ou Criança |
| Token | Código único no final do link (/r/Ab3xY9kL). Funciona como a "chave" do convite |
| RSVP / Status | Resposta de cada convidado: ✓ Presente, ✗ Ausente ou ⏳ Pendente |
Um evento pode ter vários convites; cada convite pode ter vários convidados.
13. Área do organizador
13.1 Entrar
- Acesse
https://<seu-domínio>/admin(ouhttp://192.168.18.97:3000/adminantes da publicação). Também há o link discreto "Área do organizador" no rodapé da página inicial. - Digite a senha do organizador e clique em Entrar.
A sessão dura 7 dias no mesmo navegador. Para sair, clique em Sair no topo da página.
13.2 Painel de eventos
A tela inicial do organizador lista todos os eventos com:
- data, horário e local;
- quantidade de convites;
- resumo de presença: Presentes (com divisão adultos/crianças), Ausentes e Pendentes.
Clique em um evento para abrir os detalhes.
13.3 Criar um evento
- No painel, clique em + Novo evento.
- Preencha:
- Nome da festa — ex.: "1º Aniversário do Arthur"
- Data e Horário
- Local — ex.: "Espaço Festa"
- Endereço — ex.: "Rua das Flores, 100 - Belo Horizonte/MG"
- Clique em Criar evento. Você será levado à página do evento.
13.4 Criar um convite
- Na página do evento, clique em + Novo convite.
- Preencha o Nome exibido no convite — é o título grande que o convidado vê. Ex.: "Família Silva", "Tio João e Tia Ana".
- Adicione os convidados:
- digite o nome na primeira linha e escolha Adulto ou Criança;
- use + Adulto / + Criança para adicionar mais linhas;
- use ✕ para remover uma linha. Linhas com o nome em branco são ignoradas.
- Clique em Criar convite.
O link do convite é gerado automaticamente.
💡 Dica: crie um convite por família/núcleo. Assim cada família responde por todos os seus membros de uma vez.
13.5 Enviar o convite
Em cada convite, na página do evento:
- Copiar link — copia o endereço completo (ex.:
https://seu-dominio/r/Ab3xY9kL) para colar onde quiser. - WhatsApp — abre o WhatsApp com uma mensagem pronta contendo o nome da família, o nome da festa e o link. Basta escolher o contato e enviar.
- Abrir convite — abre o convite em outra aba, exatamente como o convidado verá.
🔒 Cada link dá acesso àquele convite e permite responder por ele. Envie apenas para a família correspondente e não publique em grupos abertos ou redes sociais.
13.6 Acompanhar as respostas
Na página do evento:
- O Resumo de presença mostra o total de presentes (adultos e crianças), ausentes e pendentes de todo o evento — útil para fechar números com buffet e decoração.
- Cada convite mostra seus convidados com o status colorido:
- 🟢 ✓ Presente
- 🔴 ✗ Ausente
- 🟡 ⏳ Pendente
- Quando a família responde, aparece "respondeu em dd/mm/aaaa hh:mm" abaixo do nome.
Recarregue a página para ver respostas novas.
13.7 Editar um convite
- No convite, clique em Editar.
- Altere o nome exibido, renomeie convidados, troque o tipo, adicione ou remova pessoas.
- Clique em Salvar alterações.
O link não muda ao editar. O status de cada convidado aparece ao lado do nome durante a edição.
⚠️ Remover um convidado apaga também a resposta dele. Convidados mantidos ou apenas renomeados preservam a resposta.
13.8 Zerar as respostas de um convite
Se uma família respondeu por engano e quer começar de novo:
- No convite, clique em Zerar respostas (só aparece se já houve resposta).
- Confirme.
Todos os convidados daquele convite voltam para Pendente. O link continua o mesmo.
13.9 Excluir um convite
- No convite, clique em Excluir e confirme.
O link deixa de funcionar imediatamente (o convidado verá "Convite não encontrado"). Não é possível desfazer.
13.10 Editar ou excluir um evento
- Na página do evento, clique em Editar evento.
- Altere os dados e clique em Salvar alterações — as mudanças aparecem na hora em todos os convites do evento.
- Para excluir, use Excluir evento no final da página e confirme.
⚠️ Excluir um evento apaga todos os convites e respostas dele. Não é possível desfazer.
14. Experiência do convidado
O convidado não precisa de cadastro nem senha — só do link recebido.
14.1 Abrir o convite
Ao abrir o link, o convidado vê:
- O nome do convite em destaque (ex.: "Família Silva — esperamos vocês para celebrar!").
- Os detalhes da festa: data, horário, local e endereço.
- A lista de convidados daquele convite, cada um com seu status atual.
- O quadro de Confirmação de Presença, com a contagem de presentes, ausentes e pendentes.
14.2 Responder
- Para cada pessoa da lista, toque em:
- ✓ para confirmar presença, ou
- ✗ para informar ausência. Para mudar de ideia antes de enviar, basta tocar no outro botão.
- Toque em Confirmar Presença.
- Aparece a tela "Presença Confirmada!" com o resumo de quem vai e quem não vai. Toque em Fechar.
Não é obrigatório responder por todos de uma vez: quem ficar como pendente pode ser respondido depois.
14.3 Mudar a resposta
O convidado pode abrir o mesmo link a qualquer momento. As respostas já enviadas aparecem marcadas; basta trocar (ex.: de ✓ para ✗) e tocar em Confirmar Presença novamente.
14.4 Mensagens possíveis
| Mensagem | Significado |
|---|---|
| Convite não encontrado | Link digitado errado, incompleto ou convite excluído pelo organizador |
| Não foi possível enviar sua resposta. Tente novamente. | Falha de conexão ou o convite foi alterado no meio do caminho. Recarregue a página e responda de novo |
15. Perguntas frequentes
O convidado precisa instalar algum aplicativo? Não. Funciona no navegador do celular ou do computador.
Posso usar o mesmo sistema para várias festas? Sim. Crie um evento para cada festa; cada um tem seus próprios convites e totais.
Duas pessoas da mesma família podem responder pelo mesmo link? Sim. Vale a última resposta enviada para cada convidado.
Um convidado novo apareceu depois que a família já respondeu. O que faço? Edite o convite e adicione a pessoa. Ela aparece como Pendente e as respostas anteriores dos outros são mantidas. Peça para a família abrir o link de novo e responder por ela.
Enviei o link errado para uma família. Como invalido? Exclua o convite e crie outro — o novo convite terá um link diferente.
Esqueci a senha do organizador.
Quem administra o servidor consulta/altera ORGANIZER_PASSWORD no .env e recria o app (docker compose up -d app).
Os dados ficam salvos se o servidor reiniciar? Sim. Os dados ficam no banco Postgres, em um volume persistente. Mesmo assim, mantenha backups (seção 9).