Pular para o conteúdo principal

Referência da API

Referência completa para todas as APIs turn.* disponíveis em Apps Lua.


APIs Principais

APIs para gerenciamento de apps e configuração.

turn.app

Gerencia a configuração do seu app, subscrições de campos de contato e mapeamentos de ativos. Esta é a nova API preferencial.

Gerenciamento de Configuração

  • get_config(): Retorna uma tabela de toda a configuração (sempre busca a mais recente do banco de dados).
  • get_config_value(key): Retorna o valor para uma chave específica.
  • update_config(updates): Mescla uma tabela de atualizações na configuração existente.
  • set_config(new_config): Substitui toda a configuração.
-- Obtém configuração completa
local config = turn.app.get_config()

-- Obtém valor específico
local api_key = turn.app.get_config_value("api_key")
if not api_key then
turn.logger.error("A chave da API não está configurada!")
return false
end

-- Atualiza configuração (mesclagem)
turn.app.update_config({ last_sync = os.time() })

-- Substitui toda a configuração
turn.app.set_config({ api_key = "new_key", webhook_url = "https://example.com" })

Subscrições de Campos de Contato

  • get_contact_subscriptions(): Obtém a lista atual de campos subscritos.
  • set_contact_subscriptions(fields): Define a lista de campos de contato para subscrever.
    • fields (table): Um array Lua de nomes de campos (ex: {"name", "age"}).
    • Retorna success, reason - verifique o sucesso antes de continuar.
-- Define subscrições na instalação ou mudança de configuração
if event == "install" or event == "config_changed" then
local success, reason = turn.app.set_contact_subscriptions({"name", "surname", "email"})
if not success then
turn.logger.error("Falha ao definir subscrições: " .. reason)
return false
end
end

-- Obtém subscrições atuais
local fields = turn.app.get_contact_subscriptions()
for _, field in ipairs(fields) do
turn.logger.info("Inscrito em: " .. field)
end

-- Cancela inscrição de todos os campos
turn.app.set_contact_subscriptions({})

Mapeamento de Ativos e Jornadas (Avançado)

Para apps que incluem ativos ou jornadas, essas funções mantêm mapeamentos de UUID:

  • set_asset_mapping(mapping): Mapeia placeholders de ativos para seus UUIDs instalados.
  • get_asset_mapping(): Retorna a tabela de mapeamento de ativos atual.
  • get_asset_uuid(placeholder): Obtém o UUID para um ativo específico.
  • set_journey_mapping(mapping): Mapeia arquivos de jornada para seus UUIDs instalados.
  • get_journey_mapping(): Retorna a tabela de mapeamento de jornada atual.
-- Durante a instalação, se seu app inclui ativos empacotados
turn.app.set_asset_mapping({
["logo.png"] = "uuid-1234-5678",
["template.html"] = "uuid-abcd-efgh"
})

-- Mais tarde, quando você precisar do ativo
local logo_uuid = turn.app.get_asset_uuid("logo.png")

turn.assets

Carrega arquivos estáticos (como templates ou imagens) de uma pasta assets/ no arquivo .zip do seu app.

  • list(directory_path): Lista arquivos em um diretório.
    • directory_path (string, opcional): O caminho dentro de assets/.
  • exists(asset_path): Verifica se um arquivo existe.
    • asset_path (string): O caminho completo para o ativo.
  • load(asset_path): Carrega o conteúdo de um ativo.
    • asset_path (string): O caminho completo para o ativo.
local journey_files = turn.assets.list("journeys")
for _, filename in ipairs(journey_files) do
local content = turn.assets.load("journeys/" .. filename)
turn.logger.info("Template de jornada carregado: " .. filename)
end

turn.configuration

OBSOLETO

Esta API está obsoleta e será removida em uma versão futura. Por favor, use turn.app em vez disso. Todas as funções neste módulo registrarão um aviso nos logs do seu app.

turn.manifest

API padronizada para instalação e desinstalação de apps baseadas em manifest. Esta API lida com a instalação de campos de contato, ativos de mídia e jornadas definidos no arquivo manifest.json do seu app.

turn.manifest.install(manifest)

Instala um app baseado em seu manifest. O processo de instalação:

  1. Cria todos os campos de contato
  2. Instala todos os ativos de mídia e rastreia seus IDs
  3. Cria todas as jornadas em dois passos (criar desabilitadas, depois vincular e habilitar)
  4. Cria todos os templates (pulado se mesmo nome+idioma já existe)
  5. Cria todos os WhatsApp flows (pulado se PUBLISHED, atualizado se DRAFT)

Parâmetros:

  • manifest (table): A tabela de manifest (normalmente carregada de manifest.json)

Retorna: Uma tabela com resultados da instalação:

{
success = true|false,
app = {...}, -- Metadados do app do manifest
contact_fields = {
total = number,
created = number,
failed = {field_name1, field_name2, ...}
},
media_assets = {
total = number,
created = number,
failed = {filename1, filename2, ...}
},
journeys = {
total = number,
created = number,
failed = {journey_name1, journey_name2, ...}
},
templates = {
total = number,
created = number,
skipped = number, -- Templates que já existiam
failed = {"name:language", ...}
},
flows = {
total = number,
created = number,
updated = number, -- Flows DRAFT que foram atualizados
skipped = number, -- Flows PUBLISHED que já existiam
failed = {flow_name1, flow_name2, ...}
}
}

Estrutura do Manifest: Seu manifest.json pode incluir:

{
"app": {
"name": "my-app",
"version": "1.0.0"
},
"contact_fields": [
{
"type": "STRING",
"name": "customer_id",
"display": "ID do Cliente",
"private": false
}
],
"media_assets": [
{
"asset_path": "images/logo.png",
"filename": "logo.png",
"content_type": "image/png",
"description": "Logo da empresa"
}
],
"journeys": [
{
"name": "Jornada de Boas-vindas",
"file": "welcome.md",
"description": "Dá boas-vindas a novos usuários"
}
],
"templates": [
{
"name": "mensagem_boas_vindas",
"file": "mensagem_boas_vindas.json"
}
],
"flows": [
{
"name": "flow_cadastro",
"file": "cadastro.json",
"categories": ["SIGN_UP"],
"publish": false
}
]
}

Localização dos Arquivos de Assets: Cada tipo de asset é carregado de um subdiretório específico dentro de assets/:

Tipo de AssetCampo no ManifestLocalização do Arquivo
Ativos de mídiaasset_path: "images/logo.png"assets/images/logo.png
Jornadasfile: "welcome.md"assets/journeys/welcome.md
Templatesfile: "mensagem_boas_vindas.json"assets/templates/mensagem_boas_vindas.json
Flowsfile: "cadastro.json"assets/flows/cadastro.json

