Todas as docs

Início

  1. 01Primeiros passos
  2. 02Instalação
  3. 03Rastreamento

Recursos

  1. 04O painel
  2. 05Metas
  3. 06Links curtos
  4. 07Origens e agentes de IA
  5. 08Relatórios por email
  6. 09Verificação agendada
  7. 10Pergunte à sua IA

Referência

  1. 11Configuração
  2. 12API HTTP

Plataformas

  1. 13Servidor independente
  2. 14PHP
  3. 15WordPress
  4. 16Drupal
  5. 17Craft CMS
  6. 18Python
  7. 19Rails
  8. 20Ruby
  9. 21Go
  10. 22Java
  11. 23.NET
  12. 24Elixir
  13. 25Rust

Confiança

  1. 26Privacidade

Referência

Configuração

Esta página cobre todas as opções de runlight() e routes(), incluindo stores e sites.

ts

import { runlight } from "@runlight/sdk";

export const rl = runlight({ store, site, /* ...options */ });
export const routes = rl.routes({ /* ...route options */ });

Esta página usa os nomes da biblioteca TypeScript. Todas as outras linguagens aceitam as mesmas opções, escritas do jeito que cada linguagem escreve nomes, como mostra a página de cada uma, do PHP ao Rust.

Stores

O Runlight guarda tudo em algumas tabelas com o prefixo rl_, criadas no primeiro uso e atualizadas no lugar. Todo store aceita uma opção prefix para usar no lugar de rl_, como wp_rl_, para que dois apps que dividem um banco de dados tenham cada um as próprias tabelas. Ele tem até 32 letras, dígitos e sublinhados e não começa com dígito, e o tables() de um store lista as tabelas dele pelo nome, para um app que esteja removendo o Runlight.

SQLite

ts

import { sqlite } from "@runlight/sdk/sqlite";

sqlite({ path: "./data/runlight.db" });

O SQLite precisa do better-sqlite3 e serve para um único servidor, onde o arquivo pode ficar ao lado do banco de dados do seu app. Numa hospedagem serverless sem disco persistente, use Postgres. Um arquivo que o Runlight cria só pode ser lido e gravado pelo dono e pelo grupo (0660), então um job agendado que rode como outro usuário do mesmo grupo ainda consegue usá-lo.

Postgres

ts

import { postgres } from "@runlight/sdk/postgres";

postgres({ url: process.env.DATABASE_URL });

O Postgres precisa do pg. Passe url, ou passe pool para compartilhar um pool que o seu app já tem (o Runlight nunca encerra um pool que ele não criou). Um pool precisa de pelo menos 2 conexões. Quando o Runlight cria o pool, max define as conexões dele e o padrão é 10, uma requisição espera no máximo 10 segundos por uma conexão livre (um hit do script de rastreamento tenta mais duas vezes antes de ser descartado), e statementTimeout interrompe qualquer consulta depois desse número de milissegundos, 120000 por padrão (0 desliga). Se vários processos iniciarem ao mesmo tempo, as tabelas ainda assim são criadas uma vez só. Um processo espera no máximo 30 segundos por outro que esteja criando ou atualizando as tabelas, depois disso uma requisição responde 503 com o código tables_busy, e um hit do script de rastreamento espera no máximo 10 segundos e então é descartado.

MySQL e MariaDB

ts

import { mysql } from "@runlight/sdk/mysql";

mysql({ url: process.env.DATABASE_URL });

Este store precisa do mysql2 e funciona com MySQL 8.0.20 ou mais recente e MariaDB 11.4 ou mais recente. Passe uma URL mysql:// ou mariadb:// como url, ou passe pool para compartilhar um pool de mysql2/promise que o seu app já tem (o Runlight nunca encerra um pool que ele não criou). Um pool precisa de pelo menos 2 conexões. Quando o Runlight cria o pool, max define as conexões dele e o padrão é 10, uma requisição espera no máximo 10 segundos por uma conexão livre (um hit do script de rastreamento tenta mais duas vezes antes de ser descartado), e statementTimeout interrompe qualquer consulta depois desse número de milissegundos, 120000 por padrão (0 desliga). No MySQL esse limite vale para leituras, e no MariaDB vale para todos os comandos. Se vários processos iniciarem ao mesmo tempo, as tabelas ainda assim são criadas uma vez só. Um processo espera no máximo 30 segundos por outro que esteja criando ou atualizando as tabelas, depois disso uma requisição responde 503 com o código tables_busy, e um hit do script de rastreamento espera no máximo 10 segundos e então é descartado.

As tabelas usam utf8mb4 com a collation utf8mb4_0900_bin. O texto é comparado e ordenado por code point, mantendo maiúsculas, minúsculas e espaços no final, então todo relatório sai igual ao que sairia no SQLite ou no Postgres.

