e-li.nps (Go + HTMX)

Widget NPS embutível via 1 arquivo JS + API em Go.

Requisitos

Variáveis de ambiente

Cache do widget (e-li.nps.js)

O servidor controla o cache de /static/e-li.nps.js via ETag.

Isso evita problemas de clientes com JS antigo em cache após mudanças.

Arquivo .env

O servidor carrega automaticamente um arquivo .env na raiz do projeto (se existir) usando godotenv. Isso facilita rodar localmente sem exportar variáveis manualmente.

Exemplo de .env:

DATABASE_URL='postgres://postgres:postgres@localhost:5432/gonps?sslmode=disable'
ADDR=':8080'

Como rodar

  1. Suba um Postgres (exemplo via Docker):
docker run --rm -e POSTGRES_PASSWORD=postgres -e POSTGRES_DB=gonps -p 5432:5432 postgres:16
  1. Rode o server:
go run ./cmd/server

Rodar com Docker

Este repositório inclui:

Para subir tudo:

docker compose up --build

Dica: esse comando roda em foreground. Ao pressionar Ctrl+C, o Docker encerra os containers.

Para manter rodando em background (recomendado em servidor):

docker compose up -d --build

Para forçar rebuild da imagem (mesmo sem mudanças detectadas):

docker compose build --no-cache && docker compose up

Para parar a aplicação:

docker compose down

Para apenas parar sem remover (mantém o container):

docker compose stop

Para iniciar novamente após stop:

docker compose start

Importante:

  • O Postgres é externo.
  • O arquivo .env é obrigatório e deve ser passado como volume para /app/.env.
  • O servidor carrega esse arquivo automaticamente via godotenv ao iniciar.

Exemplo (compose): ./.env:/app/.env:ro

Postgres no host (host.docker.internal)

Se o seu Postgres estiver rodando no host (fora do container) e você quiser que o container acesse via host.docker.internal, use no .env:

DATABASE_URL='postgres://usuario:senha@host.docker.internal:5432/seu_banco?sslmode=disable'

No Linux, o docker-compose.yml já inclui extra_hosts com host-gateway para esse hostname funcionar.

Publicar com Caddy (reverse proxy)

Este repositório inclui um exemplo de Caddyfile para publicar o serviço em:

Pré-requisitos

IP real do usuário

O Caddy repassa o IP do cliente via X-Forwarded-For e X-Real-IP. O servidor Go já usa middleware.RealIP (chi), então o IP real chega corretamente e é gravado em ip_real.

Check do IP real (direto / Docker / Caddy)

O painel tem um endpoint de debug que mostra o IP que a aplicação está enxergando e os headers recebidos:

Passo a passo:

  1. Faça login no painel em /painel.
  2. Acesse /painel/debug/ip.

O JSON retornado inclui:

Interpretação esperada:

Depois acesse:

Painel:

Healthcheck:

curl -i http://localhost:8080/healthz

Incluir o widget em outra aplicação

Tipagem TypeScript (opcional)

Se você quiser ter autocomplete e validação de tipos no seu projeto (TS), pode declarar a interface abaixo:

declare global {
  interface Window {
    ELiNPS: {
      init: (opts: ELiNPSInitOptions) => Promise<void> | void;
    };
  }
}

export type ELiNPSInitOptions = {
  // apiBase (opcional)
  // Base da API do e-li.nps.
  // Se o widget estiver sendo servido pelo mesmo host, pode deixar vazio.
  apiBase?: string;

  // cooldownHours (opcional)
  // Tempo (em horas) de cooldown visual no navegador.
  cooldownHours?: number;

  // data_minima_abertura (opcional)
  // Bloqueia a abertura do modal antes de uma data.
  // Formato ISO (data): YYYY-MM-DD (ex.: "2026-01-01").
  data_minima_abertura?: string;

  // produto_nome (obrigatório)
  produto_nome: string;

  // inquilino_codigo (obrigatório)
  inquilino_codigo: string;

  // inquilino_nome (obrigatório)
  inquilino_nome: string;

  // usuario_codigo (obrigatório)
  usuario_codigo: string;

  // usuario_nome (obrigatório)
  usuario_nome: string;

  // usuario_telefone (opcional)
  usuario_telefone?: string;

  // usuario_email (opcional)
  usuario_email?: string;
};
<!-- Carrega o widget (arquivo único) -->
<script src="http://localhost:8080/static/e-li.nps.js"></script>