Por exemplo, com esta estrutura de diretórios:

my_app/
└── assets/
├── manifest.json
├── images/
│ └── logo.png
├── journeys/
│ └── welcome.md
├── templates/
│ └── mensagem_boas_vindas.json
└── flows/
└── cadastro.json

Formato do Arquivo de Template: Cada arquivo JSON de template deve conter a definição completa do template:

{
"language": "pt_BR",
"category": "UTILITY",
"components": [
{ "type": "BODY", "text": "Olá {{1}}, bem-vindo ao nosso serviço!" }
]
}

Nota: O campo name do manifest tem precedência sobre qualquer nome no arquivo.

Substituição de Placeholders: Ao criar jornadas que referenciam outras jornadas ou ativos de mídia, use placeholders:

  • Mídia: asset:filename.png - Será substituído pelo external_id real
  • Jornadas: journey:journey-file.md - Será substituído pelo UUID real da jornada

Exemplo de Uso:

function App.on_event(app, number, event, data)
if event == "install" then
-- Carrega manifest dos ativos
local manifest_json = turn.assets.load("manifest.json")
local manifest = turn.json.decode(manifest_json)

-- Instala tudo
local results = turn.manifest.install(manifest)

if results.success then
turn.logger.info("Instalação concluída com sucesso!")
turn.logger.info(string.format(
"Campos de contato: %d/%d criados",
results.contact_fields.created,
results.contact_fields.total
))
turn.logger.info(string.format(
"Ativos de mídia: %d/%d instalados",
results.media_assets.created,
results.media_assets.total
))
turn.logger.info(string.format(
"Jornadas: %d/%d criadas",
results.journeys.created,
results.journeys.total
))
else
turn.logger.error("Instalação concluída com erros")
if #results.contact_fields.failed > 0 then
turn.logger.error("Campos de contato falhados: " ..
table.concat(results.contact_fields.failed, ", "))
end
end

return results.success
end
end

turn.manifest.uninstall(manifest, options)

Desinstala um app baseado em seu manifest. Por padrão, isso realiza uma desinstalação segura que preserva dados do usuário (campos de contato).

Parâmetros:

  • manifest (table): A tabela de manifest (normalmente carregada de manifest.json)
  • options (table, opcional): Opções de configuração
    • remove_contact_fields (boolean): Se true, remove campos de contato (padrão: false)

Retorna: Uma tabela com resultados da desinstalação:

{
success = true|false,
app = {...}, -- Metadados do app do manifest
contact_fields = {
removed = number,
skipped = boolean, -- true se campos de contato foram preservados
failed = {field_name1, field_name2, ...}
},
media_assets = {
count = number,
removed = 0,
note = "Ativos de mídia são preservados e podem ser deletados manualmente se necessário"
},
journeys = {
removed = number,
failed = {journey_name1, journey_name2, ...}
},
templates = {
count = number,
removed = 0,
note = "Templates são preservados pois requerem aprovação da Meta e podem ser usados em outros lugares"
},
flows = {
count = number,
removed = 0,
note = "Flows são preservados pois não podem ser deletados via API e podem ser usados em outros lugares"
}
}

Exemplo de Uso:

function App.on_event(app, number, event, data)
if event == "uninstall" then
-- Carrega manifest dos ativos
local manifest_json = turn.assets.load("manifest.json")
local manifest = turn.json.decode(manifest_json)

-- Desinstalação segura (preserva campos de contato)
local results = turn.manifest.uninstall(manifest)

if results.success then
turn.logger.info("Desinstalação concluída com sucesso!")
turn.logger.info(string.format(
"Jornadas removidas: %d",
results.journeys.removed
))

if results.contact_fields.skipped then
turn.logger.info("Campos de contato: Preservados (dados do usuário retidos)")
end
else
turn.logger.error("Desinstalação concluída com erros")
end

return results.success
end
end

Desinstalação Completa (remove tudo incluindo dados do usuário):

-- Remove tudo, incluindo campos de contato
local results = turn.manifest.uninstall(manifest, {
remove_contact_fields = true
})

Notas Importantes:

  • Por padrão, campos de contato são preservados para proteger dados do usuário
  • Ativos de mídia são sempre preservados (podem ser reutilizados por outros apps)
  • Templates são sempre preservados (requerem aprovação da Meta e podem ser usados por outros fluxos/apps)
  • Jornadas são removidas usando um processo de duas fases (limpar conteúdo, depois deletar)
  • Apenas jornadas que correspondem aos nomes do manifest são removidas

APIs de Comunicação

APIs para comunicação externa e interação com a plataforma.

turn.apps

Chama funções em outros Apps Lua instalados no mesmo número. Isso permite arquiteturas modulares de apps onde apps especializados podem expor funcionalidades reutilizáveis.

  • call(app_name, function_name, arguments): Chama uma função em outro app.
    • app_name (string): O nome do app alvo (conforme definido em seu manifest).
    • function_name (string): A função a chamar no app alvo.
    • arguments (table): Argumentos para passar para a função. Deve ser uma tabela (use {} para nenhum argumento).
    • Retorna success, result - um booleano indicando sucesso e o resultado da função ou mensagem de erro.
-- Chamar uma função em outro app com argumentos
local success, result = turn.apps.call("fhir_app", "create_appointment", {"2024-01-15", "10:00"})
if success then
turn.logger.info("Consulta criada: " .. tostring(result))
else
turn.logger.error("Falha ao criar consulta: " .. turn.json.encode(result))
end

-- Chamar sem argumentos (ainda deve passar tabela vazia)
local success, data = turn.apps.call("analytics_app", "get_daily_stats", {})

Tratando chamadas no app alvo:

O app alvo recebe chamadas entre apps como eventos journey_event com function_name e args nos dados:

function App.on_event(app, number, event, data)
if event == "journey_event" then
local fn = data.function_name
local args = data.args or {}

if fn == "create_appointment" then
local date, time = args[1], args[2]
-- Processa e retorna resultado
return "continue", { appointment_id = "APT-12345" }
elseif fn == "get_daily_stats" then
return "continue", { visits = 150, conversions = 23 }
end
end
end

Notas de Segurança:

  • Apps só podem chamar outros apps instalados no mesmo número
  • Chamadas entre números são negadas por razões de segurança
  • Funções assíncronas (aquelas que retornam wait) não podem ser chamadas de outros apps

turn.contacts

