Railway template: analytics dashboard for Railway template publishers (earnings, deploys, health alerts, auto-withdraw). Single Go binary with embedded DuckDB.
翻译准备中
正在加载完整 README。
查看完整 README ↗This repository is the source of the Dispatcher template on Railway.
Go API + React Router SPA, shipped as a single binary.
main.go — wiring: env, database, routes, server.handlers.go — /api/* handlers.db.go — GORM models and connection.spa.go — serves the built frontend (embedded via go:embed), with an index.html fallback for client-side routes.web/ — React Router v7 app in SPA mode (Vite is its build tool; it outputs static files to web/build/client).Two terminals:
make dev-api # Go API on http://localhost:8090
make dev-web # frontend on http://localhost:5173, proxies /api to the Go server
Work against http://localhost:5173 — you get Vite HMR, and API calls hit Go.
make build # npm run build, then go build (frontend embedded)
./dispatcher # serves everything on :8090 (override with PORT)
Note: go build embeds web/build/client, so the frontend must be built first — make build handles the order.
GORM with DuckDB (via github.com/vogo/duckdb/v2). Data lives in dispatcher.duckdb next to the binary (override with DB_PATH); db.go holds the models and connection, and AutoMigrate runs on startup. Building needs CGO (DuckDB links a C library) — already the Go default.
The auth cookie carries the whole Railway OAuth grant — access token, refresh token, expiry — sealed with AES-GCM under the OAuth client secret. There is no session table: Railway stays the only authority on who you are.
A Railway access token lasts about an hour, which is how long a login used to
last. requireAuth now refreshes the grant when the access token is spent,
re-seals the cookie, and carries on, so a login lives for as long as Railway
honours the refresh token. Parallel requests that arrive on an expired token
share a single refresh. Every request is still validated against Railway, so
losing workspace access ends the session at once, and the refreshed token is
also written to the stored workspace credentials that background jobs use.
The cookie is encrypted rather than signed so a leaked cookie cannot be
replayed against Railway's own API, and it is HttpOnly, SameSite=Lax, and
Secure whenever CALLBACK_URL is HTTPS. Its lifetime is 400 days, the
ceiling browsers accept; dispatcherctl stores the same value and adopts the
renewed session whenever Dispatcher rotates it. Logging out drops the cookie,
and re-registering the OAuth client invalidates every session, since the client
secret is the encryption key.
Dispatcher registers its own OAuth client with Railway on first login. If
Railway later revokes it (invalid_client on login), Dispatcher drops it and
sends the browser back through login with a freshly registered one. Like any
new client, that signs everyone out (CLI included), and background jobs resume
after the first login.
Notification targets send payout requests, template health drops, and weekly template summaries to Discord, Slack, ntfy, or a custom HTTP webhook. Targets use editable Go text/templates and can be tested before they are enabled.
Background collection, auto-withdraw, weekly summaries, and notification delivery assume the app runs as a single process. Running multiple replicas can duplicate cron work and notifications.
dispatcherctl is a small, standalone HTTP client intended for agents and
scripts. It queries a running Dispatcher instance, never its DuckDB file or
Railway directly. Authenticated commands use the same Railway OAuth flow and
session as the browser app; there are no separate API tokens. The health check
remains public just like /api/health.
Install the latest CLI release on Linux or macOS:
curl -fsSL https://raw.githubusercontent.com/ThallesP/dispatcher/main/install.sh | sh
The installer verifies the release checksum and installs to /usr/local/bin
when it is writable, otherwise to ~/.local/bin. Set
DISPATCHER_INSTALL_DIR to choose another directory, or
DISPATCHER_VERSION=v0.1.0 to install a specific release.
You can also build or install from source:
make build-cli
# Or: make install-cli
./dispatcherctl login # asks for the Dispatcher URL, then opens Railway OAuth
./dispatcherctl whoami
./dispatcherctl summary
./dispatcherctl templates
./dispatcherctl payouts --days 90
./dispatcherctl projects umami-analytics --days 90 # a template's id or code
./dispatcherctl notifications
./dispatcherctl withdraw-settings
./dispatcherctl withdraw-accounts
The first login asks for the Dispatcher instance URL and saves it after OAuth
succeeds. Later commands and logins reuse that URL automatically. --url and
DISPATCHER_URL override the saved instance; a successful login through an
override makes it the new default.
During login Dispatcher creates a short-lived pending login and returns a
Railway authorization URL. The CLI opens that URL (or prints it in a headless
environment) and polls Dispatcher while the browser completes the existing
OAuth callback. Browser and CLI login share the same Railway authorization URL
builder, code exchange, workspace-access check, saved credentials, and
requireAuth cookie middleware. The only CLI-specific part is how the completed
session reaches the terminal. Sessions renew themselves, so a CLI login lasts
until Railway revokes the grant. That result is bound to a verifier held only by
the CLI, so the browser never receives the Railway session itself. This also
works when the CLI and browser are on different machines. Sessions are stored
with owner-only permissions in the operating system's user config directory.
Use dispatcherctl logout to remove the session for an instance.
以上为 README 的开头部分。其余内容请阅读原文。 查看完整 README ↗