# FACIL FLOW - HTTP REST — contexto público para IA

# PowerShell pronto — SQL Server e repositório

Copie o bloco abaixo para testar a API sem Node.js. Troque apenas o token e os nomes de tabela/campos que existirem no seu ambiente. A URL oficial é `https://api.facilapp.com.br`; o token nunca deve ir para HTML público.

```powershell
$base  = 'https://api.facilapp.com.br'
$token = 'SEU_TOKEN'

function Invoke-FacilFlow {
    param(
        [Parameter(Mandatory)] [string] $Funcao,
        [hashtable] $Dados = @{}
    )

    $payload = @{ funcao = $Funcao }
    foreach ($chave in $Dados.Keys) { $payload[$chave] = $Dados[$chave] }

    Invoke-RestMethod -Uri "$base/executar" -Method Post `
        -ContentType 'application/json; charset=utf-8' `
        -Headers @{ 'X-API-Token' = $token } `
        -Body ($payload | ConvertTo-Json -Depth 10 -Compress)
}

# 0. STATUS — não usa token.
Invoke-RestMethod -Uri "$base/status" -Method Get

# 1. LISTAR TABELAS SQL SERVER.
Invoke-FacilFlow 'listar_tabelas' @{
    tipo_banco = 'sqlserver'; servidor = ''; banco = ''
}

# 2. LISTAR CAMPOS, TIPOS E TAMANHOS.
Invoke-FacilFlow 'listar_campos' @{
    tipo_banco = 'sqlserver'; servidor = ''; banco = ''
    esquema = 'dbo'; tabela = 'API_TESTE'
}

# 3. CONSULTAR — tabela e campos reais.
Invoke-FacilFlow 'consultar' @{
    tipo_banco = 'sqlserver'; servidor = ''; banco = ''
    tabela = 'API_TESTE'; campos = 'ID_API_TESTE,NOME'
}

# 4. CONSULTA LIVRE — use somente em diagnóstico ou SQL previamente aprovado.
Invoke-FacilFlow 'consulta_livre' @{
    tipo_banco = 'sqlserver'; servidor = ''; banco = ''
    sql = 'SELECT TOP 10 ID_API_TESTE,NOME FROM dbo.API_TESTE ORDER BY ID_API_TESTE DESC'
}

# 5. INSERIR — execute somente em tabela de teste ou com SQL aprovado.
Invoke-FacilFlow 'inserir' @{
    tipo_banco = 'sqlserver'; servidor = ''; banco = ''
    sql = "INSERT INTO dbo.API_TESTE (NOME) VALUES ('EXEMPLO POWERSHELL')"
}

# 6. ALTERAR — ajuste o ID real.
Invoke-FacilFlow 'alterar' @{
    tipo_banco = 'sqlserver'; servidor = ''; banco = ''
    sql = "UPDATE dbo.API_TESTE SET NOME='ALTERADO POWERSHELL' WHERE ID_API_TESTE=1"
}

# 7. EXCLUIR — execute somente após confirmar a chave e o registro.
Invoke-FacilFlow 'excluir' @{
    tipo_banco = 'sqlserver'; servidor = ''; banco = ''
    sql = 'DELETE FROM dbo.API_TESTE WHERE ID_API_TESTE=1'
}

# 8. SALVAR ARQUIVO — MenuData.js, XML, imagem, PDF etc.
$arquivoLocal = 'C:\SeuProjeto\MenuData.js'
$base64 = [Convert]::ToBase64String([System.IO.File]::ReadAllBytes($arquivoLocal))
Invoke-FacilFlow 'arquivo_salvar' @{
    categoria = 'menus'; desenvolvedor = 'facilapp'; usuario = 'cliente_001'
    nome_arquivo = 'MenuData.js'; conteudo_base64 = $base64
}

# 9. LISTAR ARQUIVOS DA PASTA LÓGICA.
Invoke-FacilFlow 'arquivo_listar' @{
    categoria = 'menus'; desenvolvedor = 'facilapp'; usuario = 'cliente_001'
}

# 10. LER E REGRAVAR LOCALMENTE.
$arquivo = Invoke-FacilFlow 'arquivo_ler' @{
    categoria = 'menus'; desenvolvedor = 'facilapp'; usuario = 'cliente_001'
    nome_arquivo = 'MenuData.js'
}
[System.IO.File]::WriteAllBytes(
    'C:\SeuProjeto\MenuData-baixado.js',
    [Convert]::FromBase64String($arquivo.conteudo_base64)
)

# 11. EXCLUIR ARQUIVO — somente na mesma pasta lógica.
Invoke-FacilFlow 'arquivo_excluir' @{
    categoria = 'menus'; desenvolvedor = 'facilapp'; usuario = 'cliente_001'
    nome_arquivo = 'MenuData.js'
}
```

# Contrato literal do repositório de arquivos

Use estas quatro funções exatamente como estão abaixo. Elas recebem somente identificadores lógicos; **nunca** envie `C:\`, `D:\`, `../` ou qualquer caminho físico. A API grava em `DiretorioBase\categoria\desenvolvedor\usuario` configurado no servidor.

Para web pública, chame o seu `api.php`/backend, que guarda o token. O exemplo supõe que `apiExecutar(funcao, dados)` já faça o POST protegido para `/executar`.

```javascript
// Converte um arquivo escolhido pelo usuário em Base64 puro.
// O split remove "data:tipo;base64,"; a API NÃO aceita esse prefixo.
function arquivoParaBase64(arquivo) {
  return new Promise((resolve, reject) => {
    const leitor = new FileReader();
    leitor.onerror = () => reject(new Error('Não foi possível ler o arquivo'));
    leitor.onload = () => resolve(String(leitor.result).split(',')[1]);
    leitor.readAsDataURL(arquivo);
  });
}

const pasta = {
  categoria: 'menus',
  desenvolvedor: 'facilapp',
  usuario: 'cliente_001'
};

// 1. SALVAR — serve para menu.js, config.js, XML, imagem, PDF ou comprovante.
const conteudo_base64 = await arquivoParaBase64(inputArquivo.files[0]);
const salvo = await apiExecutar('arquivo_salvar', {
  ...pasta,
  nome_arquivo: 'menu.js',
  conteudo_base64
});

// 2. LISTAR — retorna os nomes e tamanhos da pasta lógica.
const lista = await apiExecutar('arquivo_listar', pasta);

// 3. LER — retorna conteudo_base64. Para texto/JS/XML, decodifique em UTF-8.
const lido = await apiExecutar('arquivo_ler', {
  ...pasta,
  nome_arquivo: 'menu.js'
});
const texto = new TextDecoder().decode(
  Uint8Array.from(atob(lido.conteudo_base64), c => c.charCodeAt(0))
);

// 4. EXCLUIR — remove somente o arquivo informado na mesma pasta lógica.
const excluido = await apiExecutar('arquivo_excluir', {
  ...pasta,
  nome_arquivo: 'menu.js'
});
```

JSON mínimo de cada chamada:

```json
{"funcao":"arquivo_salvar","categoria":"menus","desenvolvedor":"facilapp","usuario":"cliente_001","nome_arquivo":"menu.js","conteudo_base64":"Y29uc29sZS5sb2coJ29rJyk7"}
```

```json
{"funcao":"arquivo_ler","categoria":"menus","desenvolvedor":"facilapp","usuario":"cliente_001","nome_arquivo":"menu.js"}
```

```json
{"funcao":"arquivo_listar","categoria":"menus","desenvolvedor":"facilapp","usuario":"cliente_001"}
```

```json
{"funcao":"arquivo_excluir","categoria":"menus","desenvolvedor":"facilapp","usuario":"cliente_001","nome_arquivo":"menu.js"}
```

Atualizado em 23/07/2026. Este documento foi construído a partir do manual operacional oficial da API e é a fonte pública para qualquer IA ou desenvolvedor que precise criar, corrigir ou publicar integrações.

## Instalação direta no Windows

A FACIL FLOW - HTTP REST foi pensada para ser instalada como um executável/serviço Windows: instale, configure o `ServidorAPI.ini`, inicie o serviço e valide `GET /status`. Não exige Node.js, npm, runtime JavaScript ou um servidor REST adicional no computador que hospeda a API. O navegador ou sistema cliente apenas consome HTTP/JSON; o acesso aos bancos é executado pelo serviço instalado.

## REGRA ZERO — configuração obrigatória antes de gerar código

Toda IA deve procurar primeiro o arquivo `config.js` na raiz pública do projeto web. Esse arquivo é JavaScript, não JSON puro, e deve possuir este formato:

```javascript
window.CONFIG = {
  IP: "201.222.24.153",
  PORTA: 5000,
  TOKEN: "",
  TIMEOUT_MS: 15000,
  TIPO_BANCO: "hfsql",
  SERVIDOR_BANCO: "",
  BANCO: "",
  TABELA_PERMISSOES: "PERMISSOES_MENU"
};
```

Estrutura mínima obrigatória:

```text
raiz-do-projeto-web/
├── index.html
├── config.js
└── servidor-api.js
```

- `config.js` é o único lugar do frontend que guarda IP, porta, token, timeout, tipo do banco, servidor lógico, banco e tabela de permissões.
- `SERVIDOR_BANCO` e `BANCO` podem ficar vazios; nesse caso a API usa os valores protegidos no `ServidorAPI.ini`.
- **Nunca coloque usuário ou senha do banco no `config.js`, HTML ou JavaScript.** Usuário e senha pertencem somente à seção do banco no `ServidorAPI.ini`, ao lado do EXE.
- `servidor-api.js` nunca cria outra configuração: ele lê `window.CONFIG`.
- `index.html` carrega primeiro `config.js` e depois `servidor-api.js`.
- A IA deve preservar o token que já existir em `config.js`. Se `TOKEN` estiver vazio, deve pedir o token ao responsável.
- É proibido duplicar IP, porta, token ou timeout no HTML, em outro JavaScript ou em um novo `ServidorApi.js`.

## Como uma IA deve acessar este documento

Endereço oficial e público deste documento:

`https://api.facilapp.com.br/content.md`