Encontra contatos e gerencia seus campos personalizados.

  • find(query): Encontra um contato.
    • query (table): Pares chave-valor para buscar por (ex: { msisdn = "+27..." }).
  • update_contact_details(contact, details): Atualiza os campos de um contato.
    • contact (table): O objeto contato de find().
    • details (table): Pares chave-valor de campos para atualizar.
  • create_contact_field(field_def): Cria um novo campo personalizado no esquema.
    • field_def (table): Uma tabela com chaves type, name e display.
local contact, found = turn.contacts.find({ msisdn = "+27820000000" })
if found then
turn.contacts.update_contact_details(contact, { loyalty_id = "LTY-12345" })
end

turn.http

Faz requisições HTTP externas.

  • request(options): Envia uma requisição HTTP.
    • options (table): Uma tabela com url, method, headers (table) e body (string).
local body, status = turn.http.request({
url = "https://api.example.com/v1/events",
method = "POST",
headers = { ["Content-Type"] = "application/json" },
body = turn.json.encode({ message = "Olá" })
})

turn.journeys

Gerencia Jornadas programaticamente.

  • create(journey_def): Cria uma nova Jornada.
    • journey_def (table): Uma tabela com name, notebook e enabled.
  • update(journey_uuid, updates): Atualiza uma Jornada existente.
    • journey_uuid (string): O UUID da jornada para atualizar.
    • updates (table): Uma tabela com name, notebook ou enabled.
  • delete(journey_def): Deleta uma Jornada pelo nome.
    • journey_def (table): Uma tabela com o name da jornada.
  • list(): Retorna uma lista de todas as Jornadas.
local journey, ok = turn.journeys.create({
name = "Integração de Novo Usuário",
notebook = turn.assets.load("journeys/onboarding.md"),
enabled = true
})

turn.templates

Cria e gerencia templates de mensagens do WhatsApp.

  • get(name, language): Obtém um template pelo nome e idioma.
    • name (string): O nome do template.
    • language (string): O código do idioma (ex: "en", "pt_BR").
  • list(): Retorna uma lista de todos os templates para o número atual.
  • exists(name, language): Verifica se um template existe.
    • name (string): O nome do template.
    • language (string): O código do idioma.
    • Retorna true se o template existe, false caso contrário.
  • create(template_def): Cria um novo template.
    • template_def (table): Uma tabela com name, language, category e components.
    • Retorna template, success - o objeto do template e um booleano indicando sucesso.
    • Se um template com o mesmo nome+idioma já existe, retorna o template existente (idempotente).
    • Novos templates começam com status "PENDING" aguardando aprovação da Meta.
-- Verifica se o template existe
if not turn.templates.exists("mensagem_boas_vindas", "pt_BR") then
-- Cria um novo template
local template, success = turn.templates.create({
name = "mensagem_boas_vindas",
language = "pt_BR",
category = "UTILITY", -- AUTHENTICATION, MARKETING ou UTILITY
components = {
{ type = "HEADER", format = "TEXT", text = "Bem-vindo à {{1}}" },
{ type = "BODY", text = "Olá {{1}}, obrigado por se juntar!" },
{ type = "FOOTER", text = "Responda PARAR para cancelar" },
{ type = "BUTTONS", buttons = {
{ type = "QUICK_REPLY", text = "Começar" }
}}
}
})

if success then
turn.logger.info("Template criado: " .. template.name)
-- template.status será "PENDING" até a Meta aprovar
else
turn.logger.error("Falha ao criar template: " .. template)
end
end

-- Obtém um template existente
local template = turn.templates.get("mensagem_boas_vindas", "pt_BR")

-- Lista todos os templates
local templates = turn.templates.list()
for _, t in ipairs(templates) do
turn.logger.info(t.name .. " (" .. t.language .. "): " .. t.status)
end

Tipos de Componentes:

  • HEADER: Cabeçalho opcional com format (TEXT, IMAGE, VIDEO, DOCUMENT, LOCATION) e text para formato TEXT
  • BODY: Corpo da mensagem obrigatório com text contendo o conteúdo principal
  • FOOTER: Rodapé opcional com text
  • BUTTONS: Array de botões opcional com type (QUICK_REPLY, URL, PHONE_NUMBER, COPY_CODE, FLOW, VOICE_CALL)

Categorias:

  • AUTHENTICATION: Para senhas únicas e verificação
  • MARKETING: Para conteúdo promocional
  • UTILITY: Para mensagens transacionais (confirmações, atualizações, etc.)

turn.flows

Cria e gerencia WhatsApp Flows para interações estruturadas.

  • get(name): Obtém um flow pelo nome.
    • name (string): O nome do flow.
    • Retorna o objeto do flow ou nil se não encontrado.
  • list(): Retorna uma lista de todos os flows para o WABA atual.
  • exists(name): Verifica se um flow existe.
    • name (string): O nome do flow.
    • Retorna true se o flow existe, false caso contrário.
  • create(flow_def): Cria um novo flow.
    • flow_def (table): Uma tabela com name, json e categories opcional.
    • Retorna flow, success - o objeto do flow e um booleano indicando sucesso.
    • Se um flow com o mesmo nome já existe, retorna o flow existente (idempotente).
    • Novos flows são criados com status "DRAFT" e devem ser publicados via ferramentas da Meta.
-- Verifica se o flow existe
if not turn.flows.exists("flow_cadastro") then
-- Cria um novo flow
local flow, success = turn.flows.create({
name = "flow_cadastro",
json = '{"version":"5.0","screens":[...]}',
categories = { "SIGN_UP" } -- Opcional, padrão é { "OTHER" }
})

if success then
turn.logger.info("Flow criado: " .. flow.name .. " (ID: " .. flow.id .. ")")
-- flow.status será "DRAFT" - publique via ferramentas da Meta
else
turn.logger.error("Falha ao criar flow: " .. flow)
end
end

-- Obtém um flow existente
local flow = turn.flows.get("flow_cadastro")

-- Lista todos os flows
local flows = turn.flows.list()
for _, f in ipairs(flows) do
turn.logger.info(f.name .. ": " .. f.status)
end

Status dos Flows:

  • DRAFT: Flow está em desenvolvimento, pode ser atualizado
  • PUBLISHED: Flow está ativo e pode ser usado em conversas
  • DEPRECATED: Flow foi marcado para aposentadoria
  • BLOCKED: Flow foi bloqueado pela Meta
  • THROTTLED: Flow está com taxa limitada

Categorias:

  • SIGN_UP, SIGN_IN, APPOINTMENT_BOOKING, LEAD_GENERATION, CONTACT_US, CUSTOMER_SUPPORT, SURVEY, OTHER