libSQL e Turso

ts

import { libsql } from "@runlight/sdk/libsql";
import { createClient } from "@libsql/client"; // or "@libsql/client/web" on the edge

libsql({ client: createClient({ url: process.env.TURSO_URL, authToken: process.env.TURSO_TOKEN }) });

Este store precisa do @libsql/client. O Turso por HTTP funciona em qualquer lugar onde fetch funcione, incluindo Vercel Edge e Deno Deploy, e uma URL file: aponta para um arquivo SQLite local.

Cloudflare D1

ts

import { d1 } from "@runlight/sdk/d1";

d1({ database: env.DB });

Passe o binding do D1 do ambiente do seu Worker. O D1 não tem transações interativas, então uma importação de links grava cada link separadamente; se uma falhar no meio, rodar a importação de novo pula os links já gravados.

Bun

ts

import { bunSqlite } from "@runlight/sdk/bun";

bunSqlite({ path: "./data/runlight.db" });

Este store usa o SQLite embutido do Bun, para apps no Bun, onde o better-sqlite3 não carrega.

Opções de runlight()

Opção Padrão O que faz
store (obrigatório) Onde os números ficam, um de sqlite, postgres, mysql, libsql, d1 ou bunSqlite.
site {} O site que esta instalação conta.
sites Vários sites numa instalação, cada um com seus nomes de host, usados no lugar de site.
geo A sua própria consulta de localização, para hospedagens que não enviam cabeçalhos de localização.
trustProxy true Lê o endereço do visitante nos cabeçalhos do proxy.
linkPath "/go" Onde ficam os links curtos no domínio do próprio app.
rateLimit 120 Requisições ao script de rastreamento permitidas por endereço a cada minuto, ou false ou 0 para sem limite.
mail O serviço de e-mail para os relatórios por email, definido no código, ou o mailer do próprio app, que um serviço salvo no painel pode substituir.
mailServices todos os serviços Os serviços de e-mail que as Configurações oferecem, por id, como todos menos smtp. Salvar outro é recusado com o código mail_service, e um serviço salvo antes que não está na lista conta como nenhum, então o mailer do próprio app envia.
managedSites false Guarda os sites no banco de dados e os gerencia pelo painel, como o servidor independente faz, no lugar de site e sites.
secret RUNLIGHT_SECRET, depois RUNLIGHT_TOKEN Criptografa as chaves guardadas no banco de dados, do serviço de e-mail, do Assistente de IA e das instalações conectadas.

Sites

Campo Padrão O que faz
id "default" para o primeiro site Id estável guardado em cada linha, feito de letras, dígitos, pontos, hifens e sublinhados.
name o primeiro nome de host Aparece no painel e pode ser editado nas Configurações.
hostnames [] (qualquer um) Quais nomes de host contam para este site, ignorando www.. Com vários sites, cada um precisa de pelo menos um.
timezone "UTC" Fuso horário IANA para os dias, as semanas e as horas no painel, como "America/Toronto", e pode ser editado nas Configurações.

Uma visita de um nome de host que nenhum site reivindica é ignorada, então uma cópia de staging do seu site não conta, a menos que você adicione o nome de host dela.

Limite de requisições

Cada processo conta as requisições ao script de rastreamento por endereço de visitante ao longo de um minuto e descarta o excesso em silêncio, para que um script não consiga inundar um site com visitas inventadas. Um visitante de verdade envia algumas requisições por minuto, bem abaixo do padrão de 120. A contagem vive na memória sob um endereço em hash e é zerada a cada minuto, então nenhum endereço é guardado. Em hospedagens que rodam muitas cópias de curta duração do seu app, como Cloudflare Workers, cada cópia conta por conta própria, então ali o limite é mais folgado.

Localização

País, região e cidade vêm dos cabeçalhos da sua hospedagem na Vercel, na Cloudflare e na Netlify, sem configuração. Em outros lugares, passe geo, uma função que transforma um endereço IP numa localização. Este exemplo a apoia num arquivo MMDB.

ts

runlight({
  store,
  geo: async (ip) => {
    const found = reader.get(ip); // your MMDB reader
    return found ? { country: found.country?.iso_code, city: found.city?.names?.en } : null;
  },
});

O endereço é descartado depois da consulta, então só o local é guardado.

Opções de routes()