Ao orientar outra IA, fornecer sempre o endereço público acima. Não enviar caminho físico do Windows nem endereço `file:///`.

A ferramenta não precisa e não deve acessar o disco do servidor. O documento é entregue pela própria API na porta 5000. Não tente abrir caminhos `C:\...`, `file:///...`, subir acima da pasta `WEB` ou ler arquivos privados.

## Segurança obrigatória para aplicações cliente

Base HTTPS oficial: `https://api.facilapp.com.br`.

**Site público (HTML acessado pela internet):** o token não pode existir em
`config.js`, HTML ou JavaScript. O navegador chama uma ponte do próprio cliente
(PHP, C#, WinDev, Node ou outro backend). Essa ponte guarda o token fora da
pasta pública, envia `X-API-Token` para a FACIL Flow e devolve somente o JSON.

```text
Navegador/HTML -> backend do cliente -> https://api.facilapp.com.br -> FACIL Flow
```

**Rede local controlada:** `config.js` pode apontar diretamente para a API e
conter um token exclusivo daquele cliente/projeto. Esse token fica visível para
quem tem acesso ao navegador ou aos arquivos locais; HTTPS protege o tráfego na
rede, mas não esconde o token do próprio usuário.

**Aplicativo desktop:** guardar o token em INI/configuração local. Nunca guardar
usuário ou senha SQL no aplicativo, no HTML ou no JavaScript.

### Proxy local loopback (127.0.0.1)

Para uma página HTML executada na máquina do cliente, pode existir uma ponte
local em `http://127.0.0.1:5050`. O navegador chama apenas essa ponte. Um
processo local (WinDev, Node.js, PowerShell, Python ou outro backend) lê o token
em configuração protegida, adiciona `X-API-Token` e chama a FACIL Flow por HTTPS.

```text
HTML/JavaScript -> http://127.0.0.1:5050 -> https://api.facilapp.com.br -> FACIL Flow
```

Vantagem: o token não fica no HTML, `config.js`, inspeção do navegador ou na
requisição gerada pelo navegador para a API externa.

Proteções obrigatórias da ponte local: escutar somente em `127.0.0.1` (nunca em
`0.0.0.0`), aceitar apenas origens/telas conhecidas, validar cada operação e não
permitir que qualquer site aberto no navegador use a porta local livremente.

### Site público na Hostinger: ponte PHP sem token no navegador

Quando menu, cadastro ou dashboard HTML estiverem hospedados na internet, use
uma ponte PHP no mesmo domínio do site. A tela chama somente `api.php`; o PHP
anexa o token e chama a FACIL Flow por HTTPS.

```text
public_html/index.html  ->  public_html/api.php  ->  https://api.facilapp.com.br/executar
                                     |
                                     +-> ../config/config.php (fora do public_html)
```

Arquivo protegido `config/config.php` (pasta irmã de `public_html`):

```php
<?php
return [
    'api_url' => 'https://api.facilapp.com.br',
    'token'   => 'TOKEN_EXCLUSIVO_DO_CLIENTE'
];
```

Arquivo público `public_html/api.php`:

```php
<?php
header('Content-Type: application/json; charset=utf-8');

function responder($status, $dados) {
    http_response_code($status);
    echo json_encode($dados, JSON_UNESCAPED_UNICODE);
    exit;
}

if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
    responder(405, ['ok' => false, 'erro' => 'Método não permitido']);
}

$config = require __DIR__ . '/../config/config.php';
$entrada = json_decode(file_get_contents('php://input'), true);

if (!is_array($entrada) || empty($entrada['funcao'])) {
    responder(400, ['ok' => false, 'erro' => 'JSON ou função inválida']);
}

// Cada site libera somente as funções de que realmente precisa.
$permitidas = [
    'consultar_hfsql', 'inserir_hfsql', 'alterar_hfsql',
    'arquivo_salvar', 'arquivo_ler', 'arquivo_listar'
];
if (!in_array($entrada['funcao'], $permitidas, true)) {
    responder(403, ['ok' => false, 'erro' => 'Função não permitida']);
}

$curl = curl_init(rtrim($config['api_url'], '/') . '/executar');
curl_setopt_array($curl, [
    CURLOPT_POST => true,
    CURLOPT_POSTFIELDS => json_encode($entrada, JSON_UNESCAPED_UNICODE),
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CONNECTTIMEOUT => 10,
    CURLOPT_TIMEOUT => 30,
    CURLOPT_HTTPHEADER => [
        'Content-Type: application/json',
        'X-API-Token: ' . $config['token']
    ]
]);

$saida = curl_exec($curl);
$http = (int) curl_getinfo($curl, CURLINFO_HTTP_CODE);
$erro = curl_error($curl);
curl_close($curl);

if ($saida === false) {
    responder(502, ['ok' => false, 'erro' => 'Falha ao comunicar com a API']);
}

http_response_code($http ?: 502);
echo $saida;
```

Chamada da tela HTML/JavaScript, sem token:

```javascript
const resposta = await fetch('/api.php', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    funcao: 'consultar_hfsql',
    tabela: 'CLIENTES',
    campos: 'ID,NOME,TELEFONE'
  })
});
const dados = await resposta.json();
```

Exemplos de `body` para a mesma chamada `fetch('/api.php', ...)`:

```javascript
// Consultar dados: substitua somente por tabela/campos confirmados
{
  funcao: 'consultar_hfsql',
  tabela: 'API_TESTE',
  campos: 'ID_API_TESTE,NOME'
}

// Inserir: use os nomes reais da chave e do campo de nome
{
  funcao: 'inserir_hfsql',
  tabela: 'API_TESTE',
  campo_id: 'ID_API_TESTE',
  campo_nome: 'NOME',
  nome: 'Maria Silva'
}

// Alterar: id selecionado no browse e campo real confirmado
{
  funcao: 'alterar_hfsql',
  tabela: 'API_TESTE',
  campo_id: 'ID_API_TESTE',
  campo_nome: 'NOME',
  id: 15,
  nome: 'Maria da Silva'
}

// Salvar configuração do menu ou XML no repositório controlado da API
{
  funcao: 'arquivo_salvar',
  categoria: 'configuracoes',
  desenvolvedor: 'facilapp',
  usuario: 'cliente_001',
  nome_arquivo: 'menu.js',
  conteudo_base64: 'CONTEUDO_EM_BASE64'
}

// Ler menu.js ou XML salvo no repositório
{
  funcao: 'arquivo_ler',
  categoria: 'configuracoes',
  desenvolvedor: 'facilapp',
  usuario: 'cliente_001',
  nome_arquivo: 'menu.js'
}
```

Os nomes exatos dos campos e o formato final de cada função devem ser obtidos
nas páginas de teste, no `openapi.json` e na estrutura real do banco. Não
invente tabela, chave ou campo.

Para XML, use `nome_arquivo: 'arquivo.xml'` e envie o conteúdo convertido para
Base64. A API decide a pasta física; o HTML nunca envia caminho do Windows.

O token não aparece no HTML, no JavaScript, no `config.js` nem na aba Network
do navegador. Nunca use `consulta_livre` nessa ponte pública sem uma regra de
negócio específica e validação no servidor.

Este conteúdo não publica senhas nem o valor real de tokens.

## Roteiro HTTP obrigatório para a IA aprender a API

Execute as rotas nesta ordem, sempre usando o IP público válido.

### 1. Ler o contexto oficial

```http
GET https://api.facilapp.com.br/content.md
```

Objetivo: ler as regras de integração, configuração, terminal e bancos antes de gerar código. Use `/content.md`, que é a rota confirmada no serviço ativo.

### 2. Ler o mapa OpenAPI

```http
GET https://api.facilapp.com.br/openapi.json
```

Objetivo: obter as rotas, parâmetros, schemas e funções publicadas.

Não use `/swagger.json`: esse alias não está implementado.

### 3. Verificar o serviço

```http
GET https://api.facilapp.com.br/status
```

Objetivo: confirmar que o serviço está online, além de identificar instância, porta e tipo de comunicação. Essa rota não testa banco de dados.

Não use `/health`: esse alias não está implementado.

### 4. Abrir as interfaces

Swagger:

```http
GET https://api.facilapp.com.br/swagger
```

Diagnóstico e manual:

```http
GET https://api.facilapp.com.br/diagnostico.html
```

`/swagger` e `/diagnostico.html` são páginas diferentes. Não trate uma como alias da outra.

### 5. Executar uma função autenticada

```http
POST https://api.facilapp.com.br/executar
Content-Type: application/json
X-API-Token: valor lido de CONFIG.TOKEN
```

O corpo deve conter uma função pública realmente existente e dados compatíveis com o banco configurado. Antes do POST, a IA deve ler `config.js`, `content.md` e `openapi.json`. Não invente função, tabela, campo ou filtro e não imprima o token.

Exemplo estrutural:

```json
{
  "funcao": "consultar_hfsql",
  "tabela": "TABELA_CONFIRMADA",
  "campos": "CAMPO1,CAMPO2"
}
```

Troque somente por nomes confirmados na estrutura real. A resposta do `/status` comprova o HTTP; uma chamada em `/executar` comprova autenticação e, dependendo da função, o acesso ao banco.

## 1. Contexto obrigatório para uma nova sessão

Leia estas regras antes de alterar qualquer arquivo:

1. A API é um serviço HTTP REST criado em WINDEV 28.
2. Novos projetos acessam a API por `https://api.facilapp.com.br`.
3. A API acessa os bancos instalados na mesma máquina por `127.0.0.1`.
4. Todas as operações passam por `POST /executar` e pelo cabeçalho `X-API-Token`.
5. Usuário e senha do banco ficam somente no `ServidorAPI.ini`.
6. SQL Server, PostgreSQL, MariaDB, MySQL e Oracle usam as funções genéricas `consultar`, `consulta_livre`, `inserir`, `alterar` e `excluir`.
7. HFSQL usa `consultar_hfsql`, `inserir_hfsql`, `alterar_hfsql` e `excluir_hfsql`.
8. Não crie funções com sufixo `_teste`. As páginas de teste devem chamar exatamente as mesmas funções usadas em produção.
9. Não invente tabela, campo, chave, filtro ou conversão. Consulte a estrutura ou solicite o `CREATE TABLE` ao usuário.
10. Alteração em HTML, CSS, JavaScript ou `openapi.json` não exige recompilar. Alteração em WLanguage exige recompilar e reiniciar o serviço.

Arquitetura:

`HTML/JavaScript → HTTP :5000 → /executar → ProcessarAPI → banco em 127.0.0.1 → JSON`

## 2. Endereços e portas

| Componente | Endereço/porta | Uso |
|---|---:|---|
| API pública | `https://api.facilapp.com.br` | URL usada pelos navegadores e aplicativos remotos |
| API — todos os acessos e testes | `https://api.facilapp.com.br` | Use sempre o IP público válido, inclusive no diagnóstico |
| SQL Server | `127.0.0.1:1433` | Conexão interna da API |
| PostgreSQL | `127.0.0.1:5432` | Conexão interna da API |
| MariaDB | `127.0.0.1:3306` | Conexão interna da API |
| MySQL | `127.0.0.1:3306` | Conexão interna da API |
| HFSQL Client/Server | `127.0.0.1:4900` | Conexão interna da API |
| Oracle | `127.0.0.1:1521` ou DSN | Quando instalado e habilitado |

### Regra de desempenho

O IP público pertence ao acesso do cliente até a API. O banco que está na mesma máquina da API deve ser acessado por `127.0.0.1`.

Não use `201.222.24.153` como servidor do banco local. Isso pode fazer o tráfego sair pela rede, passar por NAT/firewall e voltar para a mesma máquina, causando demora, timeout ou falha de hairpin NAT. Para banco em outra máquina, use o IP privado fixo dessa máquina ou uma VPN.

### Regra obrigatória para a IA

- Para abrir documentação, Swagger, diagnóstico e chamar a API, use `https://api.facilapp.com.br`.
- Nos dados enviados para `POST /executar`, o campo `servidor` pode ser `127.0.0.1` quando o banco estiver instalado na mesma máquina do ServidorAPI.
- `127.0.0.1` representa a própria máquina onde o EXE está rodando. Ele não é um endereço que a IA externa consiga abrir.
- Não substitua automaticamente `127.0.0.1` pelo IP público dentro das configurações SQL, PostgreSQL, MariaDB, MySQL, HFSQL ou Oracle.
- Se o banco estiver em outro computador, use o IP privado fixo ou o nome de rede desse servidor, conforme a instalação do cliente.

## 3. Rotas atuais

| Método | Rota | Finalidade |
|---|---|---|
| `GET` | `/status` | Confirma socket, serviço e porta; não acessa banco |
| `GET` | `/swagger` | Swagger da API |
| `GET` | `/openapi.json` | Especificação usada pelo Swagger |
| `GET` | `/diagnostico.html` | Painel, manual, prompt e testes |
| `GET` | `/prompt` | Gerador de prompt para browse/update |
| `GET` | `/hfsql-teste` | Teste real HFSQL |
| `GET` | `/sqlserver-teste` | Teste real SQL Server |
| `GET` | `/postgresql-teste` | Teste real PostgreSQL |
| `GET` | `/sql-teste?banco=mariadb` | Teste real MariaDB |
| `GET` | `/mysql-teste` | Teste real MySQL |
| `POST` | `/executar` | Única entrada RPC autenticada |

Links de teste:

- `https://api.facilapp.com.br/status`
- `https://api.facilapp.com.br/swagger`
- `https://api.facilapp.com.br/diagnostico.html`
- `https://api.facilapp.com.br/hfsql-teste`
- `https://api.facilapp.com.br/sqlserver-teste`
- `https://api.facilapp.com.br/postgresql-teste`
- `https://api.facilapp.com.br/sql-teste?banco=mariadb`
- `https://api.facilapp.com.br/mysql-teste`

## 4. Configuração correta do INI

O arquivo `ServidorAPI.ini` fica ao lado do executável. Use placeholders no manual; nunca publique senhas ou tokens reais.

```ini
[Instancia]
Nome=Principal

[Servico]
NomeWindows=Service ServidorApi

[ServidorHTTP]
Ativo=1
Porta=5000
IPPublico=1
ModoTeste=1
LimiteCorpoKB=1024
TimeoutSegundos=15
MaxConexoes=50

[Rede]
PermitirLocalhost=1
PermitirRedeLocal=1
PermitirTailscale=1

[Seguranca]
ExigirToken=1
ConsultaLivre=1
Token=SEU_TOKEN_FORTE
ExigirHTTPSPublico=0

[Log]
Ativo=1
Nivel=INFO
RetencaoDias=30

```

### Regra de HTTPS

Use `ExigirHTTPSPublico=1` somente depois que a API estiver publicada atras de
HTTPS valido, com certificado e proxy/reverse proxy configurados, e a URL
publica iniciar com `https://`.

Enquanto a URL publica real for `http://IP:PORTA`, use
`ExigirHTTPSPublico=0`. Os exemplos deste manual que usam
`https://api.facilapp.com.br` sao de ambiente HTTP e nao devem ser tratados
como HTTPS de producao.

```ini
[SQL]
Ativo=1
Servidor=127.0.0.1
ServidorPadrao=127.0.0.1
Porta=1433
Banco=FACILAPP_API
Usuario=USUARIO_SQLSERVER
Senha=SENHA_SQLSERVER

[PostgreSQL]
Ativo=1
Servidor=127.0.0.1
Porta=5432
Banco=FACILAPP_API
Usuario=USUARIO_POSTGRESQL
Senha=SENHA_POSTGRESQL

[MariaDB]
Ativo=1
Servidor=127.0.0.1
Porta=3306
Banco=FACILAPP_API
Usuario=USUARIO_MARIADB
Senha=SENHA_MARIADB

[MySQL]
Ativo=1
Servidor=127.0.0.1
Porta=3306
Banco=FACILAPP_API
Usuario=USUARIO_MYSQL
Senha=SENHA_MYSQL

[Oracle]
Ativo=0
DSN=FacilAppOracle
Servidor=127.0.0.1
Porta=1521
Banco=
Usuario=
Senha=

[HFSQL]
Ativo=1
Servidor=127.0.0.1
Porta=4900
Banco=FACILAPP_API
Usuario=USUARIO_HFSQL
Senha=SENHA_HFSQL
```

Depois de mudar o INI, reinicie `Service ServidorApi`. Alterar apenas o arquivo sem reiniciar não troca a configuração carregada pelo processo.

HFSQL Client/Server só pode ser oferecido conforme a licença aplicável do WINDEV/PC SOFT.

## 5. Configuração do frontend

Cada projeto HTML que consumir a API deve manter estes arquivos na raiz pública:

```text
raiz-do-projeto-web/
├── index.html
├── config.js
└── servidor-api.js
```

Responsabilidades obrigatórias:

- `config.js`: única fonte de IP, porta, token, timeout e preferências do projeto.
- `servidor-api.js`: contém somente o helper e as funções da aplicação; lê `window.CONFIG`.
- `index.html`: carrega primeiro `config.js` e depois `servidor-api.js`.
- Outros arquivos JavaScript nunca devem repetir nem congelar uma segunda configuração.

Não crie um novo `ServidorApi.js` contendo servidor e token próprios. Se o projeto já possui `servidor-api.js`, preserve esse helper e altere somente o `config.js` da raiz.

O navegador precisa conhecer somente URL da ponte, timeout e preferencias nao
sigilosas, como `TIPO_BANCO` e `TABELA_PERMISSOES`. Nunca coloque token mestre,
usuario ou senha de banco neste arquivo:

```javascript
window.CONFIG = Object.freeze({
  IP: "127.0.0.1",
  PORTA: 5100,
  TIMEOUT_MS: 15000,
  TIPO_BANCO: "hfsql",
  TABELA_PERMISSOES: "PERMISSOES_MENU"
});
```

Para LAN controlada, a configuracao pode apontar direto para o ServidorAPI e
incluir o token do projeto:

```javascript
window.CONFIG = Object.freeze({
  IP: "192.168.2.100",
  PORTA: 5000,
  TOKEN: "TOKEN_DA_API_LOCAL",
  TIMEOUT_MS: 15000,
  TIPO_BANCO: "hfsql"
});
```

Para web publica/internet, mantenha o primeiro modelo (ponte/backend sem token
no navegador). A senha SQL jamais entra em qualquer um dos dois `config.js`.

Carregamento obrigatório no HTML:

```html
<script src="./config.js"></script>
<script src="./servidor-api.js"></script>
```

O arquivo `config.js` deve existir na raiz do projeto web implantado. A aplicação sempre obtém dele o servidor, a porta, o token e o timeout.

Nunca coloque usuário ou senha do banco em JavaScript. O campo `servidor` do JSON de uma chamada identifica o destino do banco; ele não substitui as credenciais do INI.

Importante: qualquer token entregue ao navegador pode ser visualizado pelo usuário. Portanto, ele identifica e controla o acesso da aplicação, mas não deve ser tratado como segredo absoluto. Para credencial realmente secreta, use um backend intermediário.

## 6. Helper `fetch` recomendado

Use um único helper para todas as telas. Ele trata timeout, HTTP inválido, JSON inválido e erro devolvido pela API.

Nos exemplos JavaScript, a chamada HTTP sempre usa o IP público válido. O endereço `127.0.0.1` pode aparecer dentro de `dados.servidor` somente para indicar que o banco está instalado na mesma máquina da API.

```javascript
async function apiExecutar(funcao, dados = {}) {
  const cfg = window.CONFIG || {};
  const ponteLocal = `http://${cfg.IP || "127.0.0.1"}:${cfg.PORTA || 5100}`;
  const controller = new AbortController();
  const timer = setTimeout(
    () => controller.abort(),
    Number(cfg.TIMEOUT_MS || 15000)
  );

  try {
    const resposta = await fetch(`${ponteLocal}/executar`, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ funcao, ...dados }),
      signal: controller.signal
    });

    const texto = await resposta.text();
    let json;
    try {
      json = JSON.parse(texto);
    } catch {
      throw new Error(`Resposta não é JSON: ${texto.slice(0, 200)}`);
    }

    if (!resposta.ok || !json.ok) {
      throw new Error(json.erro || `HTTP ${resposta.status}`);
    }

    return json;
  } catch (erro) {
    if (erro.name === "AbortError") {
      throw new Error("Tempo limite ao acessar a API");
    }
    throw erro;
  } finally {
    clearTimeout(timer);
  }
}
```

Use `textContent` para mostrar dados. Não use `innerHTML` com valores vindos do banco.

### Ajuste do helper para LAN controlada

No modo LAN, o helper chama diretamente `http://IP:5000/executar`. Neste caso,
troque a URL `ponteLocal` por `apiLocal` e inclua este header no `fetch`:

