# Recurso FiveM (fivedash)

Arquitetura, instalação, todas as opções do config.lua, loja.lua e logs.lua, convars, autenticação, sincronização, protocolo e atualizações do recurso fivedash.

Source: https://fivedash.fivex.gg/docs/pt/developers/fivem-resource

O `fivedash` é o recurso que liga a cidade ao FiveDash. Ele roda no lado do servidor do FXServer, detecta a framework sozinho e faz valer no jogo o que o painel decide: libera e tira a whitelist, entrega as compras da loja e manda os logs dos scripts. Esta página é para quem administra o servidor ou desenvolve scripts e quer entender como ele funciona por dentro.

## Antes de começar

* **FXServer com Node 22:** o `fxmanifest.lua` pede `node_version "22"`. Use um artifact recente do FXServer.
* **Banco MySQL (para whitelist e loja):** um destes drivers iniciado: `oxmysql`, `ghmattimysql` ou `mysql-async`. Sem driver, a conexão com o painel funciona, mas a whitelist fica só no Discord e a loja não entrega.
* **Chave do servidor:** gerada em **Servidor → Visão geral → Conexão com o jogo → Conectar servidor → Gerar chave**. Só **Dono** e **Admin** da organização geram a chave.
* **Nome da pasta:** mantenha a pasta como `fivedash`. Os scripts chamam `exports.fivedash:…` e conferem `GetResourceState("fivedash")`.

## Arquitetura

```text
Cidade (FXServer)                                  FiveDash
┌───────────────────────────────┐   HTTPS (sai)   ┌──────────────────────┐
│ fivedash/server/main.js       │ ──────────────▶ │ POST /api/fivem/sync │
│  ├─ detecção da framework      │ ◀────────────── │  comandos pendentes  │
│  ├─ módulo Discord (whitelist) │                 └──────────────────────┘
│  ├─ módulo Loja (entregas)     │   HTTPS (sai)   ┌──────────────────────┐
│  └─ módulo Logs (export)       │ ──────────────▶ │ serviço de logs      │
│ MySQL da cidade (oxmysql…)     │                 └──────────────────────┘
└───────────────────────────────┘
```

