Class: Phlex::Reactive::ActionsController

Inherits:
Object
  • Object
show all
Defined in:
app/controllers/phlex/reactive/actions_controller.rb

Overview

The single endpoint behind every reactive component. The generic reactive Stimulus controller POSTs here with a signed identity token, an action name, and params. We verify the token, rebuild the component (re-finding the record from the DB for record-backed components), run the whitelisted action, and return an auto-targeted Turbo Stream the client morphs in.

Customizing in your app:

* Authentication — by default this inherits from
Phlex::Reactive.base_controller (ActionController::Base). Set it to
your ApplicationController to get current_user/Current/CSRF, but make
sure the action path isn't force-redirected for logged-out users if
you have public reactive components.
* Authorization — DO IT IN THE COMPONENT ACTION. The token proves the
identity is ours, not that this user may act. Raise from the action
(e.g. authorize!), and configure Phlex::Reactive.authorization_errors
so it's rendered as 403 here.

Instance Method Summary collapse

Instance Method Details

#createObject



29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# File 'app/controllers/phlex/reactive/actions_controller.rb', line 29

def create
  # ONE action.phlex_reactive event per request (issue #107). The event
  # payload carries the component/action NAMES + outcome ONLY (never the
  # token, params, or state); we fill it as those become known and set
  # :outcome on every exit path — the success tail and each rescue. The
  # rescue bodies are unchanged (verbose diagnostics + reactive_error per
  # #82/#87); we only ADD the outcome finalizer. `action` is safe to read
  # up front (it comes from the request, not the verified token).
  event = { component: nil, action: reactive_action_name.to_s, outcome: nil }
  # Mint any reply.defer directive tokens UNDER the actor's binding (issue
  # #165 security), so the defer endpoint accepts them ONLY back from this
  # same actor. The defer token is built in response_streams (inside this
  # block via the Defer builder), so the binding must be established here.
  Phlex::Reactive.with_defer_binding(Phlex::Reactive.defer_binding_for(request)) do
    Phlex::Reactive.instrument("action", event) do
      create_action(event)
    end
  end
end

#deferredObject

The defer endpoint (issue #165) — the pull lane's render leg. Verifies the purpose-scoped, short-TTL defer token (an ACTION token is rejected here by signature — purpose confusion fails closed), rebuilds the component from its signed identity, and returns its replace (or morph, per the SIGNED mode) stream. No action runs and no transaction opens — this is a read. Authorization: the base controller's auth applies as on every reactive request; a component that guards visibility can raise a registered authorization error from from_identity/render (→ 403) or return false from render? (→ 204: keep content, clear pending).



58
59
60
61
62
63
64
65
66
67
68
69
70
# File 'app/controllers/phlex/reactive/actions_controller.rb', line 58

def deferred
  event = { component: nil, outcome: nil }
  # Verify UNDER the actor's binding (issue #165 security): a defer token
  # minted for another actor's session fails the binding-scoped purpose,
  # so a leaked token can't be exchanged here for this actor's render (and
  # its embedded fresh identity token). Unbound requests (no session) are
  # unchanged.
  Phlex::Reactive.with_defer_binding(Phlex::Reactive.defer_binding_for(request)) do
    Phlex::Reactive.instrument("defer", event) do
      deferred_action(event)
    end
  end
end