```javascript
headers: {
  "Content-Type": "application/json",
  "X-API-Token": cfg.TOKEN || ""
}
```

Use isso somente para instalacao interna controlada. Para web publica, mantenha
o helper sem token chamando a ponte/backend.

## 6.1. Comandos seguros para IA no terminal

Antes de executar comandos, identifique o shell atual. Se o terminal já mostra `PS C:\...>`, ele já é PowerShell: não envolva o comando em outro `powershell -Command`.

### Teste simples no PowerShell atual

Use diretamente, sem variável intermediária:

```powershell
Invoke-RestMethod -Uri "https://api.facilapp.com.br/status" -Method Get |
    ConvertTo-Json -Depth 10
```

Não use isto dentro do PowerShell:

```powershell
powershell -Command "$res = Invoke-RestMethod ...; $res"
```

O PowerShell externo pode expandir `$res` antes de entregar o texto ao processo interno, produzindo `=` isolado ou um pipe vazio.

### Regra do `curl` no Windows

No Windows PowerShell, `curl` pode ser um alias de `Invoke-WebRequest`. Por isso opções como `-H` e `-d` podem falhar.

- Para usar sintaxe real do cURL, escreva `curl.exe`.
- Para usar PowerShell, prefira `Invoke-RestMethod` com `-Headers`, `-ContentType` e `-Body`.
- Nunca coloque o token real em exemplos, logs ou respostas da IA.