<script>
  window.ELiNPS.init({
    // apiBase (opcional)
    // Base da API do e-li.nps.
    // - Se o widget estiver sendo servido pelo mesmo host, pode deixar vazio.
    // - Se a API estiver em outro host, informe a URL completa.
    // Ex.: "https://sua-api.exemplo.com".
    apiBase: 'http://localhost:8080',

    // cooldownHours (opcional)
    // Tempo (em horas) de cooldown visual no navegador para evitar o modal
    // reaparecer em sequência.
    // Default: 24.
    cooldownHours: 24,

    // data_minima_abertura (opcional)
    // Bloqueia a abertura do modal antes de uma data.
    // Formato ISO (data): YYYY-MM-DD (ex.: "2026-01-01").
    // Ex.: data_minima_abertura: '2026-01-01',
    data_minima_abertura: '',

    // produto_nome (obrigatório)
    // Nome livre do produto (é exibido ao usuário exatamente como informado).
    // Exemplos: "e-licencie.gov", "Cachaça & Churras".
    // Importante: o backend normaliza apenas para montar nome de tabela/rotas.
    produto_nome: 'e-licencie.gov',

    // inquilino_codigo (obrigatório)
    // Código do cliente/tenant (usado nas regras de exibição e no banco).
    inquilino_codigo: 'acme',

    // inquilino_nome (obrigatório)
    // Nome do cliente/tenant (exibição / auditoria).
    inquilino_nome: 'ACME LTDA',

    // usuario_codigo (obrigatório)
    // Identificador do usuário.
    // Importante: é a chave principal para as regras de exibição.
    usuario_codigo: 'u-123',

    // usuario_nome (obrigatório)
    // Nome do usuário (exibição / auditoria).
    usuario_nome: 'Maria',

    // usuario_telefone (opcional)
    // Telefone do usuário (auditoria). Pode ser vazio.
    usuario_telefone: '+55 11 99999-9999',

    // usuario_email (opcional)
    // Email do usuário. É opcional: o controle de exibição é por
    // (produto + inquilino_codigo + usuario_codigo).
    usuario_email: 'maria@acme.com',
  });
</script>

Endpoints

POST /api/e-li.nps/pedido

curl -sS -X POST http://localhost:8080/api/e-li.nps/pedido \
  -H 'Content-Type: application/json' \
  -d '{
    "produto_nome":"e-licencie.gov",
    "inquilino_codigo":"acme",
    "inquilino_nome":"ACME",
    "usuario_codigo":"u-123",
    "usuario_nome":"Maria",
    "usuario_telefone":"+55...",
    "usuario_email":"maria@acme.com"
  }'

GET /e-li.nps/{produto}/{id}/form

Abre o formulário (HTML) para responder/editar.

PATCH /api/e-li.nps/{produto}/{id}

curl -sS -X PATCH http://localhost:8080/api/e-li.nps/elicencie_gov/<id> \
  -H 'Content-Type: application/json' \
  -d '{"nota":10}'

Finalizar:

curl -sS -X PATCH http://localhost:8080/api/e-li.nps/elicencie_gov/<id> \
  -H 'Content-Type: application/json' \
  -d '{"justificativa":"muito bom", "finalizar":true}'

Observações importantes

Recomendações (para prompts / manutenção)

Alguns cuidados:


Créditos e suporte

Desenvolvido por Azteca Software (e-licencie) para pesquisa de NPS.

Suporte: ti@e-licencie.com.br ou WhatsApp (48) 9 9948 2983.

Página gerada automaticamente a partir de README.md