Aplicação web para pedidos e gestão de lojas/restaurantes (cardápio, checkout, fila e pagamentos), com módulo de entrega via motoboy.
O projeto traz sete experiências principais:
- Loja do cliente: montagem e edição do pedido, info da loja no mobile (sheet), WhatsApp e link de acompanhamento (publico com persistencia via
localStorage). - Painel interno (Admin/Churrasqueiro): dashboard com métricas, CRUD de produtos, fila do churrasqueiro (atualização a cada 5s), pagamentos e histórico.
- Acompanhar pedido: pagina publica em
/pedido/:orderIdcom status, fila, detalhes e branding da loja.- Ultimos 3 pedidos publicos ficam em
localStoragepara reabrir o acompanhamento (inclusive mesa). - Numero exibido usa prefixo do slug (3 letras) + 8 primeiros chars do ID.
- Ultimos 3 pedidos publicos ficam em
- Promoções: produto pode ter preço promocional ativo; vitrine, carrinho, fila e acompanhamento exibem valor original riscado + promocional.
- Entregador (Motoboy): fila de entregas, entrega atual, histórico, ganhos e confirmação de pagamento no final da entrega.
- Hub de condomínios: vitrines por condomínio/evento, com lojas participantes e fluxo de pedido preservado.
- Hub de destinos turísticos: cidades, chalés, pousadas, serviços locais e lojas que atendem hospedagens específicas.
- Guia funcional web:
/guia - Documentação do Hub de destinos/chalés:
docs/DESTINATION_HUB.md
frontend/: aplicação React + Vite (servida pelo nginx em produção).backend/: API Node.js/Express + TypeORM em TypeScript, com documentação Swagger em/api/docs.server/: microserviço de mapas (geocode/route) usado para ETA/distância.face-worker/: worker Python (FastAPI + DeepFace) para verificação assistida de selfie vs CNH.docker-compose.yml: sobe frontend, API, PostgreSQL e pgAdmin já apontando para as pastas certas.docker-compose.prod.yml: override de produção (principalmente segurança do volume do Postgres).scripts/: utilitários de deploy/backup/limpeza (deploy-frontend.sh,deploy-api.sh,clean-safe.sh,compose-prod.sh,pg-backup-rotate.sh).
- Todo arquivo
backend/src/**/*.tsdeve conter o cabecalho CONFIDENTIAL com@file,@Datee@author. - Classes e metodos no backend devem ter TSDoc em ingles com nome e data.
- Deixar uma linha em branco entre metodos.
- A data deve refletir a criacao do arquivo (git diff-filter=A).
- Front-end: React + Vite servido por nginx (
frontend/Dockerfile). - API: Node.js/Express/TypeORM (
backend/). - Banco de dados: PostgreSQL (schema base em
backend/schema.sql+ evolução viabackend/src/utils/runMigrations.ts). - Maps: microserviço
maps(server/) para Google Routes/Geocoding. - Face verify: worker
face-workerpara verificação assistida de documentos do motoboy (selfie vs CNH).
Requisitos mínimos para desenvolvimento local:
- Node.js 20+ e npm/yarn
- PostgreSQL 16+ (local) ou Docker
- Docker + Docker Compose (opcional, recomendado)
flowchart LR
U[Cliente / Admin / Motoboy] -->|HTTPS| N[Nginx (EC2)]
N -->|/| F[Frontend (nginx)]
N -->|/api| A[API Node (Express)]
N -->|/uploads| A
A --> P[(Postgres)]
A --> M[Maps service]
A --> W[Face worker (Python)]
O Hub de destinos é uma camada local por cidade para chalés, pousadas, serviços e lojas que entregam em hospedagens específicas.
Fluxo público:
flowchart TD
C[Cliente] --> D[Escolhe destino/cidade]
D --> H[Seleciona chalé ou pousada]
H --> L[Vê lojas que entregam ali]
D --> S[Vê passeios, restaurantes e serviços locais]
L --> P[Pedido no fluxo normal da loja]
Responsabilidades:
- Plataforma/SuperAdmin: cadastra destinos, banners, chalés/pousadas, serviços locais e aprova solicitações.
- Chalé/pousada/prestador: solicita cadastro em
/destinos/cadastrar; após aprovação vira registro real. - Lojista: entra em
Admin > Destinos, escolhe hospedagens que realmente atende, informa taxa/tempo e solicita vínculo. - Cliente/turista: acessa
/destinos, escolhe cidade e consome lojas/serviços daquele contexto.
Inteligência regional:
- O painel do lojista prioriza destinos pela cidade, UF e distância da loja quando
store_settings.city,state,lat,lngedelivery_radius_kmexistem. - Destinos recomendados aparecem primeiro; destinos fora da região ficam disponíveis em "Ver todos" para exceções revisadas pela plataforma.
- A loja só aparece em um chalé/pousada depois da aprovação da plataforma, mesmo que ela solicite o vínculo.
Rotas principais:
- Público:
/destinos,/destinos/cadastrar,/destinos/:slug,/destinos/:slug/chales/:placeSlug. - SuperAdmin:
/superadmin/destinations. - Lojista:
/admin/dashboardna abaDestinos.
Para criar uma amostra real inicial, cadastre a cidade no SuperAdmin, depois adicione chalés/pousadas e listings de restaurantes, passeios, massagens e atrações usando informações públicas e descrição neutra. Se o proprietário ainda não confirmou parceria, não comunique como parceiro oficial; use como curadoria da plataforma até ele solicitar ou aprovar a presença.
Para carregar a amostra inicial de São Bento do Sapucaí extraída do mapa turístico local, rode:
npm --prefix backend run seed:destination:sao-bentoEsse seed cria/atualiza o destino, hospedagens e serviços sem criar parceria oficial nem vínculo com lojas.
Em produção, depois de subir a nova imagem da API, rode dentro do container:
docker exec janocaminho-backend npm run seed:destination:sao-bento:distO cadastro do motoboy tem duas camadas:
- KYC global (plataforma): CNH/SELFIE/CRLV são validados pela equipe (SUPER_ADMIN) e/ou pela verificação assistida (face-worker).
- Vínculo por loja: cada loja decide se aceita o motoboy na operação (aprovar/rejeitar vínculo), mas não altera o status global do documento.
Fluxo:
flowchart TD
M[Motoboy] -->|envia docs| API[API]
API -->|verifica selfie vs CNH| FW[face-worker]
API -->|fila KYC| SA[SUPER_ADMIN]
SA -->|aprova/rejeita docs| API
M -->|solicita vínculo| API
API -->|lista pendências+docs| LOJA[Loja (Admin)]
LOJA -->|aprova vínculo (se KYC aprovado)| API
LOJA -->|rejeita vínculo + pede reenvio| API
API -->|motivo do reenvio| M
Regras principais:
- O motoboy pode solicitar vínculo assim que enviar os documentos obrigatórios (status pode estar
PENDING). - A loja só consegue aprovar vínculo quando o KYC global estiver APROVADO (SUPER_ADMIN).
- Se a loja precisar, ela pode pedir reenvio (com motivo) sem rejeitar o KYC global (pedido por loja via
metadata.review.storeReuploadRequests[storeId]).
Este é o fluxo esperado sem quebrar os fluxos antigos (retirada/mesa continuam iguais):
flowchart TD
C[Cliente (vitrine)] -->|cria pedido| O[Order]
O -->|Admin prepara| Q[Fila / Produção]
Q -->|delivery: pronto| W[Aguardando entregador]
W -->|motoboy aceita| D[Em rota]
D -->|entregue| E[Entregue]
E -->|confirma pagamento / finaliza| F[Finalizado]
Detalhes importantes:
- Motoboy só pode ter 1 entrega ativa por vez.
- Dois motoboys não conseguem aceitar a mesma entrega (concorrência).
- Ao entrar em
Em rota, o acompanhamento público do pedido mostra também nome do motoboy quando disponível.
KYC (plataforma):
GET /api/admin/motoboys/kyc/pending(SUPER_ADMIN)GET /api/admin/motoboys/:motoboyId/documents(SUPER_ADMIN)POST /api/admin/motoboys/:motoboyId/documents/:documentId/approve(SUPER_ADMIN)POST /api/admin/motoboys/:motoboyId/documents/:documentId/reject(SUPER_ADMIN)
Vínculo (loja):
POST /api/motoboy/store-requests(motoboy)GET /api/stores/:storeId/motoboy-requests(ADMIN da loja)POST /api/stores/:storeId/motoboy-requests/:requestId/approve(ADMIN da loja)POST /api/stores/:storeId/motoboy-requests/:requestId/reject(ADMIN da loja)POST /api/stores/:storeId/motoboys/:motoboyId/documents/:documentId/reupload(ADMIN da loja)
cp backend/.env.docker.example backend/.env.docker
docker compose up --buildServiços locais:
- Front-end:
http://localhost:8080 - API:
http://localhost:4000(Swagger em/api/docs)
- O volume do Postgres é pinned por nome em
docker-compose.yml:POSTGRES_VOLUME_NAME(default:edespetohub_postgres-data)
- Em produção,
docker-compose.prod.ymlmarca o volume como external, evitando perda acidental comdocker compose down -v. - A API tem bootstrap que recria o banco caso ele tenha sido dropado (e aplica
schema.sql+ migrations) para o serviço não ficar indisponível.
Backup/rotação (SQL gz) via script:
scripts/pg-backup-rotate.shMIN_INTERVAL_HOURS(default48)KEEP_LATEST(default1)
Exemplo (cron a cada 4h, mantendo apenas 1 arquivo):
BACKUP_DIR=/home/ec2-user/backups/janocaminho MIN_INTERVAL_HOURS=4 KEEP_LATEST=1 bash /home/ec2-user/EdEspetoHub/scripts/pg-backup-rotate.sh- Instalar dependências
cd backend && npm install
cd ../frontend && npm install- Banco local
createdb espetinho
psql -h localhost -U postgres -d espetinho -f backend/schema.sql- Configurar envs
cp backend/.env.example backend/.envEdite backend/.env e ajuste PG*, PORT e JWT_SECRET.
Opcional: LOG_LEVEL=debug|info|warn|error, LOG_TO_FILE=true e LOG_DIR=logs para controlar logs e salvar em arquivo.
Opcional (producao): usar AWS SSM Parameter Store (SecureString) com um JSON unico. Configure:
SSM_PARAMETER_NAME(ex:/janocaminho/prod)AWS_REGION(ex:us-east-1)SSM_OVERRIDE=truepara sobrescrever variaveis locais Opcional (dev local): se o SSM vier comPGHOST=postgres, definaSSM_LOCAL_DB_HOST=localhostpara sobrescrever apenas no host (fora do Docker). Opcional (debug):SSM_LOG_KEYS=truepara logar quais chaves foram aplicadas (somente nomes). Opcional (debug):SSM_LOG_OVERRIDES=falsepara ocultar overrides locais (padrao loga).
Exemplo de JSON no SSM:
{
"JWT_SECRET": "secret",
"APP_BASE_URL": "https://www.janocaminho.com.br",
"PGHOST": "db.prod",
"PGPORT": "5432",
"PGUSER": "postgres",
"PGPASSWORD": "senha",
"PGDATABASE": "espetinho",
"DB_POOL_MAX": "10",
"DB_POOL_IDLE_TIMEOUT_MS": "30000",
"DB_POOL_CONNECTION_TIMEOUT_MS": "5000",
"DB_STATEMENT_TIMEOUT_MS": "15000",
"DB_IDLE_IN_TRANSACTION_TIMEOUT_MS": "10000",
"MP_ACCESS_TOKEN": "xxx",
"MP_PUBLIC_KEY": "xxx",
"MP_WEBHOOK_SECRET": "xxx",
"SMTP_HOST": "smtp.seu-dominio.com",
"SMTP_PORT": "587",
"SMTP_USER": "usuario",
"SMTP_PASS": "senha",
"SMTP_SECURE": "false",
"EMAIL_FROM": "Jano Caminho <contato@janocaminho.com.br>"
}Verificacao rapida:
aws ssm get-parameter --name /janocaminho/prod --with-decryption --region us-east-2- Ao subir a API, procure o log
SSM env loaded(mostra o nome do parametro e a quantidade de chaves).
Crie frontend/.env:
VITE_API_BASE_URL=http://localhost:4000/api- Subir
cd backend && npm run dev
cd ../frontend && npm run devServiços locais:
- Front-end:
http://localhost:3000 - API:
http://localhost:4000
- Mapa da loja: OpenStreetMap embed no frontend.
- Geocoding: OpenStreetMap/Nominatim via backend principal.
- Rota/ETA: estimativa local no backend para evitar custo e dependência externa.
POST /api/maps/geocode→{ lat, lng, formattedAddress }POST /api/maps/route→{ distanceKm, durationMin, estimated }
Frontend (frontend/.env):
VITE_STORE_ORIGIN_LAT=-23.55052
VITE_STORE_ORIGIN_LNG=-46.633308
VITE_STORE_ORIGIN_LABEL=Loja
Backend (backend/.env.docker):
ENABLE_FREE_GEOCODING_FALLBACK=false
DEFAULT_PREP_MINUTES=15
DEFAULT_PREP_PER_ITEM_MINUTES=2
DEFAULT_QUEUE_MINUTES_PER_ORDER=5
DEFAULT_QUEUE_BUFFER_MINUTES=0
DEFAULT_ETA_BUFFER_MINUTES=3
npm install
cd backend && npm install
cd ../frontend && npm install
cd ..
npm run devRotas:
- Front:
http://localhost:3000 - API:
http://localhost:4000
- O proxy
/api/mapsaponta para a API principal. - O serviço Docker
mapsfoi aposentado. - Para subir tudo:
docker compose --env-file .env.prod up --build -d.
- Fluxo de delivery com motoboy (atribuir, aceitar, entregar e confirmar pagamento).
- Status de pedido (apenas
type='delivery', noorders.status):pending→preparing→ready_for_delivery→waiting_for_motoboy→in_delivery→delivered→finished
- Campos de pagamento em
orders:payment_method(pix/credito/debito/dinheiro)payment_status(PENDING|PAID)cash_tendered(dinheiro: quanto o cliente informou que vai pagar)
- Novas tabelas:
motoboysmotoboy_storesorder_deliveriesdelivery_events(auditoria de transições)
- Motoboy so ve pedidos das lojas associadas (N:N).
- Motoboy so atua se
motoboys.status = ACTIVE. - Pagamento em dinheiro/cartao fica
PENDINGate o motoboy confirmar. - Aceite de pedido e feito com transacao (conflito 409 se ja aceito).
- Exclusividade: 1 entrega ativa por motoboy (garantido por índice parcial no Postgres).
- Concorrência: dois motoboys não aceitam o mesmo pedido (apenas 1 vence; outro recebe 409).
- Expiração: entradas de fila expiram por
expires_at, mas se o pedido continuarwaiting_for_motoboy, a fila reabre automaticamente comoAVAILABLE.
stateDiagram-v2
direction LR
[*] --> pending
pending --> preparing
preparing --> ready_for_delivery
ready_for_delivery --> waiting_for_motoboy
waiting_for_motoboy --> in_delivery : motoboy aceita
in_delivery --> delivered : motoboy entrega
delivered --> finished : pagamento confirmado/fechado
order_deliveries.status (workflow da entrega):
stateDiagram-v2
direction LR
AVAILABLE --> ACCEPTED
ACCEPTED --> PICKED_UP
PICKED_UP --> IN_TRANSIT
IN_TRANSIT --> DELIVERED
AVAILABLE --> EXPIRED
AVAILABLE --> CANCELED
Motoboy:
GET /motoboy/orders/availableGET /motoboy/orders/currentGET /motoboy/orders/historyGET /motoboy/earnings/todayGET /motoboy/stats?range=day|week|monthPOST /motoboy/orders/:orderId/acceptPOST /motoboy/orders/:orderId/pickupPOST /motoboy/orders/:orderId/startPOST /motoboy/orders/:orderId/confirm-paymentPOST /motoboy/orders/:orderId/deliveredPOST /motoboy/orders/:orderId/finishGET /motoboy/profilePUT /motoboy/profileGET /motoboy/documentsPOST /motoboy/documents
Conteudo legal (publico):
GET /legal/termsGET /legal/lgpd
Admin (SUPER_ADMIN):
POST /admin/site-settings(key/value)
Chaves uteis em site_settings:
legal.termslegal.lgpdemail_templates.store_verification.subject|text|htmlemail_templates.motoboy_verification.subject|text|htmlemail_templates.password_reset.subject|text|htmlemail_templates.activation.subject|text|htmlemail_templates.subscription_reminder.subject|text|html
Responsavel (dono da loja):
POST /stores/:storeId/motoboysPOST /stores/:storeId/motoboys/:motoboyId/linkPOST /stores/:storeId/motoboys/:motoboyId/unlinkPOST /stores/:storeId/motoboys/:motoboyId/approvePOST /stores/:storeId/motoboys/:motoboyId/suspend
- Nenhum endpoint antigo foi removido.
- Campos novos sao opcionais.
orders.payment_statustem defaultPENDING.
Quando o motoboy envia CNH e Selfie, o sistema pode rodar uma verificação automática assistida (não é prova de identidade):
- Selfie deve ter exatamente 1 rosto
- Tenta detectar rosto na CNH e comparar
- Salva resultado em
motoboy_documents.metadata.face - Admin vê badge
Alta/Média/Baixa/Indisponívele revisa manualmente - Política de tentativas: configurável por ambiente (default 10 em 24h)
- Para reduzir falso negativo, rejeição automática ocorre após falhas consecutivas (default 2)
Detalhes completos em: docs/FACE_VERIFY.md
Configuração recomendada no .env.prod (API):
FACE_VERIFY_ENABLED=true
FACE_VERIFY_WORKER_URL=http://face-worker:8000
FACE_VERIFY_TIMEOUT_MS=90000
FACE_VERIFY_SCORE_MEDIUM=0.55
FACE_VERIFY_SCORE_HIGH=0.75
FACE_VERIFY_MAX_ATTEMPTS=10
FACE_VERIFY_COOLDOWN_HOURS=24
FACE_VERIFY_REJECT_AFTER_CONSECUTIVE=2
FACE_VERIFY_REJECT_APPROVED=false
FACE_VERIFY_JOB_ENABLED=true
FACE_VERIFY_JOB_INTERVAL_MS=30000curl http://localhost:4000/api/plans- Front envia
POST /api/auth/registercom dados do usuario (CPF/CNPJ), endereco com CEP e aceite de termos/LGPD. - API cria usuario (email nao verificado), gera slug unico, cria loja
open=false. - Envia e-mail de confirmacao e redireciona para
/verify-email. - Ao confirmar, o pagamento e criado e fica disponivel em
/payment/:id. - E-mail de pagamento pendente e enviado com o link/QR.
- Quando o MP aprova, o webhook confirma o pagamento, ativa a assinatura e abre a loja.
- E-mail de ativacao e enviado com links do admin e da vitrine.
Cadastro (UX):
- Termos/LGPD aparecem em modal no
/createe bloqueiam o envio se nao forem aceitos. - CEP consulta ViaCEP para preencher endereco.
Assinaturas:
- Job diario marca expiracao e envia avisos em D-3, D-1 e D-0.
- Renovacao ocorre pelo painel
/admin/renewalcom escolha de plano. - Pagamentos expirados/failed geram novo link ao renovar.
SEO basico:
robots.txtaponta para o sitemap.sitemap.xmlcom rotas publicas basicas (home, create, terms).
Trial:
- Periodo gratis configuravel via
site_settings(trial_days). - Loja ativa apos confirmacao de e-mail, sem cobrar durante o trial.
Vitrine (mobile):
- Header compacto com botao "Info" da loja.
- Sheet com endereco, WhatsApp, Instagram e horarios.
- Mapa estatico gratuito via OpenStreetMap.
Configurações (admin):
- Identidade visual da loja (logo, descrição, cores).
- Canais de pagamento (chave Pix) e e-mail de contato da loja.
Demo:
- Alias legado de vitrine em
/chamanoespeto/:storeSlugainda existe para compatibilidade, mas o dominio e marca oficiais sao Ja no Caminho. - Admin demo em
/admin/democom dados locais.
- Tela:
http://localhost:3000/superadmin - Autenticacao usa a tabela
platform_admins(nao usa mais variavel de ambiente). - Usuario seed inicial e criado em
schema.sqlerunMigrations. - Para ambiente real, altere a senha bootstrap logo apos a primeira subida.
flowchart TD
subgraph Cadastro_e_Pagamento
A[Cadastro /api/auth/register] --> B[Criar usuario]
B --> C[Gerar slug unico]
C --> D[Criar loja open=false]
D --> E[Assinatura PENDING]
E --> F[Gerar pagamento MP]
F --> G[Enviar email confirmacao]
G --> H[Confirmar e-mail /verify-email]
H --> I[Enviar email pagamento pendente]
I --> J[Redirect /payment/:id]
J --> K[Webhook MP aprovado]
K --> L[Confirmar pagamento]
L --> M[Assinatura ACTIVE + datas]
M --> N[Loja open=true]
N --> O[Enviar email de ativacao]
end
subgraph Pedido_e_Operacao
P[Cliente acessa loja online] --> Q[Abrir loja pelo slug]
Q --> R[Montar e revisar pedido]
R --> S[Enviar pedido]
S --> T[Validar loja]
T --> U[Validar itens e calcular total]
U --> V{Pedido valido?}
V -->|Rejeitado| R
V -->|Aprovado| W[Persistir pedido e itens]
W --> X[Expor pedido na fila]
X --> Y{Pedido criado por admin?}
Y -->|Nao| Z[Receber resumo + link]
Y -->|Sim| AA[Voltar para o cardapio]
end
Arquivos BPMN (layout legivel por lanes):
docs/bpmn/chama-no-espeto-signup.bpmndocs/bpmn/chama-no-espeto-orders.bpmn
Cadastro e planos:
- Criar conta com e-mail válido, confirmar e-mail, entrar no admin.
- Trial de 7 dias: não gera pagamento; expirando bloqueia loja e exige renovação.
- Renovar assinatura: só mostra planos pagos; gerar Pix/Cartão/Boleto conforme plano.
- Pagamento expirado/failed: gera novo pagamento (não reutiliza link vencido).
Vitrine / pedido:
- Buscar item por nome, filtrar por categoria e adicionar no carrinho.
- Produto com promoção: exibe original riscado + promo em verde (cardápio e carrinho).
- Mesa ocupada: impedir novo pedido e mostrar aviso.
- Pedido enviado: abre acompanhamento e salva últimos 3 pedidos no
localStorage.
Acompanhamento:
- Status em linha única (Recebido/Em preparo/Pronto/Pago).
- Exibe QR Pix apenas para cliente (cópia disponível).
- Voltar leva para a loja do slug correto.
Fila do churrasqueiro:
- Cards compactos, ordem de fila e tempo corrido.
- Promoção: mostrar preço original riscado + promo em verde.
- “Iniciar preparo” antes de “Marcar pronto”.
- Finalizados hoje com paginação e contagem.
Configurações:
- Atualizar logo/descrição/cores e validar persistência.
- Alterar chave Pix e e-mail de contato (limpar campo deve salvar vazio).
Fluxo recomendado de deploy:
git pull
./scripts/deploy-frontend.sh
./scripts/deploy-api.shO script deploy-frontend.sh atualiza automaticamente no .env.prod:
FRONTEND_BUILD_VERSIONFRONTEND_BUILD_GIT_SHAFRONTEND_BUILD_GIT_SHORT_SHAFRONTEND_BUILD_GIT_BRANCHFRONTEND_BUILD_TIME_ISO
Isso garante que o rodapé/console de versões mostrem exatamente a build em produção.
Postgres (producao):
- Defina
POSTGRES_VOLUME_NAMEem.env.prodpara fixar o volume e evitar "sumir o banco" ao mudar pasta/projeto. - O deploy via
scripts/compose-prod.shusadocker-compose.prod.ymle trata o volume do Postgres como external (nao e removido pordocker compose down -v).
A API usa TypeORM com pg e agora o pool de conexoes com o PostgreSQL e configuravel por ambiente. Isso evita que a API abra conexoes demais em pico, reaproveita conexoes existentes e coloca limite de tempo para consultas ou transacoes travadas.
Configuracao usada em producao:
{
"DB_POOL_MAX": "10",
"DB_POOL_IDLE_TIMEOUT_MS": "30000",
"DB_POOL_CONNECTION_TIMEOUT_MS": "5000",
"DB_STATEMENT_TIMEOUT_MS": "15000",
"DB_IDLE_IN_TRANSACTION_TIMEOUT_MS": "10000"
}O que cada variavel faz:
DB_POOL_MAX: maximo de conexoes abertas por cada container da API. O padrao do projeto e10.DB_POOL_IDLE_TIMEOUT_MS: tempo para fechar conexao ociosa no pool.30000= 30 segundos.DB_POOL_CONNECTION_TIMEOUT_MS: tempo maximo esperando uma conexao livre antes de falhar.5000= 5 segundos.DB_STATEMENT_TIMEOUT_MS: tempo maximo de uma query no PostgreSQL.15000= 15 segundos.DB_IDLE_IN_TRANSACTION_TIMEOUT_MS: tempo maximo de uma transacao parada/aberta sem executar nada.10000= 10 segundos.
Por que isso melhora a performance percebida:
- Reduz criacao/destruicao desnecessaria de conexoes.
- Evita que picos de acesso derrubem o banco abrindo conexoes sem limite.
- Faz queries travadas falharem mais rapido, liberando recurso para os proximos pedidos.
- Ajuda a API a responder de forma mais previsivel quando o volume de usuarios aumenta.
Importante: isso melhora estabilidade e tempo de resposta, mas nao aumenta a capacidade do banco sozinho. Se o trafego crescer muito, ainda precisa monitorar CPU, memoria, I/O, queries lentas e tamanho do Postgres.
DB_POOL_MAX vale por instancia/container. Entao o total reservado pela API e:
total_api_connections = quantidade_de_apis * DB_POOL_MAXCom Postgres em max_connections = 100, reserve conexoes para pgAdmin, scripts, backups, migrations e acesso manual. Uma regra segura:
quantidade_de_apis * DB_POOL_MAX <= max_connections - 20Exemplos:
| Instancias de API | DB_POOL_MAX | Conexoes maximas da API |
|---|---|---|
| 1 | 10 | 10 |
| 2 | 10 | 20 |
| 4 | 10 | 40 |
| 4 | 20 | 80 |
Para uma EC2 pequena e um unico Postgres, comece com DB_POOL_MAX=10. Se criar mais containers da API, mantenha 10 ou reduza por instancia antes de aumentar. So aumente quando o banco tiver folga real e houver evidencia de fila esperando conexao.
Em producao, esses valores ficam no AWS SSM Parameter Store dentro do JSON do parametro do ambiente, por exemplo /chamanoespeto/prod ou /janocaminho/prod.
Exemplo para conferir o valor atual:
aws ssm get-parameter \
--name /chamanoespeto/prod \
--with-decryption \
--region us-east-2 \
--query Parameter.Value \
--output textDepois de alterar o SSM, recrie a API para ela reler as variaveis:
git pull
./scripts/deploy-api.shO script deploy-api.sh constroi o codigo local que esta no EC2 depois do git pull, recria o container da API com --force-recreate e mostra o commit local que esta sendo implantado.
Conferir se as variaveis chegaram dentro do container:
docker exec janocaminho-backend sh -lc 'node -e "console.log({
DB_POOL_MAX: process.env.DB_POOL_MAX,
DB_POOL_IDLE_TIMEOUT_MS: process.env.DB_POOL_IDLE_TIMEOUT_MS,
DB_POOL_CONNECTION_TIMEOUT_MS: process.env.DB_POOL_CONNECTION_TIMEOUT_MS,
DB_STATEMENT_TIMEOUT_MS: process.env.DB_STATEMENT_TIMEOUT_MS,
DB_IDLE_IN_TRANSACTION_TIMEOUT_MS: process.env.DB_IDLE_IN_TRANSACTION_TIMEOUT_MS
})"'Conferir conexoes no Postgres:
docker exec janocaminho-postgres psql -U postgres -d espetinho -c "show max_connections;"
docker exec janocaminho-postgres psql -U postgres -d espetinho -c "select state, count(*) from pg_stat_activity group by state order by state;"Limpeza segura de disco (quando der ENOSPC):
./scripts/clean-safe.sh
# opcional (também limpa logs do systemd):
./scripts/clean-safe.sh --with-logsPreparar segredos (opcional/recomendado):
cp .env.prod.secrets.example .env.prod.secrets
# edite os valores reaisBackup:
sh scripts/pg-backup.shBackup com rotacao (recomendado em producao com pouco disco):
sh scripts/pg-backup-rotate.shBackup de configuracao/runtime (envs, chaves e export opcional do SSM):
sh scripts/backup-config.shBackup de configuracao a cada 15 dias:
MIN_INTERVAL_HOURS=360 sh scripts/backup-config.shBackup de configuracao para bucket privado, incluindo export dos parametros SSM referenciados no deploy:
CONFIG_BACKUP_S3_BUCKET=jnc-config-backups-prod-222984221398 \
CONFIG_BACKUP_S3_PREFIX=config/runtime \
CONFIG_BACKUP_SSM_EXPORT_MODE=required \
sh scripts/backup-config.shOpcional: incluir parametros extras do SSM que nao estejam apontados nos arquivos .env*:
CONFIG_BACKUP_EXTRA_SSM_PARAMETERS="/chamanoespeto/prod /chamanoespeto/extra" \
sh scripts/backup-config.shExemplo de cron (executa a cada 4h, faz dump quando vencida a janela e remove o anterior):
sudo crontab -e
# adicionar:
# 0 */4 * * * BACKUP_DIR=/var/backups/janocaminho MIN_INTERVAL_HOURS=4 KEEP_LATEST=1 sh /caminho/para/repo/scripts/pg-backup-rotate.sh >> /var/log/pg-backup.log 2>&1Verificar crontab e ultimos backups:
sudo crontab -l
ls -lah /var/backups/janocaminhoExemplo de cron para backup privado de configuracao:
# 15 2 * * * BACKUP_DIR=/home/ec2-user/backups/chamanoespeto/config KEEP_DAYS=30 MIN_INTERVAL_HOURS=360 CONFIG_BACKUP_S3_BUCKET=jnc-config-backups-prod-222984221398 CONFIG_BACKUP_S3_PREFIX=config/runtime CONFIG_BACKUP_SSM_EXPORT_MODE=required sh /home/ec2-user/EdEspetoHub/scripts/backup-config.sh >> /home/ec2-user/config-backup.log 2>&1Aplicar lifecycle no bucket privado de configuracao:
BACKUP_S3_LIFECYCLE_CONFIG=scripts/s3-config-backup-lifecycle.json \
sh scripts/apply-s3-backup-lifecycle.sh jnc-config-backups-prod-222984221398- Verificacao rapida:
docker ps
docker exec -it janocaminho-backend env | grep -E '^(MP_|SMTP_|EMAIL_FROM|APP_BASE_URL)'
curl -s https://www.janocaminho.com.br/api/docs.json | head -n 1- Teste de e-mail (reset de senha):
curl -X POST https://www.janocaminho.com.br/api/auth/forgot-password \
-H "Content-Type: application/json" \
-d '{"email":"seu-email@gmail.com"}'- Webhook MP (checagem rapida):
docker logs janocaminho-backend --tail 200 | grep -i "mercadopago\\|webhook"Padrão adotado no projeto: SemVer (MAJOR.MINOR.PATCH).
patch: correções sem quebra (ex:1.2.3->1.2.4)minor: recurso novo compatível (ex:1.2.3->1.3.0)major: mudança com quebra (ex:1.2.3->2.0.0)
Comandos:
npm --prefix frontend run release:patch
npm --prefix frontend run release:minor
npm --prefix frontend run release:majorCada release:
- incrementa
frontend/package.json - regenera
src/generated/buildInfo.ts - cria commit
chore(release): vX.Y.Z - cria tag git
vX.Y.Z - faz push + push das tags
Em todo deploy comum, o buildId também muda (timestamp + hash), mesmo sem mudar vX.Y.Z.
O script scripts/test-flow.sh cria usuario, confirma e-mail e valida login admin.
Requer jq instalado e o token de confirmacao copiado do e-mail.
sh scripts/test-flow.shNginx como reverse proxy:
- Use
docs/nginx/janocaminho.confou o arquivo equivalente atual do ambiente /->http://127.0.0.1:8080/api/->http://127.0.0.1:4000/api//uploads/->http://127.0.0.1:4000/uploads/client_max_body_size 20m
HTTPS:
sudo certbot --nginx -d janocaminho.com.br -d www.janocaminho.com.brMercado Pago (producao):
- A plataforma usa duas camadas de Mercado Pago:
- Conta da plataforma: usada para cobrança de planos, renovações e destaques.
- Conta do lojista via OAuth: opcional, usada para cobrar pedidos da própria loja em Pix, crédito e débito.
- Se a loja não conectar o Mercado Pago dela, o checkout continua no modo convencional: o pedido registra a forma de pagamento escolhida, mas a cobrança é feita fora do sistema.
- Se a loja conectar, novos pedidos com
pix,creditooudebitocriam registro emorder_paymentse tentam gerar cobrança no Mercado Pago do lojista.
Variáveis necessárias:
APP_BASE_URL=https://www.janocaminho.com.br
MP_ACCESS_TOKEN=<access-token-da-plataforma>
MP_PUBLIC_KEY=<public-key-da-plataforma>
MP_WEBHOOK_URL=https://www.janocaminho.com.br/api/webhooks/mercadopago
MP_WEBHOOK_SECRET=<secret-do-webhook>
MP_API_BASE_URL=https://api.mercadopago.com
MP_DEBUG=false
# OAuth para lojistas
MP_CLIENT_ID=<client-id-da-aplicacao-mercado-pago>
MP_CLIENT_SECRET=<client-secret-da-aplicacao-mercado-pago>
MP_OAUTH_REDIRECT_URL=https://www.janocaminho.com.br/api/payment-accounts/mercadopago/callback
MP_OAUTH_ENCRYPTION_KEY=<hex-64-caracteres>Em producao, preferimos guardar esses valores no AWS SSM Parameter Store (SecureString) dentro do JSON do parametro, por exemplo /janocaminho/prod ou /chamanoespeto/prod, conforme o ambiente. Para conferir o SSM:
aws ssm get-parameter \
--name /janocaminho/prod \
--with-decryption \
--region us-east-2Para gerar uma chave de criptografia OAuth localmente:
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"Configuracao no painel do Mercado Pago:
- Acesse a aplicacao no painel de desenvolvedor do Mercado Pago.
- Em produto integrado, use pagamentos online com Checkout Transparente/API Pagamentos conforme a integracao atual.
- Em configuracao avancada/OAuth, cadastre exatamente:
https://www.janocaminho.com.br/api/payment-accounts/mercadopago/callback
- Se houver opcao de PKCE para OAuth, manter desativado enquanto o backend usar o fluxo atual com
client_secret. - Permissoes esperadas:
read,writeeoffline_access. - Configure o webhook de producao para:
https://www.janocaminho.com.br/api/webhooks/mercadopago
Rotas implementadas:
GET /api/stores/:storeId/payment-accounts/mercadopagoverifica status da conexao da loja.POST /api/stores/:storeId/payment-accounts/mercadopago/connectgera a URL de autorizacao OAuth.GET /api/payment-accounts/mercadopago/callbackrecebe ocode, troca por token e salva a conta conectada.DELETE /api/stores/:storeId/payment-accounts/mercadopagodesconecta e volta a loja para o modo convencional.
Tabelas usadas:
store_payment_accounts: guarda a conexao OAuth da loja, com tokens criptografados.order_payments: guarda as tentativas/cobrancas online criadas para pedidos.
Ponto importante de inicializacao da API:
- O SSM precisa ser carregado antes de qualquer import que leia
config/env. - Se
MP_CLIENT_IDeMP_CLIENT_SECRETestiverem no SSM, mas o painel mostrar OAuth nao configurado, recrie o container da API e confira se a imagem contem o carregamento correto.
Verificacao rapida dentro do container:
docker exec janocaminho-backend node - <<'NODE'
(async () => {
const { loadSsmEnv } = require('./dist/config/ssm');
await loadSsmEnv();
const { env } = require('./dist/config/env');
console.log({
appUrl: env.appUrl,
clientId: env.mercadoPago.clientId,
redirect: env.mercadoPago.oauthRedirectUrl,
hasSecret: Boolean(env.mercadoPago.clientSecret),
hasEncryptionKey: Boolean(env.mercadoPago.encryptionKey),
webhookUrl: env.mercadoPago.webhookUrl,
});
})();
NODETeste da conexao no navegador, logado como lojista/admin:
const s = JSON.parse(localStorage.getItem('adminSession') || '{}');
fetch(`/api/stores/${s.store.id}/payment-accounts/mercadopago`, {
headers: { Authorization: `Bearer ${s.token}` },
})
.then((r) => r.json())
.then(console.log);Resultado esperado quando a aplicacao OAuth esta configurada:
{
"connected": false,
"status": "DISCONNECTED",
"oauthConfigured": true
}Depois que o lojista conectar, esperado:
{
"connected": true,
"status": "CONNECTED",
"oauthConfigured": true
}Teste de pedido online:
- Conecte a loja em Financeiro > Pagamentos > Mercado Pago.
- Faça um pedido novo na loja escolhendo Pix, crédito ou débito.
- Para Pix, a tela de sucesso deve exibir QR/link de pagamento.
- Confira no banco:
docker exec -it janocaminho-postgres psql -U postgres -d espetinho -c \
"select id, store_id, payment_status, provider, provider_id, created_at from order_payments order by created_at desc limit 5;"Para voltar ao modo convencional:
- No painel do lojista, acesse Financeiro > Pagamentos.
- Clique em Desconectar e voltar ao modo convencional.
- A loja deixa de criar cobrancas online; Pix, crédito e débito voltam a ser apenas informacao no pedido.
Nginx/HTTPS:
- O webhook e o OAuth exigem HTTPS valido.
janocaminho.com.brewww.janocaminho.com.brdevem responder corretamente.- Em producao, mantenha redirect HTTP para HTTPS e, preferencialmente, canonical para
https://www.janocaminho.com.br. - O callback deve chegar no Express. Este teste deve responder
302, nao404:
curl -I https://www.janocaminho.com.br/api/payment-accounts/mercadopago/callbackDeploy apos mudar SSM ou imagem:
cd ~/EdEspetoHub
docker compose -f docker-compose.yml -f docker-compose.prod.yml --env-file .env.prod pull api frontend
docker compose -f docker-compose.yml -f docker-compose.prod.yml --env-file .env.prod up -d --no-deps --force-recreate api frontend
docker logs --tail 60 janocaminho-backend- SMTP (exemplo Zoho):
SMTP_HOST=smtp.zoho.com
SMTP_PORT=587
SMTP_USER=contato@janocaminho.com.br
SMTP_PASS=<senha-ou-app-password>
SMTP_SECURE=false
EMAIL_FROM=Jano Caminho <contato@janocaminho.com.br>
Se configurar assinatura secreta no painel, defina MP_WEBHOOK_SECRET na API.
ngrok cria um túnel público temporário para seu servidor local. Isso permite que o Mercado Pago envie o webhook para sua máquina local durante testes. Sempre que você reiniciar o ngrok, a URL pública muda (a menos que use um plano pago com URL fixa).
cp backend/.env.example backend/.env
docker start janocaminho-postgres
cd backend && npm run devPara o front:
cd frontend && npm run dev-
Crie um banco chamado
espetinhoe aplique o schema inicial (opcional, a API também cria tabelas on-demand):psql -h localhost -U postgres -d espetinho -f backend/schema.sql
-
Variáveis de conexão usadas pelo
pg(padrões:postgres/postgres):export PGHOST=localhost export PGUSER=postgres export PGPASSWORD=postgres export PGDATABASE=espetinho
cd backend
npm install
npm run dev # desenvolvimento com reload
npm run build # gera dist/
npm start # roda dist/app.jsA API sobe em http://localhost:4000 e expõe a documentação Swagger em http://localhost:4000/api/docs. Durante a inicialização ela valida a conexão com o PostgreSQL usando as variáveis de ambiente listadas acima.
Endpoints principais:
POST /api/auth/register— cria usuário, loja e retorna token JWT.POST /api/auth/admin-login— autenticação via slug + senha.POST /api/auth/login— autenticação via e-mail + senha.GET /api/stores/:slug,PUT /api/stores/:id,PUT /api/stores/:id/status— gerenciamento de loja.GET /api/stores/:storeId/products,POST /api/stores/:storeId/products— catálogo (admin).GET /api/stores/slug/:slug/products— catálogo público por loja (vitrine).GET /api/stores/:storeId/orders,POST /api/stores/:storeId/orders— pedidos e fila.GET /api/orders/:orderId/public— acompanhamento publico do pedido (status + dados da loja).GET /api/v2/orders/:orderId/tracking— tracking v2 com ETA total + breakdown (prep/fila/rota).
O sistema diferencia:
- travelMinutes: somente tempo de deslocamento (rota).
- totalMinutes: preparo + fila + deslocamento + buffer.
Para ativar no endpoint público atual (/api/orders/:orderId/public) sem quebrar payloads, use:
ENABLE_ORDER_ETA_V2=true
Quando habilitado, o /api/orders/:orderId/public passa a incluir o campo opcional eta.
O tracking completo está sempre disponível via /api/v2/orders/:orderId/tracking.
Variáveis de configuração (backend):
ENABLE_ORDER_ETA_V2=true
ENABLE_FREE_GEOCODING_FALLBACK=false
DEFAULT_PREP_MINUTES=15
DEFAULT_PREP_PER_ITEM_MINUTES=2
DEFAULT_QUEUE_MINUTES_PER_ORDER=5
DEFAULT_QUEUE_BUFFER_MINUTES=0
DEFAULT_ETA_BUFFER_MINUTES=3
Exemplo (v1) — inalterado:
{
"id": "order-id",
"status": "pending",
"type": "delivery",
"total": 42.5,
"queuePosition": 1,
"queueSize": 3,
"items": []
}Exemplo (v1 + flag ENABLE_ORDER_ETA_V2):
{
"id": "order-id",
"status": "pending",
"type": "delivery",
"total": 42.5,
"queuePosition": 1,
"queueSize": 3,
"items": [],
"eta": {
"totalMinutes": 28,
"windowMin": 22,
"windowMax": 34,
"breakdown": {
"prepMinutes": 17,
"queueMinutes": 5,
"travelMinutes": 3,
"bufferMinutes": 3
},
"travel": {
"distanceKm": 1.4,
"travelMinutes": 3
},
"confidence": "high",
"algoVersion": "eta_v2.0"
}
}Exemplo (v2 tracking):
{
"id": "order-id",
"status": "pending",
"type": "delivery",
"createdAt": "2026-01-28T12:00:00.000Z",
"queuePosition": 1,
"queueSize": 3,
"timeline": [
{ "status": "pending", "at": "2026-01-28T12:00:00.000Z" }
],
"eta": {
"totalMinutes": 28,
"windowMin": 22,
"windowMax": 34,
"prepMinutes": 17,
"queueMinutes": 5,
"travelMinutes": 3,
"bufferMinutes": 3,
"confidence": "high",
"algoVersion": "eta_v2.0"
},
"travel": {
"distanceKm": 1.4,
"travelMinutes": 3
}
}cd frontend
npm install
npm run devCrie um arquivo .env (ou use .env.production) na pasta frontend/ com o endpoint da API:
VITE_API_BASE_URL=http://localhost:4000/apiSSM local (setup rapido):
- Veja
docs/ssm-local.md
Com a API em execução, a loja fica acessível em:
- Vitrine (cliente):
http://localhost:3000/<slug>(ex:http://localhost:3000/lojadoedmilson) - Admin pedidos:
http://localhost:3000/admin/orders - Fila do churrasqueiro:
http://localhost:3000/admin/queue - Admin entregadores:
http://localhost:3000/admin/motoboys - Motoboy cadastro:
http://localhost:3000/motoboy/register - Motoboy login:
http://localhost:3000/motoboy/login - Motoboy pedidos disponíveis:
http://localhost:3000/motoboy/available - Motoboy entrega atual:
http://localhost:3000/motoboy/current - Motoboy histórico:
http://localhost:3000/motoboy/history
- Host:
localhost - Porta:
5432 - Usuário:
postgres - Senha: a que você definiu
Antes de subir, copie o arquivo de ambiente do Docker:
cp backend/.env.docker.example backend/.env.dockerdocker compose up --buildServiços expostos:
- Front-end: http://localhost:8080
- API: http://localhost:4000 (Swagger em
/api/docs) - PostgreSQL: porta 5432 (volume
postgres-data) - pgAdmin: http://localhost:5050
Rotas úteis no front (Docker):
- Vitrine (cliente):
http://localhost:8080/<slug> - Admin pedidos:
http://localhost:8080/admin/orders - Fila de produção:
http://localhost:8080/admin/queue - Admin entregadores:
http://localhost:8080/admin/motoboys - Motoboy cadastro:
http://localhost:8080/motoboy/register - Motoboy login:
http://localhost:8080/motoboy/login - Motoboy pedidos disponíveis:
http://localhost:8080/motoboy/available - Motoboy entrega atual:
http://localhost:8080/motoboy/current - Motoboy histórico:
http://localhost:8080/motoboy/history
Crie um arquivo .env.prod com FRONTEND_PORT=80 e suba assim:
docker compose --env-file .env.prod up --build -dNo ambiente atual, o deploy nao depende de subir imagem no registry. O fluxo e entrar no EC2, atualizar o codigo pelo Git e recriar os containers necessarios:
git pull
./scripts/deploy-frontend.sh
./scripts/deploy-api.shO deploy-api.sh usa o codigo local recem-atualizado, executa docker compose up -d --build --no-deps --force-recreate api e imprime o commit que esta sendo implantado. Isso ajuda a confirmar que a API subiu com o codigo novo.
Em instâncias pequenas (ex: t3.small) o docker compose up --build pode travar o SSH por falta de CPU/RAM/créditos.
O caminho novo mantém o deploy antigo intacto, mas adiciona uma trilha paralela:
git push -> GitHub Actions builda -> GHCR publica -> EC2 só faz pull + up -d
Fluxo:
- Verifique se o workflow
.github/workflows/publish-ghcr.ymlrodou após ogit push. - No servidor, mantenha
.env.prodcom:
IMAGE_REGISTRY=ghcr.ioIMAGE_NAMESPACE=edmilsonfernandesIMAGE_TAG=mainou uma SHA curta
- Faça login no GHCR no servidor se o repositório for privado:
docker login ghcr.ioOu configure em .env.prod.secrets:
GHCR_USERNAME=<seu-usuario-github>
GHCR_TOKEN=<pat-com-read-packages>Recomendado em produção: guardar GHCR_USERNAME e GHCR_TOKEN dentro do mesmo JSON SecureString já usado no SSM, por exemplo /chamanoespeto/prod, e apontar no .env.prod:
AWS_REGION=us-east-2
SSM_PARAMETER_NAME=/chamanoespeto/prodSe o seu backend/.env.docker já usa esse SSM_PARAMETER_NAME, o script de release agora também reaproveita esse arquivo e você não precisa duplicar a configuração no .env.prod.
- Deploy por release específica ou pela
mainmais recente:
sh scripts/deploy-release-api.sh
sh scripts/deploy-release-frontend.sh
# ou travando em uma release específica
sh scripts/deploy-release-api.sh 3a254581
sh scripts/deploy-release-frontend.sh 3a254581Ou tudo em um passo:
sh scripts/deploy-release.sh
# ou travando tag e serviços
sh scripts/deploy-release.sh 3a254581 api frontend face-workerFallback:
- Se esse fluxo novo falhar, o deploy antigo continua disponível:
./scripts/deploy-api.sh
./scripts/deploy-frontend.shPara produção, o fluxo mais seguro agora é:
git push -> Publish Docker Images (GHCR) -> Approve and deploy
Workflow:
.github/workflows/deploy-production.yml- Nome no GitHub Actions:
Deploy to EC2 (Approval)
Como funciona:
- Você faz
git pushnamain. - O workflow
Publish Docker Images (GHCR)publica apenas as imagens dos serviços alterados. - Quando esse workflow termina com sucesso, nasce um run de
Deploy to EC2 (Approval). - Esse run fica parado no environment
production. - No GitHub, você clica em
Review deployments->Approve and deploy. - O workflow entra no EC2, faz
git pull --ff-onlye rodascripts/./deploy-release.sh <sha-curta> <serviços>. - Em modo
auto, ele só deploya os serviços cuja imagem daquela SHA realmente existe.
Setup mínimo no GitHub:
- Crie o environment
production. - Configure pelo menos 1 reviewer obrigatório nesse environment.
- Configure os secrets do environment:
PROD_SSH_HOSTPROD_SSH_USERPROD_SSH_KEYPROD_SSH_PORT(opcional, default22)
Observações:
- O deploy automático por approval usa a SHA curta do commit que acabou de publicar a imagem.
- Se o commit mexer só em
frontend/, ele não precisa deployarapi. face-workersó entra quando a imagem dele também tiver sido publicada para aquela SHA.- Se entrar um commit novo antes da aprovação, o deploy pendente antigo é cancelado e fica valendo o mais recente da
main. - Se quiser redeploy manual ou rollback, também pode usar
Run workflowemDeploy to EC2 (Approval)e informar:image_tag(mainou SHA curta)deploy_scope(auto,frontend,api,face-worker,api+frontend,all)
- Teste manual rápido no EC2:
cd ~/EdEspetoHub
git pullAdicionar GHCR_USERNAME e GHCR_TOKEN dentro do JSON do parâmetro SSM já existente:
aws ssm get-parameter \
--name "/chamanoespeto/prod" \
--with-decryption \
--region us-east-2 \
--query 'Parameter.Value' \
--output textPermissões mínimas para a role/usuário AWS usado no EC2:
ssm:GetParameterkms:Decrypt
Edite o JSON retornado e inclua:
{
"GHCR_USERNAME": "EdmilsonFernandes",
"GHCR_TOKEN": "<pat-classic-com-read-packages>"
}Depois grave o JSON completo de volta:
aws ssm put-parameter \
--name "/chamanoespeto/prod" \
--value '<JSON_COMPLETO_ATUALIZADO>' \
--type SecureString \
--overwrite \
--region us-east-2Se ainda não estiver definido em backend/.env.docker, aponte no .env.prod:
cat >> .env.prod <<'EOF'
AWS_REGION=us-east-2
SSM_PARAMETER_NAME=/chamanoespeto/prod
EOFTestar acesso e deploy:
aws ssm get-parameter --name /chamanoespeto/prod --with-decryption --region us-east-2 --query 'Parameter.Value' --output text
docker pull ghcr.io/edmilsonfernandes/janocaminho-backend:main
docker pull ghcr.io/edmilsonfernandes/janocaminho-frontend:main
docker pull ghcr.io/edmilsonfernandes/janocaminho-face-worker:main
sh scripts/deploy-release-api.sh
sh scripts/deploy-release-frontend.sh
docker psSe quiser, .env.prod.secrets continua funcionando como fallback local.
Execução local (porta 8080):
sh scripts/compose-dev.shRebuild local de um serviço específico:
sh scripts/compose-dev-backend.sh
sh scripts/compose-dev-frontend.sh
sh scripts/compose-dev-apis.shExecução produção (porta 80):
sh scripts/compose-prod.shExecução produção opcional (pull-only, sem build):
sh scripts/compose-prod-pull.shDeploy por release pronta do GHCR:
sh scripts/deploy-release-api.sh
sh scripts/deploy-release-frontend.sh
# opcional: fixar uma release
sh scripts/deploy-release-api.sh <sha-curta>
sh scripts/deploy-release-frontend.sh <sha-curta>Deploy direto por serviço (EC2):
./scripts/deploy-frontend.sh
./scripts/deploy-api.shLimpeza segura de disco:
./scripts/clean-safe.shCredenciais padrão do pgAdmin (pode sobrescrever via variáveis de ambiente ao subir): admindatony@datony.com / Datony20025#!.
Para bancos existentes, a API aplica a migração automaticamente ao iniciar
(opening_hours e social_links em store_settings). Basta reiniciar a API.
Se quiser aplicar manualmente, use:
ALTER TABLE store_settings
ADD COLUMN IF NOT EXISTS opening_hours JSONB DEFAULT '[]';Com Docker:
docker exec -i janocaminho-postgres psql -U postgres -d espetinho <<'SQL'
ALTER TABLE store_settings
ADD COLUMN IF NOT EXISTS opening_hours JSONB DEFAULT '[]';
SQLPara um banco vazio, continue usando o backend/schema.sql (já contém a coluna nova).
-
Front-end (usa
frontend/Dockerfilecom nginx):cd frontend docker build -t espetinho-app . docker run --rm -p 80:80 espetinho-app
-
API (usa
backend/Dockerfile):cd backend docker build -t espetinho-api . docker run --rm -p 4000:4000 \ -e PGHOST=<host> -e PGUSER=<usuario> -e PGPASSWORD=<senha> -e PGDATABASE=<db> \ espetinho-api
-
PostgreSQL + schema
docker run --name espetinho-db -e POSTGRES_PASSWORD=postgres -e POSTGRES_DB=espetinho -p 5432:5432 -d postgres:16 docker exec -i espetinho-db psql -U postgres -d espetinho < backend/schema.sql
-
pgAdmin
docker build -f Dockerfile.pgadmin -t espetinho-pgadmin . docker run --rm -p 5050:80 \ -e PGADMIN_DEFAULT_EMAIL=admindatony@datony.com \ -e PGADMIN_DEFAULT_PASSWORD=Datony20025#! \ -v pgadmin-data:/var/lib/pgadmin \ espetinho-pgadmin
Um diagrama BPMN resumindo o fluxo do "Ja no Caminho" esta disponivel em docs/bpmn/chama-no-espeto.bpmn. O arquivo segue o padrao BPMN 2.0 (pode ser aberto no Camunda Modeler, Draw.io ou semelhantes) e destaca:
- Jornada do cliente na loja pública (montagem e envio do pedido com Pix).
- Validação e criação do pedido pela API.
- Operação diária do painel interno (cadastro, catálogo, publicação da loja e fila do churrasqueiro).
-
Ao publicar em produção (ex.: EC2), exponha apenas as portas necessárias e substitua credenciais padrão.
-
O diretório
.vscode/traz recomendações de formatação (2 espaços, LF, remoção de espaços em branco e nova linha final), aplicadas automaticamente se o Prettier estiver instalado. Vitrine e painel com Docker: -
Vitrine (cliente):
http://localhost:8080/<slug>(ex:http://localhost:8080/lojadoedmilson) -
Admin pedidos:
http://localhost:8080/admin/orders -
Fila do churrasqueiro:
http://localhost:8080/admin/queue