### Método recomendado para a IA: ler `config.js` com Node

Execute na raiz do projeto web, onde está o `config.js`. O comando carrega `window.CONFIG`, monta a URL e não imprime o token.

Status:

```powershell
node -e "global.window=global; require('./config.js'); const c=window.CONFIG; fetch('http://'+c.IP+':'+c.PORTA+'/status').then(async r=>console.log(r.status,await r.text())).catch(e=>{console.error(e.message);process.exit(1)})"
```

Consulta autenticada:

```powershell
node -e "global.window=global; require('./config.js'); const c=window.CONFIG; const dados={funcao:'consultar_hfsql',tabela:c.TABELA_PERMISSOES,campos:'ID,DADOS_JSON'}; fetch('http://'+c.IP+':'+c.PORTA+'/executar',{method:'POST',headers:{'Content-Type':'application/json','X-API-Token':c.TOKEN},body:JSON.stringify(dados)}).then(async r=>console.log(r.status,await r.text())).catch(e=>{console.error(e.message);process.exit(1)})"
```

Esse é o padrão preferencial para agentes de IA: os valores vêm do `config.js` existente e não são duplicados no comando.

### Se for obrigatório iniciar outro PowerShell

Evite variáveis `$...` no texto passado a `-Command`:

```powershell
powershell.exe -NoProfile -Command "Invoke-RestMethod -Uri 'https://api.facilapp.com.br/status' -Method Get | ConvertTo-Json -Depth 10"
```

## 7. Funções externas atuais

### SQL Server, PostgreSQL, MariaDB, MySQL e Oracle

| `funcao` | Campos obrigatórios | Resultado |
|---|---|---|
| `consultar` | `tipo_banco`, `servidor`, `banco`, `tabela`, `campos` | SELECT estruturado, até 500 linhas |
| `consulta_livre` | `tipo_banco`, `servidor`, `banco`, `sql` | Linhas de um SELECT livre |
| `inserir` | `tipo_banco`, `servidor`, `banco`, `sql` | Resultado ou erro do INSERT |
| `alterar` | `tipo_banco`, `servidor`, `banco`, `sql` | Resultado ou erro do UPDATE |
| `excluir` | `tipo_banco`, `servidor`, `banco`, `sql` | Resultado ou erro do DELETE |

Valores aceitos em `tipo_banco`: `sqlserver`, `postgresql`, `mariadb`, `mysql` e `oracle`.

### Seguranca de SQL livre e alteracoes SQL

As funcoes `consulta_livre`, `inserir`, `alterar` e `excluir` para bancos SQL
recebem um comando SQL completo. Elas existem para diagnostico, homologacao,
administracao controlada ou integracao servidora confiavel.

Nao use essas funcoes diretamente em paginas publicas ou no navegador de um
usuario final: o token ficaria exposto e escape manual de aspas nao protege
contra SQL injection. Para uma tela de producao, use `consultar` estruturado
para leitura e solicite uma funcao estruturada especifica no ServidorAPI para
cada gravacao necessaria.

No momento nao existe insert/update/delete estruturado generico para SQL
Server, PostgreSQL, MariaDB, MySQL e Oracle. Nao invente um contrato: marque
como PENDENTE ou implemente a nova chamada no ServidorAPI antes de publicar a
tela.

### HFSQL

