# ----------------------------------------------------------------------
# Limite de taxa para a busca de cliente por telefone (D59).
#
# O QUE ELA ENTREGA HOJE. `GET /api/agendas/getCliente?telefone=...` nao
# autentica nada, e devolve nome, CPF, RG, telefone e e-mail. O telefone e a
# credencial, e nao ha cabecalho nenhum.
#
# E DEVOLVE TODOS OS CLIENTES DAQUELE NUMERO, sem limite -- a consulta e um
# `getResult()` sem `setMaxResults`. Cruzando com o D41, medido na replica de
# producao: 5.670 telefones tem mais de um cliente ativo, 13.379 clientes estao
# envolvidos, e o pior caso e de 354 CLIENTES NUM UNICO NUMERO. Uma requisicao
# pode devolver o documento de 354 pessoas.
#
# ISTO NAO TOCA NO CONTRATO (D5): nao muda resposta nenhuma, so limita quantas
# vezes ela pode ser pedida. Por isso pode entrar sem esperar o inventario de
# versoes do aplicativo.
#
# O VALOR AINDA NAO E O CERTO, e vale ser honesto sobre isso.
#
# A conta que da para fazer hoje: sao ~800 clientes por dia em 32 unidades. Se
# cada um abrisse o aplicativo cinco vezes, seriam ~4.000 requisicoes por dia,
# ou ~3 por minuto somando TODO MUNDO. Trinta por minuto por endereco deixa dez
# vezes essa folga.
#
# O que essa conta NAO cobre: operadora movel usa CGNAT, entao milhares de
# clientes legitimos podem sair do MESMO endereco. Por isso o `burst` e largo e
# o limite e por minuto, e nao por segundo -- e por isso o numero definitivo sai
# do log do nginx, que e a pendencia W3
# (`scripts/inventariar-versoes-app.php`). Ate la, este valor e conservador de
# proposito: aperta a colheita em massa sem chegar perto do uso real.
#
# E ELE NAO RESOLVE O D59. Com trinta por minuto ainda se colhe muita coisa, e
# uma unica requisicao ja pode trazer 354 pessoas. Limite de taxa encurta a
# janela; quem fecha a porta e a autenticacao.
# ----------------------------------------------------------------------
limit_req_zone $binary_remote_addr zone=busca_cliente:10m rate=30r/m;

# ----------------------------------------------------------------------
# As duas ESCRITAS que continuam abertas: cadastro de cliente e SAC.
#
# Elas nao podem exigir credencial -- quem se cadastra ainda nao e cliente,
# e o SAC e um formulario de contato. Mas as duas produzem efeito e nao
# tinham teto nenhum: `POST /api/clientes/novo` cria linha na tabela de 243
# mil clientes, e `POST /api/clientes/sac` dispara e-mail.
#
# O DESTINATARIO DO SAC MUDOU EM 11/09/2026, e esta frase dizia "para a
# unidade". Por decisao do dono, o chamado vai para a FRANQUEADORA, e so para
# ela -- ver o D172. Ate aquele dia a frase descrevia um desenho que nunca
# aconteceu: o endpoint respondia 500 por um template trocado, e nenhum e-mail
# saia. Hoje sai, o que torna este teto defesa de verdade, e nao precaucao
# sobre caminho morto.
#
# TIRAR O `Access-Control-Allow-Origin: *` DELAS NAO RESOLVERIA, e vale
# entender por que: um POST com formulario e "requisicao simples" -- o
# navegador MANDA de qualquer jeito, e o CORS so decide quem pode LER a
# resposta. Contra escrita, CORS nao e defesa; e teto de taxa.
#
# 10 por minuto e folgado para gente de verdade: ninguem se cadastra dez
# vezes por minuto, nem manda dez reclamacoes. E o mesmo `burst` do
# `busca_cliente`, pela mesma razao -- recusar explicito em vez de enfileirar.
#
# O CGNAT continua sendo a ressalva de sempre (D59): milhares de clientes
# legitimos saem do mesmo endereco numa operadora movel. Por isso o teto e
# alto o bastante para nao pegar uso normal, e nao baixo o bastante para
# impedir abuso decidido -- ele encurta a janela, nao fecha a porta.
# ----------------------------------------------------------------------
limit_req_zone $binary_remote_addr zone=escrita_publica:10m rate=10r/m;