Opção Padrão O que faz
basePath "/runlight" Onde as rotas são montadas, com o script em {basePath}/s.js.
token RUNLIGHT_TOKEN Protege o painel e a API. Envie-o como Authorization: Bearer <token>, ou abra o painel uma vez com ?token= para receber um cookie. null deixa tudo aberto, por exemplo atrás da sua própria autenticação.
accounts false Contas de login para o painel, com convites por função. Veja Contas abaixo.
authorize A sua própria verificação, usada no lugar de um token. Retorne true para acesso total, "member" para alguém que pode mudar tudo exceto o serviço de e-mail, as configurações do assistente e a exclusão de um site, "read" para alguém que só pode ler, ou false, a partir de uma função ou de uma promise.
cronSecret CRON_SECRET Um segundo segredo que a verificação agendada aceita além do token.
origin O endereço público do seu app, como https://example.com. Um domínio de links nunca pode ser o host dele, e os links nos relatórios por email apontam para ele, qualquer que seja o cabeçalho Host de uma requisição. Sem ele, o próprio host da requisição é usado no lugar, e um hub conectado não pode adicionar domínios de links nem relatórios por email.
ownHosts Uma função que retorna outros nomes em que o seu app responde, como ["app.example.com"], que também nunca podem virar domínios de links.
signIn A sua própria página de login. Um app que se conecta por OAuth manda para lá um proprietário sem login, com ?next=, e o painel aponta para ela quando uma sessão termina.
signOut Um link para sair, mostrado no rodapé do painel.
geoCredit false Dá crédito ao DB-IP no rodapé do painel, como os dados de localização gratuitos dele pedem.
cms O CMS que adiciona o script a cada página, como "WordPress", quando o Runlight roda dentro de um. O painel então abre nos números dele mesmo antes da primeira visita, Configurações, Instalação diz que o CMS adiciona o script, e páginas na mesma origem, como a administração do CMS, podem mostrar o painel num frame. Os plugins de WordPress, Drupal e Craft o definem.
observeKey RUNLIGHT_OBSERVE_KEY Uma chave para toda a instalação que um site em WordPress, Drupal ou Craft pode usar para informar leituras de agentes de IA a POST /api/observe para qualquer site. Cada site também tem a própria chave de agentes, mostrada em Configurações, Instalação, que informa só para aquele site e é a melhor escolha.

As opções accountOf e tokenMade ligam as rotas às contas do próprio servidor independente e são internas a ele.

Sem token, o painel e a API respondem 503 até você definir um, a menos que NODE_ENV seja development. Coletar visitas, o script, os links curtos, os links de compartilhamento e os links de cancelamento de inscrição nunca precisam do token.

Contas

Ative as contas para deixar várias pessoas entrarem no painel, cada uma com o próprio e-mail, senha e função, do mesmo jeito que o servidor independente faz.

ts

export const { GET, POST, PUT, PATCH, DELETE } = rl.routes({ accounts: true });

O painel então pede que todos entrem em /runlight/login. Para criar a primeira conta, abra /runlight/setup e informe o seu RUNLIGHT_TOKEN junto com seu e-mail e senha, para que só quem roda o app consiga fazer isso. Essa conta é a do proprietário. Em desenvolvimento sem token, ou com token: null, que deixa tudo aberto, a primeira conta não precisa de token, então crie-a antes que qualquer outra pessoa consiga chegar à página.

O proprietário e os administradores convidam mais pessoas em Configurações, Pessoas, como administrador, membro ou leitor, e o convite sai por e-mail quando há um serviço de e-mail configurado. Todos podem ativar o login em duas etapas em Conta. O token continua funcionando como bearer token para scripts.

As sessões são assinadas com RUNLIGHT_SECRET, ou com o token quando ele não está definido, então mantenha-o igual entre reinicializações e deploys. As senhas são guardadas em hash com scrypt onde o runtime o tem, como Node, Bun e Deno, e com PBKDF2 em runtimes de edge como Cloudflare Workers.

Se alguém esquecer a senha, o proprietário ou um administrador pode remover a pessoa e convidá-la de novo. Se o proprietário esquecer a dele, dê uma nova à conta a partir de um script que use o seu Runlight.

ts

import { Accounts } from "@runlight/sdk";
import { rl } from "./lib/runlight";

const accounts = new Accounts(rl.store, process.env.RUNLIGHT_SECRET ?? process.env.RUNLIGHT_TOKEN!);
const user = await accounts.setPassword("you@example.com", "a new long password", Date.now());
await accounts.disableTwoFactor(user.id);

Variáveis de ambiente

Variável Usada para
RUNLIGHT_TOKEN O token do painel e da API.
RUNLIGHT_SECRET Criptografar as chaves guardadas do e-mail, do Assistente de IA e das instalações conectadas (na falta dela, usa o token).
CRON_SECRET Chamar a verificação agendada.
RUNLIGHT_OBSERVE_KEY Plugins de CMS que informam leituras de agentes de IA.