| `funcao` | Campos obrigatórios |
|---|---|
| `consultar_hfsql` | `tabela`, `campos` |
| `inserir_hfsql` | `tabela`, `campo_id`, `campo_nome`, `nome` |
| `alterar_hfsql` | `tabela`, `campo_id`, `campo_nome`, `id`, `nome` |
| `excluir_hfsql` | `tabela`, `campo_id`, `campo_nome`, `id` |

HFSQL não usa `consulta_livre` na versão atual.

## 8. Chamadas reais

### Consulta estruturada

```javascript
const retorno = await apiExecutar("consultar", {
  tipo_banco: "sqlserver",
  servidor: "127.0.0.1",
  banco: "FACILAPP_API",
  tabela: "API_TESTE",
  campos: "ID_API_TESTE,NOME"
});

const linhas = retorno.dados || [];
```

Troque somente `tipo_banco` para `postgresql`, `mariadb` ou `mysql`. Respeite maiúsculas, schema e nomes reais do banco.

### SELECT livre

```javascript
const retorno = await apiExecutar("consulta_livre", {
  tipo_banco: "postgresql",
  servidor: "127.0.0.1",
  banco: "FACILAPP_API",
  sql: "SELECT id_api_teste, nome FROM public.api_teste ORDER BY id_api_teste"
});
```

`consulta_livre` depende de `[Seguranca] ConsultaLivre=1`.

### INSERT

```javascript
await apiExecutar("inserir", {
  tipo_banco: "mariadb",
  servidor: "127.0.0.1",
  banco: "FACILAPP_API",
  sql: "INSERT INTO API_TESTE (NOME) VALUES ('DANIEL')"
});
```

Não envie campo autonumérico no INSERT. O banco deve gerar o ID.

### UPDATE

```javascript
await apiExecutar("alterar", {
  tipo_banco: "sqlserver",
  servidor: "127.0.0.1",
  banco: "FACILAPP_API",
  sql: "UPDATE dbo.API_TESTE SET NOME='DANIEL ALTERADO' WHERE ID_API_TESTE=1"
});
```

### DELETE

```javascript
await apiExecutar("excluir", {
  tipo_banco: "sqlserver",
  servidor: "127.0.0.1",
  banco: "FACILAPP_API",
  sql: "DELETE FROM dbo.API_TESTE WHERE ID_API_TESTE=1"
});
```

UPDATE e DELETE sempre precisam de `WHERE` baseado na chave única selecionada no browse.

### HFSQL dinâmico

```javascript
const lista = await apiExecutar("consultar_hfsql", {
  tabela: "API_TESTE",
  campos: "API_TESTEID,NOME"
});

await apiExecutar("inserir_hfsql", {
  tabela: "API_TESTE",
  campo_id: "API_TESTEID",
  campo_nome: "NOME",
  nome: "DANIEL"
});
```

O nome da tabela e dos campos é dinâmico. Não fixe `API_TESTE` dentro da API; ele é somente o banco de homologação.

### Descobrir tabelas e campos

Essas funções são somente leitura e não dependem de `ConsultaLivre=1`.
As credenciais são lidas do `ServidorAPI.ini`; nunca envie `usuario` ou
`senha` no JSON.

Listar todas as tabelas:

```json
{
  "funcao": "listar_tabelas",
  "tipo_banco": "hfsql",
  "servidor": "",
  "banco": ""
}
```

Resposta:

```json
{
  "ok": true,
  "tipo_banco": "hfsql",
  "tabelas": [
    { "esquema": "", "tabela": "CLIENTE" }
  ]
}
```

Listar campos, tipos e tamanhos:

```json
{
  "funcao": "listar_campos",
  "tipo_banco": "hfsql",
  "servidor": "",
  "banco": "",
  "esquema": "",
  "tabela": "CLIENTE"
}
```

Resposta:

```json
{
  "ok": true,
  "tipo_banco": "hfsql",
  "tabela": "CLIENTE",
  "campos": [
    {
      "campo": "ID_CLIENTE",
      "tipo": "Inteiro 8 bytes",
      "tipo_hfsql": "8-byte integer",
      "tamanho": "8"
    }
  ]
}
```

No SQL Server e PostgreSQL, envie `esquema` quando houver tabelas com o mesmo
nome em schemas diferentes. Nos demais casos ele pode ficar vazio.

Uso pelo helper oficial:

```javascript
const tabelas = await ServidorAPI.listarTabelas();
const campos = await ServidorAPI.listarCampos("CLIENTE");
```

## 8.1. Sintaxe que muda entre os bancos

| Banco | Schema comum | Limite de linhas | ID automático | Observação |
|---|---|---|---|---|
| SQL Server | `dbo.API_TESTE` | `SELECT TOP 10 ...` | `IDENTITY(1,1)` | Use `VARCHAR/NVARCHAR` para texto; `VARBINARY` exige conversão e não deve ser usado para nome comum |
| PostgreSQL | `public.api_teste` | `... LIMIT 10` | `GENERATED ALWAYS AS IDENTITY` | Identificadores criados entre aspas ficam sensíveis a maiúsculas/minúsculas |
| MariaDB | `API_TESTE` | `... LIMIT 10` | `AUTO_INCREMENT` | Banco/tabela podem variar por sistema operacional quanto a maiúsculas |
| MySQL | `API_TESTE` | `... LIMIT 10` | `AUTO_INCREMENT` | Usa o conector nativo MySQL e seção `[MySQL]` própria no INI |
| HFSQL | nome lógico/arquivo `.fic` | limite controlado pela API | `Automatic ID` | Use os nomes exatos da análise/Control Center |

Não copie `TOP 10` para PostgreSQL/MariaDB/MySQL e não copie `LIMIT 10` para SQL Server.

Para texto inserido em SQL livre, no mínimo duplique aspas simples. O ideal é evoluir o backend para parâmetros SQL.

```javascript
function sqlTexto(valor) {
  return `'${String(valor ?? "").replaceAll("'", "''")}'`;
}

