Plateformes
Python
Runlight tourne sous Python 3.11 ou plus récent, dans une application Django, Flask ou FastAPI ou n’importe quelle application WSGI ou ASGI, ou comme serveur à part entière.
Le paquet Python, c’est Runlight réécrit en Python. Il sert le même tableau de bord et la même API, et il donne à chaque requête la même réponse que la bibliothèque TypeScript. Ses chiffres vont dans les mêmes tables, donc chacun peut lire une base de données écrite par l’autre. Il lui faut Python 3.11 ou plus récent et il n’a aucune dépendance propre.
Installation
Ajoutez le paquet avec pip.
bash
pip install runlightSQLite est fourni avec Python. Pour Postgres, installez l’extra postgres, qui ajoute psycopg 3, et pour MySQL ou MariaDB, installez l’extra mysql, qui ajoute PyMySQL.
bash
pip install "runlight[postgres]"
pip install "runlight[mysql]"Les fuseaux horaires viennent de la base de fuseaux horaires du système. Windows n’en a pas, donc ajoutez l’extra tzdata sous Windows.
Créer l’instance
Créez un module qui crée l’instance, et importez-le partout où vous avez besoin de Runlight.
myproject/analytics.py
from runlight import Runlight
from runlight.store import Stores
rl = Runlight(
store=Stores.sqlite("data/runlight.db"),
site={"name": "example.com", "hostnames": ["example.com"], "timezone": "Europe/London"},
)Les options portent les mêmes noms que dans Configuration. Passez-les comme arguments nommés en snake case, comme trust_proxy=False et rate_limit=60, ou dans un seul dict avec les noms TypeScript, comme Runlight({"store": ..., "trustProxy": False}). Les tables sont créées à la première requête.
Stockages
Les tables de Runlight commencent par rl_. Chaque stockage accepte prefix à utiliser à la place, comme Stores.sqlite(path, prefix="wp_rl_"), pour que deux applications qui partagent une base gardent chacune leurs propres tables. Un préfixe compte jusqu’à 32 lettres, chiffres et tirets bas, et ne commence pas par un chiffre. store.tables() liste les tables du stockage par leur nom, pour une application qui retire Runlight.
SQLite
python
Stores.sqlite("data/runlight.db")Gardez le fichier hors de tout dossier que sert votre serveur web, dans un dossier où l’application peut écrire, puisque SQLite écrit un journal à côté. Le dossier est créé s’il manque.
Postgres
python
Stores.postgres("postgres://runlight:password@127.0.0.1:5432/runlight")statement_timeout arrête toute instruction au bout de ce nombre de millisecondes, 120000 par défaut. schema garde les tables dans leur propre schéma, comme dans Stores.postgres(url, schema="analytics"). À la place d’une URL, vous pouvez passer une connexion psycopg que votre application a déjà, que Runlight utilise telle quelle et ne ferme jamais.
MySQL et MariaDB
python
Stores.mysql("mysql://runlight:password@127.0.0.1:3306/runlight")Ce stockage fonctionne avec MySQL 8.0.20 ou plus récent et MariaDB 11.4 ou plus récent. Une URL mariadb:// fonctionne aussi. Il accepte statement_timeout comme Postgres, que MySQL applique aux lectures et MariaDB à chaque instruction, comme dans Configuration.
Stores.url() choisit le stockage d’après un DATABASE_URL qui commence par postgres://, mysql://, mariadb:// ou sqlite:.
Django
Ajoutez l’application de Runlight et son middleware à vos settings, et nommez l’instance dans RUNLIGHT. Le middleware vient en premier, pour qu’aucun autre middleware ne modifie les requêtes de Runlight ou ses réponses.
settings.py
INSTALLED_APPS = [
# ...
"runlight.django",
]
MIDDLEWARE = [
"runlight.django.RunlightMiddleware",
# ...
]
RUNLIGHT = "myproject.analytics.rl"RUNLIGHT contient l’instance elle-même ou son chemin pointé, et le chemin d’une fonction qui en crée une fonctionne aussi. RUNLIGHT_ROUTES passe des options aux routes avec leurs noms TypeScript, comme {"accounts": True}. Le middleware sert le tableau de bord et l’API sous /runlight, les liens courts sur /go et chaque domaine de liens que vous ajoutez dans Réglages, avant les URL de Django. Chaque domaine de liens doit aussi figurer dans ALLOWED_HOSTS.
Lancez la vérification planifiée depuis cron avec la commande de gestion.
bash
*/5 * * * * cd /srv/myproject && python manage.py runlight_checkFlask
Passez l’application et l’instance à init_app, qui place Runlight devant les vues de l’application. Il répond sous /runlight, sur /go et sur chaque domaine de liens que vous ajoutez dans Réglages, et passe tout le reste à votre application.
app.py
from flask import Flask
import runlight.flask
from myproject.analytics import rl
app = Flask(__name__)
runlight.flask.init_app(app, rl)Les options des routes se passent comme arguments nommés, comme runlight.flask.init_app(app, rl, accounts=True). init_app ajoute aussi une commande qui lance la vérification planifiée, pour cron.
bash
*/5 * * * * cd /srv/myproject && flask --app app runlight checkFastAPI
init_app ajoute Runlight comme middleware devant les routes de l’application, donc il répond sous /runlight, sur /go et sur chaque domaine de liens que vous ajoutez dans Réglages. Il fonctionne aussi pour les applications Starlette.
main.py
from fastapi import FastAPI
import runlight.fastapi
from myproject.analytics import rl
app = FastAPI()
runlight.fastapi.init_app(app, rl)Le travail de Runlight s’exécute dans des threads de travail, donc il ne bloque jamais la boucle d’événements. Pour la vérification planifiée, appelez rl.check() depuis un script lancé par cron, ou définissez CRON_SECRET et appelez la vérification en HTTP.
N’importe quelle application WSGI ou ASGI
Bottle, Pyramid et toute autre application WSGI prennent le middleware WSGI.
python
from runlight.wsgi import RunlightMiddleware
application = RunlightMiddleware(application, rl)Quart, Django sous ASGI et toute autre application ASGI prennent le middleware ASGI, qui reçoit l’instance avec runlight=.
python
from runlight.asgi import RunlightMiddleware
application = RunlightMiddleware(application, runlight=rl)Les deux répondent sous /runlight, sur /go et sur chaque domaine de liens, et passent tout le reste à votre application. Les options des routes se passent comme arguments nommés, comme avec Flask. Pour faire tourner Runlight comme une application à part entière, sans l’une des vôtres derrière, utilisez runlight.wsgi.app(rl) avec un serveur WSGI comme gunicorn, ou runlight.asgi.app(rl) avec un serveur ASGI comme uvicorn. Les chemins hors de ceux de Runlight répondent alors 404.
Un lien court sur /go/<name> sans lien de ce nom continue vers votre application, donc votre propre page 404 y répond, que ce soit celle de Django, de Flask, de FastAPI ou de votre application WSGI ou ASGI. runlight.wsgi.app() et runlight.asgi.app() n’ont aucune application derrière eux et y répondent eux-mêmes avec un 404. Sur un domaine de liens, /go/<name> est un 404 comme tout autre chemin qui n’est pas l’un des liens de ce domaine.
Quelle que soit la façon dont vous l’ajoutez, mettez le script sur chaque page, juste avant </head>.
html
<script defer src="/runlight/s.js"></script>Définissez ensuite RUNLIGHT_TOKEN avec une longue chaîne aléatoire et ouvrez une fois /runlight/?token= suivi de cette chaîne pour vous connecter, comme l’explique Pour commencer. Avec accounts=True et un RUNLIGHT_SECRET, chacun se connecte avec son propre e-mail et son propre mot de passe, comme dans Comptes.
Agents IA
Le middleware enregistre les requêtes des agents IA pendant que votre application sert ses pages, et laisse passer toutes les autres requêtes sans y toucher. Passez observe=False à init_app ou au middleware pour le désactiver, ou mettez "observe": False dans RUNLIGHT_ROUTES avec Django. Sans middleware, appelez rl.observe(request) avec un runlight.http.Request pour chaque requête de page.
La vérification planifiée
La vérification planifiée renouvelle les sels quotidiens, envoie les rapports par e-mail arrivés à échéance, applique la durée de conservation des visites de chaque site et construit les agrégats qui gardent les longues périodes rapides. Lancez-la toutes les quelques minutes avec python manage.py runlight_check dans Django, flask runlight check dans Flask, ou rl.check() depuis n’importe quel script. Quand rien ne peut lancer une commande à intervalles réguliers, définissez CRON_SECRET et faites appeler /runlight/api/check avec lui par un planificateur, comme le montre Vérification planifiée.
Le serveur autonome
Le paquet inclut le serveur autonome, qui fait tourner Runlight sur son propre domaine, comme stats.example.com, avec le tableau de bord à la racine, des sites ajoutés depuis le tableau de bord, des comptes de connexion et des liens courts sur n’importe quel domaine que vous faites pointer vers lui. Installez le paquet et lancez la commande runlight.
bash
pip install runlight
runlightLe serveur écoute sur le port 3000 et garde ses données dans un dossier nommé runlight-data dans le répertoire depuis lequel vous le lancez. À son premier démarrage, il affiche un lien de configuration avec un code à usage unique. Ouvrez ce lien et créez votre compte, comme l’explique Créer votre compte. Le serveur lance lui-même la vérification planifiée toutes les cinq minutes, et il télécharge les données de localisation du mois comme le fait le serveur Node. Placez-le derrière un proxy qui ajoute HTTPS, comme dans Le mettre sur Internet.
Réglages
Le serveur lit ses réglages dans des variables d’environnement.
| Variable | Ce qu’elle fait |
|---|---|
PORT |
Le port d’écoute. 3000 par défaut. |
HOST |
L’adresse d’écoute. 0.0.0.0 par défaut. |
DATA_DIR |
Le dossier du fichier SQLite, du secret et des données de localisation. ./runlight-data par défaut. |
DATABASE_URL |
Une adresse postgres:// garde les données dans Postgres au lieu de SQLite, et une adresse mysql:// ou mariadb:// les garde dans MySQL ou MariaDB. |
RUNLIGHT_SECRET |
La clé qui signe les connexions et chiffre les clés enregistrées. Sans elle, le serveur en crée une et la garde dans DATA_DIR, lisible uniquement par l’utilisateur qui le fait tourner. |
RUNLIGHT_URL |
L’adresse publique du serveur, par exemple https://stats.example.com, qui ne peut jamais devenir un domaine de liens. Les e-mails d’invitation et de rapport pointent vers elle. |
RUNLIGHT_TOKEN |
Un jeton que des scripts peuvent envoyer comme bearer token, en plus des jetons d’API créés dans le tableau de bord. |
RUNLIGHT_OBSERVE_KEY |
Une seule clé pour les signalements d’agents IA de tous les sites. La clé des agents propre à chaque site, affichée dans Réglages, Installation, est le meilleur choix. |
TRUST_PROXY |
Mettez false quand aucun proxy ne se trouve devant le serveur, ou x-real-ip ou cf-connecting-ip quand c’est cet en-tête qui contient l’adresse du visiteur. |
RUNLIGHT_GEO |
D’où viennent les lieux quand aucun en-tête ne les donne, comme dans Lieux. city par défaut, et peut valoir country, off ou le chemin d’un fichier MMDB. |
CRON_SECRET |
Permet à un planificateur de lancer la vérification sur POST /api/check. |
Commandes
runlight check lance une fois la vérification planifiée et récupère les données de localisation du mois. runlight migrate crée ou met à jour les tables tout de suite. Les tables se mettent aussi à jour toutes seules au démarrage du serveur, donc une mise à jour ne demande que pip install --upgrade runlight et un redémarrage. Sauvegardez le dossier de données, puisque les clés enregistrées ne peuvent pas être lues sans le secret qu’il contient.
Mots de passe oubliés
Lancez la commande password là où tourne le serveur pour donner à un compte un nouveau mot de passe, qu’elle affiche. Elle désactive aussi la connexion à deux facteurs pour ce compte, et elle crée le compte s’il n’existe pas, comme propriétaire sur un serveur encore vide et comme administrateur sinon.
bash
runlight password someone@example.comAgents IA depuis un journal
Les agents IA comme GPTBot et ClaudeBot récupèrent les pages sans exécuter de JavaScript, donc la balise script ne les voit jamais. Une application Python qui contient Runlight les compte là où la page est servie, comme le montre Agents IA. Pour tout autre site que vous hébergez vous-même, le journal d’accès du serveur web les contient, et la commande agents le lit.
bash
runlight agents --log /var/log/nginx/access.log --to https://stats.example.com --key rlo_... --site https://example.com --followLa clé est la clé des agents du site, affichée dans Runlight dans Réglages, Installation, et elle ne peut signaler que les requêtes de ce site. Quand --to ou --key est omis, la commande lit RUNLIGHT_URL ou RUNLIGHT_OBSERVE_KEY dans l’environnement. La commande fonctionne comme sur le serveur autonome, avec --follow pour continuer de tourner et --state pour reprendre là où la dernière exécution s’est arrêtée. Seules les requêtes de pages réussies des agents IA connus quittent la machine, et les adresses des visiteurs restent où elles sont.
Différences avec la bibliothèque TypeScript
Le paquet Python passe les tests de conformité imposés à la bibliothèque TypeScript, sur SQLite, Postgres, MySQL et MariaDB. Les différences viennent de la façon dont Python s’exécute.
- Le travail de Runlight est synchrone et s’exécute dans des threads. Le middleware ASGI confie chacune de ses requêtes à un thread de travail, donc la boucle d’événements reste libre pour le reste de votre application.
- SQLite garde une seule connexion, que tous les threads partagent sous un verrou. Postgres et MySQL ouvrent une connexion par thread, donc un serveur à seize threads garde jusqu’à seize connexions.
- Ce que la bibliothèque TypeScript fait en arrière-plan, comme supprimer des visites après le choix d’une durée de conservation plus courte, s’exécute une fois la réponse envoyée.
- La limite de débit du traceur et les listes en cache des domaines de liens et des icônes de sites sont gardées en mémoire, donc chaque processus a les siennes. Un serveur à plusieurs processus de travail accorde la limite à chaque adresse une fois dans chaque processus. Les limites de mots de passe erronés sont gardées dans la base, donc tous les processus les partagent.
- Les mots de passe sont hachés avec scrypt par le
hashlibde Python. Les hachages sont identiques à celles de la bibliothèque TypeScript, donc les comptes se transfèrent.