turn.leases

Usado para retomar Jornadas em espera. Veja a seção de fluxo assíncrono para um exemplo detalhado.

  • send_input(chat_uuid, input_data): Envia dados para uma Jornada pausada, retomando-a.
    • chat_uuid (string): O UUID do chat cuja Jornada está aguardando.
    • input_data (any): Os dados para enviar como resultado do bloco app().
turn.leases.send_input(chat_uuid, { payment_status = "confirmed" })

Dados e Utilitários

APIs para processamento de dados, segurança e utilitários.

turn.json

Codifica e decodifica dados JSON.

  • encode(data, options): Codifica uma tabela Lua em uma string JSON.
    • data (table): A tabela Lua para codificar.
    • options (table, opcional): ex: { indent = true }.
  • decode(json_string): Decodifica uma string JSON em uma tabela Lua.
    • json_string (string): A string para decodificar.
local my_table = { name = "João Silva", age = 30 }
local json_string = turn.json.encode(my_table)

turn.encoding

Codifica e decodifica dados binários em vários formatos.

Codificação Base64

Codificação base64 padrão para incorporar dados binários em formatos JSON ou texto.

  • base64_encode(data): Codifica dados binários para string base64 com caracteres padrão (+, /) e preenchimento (=).
  • base64_decode(encoded): Decodifica string base64 para dados binários. Gera erro em entrada inválida (capture com pcall).
-- Codificar dados de imagem para JSON
local image_data = turn.assets.load("images/logo.png")
local base64_image = turn.encoding.base64_encode(image_data)

-- Enviar em requisição HTTP
turn.http.request({
url = "https://api.example.com/upload",
method = "POST",
body = turn.json.encode({ image = base64_image })
})

-- Decodificar com tratamento de erros
local ok, decoded = pcall(function()
return turn.encoding.base64_decode(encoded_data)
end)
if not ok then
turn.logger.error("Base64 inválido: " .. decoded)
end

Codificação Base64 Segura para URL

Variante segura para URL usando - e _ em vez de + e /, sem preenchimento. Ideal para tokens em URLs e parâmetros de consulta.

  • base64_url_encode(data): Codifica para base64 seguro para URL (sem preenchimento).
  • base64_url_decode(encoded): Decodifica base64 seguro para URL. Gera erro em entrada inválida.
-- Criar token seguro para URL
local session_data = turn.json.encode({ user_id = 123, timestamp = os.time() })
local token = turn.encoding.base64_url_encode(session_data)
-- Usar em URL: https://example.com/verify?token=<token>

Codificação Hexadecimal

Converte dados binários de/para strings hexadecimais. Útil para exibir hashes e depuração.

  • hex_encode(data): Codifica dados binários para string hexadecimal em minúsculas.
  • hex_decode(hex_string): Decodifica string hexadecimal para binário. Aceita maiúsculas e minúsculas. Gera erro em entrada inválida.
-- Exibir hash em formato hexadecimal
local hash = turn.crypto.sha256("dados importantes")
local hex_hash = turn.encoding.hex_encode(hash)
turn.logger.info("Hash dos dados: " .. hex_hash)

-- Decodificar hexadecimal
local binary = turn.encoding.hex_decode("48656c6c6f")
-- Retorna: "Hello"

turn.crypto

Operações criptográficas para hash seguro, HMAC e geração aleatória.

Aviso de Segurança

Todas as operações criptográficas usam o módulo :crypto do Erlang com algoritmos padrão da indústria. A verificação HMAC usa comparação de tempo constante para prevenir ataques de temporização.

Geração de Assinatura HMAC

Gera assinaturas HMAC para verificação de webhook e autenticação de API.

  • hmac_sha256(key, message): Retorna HMAC-SHA256 como binário.
  • hmac_sha256_hex(key, message): Retorna HMAC-SHA256 como string hexadecimal.
  • hmac_sha256_base64(key, message): Retorna HMAC-SHA256 como base64.
  • hmac_sha512(key, message): Retorna HMAC-SHA512 como binário.
  • hmac_sha512_hex(key, message): Retorna HMAC-SHA512 como string hexadecimal.
  • hmac_sha512_base64(key, message): Retorna HMAC-SHA512 como base64.
-- Gerar assinatura de webhook
local secret = turn.app.get_config_value("webhook_secret")
local payload = turn.json.encode({ event = "payment.completed" })
local signature = turn.crypto.hmac_sha256_hex(secret, payload)

-- Enviar para serviço externo
turn.http.request({
url = "https://partner.com/webhook",
method = "POST",
headers = {
["X-Signature"] = signature
},
body = payload
})

Verificação de Assinatura HMAC

Verifica assinaturas de webhook de serviços externos (Stripe, GitHub, PayPal, etc.).

  • verify_hmac_sha256(key, message, expected_signature): Verifica assinatura HMAC-SHA256 usando comparação de tempo constante. Retorna true se válido, false caso contrário. Aceita assinatura como hexadecimal ou binário.
-- Verificar webhook do Stripe
function App.on_event(app, number, event, data)
if event == "http_request" then
local signature = data.headers["Stripe-Signature"]
local webhook_secret = turn.app.get_config_value("stripe_webhook_secret")

-- Verificação de tempo constante previne ataques de temporização
if not turn.crypto.verify_hmac_sha256(webhook_secret, data.body, signature) then
turn.logger.warn("Assinatura de webhook inválida")
return true, { status = 401, body = "Assinatura inválida" }
end

-- Processar webhook verificado
local event_data = turn.json.decode(data.body)
-- ... processar evento ...

return true, { status = 200, body = "OK" }
end
end

Hash Criptográfico

Gera hashes para integridade de dados e checksums.

  • sha256(data): Retorna hash SHA-256 como binário.
  • sha256_hex(data): Retorna hash SHA-256 como string hexadecimal.
  • md5(data): Retorna hash MD5 como binário.
  • md5_hex(data): Retorna hash MD5 como string hexadecimal.
-- Gerar hash de conteúdo
local content = "Dados importantes para rastrear"
local hash = turn.crypto.sha256_hex(content)
turn.logger.info("Hash do conteúdo: " .. hash)

-- Armazenar hash para verificação posterior
turn.app.update_config({ last_sync_hash = hash })

Geração Aleatória Segura

Gera valores aleatórios criptograficamente seguros para tokens, IDs de sessão e nonces.

  • random_bytes(length): Gera bytes aleatórios (máx 1024). Usa :crypto.strong_rand_bytes/1.
  • random_string(length): Gera string aleatória segura para URL (máx 1024). Perfeito para tokens.