* **Só o servidor:** todo o código roda em `server/main.js` (um único bundle). O `client/main.js` está vazio: nada roda no jogo dos jogadores, e o recurso não registra nenhum evento de rede (`RegisterNetEvent`). Jogadores não conseguem mandar comandos nem logs para ele.
* **Sempre de dentro para fora:** o recurso chama o painel; o painel nunca chama a cidade. Não precisa abrir porta nem liberar IP.
* **Independente de framework:** o recurso não depende de nenhuma framework. Ele descobre qual a cidade usa e conversa com ela por exports, eventos e pelo banco. Veja [Frameworks](https://fivedash.fivex.gg/docs/pt/developers/frameworks).
* **Módulos:**
  * **Discord:** aplica as liberações da whitelist (`whitelist.allow`) e tira de quem sai do Discord (`whitelist.revoke`).
  * **Loja:** grava cada entrega no MySQL da cidade antes de responder, entrega uma vez só (nunca duas) e espera o jogador entrar quando precisa dele online.
  * **Logs:** recebe `exports.fivedash:log(...)` dos scripts e manda em lotes.

### Arquivos da pasta

| Arquivo          | O que é                                                                              | Atualizado sozinho?                          |
| ---------------- | ------------------------------------------------------------------------------------ | -------------------------------------------- |
| `fxmanifest.lua` | Manifesto do recurso (`fx_version "cerulean"`, `game "common"`, `node_version "22"`) | Sim                                          |
| `server/main.js` | Todo o código do servidor                                                            | Sim                                          |
| `client/main.js` | Lado do cliente (vazio por enquanto)                                                 | Sim                                          |
| `config.lua`     | Chave e opções gerais. Lido só pelo servidor                                         | Nunca. Opções novas são acrescentadas no fim |
| `loja.lua`       | Opções da entrega da loja                                                            | Nunca. Criado no primeiro boot se faltar     |
| `logs.lua`       | Opções dos logs pelo export                                                          | Nunca. Criado no primeiro boot se faltar     |

Os arquivos `.lua` de configuração não estão em `files` no manifesto: eles nunca vão para os jogadores.

## Instalar

### Gere a chave e baixe o recurso

No painel, vá em **Servidor → Visão geral**, no cartão **Conexão com o jogo** clique em **Conectar servidor** e depois em **Gerar chave**. Clique em **Baixar fivedash.zip**: o `config.lua` já vem com a chave.

### Extraia na pasta resources

Extraia a pasta `fivedash` dentro de `resources` (ou de uma subpasta como `resources/[fivedash]`).

### Inicie depois da framework

No `server.cfg`, coloque `ensure fivedash` depois do driver do banco e da framework:

```bash
ensure oxmysql
ensure qb-core
ensure fivedash
```

### Reinicie e confira

Reinicie o servidor (ou digite `refresh` e `ensure fivedash` no console). No console aparece `[FiveDash] Conectado ao FiveDash como "Cidade Exemplo".` e o painel mostra **Conectado**.

Já tinha o recurso instalado? Cole só a chave nova no `fivedash/config.lua`:

```lua
FiveDash = {
	Key = "fdk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
	Framework = "auto",
	AutoUpdate = true,
	Debug = false,
}
```

> A chave dá acesso ao seu servidor no FiveDash. Não publique o `config.lua` no GitHub, não mande em tickets e não cole em chats ou IAs. Se ela vazar, clique em **Gerar nova chave**: a antiga para de funcionar na hora.

> Recursos que iniciam depois do `fivedash` também são percebidos: quando o driver do banco, a framework ou o inventário (`ox_inventory`, `qb-inventory`) iniciam, o recurso detecta de novo sozinho. O boot espera até 30 segundos pelo banco.

## config.lua

Lido só pelo servidor. Depois de mudar algo, rode `restart fivedash`.

| Opção        | O que faz                                                                                                                                                                               | Padrão   |
| ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- |
| `Key`        | A chave do servidor no FiveDash (começa com `fdk_`). Vazia, o recurso não conecta e avisa no console                                                                                    | `""`     |
| `Framework`  | `"auto"` detecta sozinho. Para forçar: `"creative-enchanted"`, `"qbox"`, `"qbcore"`, `"esx"`, `"ox-core"` ou `"vrp"`. Forçada e não encontrada, nenhuma é usada e o console mostra erro | `"auto"` |
| `AutoUpdate` | Instala sozinho as versões novas do recurso (os `.lua` de configuração ficam como estão). Desligado, só avisa no console                                                                | `true`   |
| `Debug`      | Mostra cada sincronização e comando no console, e as evidências da detecção                                                                                                             | `false`  |

Duas opções não vêm no arquivo, mas são aceitas:

| Opção    | O que faz                                                                                                                                                                              | Padrão                                        |
| -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------- |
| `Url`    | Endereço do painel. O recurso baixado pelo painel já traz esta linha preenchida com `https://fivedash.fivex.gg`; deixe como veio                                                       | `https://fivedash.fivex.gg` (vem no download) |
| `Locale` | Idioma do console antes da primeira conexão: `"pt-BR"` ou `"en"`. Depois, vale o **Idioma da cidade** do painel. O download já traz essa linha para cidades que não estão em português | português                                     |

### Como as atualizações mexem nos arquivos de configuração

Quando uma versão nova traz uma opção que o seu arquivo não tem, o recurso acrescenta essa opção (com o comentário e o valor padrão) antes do `}` final, sem mudar nem apagar o que você escreveu. Antes, ele guarda uma cópia em `config.lua.bak` (ou `loja.lua.bak`, `logs.lua.bak`). O console avisa quais opções entraram. Se o arquivo não tem a forma `FiveDash = { ... }`, ele não é tocado e as opções novas usam o padrão.

## loja.lua

Opções da entrega da loja na cidade. Detalhes de cada tipo de entrega em [Entrega no jogo](https://fivedash.fivex.gg/docs/pt/store/in-game-delivery).

| Opção             | O que faz                                                                                                                                                     | Padrão   |
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- |
| `Enabled`         | Liga a entrega da loja nesta cidade. Desligada, o site da loja para de vender até você ligar de novo                                                          | `true`   |
| `Notify`          | Avisa o jogador na tela quando a compra chega                                                                                                                 | `true`   |
| `NotifyTitle`     | Título do aviso (até 40 caracteres). `"Loja"` aparece no idioma da cidade                                                                                     | `"Loja"` |
| `Announce`        | Anuncia para a cidade inteira quem comprou o quê (uma vez por pedido; presentes não são anunciados)                                                           | `false`  |
| `SpawnDelay`      | Segundos entre o jogador escolher o personagem e a entrega (de 0 a 120)                                                                                       | `10`     |
| `AllowedCommands` | Comandos de console que a loja pode rodar. Vazio libera qualquer um, menos os bloqueados. Para travar, liste o começo de cada um: `{ "darmoedas", "addcar" }` | `{}`     |

Comandos sempre bloqueados na entrega por comando, mesmo com `AllowedCommands` vazio: `quit`, `stop`, `start`, `restart`, `ensure`, `refresh`, `exec`, `set`, `sets`, `setr`, `seta`, `rcon_password`, `sv_licensekey`, `sv_hostname`, `sv_maxclients`, `endpoint_add_tcp`, `endpoint_add_udp`, `load_server_icon` e `fivedash`. Uma linha é um comando só: `;` e quebras de linha são recusados. Cada comando roda no máximo 100 vezes por entrega.

## logs.lua

Opções dos logs mandados pelo export `exports.fivedash:log`. Os webhooks não dependem deste arquivo.

| Opção              | O que faz                                                                                                                        | Padrão                     |
| ------------------ | -------------------------------------------------------------------------------------------------------------------------------- | -------------------------- |
| `Enabled`          | Liga o envio de logs pelo export. Desligado, as chamadas são ignoradas                                                           | `true`                     |
| `PlayerInfo`       | Lê o passaporte e o nome do personagem de cada jogador do log, pela framework                                                    | `true`                     |
| `Identifiers`      | Identificadores do jogador que vão em cada log. Aceitos: `license`, `license2`, `discord`, `steam`, `fivem`, `xbl`, `live`, `ip` | `{ "license", "discord" }` |
| `Coords`           | Guarda a posição do primeiro jogador do log quando o script não manda `coords`                                                   | `true`                     |
| `Disabled`         | Chaves de logs que nunca saem da cidade. Ex.: `{ "chat" }`                                                                       | `{}`                       |
| `AllowedResources` | Recursos que podem mandar logs. Vazio deixa qualquer um. Ex.: `{ "vrp", "inventory" }`                                           | `{}`                       |
| `Echo`             | Mostra cada log no console quando entra (só para testes)                                                                         | `false`                    |

O ritmo de envio é fixo: um lote por segundo, até 250 logs por lote, e até 20.000 logs guardados na fila enquanto o painel estiver fora. Linhas antigas como `FlushInterval`, `BatchSize` e `MaxQueue` ficam no arquivo, mas são ignoradas.

## Convars

As convars ganham do `config.lua`. Use `set` no `server.cfg`, nunca `sets` (o `sets` publica o valor na lista de servidores, e a chave ficaria visível).

| Convar               | Equivale a                   | Exemplo                                                                                     |
| -------------------- | ---------------------------- | ------------------------------------------------------------------------------------------- |
| `fivedash_key`       | `Key`                        | `set fivedash_key "fdk_xxxxxxxxxxxxxxxx"`                                                   |
| `fivedash_framework` | `Framework`                  | `set fivedash_framework "qbox"`                                                             |
| `fivedash_debug`     | `Debug` (use `1` para ligar) | `set fivedash_debug 1`                                                                      |
| `fivedash_locale`    | `Locale`                     | `set fivedash_locale "en"`                                                                  |
| `fivedash_url`       | `Url`                        | `set fivedash_url "https://fivedash.fivex.gg"` (só se o seu `config.lua` não tiver o `Url`) |

As convars precisam estar no `server.cfg` antes do `ensure fivedash`.

## Como ele se autentica

* **Uma chave por servidor:** a chave tem o formato `fdk_` + 32 caracteres. O painel guarda só o hash dela e mostra apenas os 4 últimos caracteres (**Recurso** `…a1b2`). Ela aparece inteira uma vez, quando é gerada.
* **Cabeçalho:** toda chamada manda `Authorization: Bearer fdk_…`.
* **Uma cidade por chave:** cada boot do recurso cria um ID de instância (`X-FiveDash-Instance`). Se a mesma chave estiver ligada em dois servidores ao mesmo tempo, o painel mantém o que já sincronizava e recusa o outro (`409`) até o primeiro ficar 10 segundos quieto (um restart). Assim, nenhuma entrega vai para a cidade errada nem acontece duas vezes.
* **Trocar ou desligar:** **Gerar nova chave** substitui a chave na hora. **Desconectar** apaga a ligação e descarta os comandos que ainda não foram entregues.

## Como ele sincroniza

O recurso faz uma sincronização atrás da outra com `POST /api/fivem/sync`. Cada uma leva o estado da cidade e os resultados dos comandos anteriores; a resposta traz os comandos novos. Quando não há nada, o painel segura a resposta por até 3 segundos (long polling), então uma liberação ou uma compra chega na cidade em poucos segundos.

| Regra                   | Valor                                                                         |
| ----------------------- | ----------------------------------------------------------------------------- |
| Frequência máxima       | 1 sincronização por segundo por servidor (mais rápido recebe `429`)           |
| Espera sem comandos     | até 3 segundos                                                                |
| Tempo limite da chamada | 45 segundos                                                                   |
| Sem conexão             | tenta de novo de 2 s em 2 s, dobrando até 60 s, e avisa no console uma vez só |
| Comandos em paralelo    | até 8                                                                         |

### O que vai em cada sincronização

```json
{
  "protocol": 1,
  "resource": "0.6.0",
  "framework": {
    "id": "qbcore",
    "name": "QBCore",
    "version": "1.3.0",
    "features": ["whitelist", "store"],
    "idKind": "discord",
    "warnings": []
  },
  "players": 48,
  "maxPlayers": 64,
  "results": [{ "id": "cmd_exemplo", "ok": true }],
  "store": { "enabled": true, "problem": null, "kinds": ["group", "vehicle", "money", "item", "command"], "idLabel": "Citizen ID", "waiting": 2 },
  "deliveries": [{ "id": "<pedido>:0:0", "state": "done", "reason": null, "at": "2026-11-09T18:00:00.000Z" }],
  "hold": true
}
```

| Campo                   | O que é                                                                                                                                                                              |
| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `protocol`              | Versão do protocolo do recurso (hoje `1`)                                                                                                                                            |
| `resource`              | Versão do recurso                                                                                                                                                                    |
| `framework`             | Framework detectada: `id`, `name`, `version`, `features` (`whitelist`, `store`), `idKind` (`account`, `token` ou `discord`) e `warnings` (no idioma da cidade). `null` sem framework |
| `players`, `maxPlayers` | Jogadores agora e `sv_maxclients` (padrão 32)                                                                                                                                        |
| `results`               | Resultado de cada comando: `{ "id", "ok": true }` ou `{ "id", "ok": false, "code", "message" }`                                                                                      |
| `store`                 | Estado da loja na cidade: ligada, problema, o que entrega, nome do ID e entregas esperando                                                                                           |
| `deliveries`            | Entregas que mudaram de estado (`done`, `waiting`, `failed`), até 200 por sincronização                                                                                              |
| `hold`                  | `false` na primeira sincronização (e depois de uma queda), para o painel responder na hora                                                                                           |

### O que volta

```json
{
  "server": { "name": "Cidade Exemplo" },
  "commands": [
    { "id": "cmd_exemplo", "type": "whitelist.allow", "gameId": "1234567", "discordId": "123456789012345678" }
  ],
  "update": null,
  "locale": "pt-BR"
}
```

| Tipo de comando    | O que faz na cidade                                                                                      |
| ------------------ | -------------------------------------------------------------------------------------------------------- |
| `whitelist.allow`  | Libera o ID (e liga o Discord, quando vem) na whitelist da framework                                     |
| `whitelist.revoke` | Tira a whitelist pelo ID e/ou pelo Discord e, se vier `kickMessage`, derruba o jogador com essa mensagem |
| `store.deliver`    | Recebe as entregas de um pedido (com `retry: true` no **Reenviar**, só o que falhou roda de novo)        |
| `store.cancel`     | Pedido devolvido: descarta o que ainda espera na cidade                                                  |
| `store.lookup`     | Pergunta para quem a compra iria (ID e nome do personagem, e se está online)                             |

Todo comando é idempotente: se o resultado se perde e o comando volta, o recurso responde o que já fez, sem rodar de novo. Códigos de erro: `not_found` (o ID não existe no banco), `unsupported` (a framework não faz isso), `invalid` (ID em formato errado) e `failed` (outro problema, com `message`).

### Respostas do painel

| Status | Erro          | O que o recurso faz                                                          |
| ------ | ------------- | ---------------------------------------------------------------------------- |
| `200`  |               | Aplica os comandos e segue                                                   |
| `400`  | `bad_request` | Corpo inválido ou maior que 256 KB. Tenta de novo com espera                 |
| `401`  | `invalid_key` | Chave inválida ou substituída. Mostra o erro e tenta de novo em 60 s         |
| `409`  | `in_use`      | A chave está ligada em outro servidor agora. Tenta de novo em 10 s           |
| `426`  | `outdated`    | Versão do recurso não aceita mais. Tenta de novo em 10 min                   |
| `429`  | `too_many`    | Rápido demais (ou chaves erradas demais do mesmo IP). Espera o `Retry-After` |

## Logs dos scripts

Os logs do export não passam pela sincronização: eles vão por outro caminho, para uma cidade cheia nunca atrasar os comandos.

1. O recurso pede o mapa dos logs em `GET /api/fivem/logs/channels` (com a mesma chave). O mapa diz quais chaves de log existem e para onde mandar, e vale por pouco tempo.
2. A cada segundo, manda até 250 logs em `POST /api/fivem/logs`, com gzip quando o lote passa de 1 KB.
3. Sem conexão, os logs ficam na fila (até 20.000) e vão quando o painel voltar.

Logs com uma chave que ainda não existe no painel ficam esperando na fila até você criar o log. Veja [API de logs](https://fivedash.fivex.gg/docs/pt/developers/logs-api) e [Exports, eventos e comandos](https://fivedash.fivex.gg/docs/pt/developers/exports-and-events).

## Versão do protocolo

O recurso e o painel combinam o formato das mensagens pelo campo `protocol` (`GAME_PROTOCOL`, hoje `1`). Quando o painel muda algo que quebra versões antigas, ele sobe esse número e passa a responder `426` para recursos mais velhos, que mostram no console "versão do recurso não aceita mais" e pedem para baixar a nova. Mudanças que não quebram nada não mexem no número.

## Atualizações

Com `AutoUpdate = true` (padrão), o recurso se atualiza sozinho:

1. A sincronização avisa que existe uma versão nova.
2. O recurso baixa cada arquivo e confere o tamanho e o SHA-256 antes de gravar qualquer coisa. Só podem ser atualizados `fxmanifest.lua`, `server/*.js` e `client/*.js` (até 10 MB cada). Os `.lua` de configuração nunca são tocados.
3. Grava os arquivos e confere cada um. Se um falha, os que já foram gravados voltam ao conteúdo anterior.
4. A versão nova entra no próximo restart do servidor, ou com `restart fivedash` no console. O recurso nunca se reinicia sozinho.

O painel mostra no cartão **Conexão com o jogo**: "Versão X do recurso disponível…". Se uma atualização falha, o recurso tenta de novo em 10 minutos. Com `AutoUpdate = false`, ele só avisa no console: `FiveDash X disponível (este é o Y). AutoUpdate está desligado no config.lua.`

Para atualizar à mão: no cartão **Conexão com o jogo**, clique em **Conectar servidor** e depois em **Baixar a última versão**. Esse zip vem sem chave: troque só `fxmanifest.lua`, `server/` e `client/` e mantenha os seus `.lua` de configuração.

## Tabelas no banco da cidade

O recurso cria estas tabelas no MySQL da cidade (com `CREATE TABLE IF NOT EXISTS`). Não apague: elas guardam o que precisa sobreviver a um restart.

| Tabela                      | Para quê                                                                                                                                     |
| --------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
| `fivedash_identities`       | A license, license2, Discord, Steam e FiveM de quem tenta entrar. Liga um Discord às licenses (whitelist por Discord e compras pelo Discord) |
| `fivedash_whitelist_grants` | As liberações dadas pelo FiveDash nas whitelists da QBCore, do Qbox e do esx_whitelist, reaplicadas a cada boot                              |
| `fivedash_ace_groups`       | Grupos ACE dados pela loja (`group.vip`, por exemplo), reaplicados a cada boot                                                               |
| `fivedash_store_deliveries` | Cada entrega da loja e o estado dela. Garante que nada é entregue duas vezes                                                                 |
| `fivedash_store_groups`     | Grupos com prazo comprados na loja, para tirar quando vencem                                                                                 |

## Mensagens do console

Toda linha do recurso começa com `[FiveDash]`.

| Mensagem                                                          | O que fazer                                                                               |
| ----------------------------------------------------------------- | ----------------------------------------------------------------------------------------- |
| "Sem chave do painel: o servidor não está conectado ao FiveDash." | Cole a chave no `Key` do `fivedash/config.lua`                                            |
| "A chave do painel não parece certa (começa com fdk\_)."          | Confira se copiou a chave inteira                                                         |
| "chave recusada pelo painel"                                      | A chave foi trocada ou o servidor foi desconectado. Gere uma nova e troque o `Key`        |
| "esta chave já está sendo usada por outro servidor ligado agora"  | Cada servidor tem a sua chave. Se você só reiniciou, ele entra sozinho em alguns segundos |
| "versão do recurso não aceita mais"                               | Baixe a versão nova no painel                                                             |
| "Sem conexão com o painel (…). Tentando de novo sozinho."         | Problema de rede ou do painel. O recurso tenta sozinho                                    |
| "Nenhuma framework reconhecida…"                                  | Rode `fivedash detect` para ver as evidências, ou force com `Framework`                   |

## Perguntas frequentes

**Preciso abrir alguma porta no servidor?**
Não. O recurso só faz chamadas HTTPS para fora.

**Posso usar a mesma chave em dois servidores (teste e produção)?**
Não. Cada servidor do FiveDash tem a sua chave e só uma cidade fica ligada por vez. Crie outro servidor no painel para o ambiente de teste.

**A atualização automática apaga minha configuração?**
Não. `config.lua`, `loja.lua` e `logs.lua` nunca são sobrescritos. Opções novas são só acrescentadas, com cópia `.bak` antes.

**Funciona sem framework?**
Sim. A conexão funciona, a whitelist fica só no Discord e a loja entrega só por comandos de console.

## Próximos passos

[Frameworks](https://fivedash.fivex.gg/docs/pt/developers/frameworks)Como cada framework é detectada e o que funciona nela.
[Exports, eventos e comandos](https://fivedash.fivex.gg/docs/pt/developers/exports-and-events)O export de logs, o evento e os comandos de console.
[API de logs](https://fivedash.fivex.gg/docs/pt/developers/logs-api)O endpoint compatível com webhook do Discord.
[Entrega no jogo](https://fivedash.fivex.gg/docs/pt/store/in-game-delivery)Como as compras chegam na cidade.
