1

I am building an admin tool app for our system. I want to record every action made by every user.

Here's what I did

defmodule AdminToolWeb.UserController do
  use AdminToolWeb, :controller
  
  ...

  def delete(conn, %{"id" => id}) do
    current_user = Guardian.Plug.current_resource(conn)

    with %User{} <- user = Accounts.get_user(id) do
      Accounts.delete_user(user)

      conn
      |> put_flash(:info, "#{user.id} deleted.")
      |> Activities.log(current_user)
      |> redirect(to: Routes.user_path(conn, :index))
    end
  end

  ...
end

The problem is I have to pipe |> Activity.log(current_user) in every action of every controller I have in my app.

Is there a way to implement something like this? Controller -> (ActivityLogPlugOfSorts) -> View using a custom plug and call it like this?

defmodule AdminToolWeb.UserController do
  use AdminToolWeb, :controller
  import AdminToolWeb.Plugs.Activities

  plug :log

...

but It should be called between controller and view.

Or should I put a function inside a View module instead?

I hope there is a better way.

Dyey517
  • 426
  • 5
  • 15

1 Answers1

1

What you're looking for here are controller plugs which can indeed be inserted directly at the controller level. They will run before the controller action, so you will not have the opportunity when the plug runs to know if the attempted action will be successful. However you can use the controller plug to setup a callback that will be run after the controller action (but before the response is sent). An example might be:

defmodule HelloWeb.Plugs.ActionLogger do
  import Plug.Conn
  require Logger

  def init(default), do: default

  def call(conn, _default) do
    register_before_send(conn, fn conn ->
      if (response_code_2xx?(conn) do
        Logger.info("action taken: #{extract_action(conn)}")
      end
      conn
    end)
  end
end

where response_code_2xx?/1 and extract_action/1 are left as exercises for the reader.

Segfault
  • 8,036
  • 3
  • 35
  • 54
  • 1
    That `register_before_send/2` is what I'm looking for! Thanks for your quick answer! Because of that, I made another custom plug for assigning current_user so I can fetch it inside conn instead of passing it as argument. I really appreciate it! – Dyey517 Jan 25 '21 at 15:20