-- Gerar token de sessão
local session_token = turn.crypto.random_string(32)
turn.app.update_config({ session_token = session_token })

-- Gerar chave de API
local api_key = turn.crypto.random_string(64)

-- Gerar bytes aleatórios para chave de criptografia
local encryption_key = turn.crypto.random_bytes(32)
local key_hex = turn.encoding.hex_encode(encryption_key)

Criptografia Autenticada AES-GCM

Criptografa e descriptografa dados sensíveis usando AES-256-GCM, fornecendo confidencialidade e integridade. Ideal para tokens de sessão, cookies e dados de configuração sensíveis.

  • aes_gcm_encrypt(plaintext, key): Criptografa dados com AES-256-GCM.
    • plaintext (string): Os dados a serem criptografados.
    • key (string): Deve ter exatamente 32 bytes (use sha256() para derivar de segredos).
    • Retorna: Texto cifrado codificado em Base64 contendo nonce, dados criptografados e tag de autenticação.
  • aes_gcm_encrypt(plaintext, key, aad): Criptografa com Dados Adicionais Autenticados.
    • aad (string): Dados de contexto que devem corresponder durante a descriptografia (ex: ID do usuário).
  • aes_gcm_decrypt(ciphertext, key): Descriptografa dados criptografados com AES-256-GCM.
    • ciphertext (string): Texto cifrado codificado em Base64 de aes_gcm_encrypt().
    • key (string): Deve ter exatamente 32 bytes.
    • Retorna: plaintext, nil em caso de sucesso, ou nil, mensagem_de_erro em caso de falha.
  • aes_gcm_decrypt(ciphertext, key, aad): Descriptografa com verificação de AAD.
-- Derivar uma chave de 32 bytes de um segredo
local key = turn.crypto.sha256(turn.app.get_config_value("encryption_secret"))

-- Criptografar dados sensíveis
local encrypted = turn.crypto.aes_gcm_encrypt("dados secretos", key)
turn.logger.info("Criptografado: " .. encrypted)

-- Descriptografar dados
local plaintext, err = turn.crypto.aes_gcm_decrypt(encrypted, key)
if err then
turn.logger.error("Falha na descriptografia: " .. err)
else
turn.logger.info("Descriptografado: " .. plaintext)
end

-- Usando AAD para vincular texto cifrado ao contexto (ex: ID do usuário)
-- Isso previne ataques de replay de tokens entre diferentes usuários
local user_id = "user-123"
local session = turn.json.encode({ token = "abc", expires = os.time() + 3600 })
local encrypted_session = turn.crypto.aes_gcm_encrypt(session, key, user_id)

-- A descriptografia falhará se o AAD não corresponder
local data, err = turn.crypto.aes_gcm_decrypt(encrypted_session, key, user_id)
if err then
turn.logger.warn("Falha na verificação da sessão: " .. err)
return false
end

Notas de Segurança:

  • Cada criptografia gera um nonce aleatório único (sem reutilização de nonce)
  • GCM fornece confidencialidade e integridade (criptografia autenticada)
  • A descriptografia falha se o texto cifrado for adulterado, a chave estiver errada ou o AAD não corresponder
  • Use AAD para vincular o texto cifrado ao contexto quando apropriado (previne ataques de replay)
  • Nunca codifique chaves diretamente - armazene segredos de forma segura na configuração do app

turn.i18n

Suporte à internacionalização (i18n) para apps multi-idioma usando arquivos PO. As traduções são carregadas de assets/locales/{locale}/LC_MESSAGES/{domain}.po no arquivo ZIP do seu app.

Funções de Tradução

  • t(msgid): Traduz uma string usando o locale padrão e o domínio "messages".
  • t(msgid, opts): Traduz com opções:
    • locale (string, opcional): Sobrescreve o locale de destino
    • domain (string, opcional): Usa um domínio de tradução específico (padrão: "messages")
    • Quaisquer outras chaves são usadas como bindings de interpolação para placeholders %{key}
-- Tradução simples usando locale padrão
turn.i18n.t("Hello")

-- Sobrescreve o locale para esta chamada
turn.i18n.t("Hello", {locale = "spa"})

-- Usa um domínio específico
turn.i18n.t("Invalid input", {domain = "errors"})

-- Com bindings de interpolação
turn.i18n.t("Hello %{name}", {name = "Maria"})

-- Combinado: locale, domínio e bindings
turn.i18n.t("Welcome %{name}", {locale = "por_BR", name = "João"})

Tradutor Vinculado a Locale

  • for_locale(locale): Cria uma função tradutora vinculada a um locale específico. Recomendado para traduções por contato.
  • for_locale(locale, opts): Cria um tradutor vinculado com domínio padrão:
    • domain (string, opcional): Domínio de tradução padrão para este tradutor
-- Cria um tradutor vinculado ao idioma do contato
local t = turn.i18n.for_locale(contact.language)
t("Hello") -- Usa o locale vinculado
t("Hello %{name}", {name = "Maria"}) -- Com interpolação
t("Goodbye", {locale = "eng"}) -- Ainda pode sobrescrever por chamada

-- Com domínio vinculado para mensagens de erro
local t_errors = turn.i18n.for_locale(contact.language, {domain = "errors"})
t_errors("Invalid input") -- Usa locale vinculado e domínio "errors"
t_errors("Other", {domain = "messages"}) -- Pode sobrescrever domínio por chamada

Estrutura de Arquivos de Tradução

Os arquivos PO devem ser colocados em assets/locales/{locale}/LC_MESSAGES/{domain}.po:

assets/
└── locales/
├── eng/
│ └── LC_MESSAGES/
│ ├── messages.po # Domínio padrão
│ └── errors.po # Domínio personalizado
├── spa/
│ └── LC_MESSAGES/
│ ├── messages.po
│ └── errors.po
└── por_BR/
└── LC_MESSAGES/
├── messages.po
└── errors.po

Exemplo de Arquivo PO

Crie assets/locales/por_BR/LC_MESSAGES/messages.po:

# Traduções para Português (Brasil)
msgid ""
msgstr ""
"Language: por_BR\n"

msgid "Hello"
msgstr "Olá"

msgid "Hello %{name}"
msgstr "Olá %{name}"

msgid "Welcome to our service"
msgstr "Bem-vindo ao nosso serviço"

Configuração do Manifest

Defina o locale padrão no seu manifest.json:

{
"app": {
"name": "my-app",
"version": "1.0.0",
"default_locale": "eng"
}
}

