| defmodule PlausibleWeb.Api.SystemController do |
| use Plausible |
| use PlausibleWeb, :controller |
| require Logger |
|
|
| def info(conn, _params) do |
| build = |
| :plausible |
| |> Application.get_env(:runtime_metadata) |
| |> Keyword.take([:version, :commit, :created, :tags]) |
| |> Map.new() |
|
|
| geo_database = Plausible.Geo.database_type() || "(not configured)" |
|
|
| json(conn, %{ |
| geo_database: geo_database, |
| build: build |
| }) |
| end |
|
|
| def liveness(conn, _params) do |
| json(conn, %{ok: true}) |
| end |
|
|
| def api_docs(conn, _params) do |
| json(conn, %{ |
| endpoints: [ |
| %{ |
| method: "GET", |
| path: "/health", |
| purpose: "Returns HTTP 200 when the app is ready for Hugging Face Space transition from starting to running." |
| }, |
| %{ |
| method: "GET", |
| path: "/api-docs", |
| purpose: "Exposes documentation of all available API endpoints." |
| }, |
| %{ |
| method: "POST", |
| path: "/api/event", |
| purpose: "Send analytical events directly via the API." |
| }, |
| %{ |
| method: "GET", |
| path: "/api/v1/stats/realtime/visitors", |
| purpose: "Get current number of active visitors on a site." |
| }, |
| %{ |
| method: "GET", |
| path: "/api/v1/stats/aggregate", |
| purpose: "Retrieve aggregate metrics over a given period." |
| }, |
| %{ |
| method: "GET", |
| path: "/api/v1/stats/breakdown", |
| purpose: "Break down stats by metrics like browser, country, etc." |
| }, |
| %{ |
| method: "GET", |
| path: "/api/v1/stats/timeseries", |
| purpose: "Get metrics over time at regular intervals." |
| } |
| ] |
| }) |
| end |
|
|
| @task_timeout 15_000 |
| @critical_caches [ |
| Plausible.Site.Cache, |
| Plausible.Shield.IPRuleCache, |
| on_ee do |
| Plausible.Site.TrackerScriptIdCache |
| else |
| Plausible.Site.TrackerScriptCache |
| end |
| ] |
|
|
| def readiness(conn, _params) do |
| postgres_health_task = |
| Task.async(fn -> |
| Ecto.Adapters.SQL.query(Plausible.Repo, "SELECT 1", []) |
| end) |
|
|
| clickhouse_health_task = |
| Task.async(fn -> |
| Ecto.Adapters.SQL.query(Plausible.ClickhouseRepo, "SELECT 1", []) |
| end) |
|
|
| postgres_health = |
| case Task.await(postgres_health_task, @task_timeout) do |
| {:ok, _} -> |
| "ok" |
|
|
| e -> |
| Logger.error("Postgres health check failure: #{inspect(e)}") |
| "error" |
| end |
|
|
| clickhouse_health = |
| case Task.await(clickhouse_health_task, @task_timeout) do |
| {:ok, _} -> |
| "ok" |
|
|
| e -> |
| Logger.error("Clickhouse health check failure: #{inspect(e)}") |
| "error" |
| end |
|
|
| cache_health = |
| if postgres_health == "ok" and Enum.all?(@critical_caches, & &1.ready?()) do |
| "ok" |
| end |
|
|
| sessions_health = |
| if Plausible.Session.Transfer.attempted?() do |
| "ok" |
| else |
| "waiting" |
| end |
|
|
| status = |
| case {postgres_health, clickhouse_health, cache_health, sessions_health} do |
| {"ok", "ok", "ok", "ok"} -> 200 |
| _ -> 500 |
| end |
|
|
| put_status(conn, status) |
| |> json(%{ |
| postgres: postgres_health, |
| clickhouse: clickhouse_health, |
| sites_cache: cache_health, |
| sessions: sessions_health |
| }) |
| end |
| end |
|
|