Ambiente local
Pré-requisitos
- Node.js 18+
- Docker + Docker Compose
- npm
Subir a infraestrutura
docker compose up -d postgres redisPostgreSQL em localhost:5432, Redis em localhost:6379.
Para desenvolver contra SQL Server em vez de Postgres:
docker compose -f docker-compose.test.yml --profile mssql up -d mssqle configure a API com DB_TYPE=mssql (ver cms-api/src/app.module.ts).
Iniciar a API
cd cms-api
npm install
npm run start:devA API sobe em http://localhost:3000/api.
Na primeira execução o módulo seed cria automaticamente:
- perfil Administrador
- usuário admin —
admin@cms.local, com senha aleatória impressa no log da primeira inicialização da API (procure o blocoUsuário administrador criado). A senha nasce expirada: o primeiro acesso obriga a trocá-la antes de abrir qualquer tela. - catálogo de Ferramentas (menus e permissões)
Iniciar o frontend
cd cms-frontend
npm install
npm run devFrontend em http://localhost:3001. Login: admin@cms.local, com a senha que apareceu no log da
API na primeira execução — ela não é exibida de novo, e precisa ser trocada no primeiro acesso.
Iniciar a documentação (opcional)
cd cms-docs
npm install
npm run devDocumentação em http://localhost:3002/docs.
Variáveis de ambiente
cms-api/.env
# As senhas abaixo são as do .env da raiz, que ./scripts/instalar.sh gera na primeira execução.
# Não existe valor de exemplo que funcione: a checagem de boot recusa subir com segredo publicado
# (ver cms-api/src/common/validar-segredos-boot.ts), e isso inclui a senha dentro da DATABASE_URL.
DATABASE_URL=postgres://cms:<POSTGRES_PASSWORD do .env>@localhost:5432/cms
REDIS_URL=redis://:<REDIS_PASSWORD do .env>@localhost:6379
JWT_SECRET=<openssl rand -hex 32>
JWT_EXPIRES_IN=8h
APP_PORT=3000
APP_URL=http://localhost:3000
CORS_ORIGIN=http://localhost:3001
WORKER_CONCURRENCY=100
DB_POOL_MAX=70
HTTP_BACKLOG=1024
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SMTP_USER=
SMTP_PASS=
SMTP_FROM=noreply@cms.localPara SQL Server, troque DATABASE_URL por DB_TYPE=mssql + MSSQL_HOST, MSSQL_PORT (padrão
1433), MSSQL_USER, MSSQL_PASSWORD, MSSQL_DATABASE, MSSQL_ENCRYPT e
MSSQL_TRUST_SERVER_CERTIFICATE.
Em desenvolvimento, os conectores SAP (cms-sap-connector, cms-idoc-connector) sobem com o
stub local: não é preciso ter o SAP NW RFC SDK instalado para rodar o resto do sistema. Para
falar com um SAP de verdade, instale o SDK e defina SAP_RFC_CLIENT=real — veja
SAP RFC / BAPI.
Serviços e portas
| Serviço | Porta | Observação |
|---|---|---|
cms-api | 3000 | NestJS |
cms-frontend | 3001 | Next.js |
cms-docs | 3002 | Nextra (esta documentação) |
cms-sap-connector | 4000 | Publicada no host porque em dev o cms-api roda nativo no Windows |
cms-idoc-connector | — | Só na rede interna do compose |
| PostgreSQL | 5432 | |
| Redis | 6379 | |
| Nginx | 80 / 443 | Só no compose completo · HTTP/2 no vhost do aplicativo |
Ambiente de teste (sistemas externos)
Os bancos e o broker usados para exercitar os conectores não estão no docker-compose.yml: eles
vivem num projeto Docker separado, o industry-test, descrito pelo docker-compose.test.yml. Na
visão do CMS eles são sistemas do cliente — não fazem parte da stack que é instalada.
docker compose -f docker-compose.test.yml up -d # tudo, menos o SQL Server
docker compose -f docker-compose.test.yml --profile mssql up -d # + SQL Server (~1 GB)
docker compose -f docker-compose.test.yml down # derruba só o ambiente de teste| Serviço | Porta publicada | Endereço na Conexão Externa | Acesso |
|---|---|---|---|
| TimescaleDB (Postgres externo) | 5433 | host.docker.internal:5433 | cms / TIMESCALE_PASSWORD, banco cms_timeseries |
| InfluxDB | 8086 | http://host.docker.internal:8086 | org cms, token INFLUX_TOKEN |
MongoDB (replica set rs0) | 27017 | mongodb://host.docker.internal:27017/?directConnection=true | cms / MONGO_PASSWORD |
| Mosquitto (MQTT) | 18830 | host.docker.internal:18830 | sem autenticação |
SQL Server — profile mssql | 1433 | host.docker.internal:1433 | sa / MSSQL_SA_PASSWORD |
| Oracle XE (~2 GB de RAM) | 1521 | host.docker.internal:1521, service XEPDB1 | system / ORACLE_PASSWORD |
A porta é sempre a publicada (a coluna do meio), inclusive onde ela difere da interna: o
Mosquitto atende em 1883 dentro da rede do Docker e em 18830 aqui fora, o TimescaleDB em 5432 e
5433. E o host é host.docker.internal, não o nome do serviço: é o único nome que o cms-api
alcança nos dois modos de execução — em container e rodando nativo na máquina, sem Docker. Uma
Conexão gravada como mongo:27017 só funciona no primeiro.
Conexões de Arquivo e SQLite têm a mesma incompatibilidade, num campo diferente: elas guardam um
caminho (/data/arquivos/...), que existe dentro do container porque o compose monta
./data/arquivos ali. Rodando nativo no Windows, o Node resolve /data/arquivos para
<drive atual>:\data\arquivos — que não existe, e o sintoma é a Aplicação parada em OFF com
“directory does not exist”.
Uma junction faz o mesmo caminho valer nos dois modos, sem tocar no cadastro das Conexões (ajuste o drive para o do seu clone):
mklink /J X:\data X:\cms\dataNão precisa de administrador. Vale saber o efeito colateral enquanto a origem está OFF: o agendador
PULA a Coleta sem consumir tentativa (ver coletarOrigens), então uma mensagem daquela Definição já
em AGUARDANDO_REENVIO fica parada até a Aplicação voltar. É o gate de Keep Alive funcionando, não
uma fila travada — mas ela conta como “Em Processamento” no monitor enquanto isso.
Os nomes em maiúsculas são variáveis do .env da raiz, geradas por ./scripts/instalar.sh. Até
2026-09-01 esta tabela publicava os valores, e o docker-compose.test.yml os trazia como default —
o que parecia inofensivo (na época estes containers publicavam só em 127.0.0.1, e não vão para
instalação de cliente) até a auditoria mostrar o caminho real do problema: valor de senha escrito no repositório
circula, e o que nasce como default de QA reaparece em instalação de verdade. Por isso nenhuma
senha aparece nesta tabela, e validar-segredos-boot.ts recusa a subida da API com qualquer valor
de exemplo conhecido.
Trocar uma dessas senhas depois do primeiro start não muda o banco: Postgres, Mongo e Influx
gravam a senha no volume, na inicialização. Ou rode o ALTER USER equivalente dentro do
container, ou descarte o volume — são bancos descartáveis:
docker compose -f docker-compose.test.yml down -v.
Os containers entram na rede cms-network, que é criada pelo docker-compose.yml — suba a stack
principal pelo menos uma vez antes, senão o Compose reclama que a rede externa não existe. E eles
não têm política de restart: reiniciar a máquina não os traz de volta, é preciso rodar o up -d
de novo.
Ferramentas de Help Desk
As ferramentas de chamado (Help Desk) ficam num terceiro projeto Docker,
o industry-helpdesk, descrito pelo docker-compose.helpdesk.yml. Os dois simulam sistemas do
cliente, com papéis diferentes: o industry-test são as fontes de dados que os conectores leem e
escrevem; este são os canais de escalonamento de um alerta. Quem mexe num conector de banco não
precisa do GLPI de pé, e derrubar os bancos não leva a ferramenta de chamados junto.
docker compose -f docker-compose.helpdesk.yml up -d # sobe o grupo Help Desk
docker compose -f docker-compose.helpdesk.yml down # derruba só ele (os volumes ficam)| Serviço | Porta publicada | URL base na ativação | Acesso |
|---|---|---|---|
| GLPI | 8380 | http://host.docker.internal:8380/apirest.php | glpi / glpi no primeiro acesso; banco com GLPI_DB_PASSWORD |
Valem as mesmas regras de rede e endereço do ambiente de teste: cms-network criada antes, e
host.docker.internal com a porta publicada.
O GLPI pede três passos manuais depois do primeiro start, uma vez só (ficam no volume): ligar a
API com login por credencial, tirar a restrição de IP do cliente de API e ligar os fusos horários
com o fuso padrão da instância. Os comandos estão no cabeçalho do docker-compose.helpdesk.yml. Sem
o terceiro, a hora de abertura dos chamados aparece adiantada — e quem já estava logado precisa
sair e entrar de novo para ver a hora certa.
As outras ferramentas do catálogo (ServiceNow, Jira SM, Freshservice, Zendesk, InvGate) são SaaS e não têm imagem Docker. Para elas valem as contas gratuitas: a instância de desenvolvedor (PDI) do ServiceNow, o plano Free do Jira SM e os trials das demais.