Ordem de Resolução de Idioma

  1. Locale vinculado via for_locale() (se usando tradutor vinculado)
  2. options.locale - Sobrescrita explícita por chamada
  3. app.default_locale - Do manifest.json
  4. "eng" - Fallback final

Normalização de Código de Locale

A API normaliza automaticamente os códigos de locale:

  • Códigos ISO-639-1 de 2 letras são convertidos para ISO-639-3 de 3 letras (ex: "en""eng", "pt""por")
  • Variantes regionais são preservadas (ex: "pt-BR""por_BR", "pt_BR""por_BR")

turn.qrcode

Gera imagens de código QR.

  • generate(options): Cria um PNG de código QR.
    • options (table): Uma tabela com data (string) e chaves opcionais como filename, color, image_data.
local qr_table, ok = turn.qrcode.generate({
data = "https://www.turn.io/",
color = "#8654CD" -- Roxo da Turn.io!
})

turn.media

Salva dados binários (como imagens ou documentos) como mídia que pode ser reutilizada em mensagens.

  • save(media_data): Salva dados binários como um item de mídia.
    • media_data (table): Uma tabela com data (string binária), filename (string) e content_type (string).
local qr_table, ok = turn.qrcode.generate({ data = "..." })
if ok then
local saved, media_info = turn.media.save(qr_table)
end

turn.google

Autentica com APIs do Google.

  • get_access_token(service_account_json, scopes): Obtém um token OAuth2.
    • service_account_json (string): O conteúdo JSON do arquivo de conta de serviço.
    • scopes (table, opcional): Uma lista de escopos da API do Google.
local ok, token = turn.google.get_access_token(sa_json)
if ok then
-- Use token na requisição turn.http
end

turn.liquid

Renderiza templates Liquid armazenados no diretório assets/liquid/ do seu app. Suporta todos os filtros e tags Liquid padrão, além de filtros de tradução personalizados para i18n.

  • render(template_name, variables): Renderiza um template Liquid com variáveis.
    • template_name (string): Caminho para o template relativo a assets/liquid/ (ex: "welcome.liquid").
    • variables (table): Variáveis para passar ao template.
  • render(template_name, variables, options): Renderiza com opções adicionais.
    • options.locale (string, opcional): Define o locale de tradução para filtros t.
    • options.strict_variables (boolean, opcional): Erro em variáveis indefinidas.
-- Renderizar um template com variáveis
local html = turn.liquid.render("welcome.liquid", {
name = "Alice",
message = "Bem-vindo ao nosso serviço!"
})

-- Renderizar com locale para traduções
local html = turn.liquid.render("welcome.liquid", {
name = contact.name
}, { locale = contact.language })

Estrutura de Templates

Armazene templates em assets/liquid/ dentro do ZIP do seu app:

my_app/
└── assets/
└── liquid/
├── welcome.liquid
├── emails/
│ └── receipt.liquid
└── partials/
├── header.liquid
└── footer.liquid

Filtros de Tradução (t e t_plural)

Templates Liquid suportam filtros de tradução que usam os arquivos PO do seu app.

Tradução Básica (t):

{% raw %}
<!-- Tradução simples -->
{{ "Hello" | t }}

<!-- Com bindings de interpolação -->
{{ "Hello %{name}" | t: name: user.name }}

<!-- Sobrescrever locale para este filtro -->
{{ "Hello" | t: locale: "spa" }}

<!-- Usar um domínio de tradução específico -->
{{ "Invalid input" | t: domain: "errors" }}
{% endraw %}

Pluralização (t_plural):

O filtro t_plural trata formas singular/plural baseado em uma contagem:

{% raw %}
<!-- Plural básico: mostra "1 item" ou "5 items" -->
{{ "One item" | t_plural: "%{count} items", count: cart.size }}

<!-- Com bindings adicionais -->
{{ "One message from %{sender}" | t_plural: "%{count} messages from %{sender}", count: messages.size, sender: contact.name }}
{% endraw %}

Ambas as formas são traduzidas usando seus arquivos PO. Para count=1, a forma singular é usada; caso contrário a forma plural é usada.

Ordem de Resolução de Locale:

  1. Argumento locale: no filtro (maior prioridade)
  2. Opção locale passada para render()
  3. default_locale do manifest.json do app
  4. "eng" (fallback final)

Incluindo Parciais

Use a tag render para incluir outros templates:

{% raw %}
<!-- Em page.liquid -->
<div class="page">
{% render 'partials/header' %}
<p>Conteúdo principal</p>
{% render 'partials/footer' %}
</div>
{% endraw %}

turn.logger

Escreve logs que são visíveis na UI da Turn.io para depuração.

  • debug(message), info(message), warning(message), error(message)
    • message (string): A mensagem de log.
turn.logger.error("Falha ao conectar com o banco de dados: " .. err_msg)

API do Servidor HTTP

Construa interfaces web dentro do seu App Lua. O roteador fornece roteamento estilo Express com middleware, gerenciamento automático de sessão e proteção CSRF.

Início Rápido

Aqui está um exemplo mínimo funcional:

-- Criar um roteador
local router = turn.http.server.router.new("Meu App")

-- Definir rotas
router:get("/")(function(request, response)
response:html("<h1>Olá Mundo!</h1>")
end)

router:get("/api/status")(function(request, response)
response:json({ status = "ok" })
end)

-- Tratar requisições HTTP no seu app
function App.on_event(app, number, event, data)
if event == "http_request" then
return true, router:handle(data)
end
end

turn.http.server.router

Roteador estilo Express para tratar requisições HTTP com suporte a middleware.

Criando um Roteador

  • new(name): Cria uma nova instância de roteador.
    • name (string): Nome do roteador para identificação.
    • Retorna um objeto roteador.
local router = turn.http.server.router.new("Meu App")

Configuração

  • router:config(options): Configura opções do roteador.
    • session_secret (string): Habilita gerenciamento automático de sessão com esta chave de criptografia.
    • session_cookie (string): Nome do cookie para sessões (padrão: "session").
    • csrf_cookie (string): Nome do cookie para tokens CSRF.
router:config({
session_secret = turn.app.get_config_value("session_secret"),
session_cookie = "session"
})

Registro de Rotas

Rotas são registradas usando funções de método HTTP que retornam uma função aceitando um handler:

  • router:get(path)(handler): Registra rota GET
  • router:post(path)(handler): Registra rota POST
  • router:put(path)(handler): Registra rota PUT
  • router:patch(path)(handler): Registra rota PATCH
  • router:delete(path)(handler): Registra rota DELETE

