diff --git a/core/lib/canary/interface/controller.ex b/core/lib/canary/interface/controller.ex index 4ed5e781..7481e06c 100644 --- a/core/lib/canary/interface/controller.ex +++ b/core/lib/canary/interface/controller.ex @@ -4,9 +4,10 @@ defmodule CanaryWeb.Interface.Controller do @canary_id_header "x-canary-operation-id" + plug :rate_limit when action in [:search, :ask] plug :set_operation_id when action in [:search, :ask] plug :find_project when action in [:search, :ask] - plug :rate_limit when action in [:search, :ask] + plug :prevent_free_plan when action in [:ask] defp set_operation_id(conn, _opts) do conn |> put_resp_header(@canary_id_header, Ecto.UUID.generate()) @@ -26,12 +27,15 @@ defmodule CanaryWeb.Interface.Controller do end defp find_project(conn, _opts) do - err_msg = "no client found with the given key" + action = Phoenix.Controller.action_name(conn) + + err_msg = "no project found with the given key" with {:ok, token} <- get_token_from_header(conn), {:ok, project} <- Canary.Accounts.Project |> Ash.Query.filter(public_key == ^token) + |> wrap_project_query(action) |> Ash.read_one(not_found_error?: true) do conn |> assign(:project, project) else @@ -39,6 +43,43 @@ defmodule CanaryWeb.Interface.Controller do end end + defp prevent_free_plan(conn, _opts) do + err_msg = "can not access this with free plan" + + cond do + Application.get_env(:canary, :env) != :prod -> + conn + + conn.host == "cloud.getcanary.dev" -> + conn + + not Canary.Membership.can_use_ask?(conn.assigns.project.account) -> + conn |> send_resp(403, err_msg) |> halt() + + true -> + conn |> send_resp(403, err_msg) |> halt() + end + end + + defp wrap_project_query(query, action) do + if action == :search do + query + else + billing_query = + Canary.Accounts.Billing + |> Ash.Query.select([:id]) + |> Ash.Query.load(:membership) + + account_query = + Canary.Accounts.Account + |> Ash.Query.select([:id]) + |> Ash.Query.load(billing: billing_query) + + query + |> Ash.Query.load(account: account_query) + end + end + defp get_token_from_header(conn) do case get_req_header(conn, "authorization") do ["Bearer " <> token] -> {:ok, token} diff --git a/core/lib/canary/membership.ex b/core/lib/canary/membership.ex index 56909593..4ab6538e 100644 --- a/core/lib/canary/membership.ex +++ b/core/lib/canary/membership.ex @@ -10,6 +10,17 @@ defmodule Canary.Membership do end end + def can_use_ask?(%Canary.Accounts.Account{} = account) do + account = ensure_membership(account) + + case account.billing.membership.tier do + :free -> false + :starter -> true + :admin -> true + _ -> false + end + end + def max_sources(%Canary.Accounts.Account{} = account) do account = ensure_membership(account) diff --git a/core/lib/canary_web/live/example_live/example.ex b/core/lib/canary_web/live/example_live/example.ex index e91c58a3..7ae122cd 100644 --- a/core/lib/canary_web/live/example_live/example.ex +++ b/core/lib/canary_web/live/example_live/example.ex @@ -6,7 +6,14 @@ defmodule CanaryWeb.ExampleLive.Example do ~H"""

<%= @example.name %>

-

<%= @example.description %>

+ +
+

<%= @example.description %>

+ + + This is not available in the free plan. + +
diff --git a/core/lib/canary_web/live/example_live/examples.ex b/core/lib/canary_web/live/example_live/examples.ex index 0788f297..2b198f19 100644 --- a/core/lib/canary_web/live/example_live/examples.ex +++ b/core/lib/canary_web/live/example_live/examples.ex @@ -138,6 +138,7 @@ defmodule CanaryWeb.ExampleLive.Examples do has_webpage and has_github and tags != "" -> %{ name: "Search with Ask AI", + paid: true, description: "Type longer question, and press tab to run 'Ask AI'.", code: """ @@ -164,6 +165,7 @@ defmodule CanaryWeb.ExampleLive.Examples do has_webpage and has_github and tags == "" -> %{ name: "Search with Ask AI", + paid: true, description: "Type longer question, and press tab to run 'Ask AI'.", code: """ @@ -189,6 +191,7 @@ defmodule CanaryWeb.ExampleLive.Examples do true -> %{ name: "Search with Ask AI", + paid: true, description: "Type longer question, and press tab to run 'Ask AI'.", code: """