const sql = `INSERT INTO API_TESTE (NOME) VALUES (${sqlTexto(nome)})`;
```

## 9. Roteiro obrigatório para criar browse e update

Antes de escrever HTML, a IA deve obter do usuário:

1. Tipo do banco.
2. Servidor e banco.
3. `CREATE TABLE` ou consulta da estrutura no catálogo.
4. SELECT exato do browse, com campos na ordem desejada.
5. SELECT exato para carregar um registro pela chave.
6. Nome da chave primária e se ela é autonumérica.
7. Campos editáveis, obrigatórios e somente leitura.
8. Validações e mensagens.
9. Regras dos botões Inserir, Alterar e Excluir.
10. Conversões especiais, especialmente datas Clarion.

Fluxo da tela:

1. Ao abrir, executar a consulta estruturada e preencher o browse.
2. Ao digitar na busca, filtrar os dados já carregados no navegador.
3. Ao clicar em uma linha, guardar o ID real e carregar os campos da edição.
4. Inserir sem enviar o ID autonumérico.
5. Alterar e excluir usando o ID selecionado; nunca assumir ID `0`.
6. Depois de gravar, executar somente uma atualização do browse.
7. Mostrar `Carregando...`, sucesso, vazio e erro de forma separada.
8. Impedir duplo clique enquanto uma requisição estiver em andamento.

Se `Todos` também retorna vazio, não culpe o filtro. Confirme servidor, banco, schema, tabela e estrutura real.

## 10. Padrão visual das telas de browse/update

Use o mesmo padrão azul das páginas de teste: cabeçalho azul, fundo cinza-claro, cards brancos, botões de 36 px e tabela compacta.

```css
* { box-sizing: border-box; }
html { overflow-y: scroll; }
body {
  margin: 0;
  background: #edf2f5;
  color: #17384f;
  font: 13px Arial, sans-serif;
}
header {
  background: #1673aa;
  color: #fff;
  padding: 12px 18px;
}
header h1 { margin: 0; font-size: 18px; line-height: 21px; }
header small { display: block; line-height: 21px; opacity: .9; }
.pagina { padding: 10px; }
.card {
  margin-bottom: 10px;
  padding: 10px;
  background: #fff;
  border: 1px solid #cad9e3;
  border-radius: 5px;
}
.barra {
  display: flex;
  align-items: end;
  gap: 8px;
  flex-wrap: wrap;
}
label { display: grid; gap: 4px; color: #486579; }
input, select, textarea {
  width: 100%;
  min-height: 36px;
  padding: 7px 10px;
  border: 1px solid #b8cddd;
  border-radius: 4px;
  background: #fff;
}
button {
  height: 36px;
  padding: 0 16px;
  border: 1px solid #1471a7;
  border-radius: 4px;
  background: #fff;
  color: #125f8c;
  cursor: pointer;
}
button.primary { background: #1673aa; color: #fff; }
button.danger { border-color: #cf4b4b; color: #b52d2d; }
button:disabled { opacity: .55; cursor: wait; }
.tabela { overflow: auto; max-height: 430px; }
table { width: 100%; border-collapse: collapse; white-space: nowrap; }
th, td { padding: 8px; border: 1px solid #cad9e3; text-align: left; }
th { position: sticky; top: 0; background: #e4eef5; }
th:first-child, td:first-child { width: 120px; }
pre.resultado {
  overflow: auto;
  padding: 10px;
  background: #0b3445;
  color: #bcecff;
  border-radius: 4px;
}
@media print {
  body { background: #fff; }
  header { background: #1673aa !important; color: #fff !important; print-color-adjust: exact; }
  .nao-imprimir, button { display: none !important; }
}
```

Ordem padrão da tela:

1. Cabeçalho com nome da tela e banco.
2. Campo Nome e botões Inserir, Alterar, Excluir, Atualizar, Imprimir e Exportar.
3. Campo Buscar ocupando a largura disponível.
4. Browse com coluna ID estreita.
5. Consulta livre e resultado JSON somente nas telas técnicas de teste.

## 11. Funções WINDEV existentes: Fetch, FetchSQL e FetchManual

Estas são funções do projeto WINDEV/Clarion. Elas não são nomes enviados para `POST /executar`.

### `Fetch(pSelect is string)`

Executa um `SELECT TOP 1` simples, identifica a tabela e o primeiro campo ID, e usa `FetchManual` para posicionar o arquivo real da análise.

```wlanguage
IF Fetch("SELECT TOP 1 * FROM CLIENTES WHERE CPF='123'") THEN
    Info(CLIENTES.NOME)
END
```

Regras atuais: uma tabela, sem JOIN/UNION, primeiro campo deve ser `ID` ou começar por `ID_`.

### `FetchSQL(pSelect is string)`

Executa um SELECT em `dsTabela` usando `CONN`. Retorna `True` quando executou e encontrou o primeiro registro. Depois do uso, feche `dsTabela`.

```wlanguage
IF FetchSQL("SELECT ID_CLIENTE,NOME FROM CLIENTES") THEN
    WHILE NOT HOut(dsTabela)
        Trace(dsTabela.NOME)
        HReadNext(dsTabela)
    END
END
HClose(dsTabela)
```

Na versão legada, `False` pode significar vazio ou erro; consulte também `gnFalha`.

### `FetchSQLUpdate(pSelect)`

O nome é histórico. A implementação atual usa `HExecuteSQLQuery` e devolve `dsTabela`; o retorno sozinho não confirma registros afetados. Não copie essa procedure para o serviço REST e não a trate como contrato moderno de INSERT/UPDATE/DELETE.

### `FetchManual(pTabela, pChave, pValor1, pValor2="")`

Posiciona o arquivo da análise pela chave simples ou composta.

```wlanguage
FetchManual(CLIENTES, "ID_CLIENTE", 10)
IF gnFalha = 0 THEN
    Trace(CLIENTES.NOME)
END
```

Em código novo, prefira `HFound(pTabela)` explicitando a tabela. `HFound()` sem parâmetro depende do último arquivo manipulado e pode causar erro difícil de reproduzir.

### Regra para o serviço

As funções legadas dependem de `CONN`, `dsTabela` e `gnFalha` globais e podem abrir `Info/Error`. No serviço HTTP:

- não abra janelas;
- use conexão e Data Source locais por requisição;
- devolva sucesso, vazio e erro em JSON;
- libere conexão e consulta mesmo em falha;
- nunca devolva senha, token ou SQL sensível.

## 12. Datas e conversões conforme o projeto

O formato de data não é único. Alguns projetos armazenam datas como inteiro Clarion; outros usam tipos nativos do banco, como `DATE`, `DATETIME` ou `TIMESTAMP`. Confirme o tipo real da coluna na estrutura do banco ou no `CREATE TABLE` antes de escrever qualquer conversão.

```wlanguage
nDataBanco is int = ConverteDataParaInteiro(dDataInicial)
dDataTela is Date = ConverteInteiroParaData(nValorLidoDoBanco)
```

- Se a coluna realmente armazenar data como inteiro Clarion, antes de gravar ou filtrar use `ConverteDataParaInteiro`.
- Se a coluna realmente for um inteiro Clarion, depois da leitura use `ConverteInteiroParaData` antes de exibir.
- Se a coluna for SQL `DATE`, `DATETIME` ou `TIMESTAMP`, use o valor nativo do banco e a formatação apropriada da aplicação.
- Se o tipo não estiver confirmado, pare e solicite a estrutura da tabela; não presuma Clarion nem data SQL nativa.
- A IA não deve inventar a fórmula de conversão no JavaScript; deve usar as funções oficiais do projeto ou solicitar sua implementação.

## 13. Respostas da API

Consulta com dados:

```json
{
  "ok": true,
  "dados": [
    { "ID_API_TESTE": "1", "NOME": "DANIEL" }
  ]
}
```

Consulta vazia válida:

```json
{ "ok": true, "dados": [] }
```

Erro controlado:

```json
{ "ok": false, "erro": "mensagem controlada" }
```

Vazio não é erro. A tela deve mostrar “Nenhum registro encontrado” e continuar operacional.

## 14. Diagnóstico das falhas que realmente ocorreram

| Sintoma | Causa provável | Ação correta |
|---|---|---|
| `funcao nao encontrada` | HTML chamou nome antigo/incorreto ou EXE antigo está em execução | Conferir função no Swagger e versão instalada; recompilar somente se mudou WLanguage |
| `tipo_banco nao suportado` | `tipo_banco` ausente ou com valor não aceito | Usar `sqlserver`, `postgresql`, `mariadb`, `mysql` ou `oracle` |
| `entrada nao encontrada` ao abrir URL | Rota não existe na versão carregada | Conferir nome exato da rota e arquivo publicado |
| `falha ao conectar` | INI, porta, serviço do banco, usuário ou senha | Testar o mesmo usuário diretamente no banco e usar `127.0.0.1` |
| `erro interno` | Driver/conector, exceção não convertida ou banco indisponível | Ver log do serviço e testar conector nativo de 64 bits |
| `tabela HFSQL nao encontrada ou nao declarada` | Nome/campo diferente ou arquivo não declarado | Confirmar nome real e `HDeclareExternal`; não fixar tabela de teste |
| `signal is aborted without reason` | Timeout do navegador ou requisição anterior cancelada | Corrigir lentidão, evitar chamadas duplicadas e aumentar timeout apenas após medir |
| `ok:true` com `dados:[]` | Consulta válida sem linhas, banco/schema errado ou ambiente errado | Executar o SELECT no banco com o mesmo usuário e confirmar banco/schema |
| Swagger vazio/antigo | `openapi.json` não publicado no diretório servido | Validar `/openapi.json`, copiar arquivo para `WEB` e recarregar sem cache |
| Alteração no código não aparece | Serviço ainda executa EXE anterior | Recompilar, reinstalar/substituir EXE e reiniciar serviço |
| Oito registros demoram muito | Banco acessado pelo IP público, chamadas duplicadas ou conexão lenta | Usar `127.0.0.1`, fazer um refresh por ação e medir separadamente API/banco |

## 15. Publicação sem erro

### Mudou somente arquivo web

Arquivos como `diagnostico.html`, `sql-teste.html`, `hfsql-teste.html`, `swagger.html`, `openapi.json`, `config.js` e o manual podem ser copiados diretamente para a pasta `WEB` instalada. Depois recarregue a página sem cache.

### Mudou WLanguage

Arquivos como `ProcessarAPI`, `ServidorSocketHTTP` ou procedures do Tray exigem:

1. Colar o código na procedure/evento com o nome correto.
2. Manter declarações globais na seção “Global declarations”.
3. Compilar o projeto.
4. Gerar/substituir o executável.
5. Reiniciar `Service ServidorApi`.
6. Testar `/status`, token, consulta estruturada e, quando habilitada, consulta livre.

Não misture declaração global com “End of initialization”. Em WINDEV, colocar a variável na seção errada pode compilar diferente ou interromper a inicialização.

## 16. Ordem de homologação

1. `GET /status`.
2. `POST /executar` com `{ "funcao": "status" }` e token.
3. Consulta estruturada `consultar` ou `consultar_hfsql`.
4. Inserir um nome.
5. Atualizar o browse e confirmar novo ID.
6. Alterar pelo ID selecionado.
7. Excluir pelo ID selecionado.
8. Executar `consulta_livre` nos bancos SQL habilitados.
9. Testar busca local, impressão e exportação.
10. Só então testar pelo IP público.

## 17. Benchmark de 5.000 requisições

Teste primeiro o HTTP sem banco:

```powershell
powershell -ExecutionPolicy Bypass -File ".\07-Benchmark-ServidorApi.ps1" -BaseUrl "https://api.facilapp.com.br" -Modo status -TotalRequisicoes 5000 -Concorrencias 1,5,10,20
```

Depois teste banco. Compare o resultado local com o IP público. O relatório deve informar sucessos, falhas, RPS, média, P50, P95 e máxima.

Se o teste local é rápido e o público é lento, investigue rede/NAT. Se ambos são lentos apenas no modo SQL, investigue conexão, consulta, índice e driver do banco.

## 18. Bloco para entregar a outra IA

Copie este contexto ao iniciar outra sessão:

```text
Você trabalhará com o Servidor API FacilApp em WINDEV 28.
Base pública: https://api.facilapp.com.br
Rota RPC: POST /executar
Token: cabeçalho X-API-Token, lido do config.js/INI; nunca peça senha do banco ao navegador.
Bancos locais da API usam 127.0.0.1: SQL Server 1433, PostgreSQL 5432, MariaDB/MySQL 3306 e HFSQL 4900.
Funções SQL atuais: consultar, consulta_livre, inserir, alterar e excluir, sempre com tipo_banco.
Funções HFSQL atuais: consultar_hfsql, inserir_hfsql, alterar_hfsql e excluir_hfsql.
Não crie funções _teste nem use nomes antigos separados por banco.
Antes de criar browse/update, solicite CREATE TABLE, SELECT do browse, SELECT por ID, chave primária, campos obrigatórios, validações e regra de gravação.
Não invente campos. Não envie ID autonumérico no INSERT. UPDATE/DELETE usam o ID selecionado.
Use o padrão CSS azul deste manual e um único refresh após gravar.
Web/openapi não exige recompilar; WLanguage exige recompilar e reiniciar o serviço.
Valide na ordem: status, token, consultar, inserir, alterar, excluir e consulta_livre.
```

## 19. Configuracao e anexos

O arquivo `ServidorAPI.ini` fica ao lado do EXE e e a unica fonte das
credenciais dos bancos e do diretorio de anexos. O frontend nunca envia senha
de banco nem um caminho do Windows.

Exemplo da configuracao de anexos:

```ini
[Arquivos]
Ativo=1
DiretorioBase=D:\ERP\ANEXOS
TamanhoMaximoMB=15
ExtensoesPermitidas=pdf,jpg,jpeg,png,webp,xml
```

Quando a rota de anexos estiver habilitada, o frontend deve enviar somente a
categoria (`comprovantes`, `produtos`, `documentos`) e o conteudo do arquivo.
O servidor valida extensao e tamanho, gera o nome seguro e grava dentro de
`DiretorioBase`. Nunca aceite `C:\...`, `..\` ou um caminho informado pelo navegador.

## 20. Estrategia obrigatoria: cliente x Servidor API

Antes de criar telas, menu ou integracao, mantenha estas responsabilidades
separadas:

```text
CLIENTE / APLICACAO WEB OU DESKTOP
  - arquivo local do desenvolvedor (XML ou INI): IP, porta e token da API
  - chama POST /executar
  - exibe os dados e aplica o menu recebido da API

SERVIDOR API
  - ServidorAPI.ini: bancos, usuarios, senhas, limites e diretorio de anexos
  - executa consultas e regras autorizadas
  - guarda/consulta as permissoes de menu no banco configurado
```

Regras para a IA:

1. Nunca colocar banco, usuario, senha, diretorio do ERP ou regras internas no
   XML/INI/JavaScript do cliente.
2. No cliente, guardar somente endereco da API, porta e token.
3. Nao criar JSON local de permissoes nem duplicar o menu em varios arquivos.
   O cliente deve pedir os acessos a API e montar a tela conforme a resposta.
4. Antes de criar o menu, solicitar a tabela, a chave, o campo JSON e a regra de
   permissao existentes no banco do cliente. Nao inventar campos ou tabelas.
5. Para comprovantes e anexos, o cliente envia categoria e conteudo; a API decide
   a pasta usando `[Arquivos]` do `ServidorAPI.ini`.

## 21. Repositorio de arquivos da API

O repositorio foi validado com salvar, alterar o arquivo diretamente no disco e
ler novamente pela API. A leitura nao usa cache: sempre retorna o conteudo atual
do arquivo armazenado.

Todas as operacoes usam `POST /executar`, o cabecalho `X-API-Token` e uma
destas funcoes:

| Funcao | Finalidade |
|---|---|
| `arquivo_salvar` | Cria ou substitui um arquivo enviado em Base64 |
| `arquivo_ler` | Retorna o arquivo em Base64 |
| `arquivo_listar` | Lista os arquivos de uma pasta logica |
| `arquivo_excluir` | Remove um arquivo da pasta logica |

O cliente nunca envia um caminho Windows. Ele informa apenas os identificadores:
`categoria`, `desenvolvedor`, `usuario` e `nome_arquivo`.

Estrutura criada pelo servidor:

```text
DiretorioBase\categoria\desenvolvedor\usuario\nome_arquivo
```

Exemplo de salvar:

```json
{
  "funcao": "arquivo_salvar",
  "categoria": "comprovantes",
  "desenvolvedor": "facilapp",
  "usuario": "cliente_123",
  "nome_arquivo": "pagamento.pdf",
  "conteudo_base64": "BASE64_DO_ARQUIVO"
}
```

Exemplo de ler:

```json
{
  "funcao": "arquivo_ler",
  "categoria": "comprovantes",
  "desenvolvedor": "facilapp",
  "usuario": "cliente_123",
  "nome_arquivo": "pagamento.pdf"
}
```

Seguranca obrigatoria:

1. Nao enviar `C:\\...`, `..`, barras ou caracteres de caminho nos campos.
2. A API valida os nomes e monta a pasta abaixo de `DiretorioBase`.
3. Antes de enviar, respeitar `TamanhoMaximoMB` e `ExtensoesPermitidas` da
   secao `[Arquivos]` do `ServidorAPI.ini`.

## 22. Fechamento obrigatorio de uma entrega

Nao declarar projeto, tela ou integracao como concluido apenas porque os arquivos
foram criados. Ao final, a IA deve validar cada item aplicavel e entregar uma
checklist com um estado por linha:

- OK: testado com resposta ou evidencia real;
- PENDENTE: existe, mas ainda nao foi testado;
- NAO TESTADO: nao foi possivel testar e deve informar o motivo.

Checklist minima:

1. API HTTP: GET /status.
2. Autenticacao: POST /executar com token.
3. Configuracao local do cliente: URL, porta e token lidos de um unico arquivo.
4. Cada tela criada: carregamento, acao principal, erro e estado vazio.
5. Cada operacao de banco criada: consulta, inserir, alterar e excluir quando
   fizerem parte do escopo.
6. Repositorio, quando usado: salvar, listar, ler e excluir.
7. Publicacao: confirmar qual EXE e qual pasta WEB estao em uso.

Se nao houver evidencia real, a IA deve escrever PENDENTE ou NAO TESTADO; nunca
usar as frases "validado com sucesso" ou "concluido" por suposicao.

## 23. Catalogo copiavel: todos os endpoints e funcoes publicas

Substitua somente os valores de exemplo por nomes reais. Todos os exemplos de
funcao abaixo usam o helper `apiExecutar(funcao, dados)`, que chama a ponte
confiavel do cliente. A ponte envia `POST /executar` com `X-API-Token`; o
navegador nao recebe esse token. A resposta normal possui `ok`; consultas
retornam `dados`; falhas retornam `erro`.

### Rotas HTTP

```text
GET  /status                       -> saude da API, sem token
POST /executar                     -> todas as funcoes abaixo, com token
GET  /openapi.json                 -> contrato OpenAPI
GET  /content.md                   -> manual principal para IA
GET  /prompt_inicio_sessao.md      -> prompt para iniciar um projeto cliente
GET  /memoria_de_contexto.md       -> memoria tecnica do projeto
GET  /diagnostico.html             -> painel local de diagnostico
GET  /swagger                      -> Swagger UI
GET  /cfg                          -> configuracao local do ServidorAPI
```

Teste minimo de rota:

```powershell
Invoke-RestMethod -Uri "http://127.0.0.1:5000/status" -Method Get
```

### Descoberta e leitura SQL

```javascript
// 1. Validar token no barramento
await apiExecutar("status", {});

// 2. Listar tabelas do banco configurado
await apiExecutar("listar_tabelas", {
  tipo_banco: "sqlserver", servidor: "127.0.0.1", banco: "FACILAPP_API"
});

// 3. Listar campos, tipos e tamanhos de uma tabela
await apiExecutar("listar_campos", {
  tipo_banco: "sqlserver", servidor: "127.0.0.1", banco: "FACILAPP_API",
  esquema: "dbo", tabela: "API_TESTE"
});

// 4. SELECT estruturado: campos separados por virgula, maximo de 500 linhas
await apiExecutar("consultar", {
  tipo_banco: "sqlserver", servidor: "127.0.0.1", banco: "FACILAPP_API",
  tabela: "API_TESTE", campos: "ID_API_TESTE,NOME"
});

// 5. SELECT livre: somente administracao/homologacao confiavel
await apiExecutar("consulta_livre", {
  tipo_banco: "postgresql", servidor: "127.0.0.1", banco: "FACILAPP_API",
  sql: "SELECT id_api_teste, nome FROM public.api_teste ORDER BY id_api_teste"
});
```

Para `listar_tabelas`, `listar_campos`, `consultar` e `consulta_livre`,
`tipo_banco` pode ser `sqlserver`, `postgresql`, `mariadb`, `mysql` ou
`oracle`. Use `esquema: "dbo"` no SQL Server e `esquema: "public"` no
PostgreSQL quando aplicavel.

### Gravacao SQL generica: somente integracao controlada

```javascript
await apiExecutar("inserir", {
  tipo_banco: "mariadb", servidor: "127.0.0.1", banco: "FACILAPP_API",
  sql: "INSERT INTO API_TESTE (NOME) VALUES ('EXEMPLO')"
});

await apiExecutar("alterar", {
  tipo_banco: "mariadb", servidor: "127.0.0.1", banco: "FACILAPP_API",
  sql: "UPDATE API_TESTE SET NOME='ALTERADO' WHERE ID_API_TESTE=1"
});

await apiExecutar("excluir", {
  tipo_banco: "mariadb", servidor: "127.0.0.1", banco: "FACILAPP_API",
  sql: "DELETE FROM API_TESTE WHERE ID_API_TESTE=1"
});
```

Essas tres funcoes enviam SQL inteiro. Nao use em frontend publico; veja a
regra de seguranca da secao 7.

### HFSQL Client/Server

```javascript
await apiExecutar("consultar_hfsql", {
  tabela: "API_TESTE", campos: "ID_API_TESTE,NOME"
});

await apiExecutar("inserir_hfsql", {
  tabela: "API_TESTE", campo_id: "ID_API_TESTE",
  campo_nome: "NOME", nome: "EXEMPLO"
});

await apiExecutar("alterar_hfsql", {
  tabela: "API_TESTE", campo_id: "ID_API_TESTE",
  campo_nome: "NOME", id: 1, nome: "ALTERADO"
});

await apiExecutar("excluir_hfsql", {
  tabela: "API_TESTE", campo_id: "ID_API_TESTE",
  campo_nome: "NOME", id: 1
});
```

### Repositorio de arquivos controlado pela API

Nao envie caminho Windows. A API monta a pasta a partir de `categoria`,
`desenvolvedor` e `usuario` abaixo de `[Arquivos] DiretorioBase`.

```javascript
// Salvar texto, JS, INI, XML ou Base64 de PDF/imagem
await apiExecutar("arquivo_salvar", {
  categoria: "configuracoes", desenvolvedor: "facilapp", usuario: "cliente_001",
  nome_arquivo: "menu.js",
  conteudo_base64: btoa("window.MENU = [];")
});

// Ler: a resposta contem conteudo_base64
await apiExecutar("arquivo_ler", {
  categoria: "configuracoes", desenvolvedor: "facilapp", usuario: "cliente_001",
  nome_arquivo: "menu.js"
});

// Listar arquivos daquele espaco controlado
await apiExecutar("arquivo_listar", {
  categoria: "configuracoes", desenvolvedor: "facilapp", usuario: "cliente_001"
});

// Excluir um unico arquivo
await apiExecutar("arquivo_excluir", {
  categoria: "configuracoes", desenvolvedor: "facilapp", usuario: "cliente_001",
  nome_arquivo: "menu.js"
});
```

Depois de cada chamada, valide explicitamente:

```javascript
const resposta = await apiExecutar("consultar_hfsql", {
  tabela: "API_TESTE", campos: "ID_API_TESTE,NOME"
});
if (!resposta.ok) throw new Error(resposta.erro || "Falha sem detalhe");
console.log(resposta.dados || []);
```

## 24. Protecao do cliente: LAN controlada x web publica

### Dois modos permitidos

**Modo LAN controlada:** para computadores e tablets internos do cliente, o
token pode ficar no INI/XML/config.js do projeto e seguir no header
`X-API-Token`. A protecao essencial e que usuario, senha e endereco do banco
continuem somente no `ServidorAPI.ini`; o cliente nunca abre SQL diretamente.
Firewall, VLAN, VPN e permissoes do Windows/rede reduzem quem consegue chegar
na API.

**Modo web publica/internet:** token mestre nao pode ficar no navegador. Use PHP
ou outro backend/ponte confiavel, que guarda o token e chama a API no servidor.

Fluxo reforcado para internet:

```text
Tela do usuario (sem token)
  -> ponte local ou backend confiavel
     -> POST /executar com X-API-Token
        -> ServidorAPI
```

Portanto, a IA deve escolher o modo antes de criar a tela. Nao tratar o token
LAN como senha SQL: ele identifica a aplicacao na API, mas quem protege o banco
e a credencial SQL mantida exclusivamente no ServidorAPI.ini.

### Opcao preferida em rede local: ponte WINDEV

Para ERP WINDEV em LAN, e possivel chamar a API diretamente com token no INI do
projeto. Uma ponte WINDEV/servico local e opcional: use-a quando quiser ocultar
o token, centralizar permissoes por usuario ou limitar ainda mais as acoes.

1. Se houver ponte, o token fica no INI protegido da ponte ou em credencial do Windows.
2. A tela HTML chama a ponte em `http://127.0.0.1:PORTA_LOCAL` ou no backend do
   ERP, sem token.
3. A ponte aceita somente operacoes previamente permitidas, por exemplo
   `listar_pedidos` e `gravar_pedido`; ela nao recebe SQL livre do navegador.
4. A ponte chama o ServidorAPI usando `X-API-Token` internamente.
5. A ponte retorna ao navegador apenas os dados autorizados.

Se a ponte for local, escute somente em `127.0.0.1`, valide `Origin`, use uma
sessao/nonce por usuario e nao exponha a porta para a rede inteira sem
autenticacao adicional.

### Outras opcoes validas

| Cenario | Onde fica o token | Quem chama o ServidorAPI |
|---|---|---|
| Aplicacao desktop WINDEV em LAN | INI da aplicacao ou ponte opcional | codigo WINDEV |
| Site interno ou internet | backend do site (PHP, .NET, Java, Python etc.) | backend |
| Aplicacao mobile | backend proprio; nunca no APK/IPA | backend |
| Integracao servidor-servidor | cofre de segredos ou variavel protegida | servico integrador |

Nao use proxy JavaScript ou extensao de navegador para fingir que escondeu o
token. Em LAN isso pode ser uma decisao operacional aceita; em web publica,
quem recebe o navegador pode le-lo e por isso o token deve ficar no backend.

### Contrato recomendado para web publica

O frontend envia somente dados de negocio e autenticacao do usuario do proprio
cliente:

```javascript
// Frontend: sem URL do ServidorAPI e sem token mestre.
const resposta = await fetch("/api/pedidos", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ acao: "listar", filial: 1 })
});
```

O backend/ponte decide se o usuario pode executar `listar`, monta a chamada
permitida e guarda a evidencia no log. Para gravacao, ele valida campos e IDs;
nunca repassa `sql` recebido do navegador para `consulta_livre`, `inserir`,
`alterar` ou `excluir`.

### Checklist de escolha

- Aplicacao WINDEV ou HTML usada somente na rede local do cliente: token direto
  e permitido; manter SQL somente no ServidorAPI.ini.
- Site exposto na internet, dominio publico ou mobile distribuido: usar
  PHP/backend/ponte e nunca entregar token mestre ao navegador.

### Fluxo preferido para um site PHP

Diagrama: `fluxo_php_seguro.svg` (na pasta `WEB` da instalacao).

```text
Navegador sem token -> Apache/Nginx + api.php -> ServidorAPI -> JSON final
                              |
                       $TOKEN somente no PHP
```

O navegador faz `POST /api.php` no proprio site. O PHP valida a sessao do
usuario, aceita apenas a acao prevista, usa cURL para chamar
`POST /executar` com `X-API-Token` e devolve apenas o JSON filtrado. O arquivo
PHP e a variavel `$TOKEN` nunca sao enviados ao navegador.

Exemplo minimo de ponte PHP (o token deve vir de variavel de ambiente ou arquivo
fora da pasta publica):

```php
<?php
// api.php - executado somente no servidor web
$token = getenv('FACILAPP_API_TOKEN');
$entrada = json_decode(file_get_contents('php://input'), true) ?: [];

// Exemplo: permitir somente a acao de listar clientes.
if (($entrada['acao'] ?? '') !== 'listar_clientes') {
    http_response_code(403);
    exit(json_encode(['ok' => false, 'erro' => 'acao nao permitida']));
}

$payload = json_encode([
    'funcao' => 'consultar',
    'tipo_banco' => 'sqlserver',
    'servidor' => '127.0.0.1',
    'banco' => 'FACILAPP_API',
    'tabela' => 'CLIENTE',
    'campos' => 'ID_CLIENTE,NOME'
]);

$curl = curl_init('http://127.0.0.1:5000/executar');
curl_setopt_array($curl, [
    CURLOPT_POST => true,
    CURLOPT_POSTFIELDS => $payload,
    CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'X-API-Token: ' . $token],
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT => 15
]);
$resposta = curl_exec($curl);
curl_close($curl);
header('Content-Type: application/json; charset=utf-8');
echo $resposta;
```