Parâmetros de caminho usam a sintaxe :param:

-- Rota simples
router:get("/status")(function(request, response)
response:json({ status = "ok" })
end)

-- Rota com parâmetros
router:get("/users/:id")(function(request, response)
local user_id = request.params.id
response:json({ user_id = user_id })
end)

-- Múltiplos parâmetros
router:get("/orgs/:org_id/users/:user_id")(function(request, response)
response:json({
org = request.params.org_id,
user = request.params.user_id
})
end)

-- POST com dados de formulário
router:post("/users")(function(request, response)
local name = request.form.name
response:set_status(201):json({ created = true, name = name })
end)

Objeto Request

Handlers de rota recebem um objeto request com:

  • method (string): Método HTTP (GET, POST, etc.)
  • path (string): Caminho da requisição
  • params (table): Parâmetros de caminho (ex: :id vira request.params.id)
  • query (table): Parâmetros de query string
  • form (table): Parâmetros de form/body
  • headers (table): Cabeçalhos da requisição como lista de tuplas {name, value}
  • cookies (table): Cookies parseados
  • session (table): Dados de sessão (quando session_secret está configurado)
  • csrf_token (string): Token CSRF para proteção de formulário
  • is_htmx (boolean): true se a requisição tem o cabeçalho HX-Request: true

Objeto Response

Handlers de rota recebem um objeto response com métodos encadeáveis:

  • set_status(code): Define o código de status HTTP
  • header(name, value): Define um cabeçalho (substitui cabeçalho existente com mesmo nome)
  • add_header(name, value): Adiciona um cabeçalho (permite duplicatas, ex: para Set-Cookie)
  • json(data): Envia resposta JSON com content-type apropriado
  • html(content): Envia resposta HTML com content-type apropriado
  • text(content): Envia resposta texto plano com content-type apropriado
  • redirect(url, status?): Redireciona para URL (padrão 302)
  • Métodos HTMX: hx_trigger(), hx_redirect(), hx_refresh(), etc. (veja Suporte HTMX)
-- Resposta JSON
response:json({ message = "Sucesso" })

-- Resposta HTML
response:html("<h1>Olá!</h1>")

-- Resposta texto plano
response:text("OK")

-- Redirecionamento
response:redirect("/dashboard")

-- Status e cabeçalhos personalizados
response:set_status(201)
:header("X-Custom", "valor")
:json({ created = true })

Middleware

  • router:use(middleware): Registra middleware que executa antes dos handlers de rota.
    • middleware (function): Função recebendo (request, response, next).
    • Chame next(request, response) para continuar para o próximo middleware/handler.
    • Não chame next() para interromper (ex: para falhas de autenticação).
-- Middleware de logging
router:use(function(request, response, next)
turn.logger.info(request.method .. " " .. request.path)
next(request, response)
end)

-- Middleware de autenticação
router:use(function(request, response, next)
if not request.session.user_id then
response:set_status(401):json({ error = "Não autorizado" })
return -- Não chamar next() para bloquear a requisição
end
next(request, response)
end)

-- Middleware de transformação de requisição
router:use(function(request, response, next)
-- Adicionar propriedades computadas
request.user = load_user(request.session.user_id)
next(request, response)
end)

Gerenciamento de Sessão

Quando você configura session_secret, o roteador automaticamente trata cookies de sessão criptografados:

local router = turn.http.server.router.new("Meu App")
router:config({
session_secret = turn.app.get_config_value("session_secret")
})

-- Agora use request.session como uma tabela normal
router:post("/login")(function(request, response)
-- Apenas atribua valores - o roteador trata a criptografia automaticamente
request.session.user_id = 123
request.session.role = "admin"
response:redirect("/dashboard")
end)

router:get("/dashboard")(function(request, response)
-- Leia valores diretamente
if not request.session.user_id then
response:redirect("/login")
return
end
response:json({ user_id = request.session.user_id })
end)

router:post("/logout")(function(request, response)
-- Limpar todos os dados da sessão
request.session:clear()
response:redirect("/")
end)

Como as sessões funcionam:

  • As sessões são criptografadas usando AES-256-GCM e armazenadas em cookies
  • O roteador rastreia modificações de forma transparente usando metatables Lua
  • Quando você escreve em request.session.foo, ele marca a sessão como modificada
  • Após seu handler completar, sessões modificadas são criptografadas e salvas em cookies
  • Ler dados de sessão não dispara um salvamento - apenas modificações

Suporte HTMX

O roteador inclui suporte integrado para HTMX, facilitando a construção de UIs dinâmicas com HTML-over-the-wire.

Propriedades da Requisição:

  • request.is_htmx (boolean): true se a requisição inclui o cabeçalho HX-Request: true
router:post("/update")(function(request, response)
-- Verifica se é uma requisição HTMX
if request.is_htmx then
-- Retorna apenas o fragmento atualizado
response:html("<div>Atualizado!</div>")
else
-- Página completa para requisições não-HTMX
response:redirect("/page")
end
end)

Métodos de Resposta HTMX:

Todos os métodos são encadeáveis e definem os cabeçalhos HTMX apropriados:

  • hx_trigger(event, detail?): Dispara um evento no lado do cliente
  • hx_trigger_after_settle(event, detail?): Dispara após o DOM estabilizar
  • hx_trigger_after_swap(event, detail?): Dispara após a troca completar
  • hx_redirect(url): Redirecionamento no lado do cliente (via cabeçalho HX-Redirect)
  • hx_refresh(): Atualização completa da página
  • hx_push_url(url): Adiciona URL ao histórico do navegador
  • hx_replace_url(url): Substitui URL no histórico do navegador (sem nova entrada)
  • hx_reswap(method): Sobrescreve o método de swap (innerHTML, outerHTML, etc.)
  • hx_retarget(selector): Sobrescreve o elemento alvo
  • hx_reselect(selector): Sobrescreve a seleção da resposta
-- Dispara um evento no lado do cliente com dados
router:post("/item/:id")(function(request, response)
local item = update_item(request.params.id, request.form)
response:hx_trigger("itemUpdated", {id = item.id})
:html("<div class='item'>" .. item.name .. "</div>")
end)

-- Redirecionamento com suporte a HTMX (lado do cliente, não 302)
router:post("/login")(function(request, response)
if authenticate(request.form) then
if request.is_htmx then
-- HTMX trata isso no lado do cliente
response:hx_redirect("/dashboard"):html("")
else
-- Redirecionamento HTTP padrão
response:redirect("/dashboard")
end
else
response:set_status(401):html("<div class='error'>Credenciais inválidas</div>")
end
end)

