Plataformas
Ruby
O Runlight roda em Ruby 3.2 ou mais recente, dentro de um app Rack como Sinatra, Hanami ou Roda, ou num domínio próprio como servidor independente.
A gem Ruby é o Runlight reescrito em Ruby. Ela serve o mesmo painel e a mesma API, e dá a cada requisição a mesma resposta que a biblioteca TypeScript dá. Os números vão para as mesmas tabelas, então qualquer uma das duas consegue ler um banco de dados que a outra escreveu. Ela precisa do Ruby 3.2 ou mais recente e guarda os dados pelo ActiveRecord, que já vem junto com ela. Para um app Rails, o guia do Rails tem uma engine e um gerador que fazem a configuração para você.
Instalação
Adicione a gem junto com o driver do seu banco de dados.
bash
bundle add runlight sqlite3No lugar de sqlite3, use pg para Postgres, ou trilogy ou mysql2 para MySQL e MariaDB.
Crie a instância
Crie uma instância quando o app inicia e compartilhe essa instância.
runlight.rb
require "runlight"
RL = Runlight.new(
store: Runlight::Stores.sqlite("data/runlight.db"),
site: { name: "example.com", hostnames: ["example.com"], timezone: "Europe/London" },
)As opções têm os nomes de Configuração, escritos em snake case, então sites, trust_proxy, rate_limit, mail e managed_sites funcionam como lá. As tabelas são criadas na primeira requisição.
Stores
As tabelas do Runlight começam com rl_. Todo store aceita prefix: para usar outro prefixo no lugar, como Runlight::Stores.sqlite(path, prefix: "wp_rl_"), para que dois apps que compartilham um banco de dados mantenham cada um as suas tabelas. Um prefixo tem até 32 letras, dígitos e sublinhados e não começa com dígito. store.tables lista as tabelas do store pelo nome, para um app que esteja removendo o Runlight.
SQLite
ruby
Runlight::Stores.sqlite("data/runlight.db")Mantenha o arquivo fora de qualquer pasta que o servidor web sirva. A pasta é criada quando não existe, e o banco de dados roda em modo WAL, então vários processos podem compartilhá-lo.
MySQL e MariaDB
ruby
Runlight::Stores.mysql("mysql://runlight:password@127.0.0.1:3306/runlight")Este store funciona com MySQL 8.0.20 ou mais recente e MariaDB 11.4 ou mais recente, pela gem trilogy quando ela está instalada e pela mysql2 caso contrário. Uma URL mariadb:// também funciona. statement_timeout: 30_000 interrompe qualquer consulta depois desse número de milissegundos, e o padrão é 120000, como em Configuração.
Postgres
ruby
Runlight::Stores.postgres("postgres://runlight:password@127.0.0.1:5432/runlight")Este store precisa da gem pg. Ele também aceita statement_timeout:, e schema: "analytics" mantém as tabelas num schema próprio. Runlight::Stores.url escolhe o store a partir de uma DATABASE_URL que começa com postgres://, mysql://, mariadb:// ou sqlite:.
O banco de dados ActiveRecord do próprio app
ruby
Runlight::Stores.active_recordEste store guarda as tabelas do Runlight no banco de dados ao qual o ActiveRecord do app já se conecta, ao lado das tabelas do próprio app. Passe uma classe de modelo abstrata, como Runlight::Stores.active_record(AnalyticsRecord), para usar outro banco de dados do app. As escritas dele entram numa transação que o app tenha aberta.
Apps Rack
Coloque o Runlight na frente do app no config.ru.
config.ru
require_relative "runlight"
require_relative "app"
use Runlight::RackApp, runlight: RL, base_path: "/runlight"
run AppO Runlight responde aos caminhos sob /runlight, aos links curtos em /go e a qualquer domínio de links que você adicionar em Configurações, e passa todas as outras requisições para o app. Ele também registra as leituras de agentes de IA de passagem, o que não custa tempo para mais ninguém. O Runlight verifica as próprias requisições, então ele vem antes de qualquer middleware de CSRF. Adicione o script em todas as páginas, logo antes de </head>.
html
<script defer src="/runlight/s.js"></script>Depois defina RUNLIGHT_TOKEN como uma string longa e aleatória e abra /runlight/?token= seguido dessa string uma vez para entrar, como descreve Primeiros passos. Com accounts: true nas opções e um RUNLIGHT_SECRET, as pessoas entram com o próprio e-mail e senha, como em Contas. Passe token: para definir o token no código. Uma string vazia mantém o painel fechado, e nil o deixa aberto para todo mundo.
RL.rack_app(base_path: "/runlight") é o mesmo app sem nada por trás, para um config.ru que o monta num caminho próprio com map.
Um link curto em /go/<name> sem nenhum link com esse nome segue para o app, então a própria página 404 dele responde, como a do Rails. RL.rack_app não tem nenhum app por trás e responde ele mesmo com um 404. Num domínio de links, /go/<name> é um 404 como qualquer outro caminho que não seja um dos links desse domínio.
A verificação agendada
A verificação agendada envia os relatórios por email e mantém os dados em ordem. Chame-a a cada poucos minutos pelo cron, por um agendador de jobs ou por uma thread sua.
ruby
RL.checkQuando nada no app consegue rodá-la num timer, defina CRON_SECRET e faça um agendador externo chamar /runlight/api/check com ele, como mostra Verificação agendada.
O servidor independente
O comando runlight da gem roda o Runlight num domínio próprio, como stats.example.com, como faz o servidor independente. Ele serve o painel na raiz, mantém os sites no painel e tem contas com login e links curtos em qualquer domínio que você apontar para ele.
bash
mkdir stats && cd stats
bundle init && bundle add runlight sqlite3 puma
bundle exec runlight serveO servidor escuta na porta 3000, ou em PORT. Ele roda sob o Puma quando a gem está presente e sob o WEBrick caso contrário, e o config.ru da pasta o roda sob qualquer outro servidor Rack. Os dados ficam em runlight-data, na pasta de onde você o inicia. Na primeira vez que inicia, ele mostra um link com um código de uso único que cria a primeira conta, e bundle exec runlight setup mostra o link de novo. Funções, convites, login em duas etapas e a adição de sites funcionam como no servidor independente.
As configurações vêm das variáveis de ambiente que o drop-in em PHP lê, como RUNLIGHT_URL, DATABASE_URL, DATA_DIR, RUNLIGHT_SECRET, TRUST_PROXY e RUNLIGHT_GEO, ou de um config.rb na pasta que retorna um Hash com os mesmos nomes. As variáveis de ambiente têm prioridade quando as duas estão definidas. O servidor roda a verificação agendada a cada cinco minutos, e bundle exec runlight cron a roda pelo cron para um servidor que não fica sempre no ar.
O resto do que o comando faz está em bundle exec runlight --help. password dá uma nova senha a uma conta e a mostra, migrate atualiza as tabelas na hora, e agents lê os agentes de IA do log de acesso de um servidor web, como faz o do drop-in em PHP.
Diferenças em relação à biblioteca TypeScript
A gem Ruby passa nos testes de conformidade aos quais a biblioteca TypeScript é submetida, em SQLite, Postgres, MySQL e MariaDB. As diferenças vêm da forma como os apps Ruby rodam.
- O pool de conexões é o do ActiveRecord. Cada store que abre o próprio banco de dados tem um pool de cinco conexões por processo, e o pool ActiveRecord do próprio app atende
Runlight::Stores.active_record. - O que a biblioteca TypeScript faz em segundo plano, como apagar visitas depois que um período de retenção menor é escolhido, roda depois que a resposta é enviada, quando o servidor Rack fecha o corpo da resposta.
- Os limites de senha errada ficam no banco de dados, então todos os processos os compartilham. O limite de requisições do script de rastreamento fica em pequenos arquivos na pasta temporária do sistema, que todos os processos de uma mesma máquina compartilham.
- Os relatórios por email formatam números, datas, valores em dinheiro e nomes de países a partir de uma cópia dos dados ICU que o Node usa, já que o Ruby não traz nenhum.