Class: ActionAgent::Api::SessionRecordingsController
- Inherits:
-
BaseController
- Object
- ActionController::Base
- ActionAgent::ApplicationController
- BaseController
- ActionAgent::Api::SessionRecordingsController
- Defined in:
- app/controllers/action_agent/api/session_recordings_controller.rb
Instance Method Summary collapse
-
#actions ⇒ Object
GET /api/session_recordings/:id/actions Get the action timeline for playback.
-
#complete_session ⇒ Object
POST /api/session_recordings/:id/complete Complete a user session recording.
-
#destroy ⇒ Object
DELETE /api/session_recordings/:id.
-
#export ⇒ Object
POST /api/session_recordings/:id/export Export recording as VCR cassette.
-
#handoff ⇒ Object
POST /api/session_recordings/:id/handoff Get handoff state to continue where agent left off.
-
#index ⇒ Object
GET /api/session_recordings List recordings with optional filters.
-
#recent ⇒ Object
GET /api/session_recordings/recent Get recent recordings for the current user.
-
#record_action ⇒ Object
POST /api/session_recordings/:id/record_action Record a user action in an active session.
-
#show ⇒ Object
GET /api/session_recordings/:id Get full recording details for playback.
-
#snapshot ⇒ Object
GET /api/session_recordings/:id/snapshot/:action_id Get a specific snapshot (screenshot or DOM).
-
#start_user_session ⇒ Object
POST /api/session_recordings/start_user_session Start a new user takeover session for analytics.
Methods inherited from ActionAgent::ApplicationController
Instance Method Details
#actions ⇒ Object
GET /api/session_recordings/:id/actions Get the action timeline for playback
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 |
# File 'app/controllers/action_agent/api/session_recordings_controller.rb', line 77 def actions actions = @recording.recording_actions.ordered # Support pagination for large recordings if params[:after_sequence].present? actions = actions.where("sequence > ?", params[:after_sequence].to_i) end limit = [ params[:limit]&.to_i || 100, 500 ].min actions = actions.limit(limit) render json: { actions: actions.map(&:as_json_for_api), has_more: actions.count == limit, total_actions: @recording.action_count } end |
#complete_session ⇒ Object
POST /api/session_recordings/:id/complete Complete a user session recording
190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 |
# File 'app/controllers/action_agent/api/session_recordings_controller.rb', line 190 def complete_session recording = SessionRecording.find(params[:id]) return not_found unless can_manage_recording?(recording) unless recording.recording? render json: { error: "Recording already completed" }, status: :unprocessable_entity return end # Record the completion action recording.record_action!( action_type: "completion", value: params[:completion_type] || "session_end", metadata: { email_submitted: params[:email_submitted], success: params[:success] } ) recording.complete! render json: { recording_id: recording.id, status: recording.status, action_count: recording.action_count, duration_ms: recording.duration_ms } end |
#destroy ⇒ Object
DELETE /api/session_recordings/:id
252 253 254 255 256 257 258 259 260 261 262 263 |
# File 'app/controllers/action_agent/api/session_recordings_controller.rb', line 252 def destroy @recording = SessionRecording.find(params[:id]) # Only allow deletion of own recordings (or any if admin) unless can_manage_recording?(@recording) render json: { error: "Not authorized" }, status: :forbidden return end @recording.destroy! render json: { message: "Recording deleted" } end |
#export ⇒ Object
POST /api/session_recordings/:id/export Export recording as VCR cassette
118 119 120 121 122 123 124 125 126 127 |
# File 'app/controllers/action_agent/api/session_recordings_controller.rb', line 118 def export format = params[:format] || "json" cassette = build_cassette(@recording, format) render json: { cassette: cassette, filename: "#{@recording.name}_recording.#{format}" } end |
#handoff ⇒ Object
POST /api/session_recordings/:id/handoff Get handoff state to continue where agent left off
221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 |
# File 'app/controllers/action_agent/api/session_recordings_controller.rb', line 221 def handoff handoff_state = @recording.["handoff_state"] unless handoff_state render json: { error: "No handoff state available" }, status: :unprocessable_entity return end # Create a new recording for the user's continuation continuation = SessionRecording.create!( sandbox_session: @recording.sandbox_session, agent_run: @recording.agent_run, name: "#{@recording.name}_continuation", status: :recording, metadata: { parent_recording_id: @recording.id, handoff_from: @recording.action_count, started_at: Time.current.iso8601, user_id: current_user&.id } ) render json: { handoff_state: handoff_state, continuation_recording_id: continuation.id, parent_recording: recording_summary(@recording), message: "Ready to continue from action #{@recording.action_count}" } end |
#index ⇒ Object
GET /api/session_recordings List recordings with optional filters
18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
# File 'app/controllers/action_agent/api/session_recordings_controller.rb', line 18 def index # Recordings the caller owns, plus the shared demo. Ownership is a # real column rather than a JSON metadata key, so this works on # every adapter. recordings = owned(SessionRecording).or(SessionRecording.where(name: "lander_demo")).recent # Filter by status recordings = recordings.where(status: params[:status]) if params[:status].present? # Filter by agent if params[:agent_id].present? recordings = recordings.joins(:agent_run) .where(agent_runs: { agent_id: params[:agent_id] }) end # Filter by sandbox session if params[:sandbox_session_id].present? recordings = recordings.where(sandbox_session_id: params[:sandbox_session_id]) end # Pagination page = (params[:page] || 1).to_i per_page = [ (params[:per_page] || 20).to_i, 100 ].min offset = (page - 1) * per_page total = recordings.count recordings = recordings.offset(offset).limit(per_page) render json: { recordings: recordings.map { |r| recording_summary(r) }, pagination: { page: page, per_page: per_page, total: total, total_pages: (total.to_f / per_page).ceil } } end |
#recent ⇒ Object
GET /api/session_recordings/recent Get recent recordings for the current user
59 60 61 62 63 64 65 |
# File 'app/controllers/action_agent/api/session_recordings_controller.rb', line 59 def recent recordings = owned(SessionRecording).recent.limit(10) render json: { recordings: recordings.map { |r| recording_summary(r) } } end |
#record_action ⇒ Object
POST /api/session_recordings/:id/record_action Record a user action in an active session
165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 |
# File 'app/controllers/action_agent/api/session_recordings_controller.rb', line 165 def record_action recording = SessionRecording.find(params[:id]) return not_found unless can_manage_recording?(recording) unless recording.recording? render json: { error: "Recording already completed" }, status: :unprocessable_entity return end action = recording.record_action!( action_type: params[:action_type], selector: params[:selector], value: params[:value], metadata: params[:metadata]&.to_unsafe_h || {} ) render json: { action_id: action.id, sequence: action.sequence, timestamp_ms: action. } end |
#show ⇒ Object
GET /api/session_recordings/:id Get full recording details for playback
69 70 71 72 73 |
# File 'app/controllers/action_agent/api/session_recordings_controller.rb', line 69 def show render json: { recording: recording_detail(@recording) } end |
#snapshot ⇒ Object
GET /api/session_recordings/:id/snapshot/:action_id Get a specific snapshot (screenshot or DOM)
97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 |
# File 'app/controllers/action_agent/api/session_recordings_controller.rb', line 97 def snapshot action = @recording.recording_actions.find(params[:action_id]) snapshot_type = params[:type] || "screenshot" case snapshot_type when "screenshot" url = action.screenshot_url render json: { url: url, type: "screenshot" } when "dom" content = action.dom_snapshot_content render json: { content: content, type: "dom" } else render json: { error: "Unknown snapshot type" }, status: :bad_request end rescue ActiveRecord::RecordNotFound render json: { error: "Action not found" }, status: :not_found end |
#start_user_session ⇒ Object
POST /api/session_recordings/start_user_session Start a new user takeover session for analytics
131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 |
# File 'app/controllers/action_agent/api/session_recordings_controller.rb', line 131 def start_user_session recording = SessionRecording.start_user_session!( visitor_id: params[:visitor_id] || generate_visitor_id, parent_demo_id: params[:parent_demo_id], page_url: params[:page_url] ) # Set user agent from request recording.update!( metadata: recording..merge( user_agent: request.user_agent, ip_hash: Digest::SHA256.hexdigest(request.remote_ip.to_s)[0..16] ) ) # Record the handoff action recording.record_action!( action_type: "handoff", value: "User took over from agent demo", metadata: { source: "lander_demo", step: params[:step] || 4 } ) render json: { recording_id: recording.id, visitor_id: recording.["visitor_id"], message: "User session started" }, status: :created end |