-- Atualiza histórico do navegador
router:get("/page/:num")(function(request, response)
local content = get_page_content(request.params.num)
response:hx_push_url("/page/" .. request.params.num)
:html(content)
end)

-- Muda como o conteúdo é trocado
router:delete("/item/:id")(function(request, response)
delete_item(request.params.id)
-- Remove o elemento do DOM
response:hx_reswap("delete"):html("")
end)

Diferença entre redirect() e hx_redirect():

  • response:redirect(url) envia redirecionamento HTTP 302 - navegador navega completamente
  • response:hx_redirect(url) define cabeçalho HX-Redirect - HTMX trata a navegação no lado do cliente

Use hx_redirect() para requisições HTMX para evitar recarregamentos completos de página e manter as transições suaves do HTMX.

Tratando Requisições

  • router:handle(conn_data): Roteia uma requisição para o handler apropriado.
    • conn_data (table): Dados de conexão do evento HTTP.
    • Retorna tabela de resposta com status, headers, body.
    • Define automaticamente o cookie CSRF em todas as respostas.
function App.on_event(app, number, event, data)
if event == "http_request" then
return true, router:handle(data)
end
end

Respostas de Erro Integradas

O roteador trata automaticamente:

  • 404 Not Found: Quando nenhuma rota corresponde ao caminho
  • 405 Method Not Allowed: Quando o caminho corresponde mas o método não

Exemplo Completo

Aqui está um exemplo completo de uma aplicação web com autenticação, proteção CSRF e gerenciamento de sessão:

local server = require("turn.http.server")
local router = require("turn.http.server.router")

-- Criar roteador com suporte a sessão
local app = router.new("Gerenciamento de Usuários")
app:config({
session_secret = turn.app.get_config_value("session_secret")
})

-- Middleware de logging
app:use(function(request, response, next)
turn.logger.info(request.method .. " " .. request.path)
next(request, response)
end)

-- Rotas públicas
app:get("/")(function(request, response)
response:html(server.render(request, "home.liquid"))
end)

app:get("/login")(function(request, response)
response:html(server.render(request, "login.liquid"))
end)

app:post("/login")(function(request, response)
-- Validar CSRF
if not server.validate_csrf(request) then
response:set_status(403):json({ error = "Token CSRF inválido" })
return
end

local email = request.form.email
local password = request.form.password

-- Validar credenciais (simplificado)
local user = authenticate(email, password)
if user then
-- Apenas atribua à sessão - o roteador cuida do resto
request.session.user_id = user.id
request.session.email = user.email
response:redirect("/dashboard")
else
response:html(server.render(request, "login.liquid", {
error = "Credenciais inválidas"
}))
end
end)

-- Rotas protegidas
app:get("/dashboard")(function(request, response)
if not request.session.user_id then
response:redirect("/login")
return
end

response:html(server.render(request, "dashboard.liquid", {
user_id = request.session.user_id,
email = request.session.email
}))
end)

app:post("/logout")(function(request, response)
if server.validate_csrf(request) then
request.session:clear()
end
response:redirect("/")
end)

-- Tratar requisições HTTP
function App.on_event(app, number, event, data)
if event == "http_request" then
local result = app:handle(data)
return true, result
end
end

Utilitários turn.http.server

Utilitários de baixo nível para casos de uso avançados. A maioria dos desenvolvedores usará o roteador.

Utilitários de Cabeçalho

  • get_header(headers, name): Obtém o valor de um cabeçalho de uma lista de cabeçalhos (case-insensitive).
    • headers (table): Cabeçalhos como lista de tuplas {name, value}.
    • name (string): Nome do cabeçalho a encontrar.
    • Retorna o valor do cabeçalho ou nil.
local content_type = turn.http.server.get_header(request.headers, "content-type")
local auth_token = turn.http.server.get_header(request.headers, "Authorization")

Proteção CSRF

O servidor usa o padrão de cookie de dupla submissão para proteção CSRF. O roteador define automaticamente o cookie CSRF, e você pode validar tokens nos seus handlers.

  • validate_csrf(request): Valida o token CSRF.
    • request (table): O objeto de requisição parseado.
    • Retorna true se válido, false caso contrário.
    • Verifica o token no cabeçalho X-CSRF-Token ou no campo de formulário _csrf_token.
router:post("/update")(function(request, response)
if not turn.http.server.validate_csrf(request) then
response:set_status(403):json({ error = "Token CSRF inválido" })
return
end
-- Processar a requisição...
end)

Renderização de Template

  • render(request, template_name, variables): Renderiza um template Liquid com injeção automática de CSRF.
    • request (table): O objeto de requisição parseado.
    • template_name (string): Caminho para o template relativo a assets/liquid/.
    • variables (table, opcional): Variáveis para passar ao template.
    • Adiciona automaticamente csrf_token e csrf_input às variáveis do template.
router:get("/form")(function(request, response)
response:html(turn.http.server.render(request, "form.liquid", {
user = current_user
}))
end)
{% raw %}
<!-- csrf_token e csrf_input estão disponíveis automaticamente -->
<form method="POST" action="/submit">
{{ csrf_input }}
<input type="text" name="name" value="{{ user.name }}">
<button type="submit">Salvar</button>
</form>

<!-- Para requisições AJAX -->
<script>
fetch('/api/data', {
method: 'POST',
headers: {
'X-CSRF-Token': '{{ csrf_token }}',
'Content-Type': 'application/json'
},
body: JSON.stringify({ ... })
});
</script>
{% endraw %}

Criptografia de Sessão (Baixo nível)

Para gerenciamento manual de sessão sem o roteador. A maioria dos casos de uso deve usar router:config({ session_secret = ... }).

  • encrypt_session(data, secret): Criptografa dados de sessão.

    • data (table): Dados de sessão para criptografar.
    • secret (string): Segredo de criptografia.
    • Retorna string criptografada codificada em base64.
  • decrypt_session(encrypted, secret): Descriptografa dados de sessão.

    • encrypted (string): String de sessão criptografada.
    • secret (string): Segredo de criptografia.
    • Retorna a tabela de dados descriptografada, ou tabela vazia em caso de falha.

Parsing de Requisição (Baixo nível)

Para gerenciamento manual de requisição sem o roteador.

  • parse_request(conn_data, options): Faz parsing dos dados brutos de conexão.

    • Retorna um objeto de requisição com method, path, query, form, headers, cookies, etc.
  • create_response(): Cria um construtor de resposta.

    • Retorna um objeto de resposta com set_status(), json(), html(), redirect(), etc.