arcadia-app issues the role slug "platform-admin" (hyphen) — confirmed from a live arcadia-dev JWT (roles: ["admin","platform-admin"]). Every authorization check here tested for "platform_admin" (underscore), so real operator tokens got 403 on billing / dashboard / drift and an empty tenant-scoped result on inventory. The smoke tests missed it because Guardian.mint_dev_token hardcoded the underscore form — fixed there too, so the dev helper now matches what arcadia-app actually emits. Replaced the string literal "platform_admin" -> "platform-admin" in all six controllers + guardian.ex. The platform_admin?/1 function names keep underscores (Elixir identifiers can't contain hyphens) — only the role string changed. Verified: with a platform-admin token, /inventory, /billing/balance, /dashboard/margin and /drift all return 200. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
52 lines
1.4 KiB
Elixir
52 lines
1.4 KiB
Elixir
defmodule ArcadiaCloudWeb.DashboardController do
|
|
@moduledoc """
|
|
Cost-vs-revenue dashboard. platform_admin only — this is the operator's
|
|
margin view across all tenants.
|
|
"""
|
|
|
|
use ArcadiaCloudWeb, :controller
|
|
|
|
alias ArcadiaCloud.Analytics
|
|
|
|
def margin(conn, params) do
|
|
with :ok <- require_platform_admin(conn) do
|
|
period = parse_period(params["period"])
|
|
summary = Analytics.margin_summary(period)
|
|
by_kind = Analytics.by_kind(period)
|
|
json(conn, Map.merge(summary, by_kind))
|
|
end
|
|
end
|
|
|
|
def accrual(conn, _params) do
|
|
with :ok <- require_platform_admin(conn) do
|
|
accrual = Analytics.live_accrual()
|
|
|
|
json(conn, %{
|
|
accrual: accrual,
|
|
total_accrued_cents: Enum.reduce(accrual, 0, &(&1.accrued_cents + &2))
|
|
})
|
|
end
|
|
end
|
|
|
|
defp require_platform_admin(conn) do
|
|
identity = conn.assigns.current_identity
|
|
|
|
if is_list(identity.roles) and "platform-admin" in identity.roles do
|
|
:ok
|
|
else
|
|
conn |> put_status(:forbidden) |> json(%{error: "platform_admin_required"}) |> halt()
|
|
end
|
|
end
|
|
|
|
# period defaults to the previous calendar month (what the last rollup invoiced)
|
|
defp parse_period(nil), do: Date.beginning_of_month(Date.add(Date.beginning_of_month(Date.utc_today()), -1))
|
|
defp parse_period(""), do: parse_period(nil)
|
|
|
|
defp parse_period(str) do
|
|
case Date.from_iso8601(str) do
|
|
{:ok, d} -> Date.beginning_of_month(d)
|
|
_ -> parse_period(nil)
|
|
end
|
|
end
|
|
end
|