server {
    listen 80;
    server_name _;

    root /var/www/html/public;
    index index.php;

    # O sistema recebe upload de fotos e contratos e gera PDF/XLSX.
    client_max_body_size 64M;

    access_log /dev/stdout;
    error_log  /dev/stderr;

    # ------------------------------------------------------------------
    # Bloco de segurança 0 — o diretório que SÓ A APLICAÇÃO LÊ fica
    # fechado inteiro (22/10/2026, o D355; a fatia ARQUIVOS-SERVIDOR).
    #
    # `public/arquivos` guarda os PDFs e as planilhas da biblioteca da
    # franqueadora. O `location /` lá embaixo servia o arquivo existente
    # antes do PHP: `/arquivos/<nome>` dava 200 `application/pdf` SEM
    # SESSÃO (medido em 02/10/2026), e desativar um arquivo não o tirava
    # do ar. Quem lê agora é só a aplicação, pelo `Armazenamento`: a
    # `/adm/api/arquivos/:id/conteudo` e o "abrir"/"baixar" do ERP, os
    # dois com sessão e com o privilégio (`EntregaDoArquivo`).
    #
    # REGEX SEM CAIXA (`~*`), E A PRIMEIRA DE TODAS (a verificacao da
    # fatia, 22/10/2026): o `^~ /arquivos/` de antes comparava a caixa, e
    # `/ARQUIVOS/<nome>` caia no `location /` -- e o disco montado do
    # Windows, que nao diferencia a caixa, entregava o PDF com 200 sem
    # sessao. As regex sao avaliadas na ordem do arquivo: esta vem antes
    # do bloco 1 e das outras, e o `.php` daqui tambem cai no 404. O
    # nginx ja decodifica (`%61rquivos`) e junta as barras (`//arquivos`)
    # antes de comparar. 404, e não 403, para não dizer que o nome existe.
    #
    # O NGINX DA PRODUÇÃO NÃO ESTÁ NESTE REPOSITÓRIO (o D12): o mesmo
    # bloco vai ao vhost na janela de corte (`docs/procedimento-de-corte.md`
    # e `docs/procedimento-de-corte-etapa-1.md`). O servidor de
    # desenvolvimento do PHP (`php -S`, o do CI) recusa o mesmo prefixo
    # em `public/index.php`, e `scripts/conferir-protecao-do-webroot.php`
    # confere que as duas listas são a mesma.
    # ------------------------------------------------------------------
    location ~* ^/arquivos/ {
        return 404;
    }

    # ------------------------------------------------------------------
    # Bloco de segurança 1 — impedir execução de PHP nos diretórios de
    # upload. O sistema grava arquivos enviados pelo usuário dentro do
    # webroot; sem esta regra, um .php enviado como "contrato" seria
    # executado pelo servidor.
    #
    # Locations com expressão regular são avaliadas na ordem em que
    # aparecem, então este bloco precisa vir ANTES do bloco geral de .php.
    #
    # `visagismo` entrou em 19/08/2026: aquele diretório recebe FOTO ENVIADA
    # PELO CLIENTE (a geração de visagismo), e ficou de fora da lista original
    # porque o levantamento do Dia 0 partiu dos diretórios que apareciam no
    # débito D1, e não de todos os que o código escreve. A migração da F1.5 é
    # que expôs a diferença.
    # ------------------------------------------------------------------
    location ~* ^/(arquivos|contratos|banners|assistentes|vip-class|visagismo|logs|layout/img/avatar)/.*\.(php|phtml|phar)$ {
        deny all;
    }

    # ------------------------------------------------------------------
    # Bloco de segurança 2 — arquivos de dado ou log soltos na raiz do
    # webroot. É a rede de proteção para o problema do Dia 0: o sistema
    # gravava clientes.txt e log_scripts.txt em public/, e qualquer um
    # baixava por URL. A regra é por categoria, não por nome, senão um
    # arquivo novo com outro nome escaparia.
    #
    # robots.txt é exceção legítima. Match exato tem prioridade sobre
    # expressão regular no nginx, então este bloco precede o deny.
    # ------------------------------------------------------------------
    location = /robots.txt {
        try_files $uri =404;
    }

    location ~* ^/[^/]+\.(txt|log|sql|bak|sh|ini|yml|yaml)$ {
        deny all;
    }

    location ~* ^/phpinfo.*$ {
        deny all;
    }

    location ~ /\. {
        deny all;
    }

    # ------------------------------------------------------------------
    # A busca de cliente por telefone, limitada (D59). Ver a nota na zona,
    # no topo do arquivo.
    #
    # `burst=20 nodelay` atende rajada de vinte de uma vez sem enfileirar --
    # abertura de aplicativo e curta e concentrada, e enfileirar deixaria o
    # cliente esperando em vez de recusar quem exagera.
    #
    # 429 e o codigo certo, e o aplicativo instalado nunca o viu. Sob uso
    # normal ele nao chega la: e uma consulta por abertura, contra trinta por
    # minuto. Quem chegar recebe recusa explicita, e nao resposta lenta.
    # ------------------------------------------------------------------
    location = /api/agendas/getCliente {
        limit_req zone=busca_cliente burst=20 nodelay;
        limit_req_status 429;

        try_files $uri /index.php$is_args$args;
    }

    location = /api/clientes/novo {
        limit_req zone=escrita_publica burst=5 nodelay;
        limit_req_status 429;

        try_files $uri /index.php$is_args$args;
    }

    location = /api/clientes/sac {
        limit_req zone=escrita_publica burst=5 nodelay;
        limit_req_status 429;

        try_files $uri /index.php$is_args$args;
    }

    # ------------------------------------------------------------------
    # Roteamento do Laminas: arquivo existente é servido direto, o resto
    # cai no index.php.
    # ------------------------------------------------------------------
    location / {
        try_files $uri $uri/ /index.php$is_args$args;
    }

    location ~ \.php$ {
        try_files $uri =404;

        include fastcgi_params;
        fastcgi_pass app:9000;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param DOCUMENT_ROOT $document_root;

        # Relatórios pesados e crons chamados por URL podem demorar.
        fastcgi_read_timeout 300;
    }
}
