From d6184b6c8456b7af5999cd1a77cd7855b8dd5abf Mon Sep 17 00:00:00 2001 From: kbe Date: Mon, 15 Sep 2025 19:52:01 +0200 Subject: [PATCH] refactor: extract cart storage to dedicated API controller with dynamic frontend URLs - Added dedicated CartsController for session-based cart storage - Refactored routes to use POST /api/v1/carts/store - Updated ticket selection JS to use dynamic data attributes for URLs - Fixed CSRF protection in API and checkout payment increment - Made checkout button URLs dynamic via data attributes - Updated tests for new cart storage endpoint - Removed obsolete store_cart from EventsController --- app/controllers/api/v1/carts_controller.rb | 25 +++++++++++++ app/controllers/api/v1/orders_controller.rb | 3 -- app/controllers/api_controller.rb | 2 +- .../ticket_selection_controller.js | 12 +++--- app/views/events/show.html.erb | 8 +++- app/views/orders/checkout.html.erb | 37 +++++++++++-------- config/routes.rb | 7 +--- .../api/v1/events_controller_test.rb | 2 +- 8 files changed, 63 insertions(+), 33 deletions(-) create mode 100644 app/controllers/api/v1/carts_controller.rb diff --git a/app/controllers/api/v1/carts_controller.rb b/app/controllers/api/v1/carts_controller.rb new file mode 100644 index 0000000..2e61d2d --- /dev/null +++ b/app/controllers/api/v1/carts_controller.rb @@ -0,0 +1,25 @@ +module Api + module V1 + class CartsController < ApiController + # Skip API key authentication for store_cart action (used by frontend forms) + skip_before_action :authenticate_api_key, only: [ :store ] + + def store + event_id = params[:event_id] + @event = Event.find(event_id) + + cart_data = params[:cart] || {} + session[:pending_cart] = cart_data + session[:event_id] = @event.id + + render json: { status: "success", message: "Cart stored successfully" } + rescue ActiveRecord::RecordNotFound + render json: { status: "error", message: "Event not found" }, status: :not_found + rescue => e + error_message = e.message.present? ? e.message : "Unknown error" + Rails.logger.error "Error storing cart: #{error_message}" + render json: { status: "error", message: "Failed to store cart" }, status: 500 + end + end + end +end diff --git a/app/controllers/api/v1/orders_controller.rb b/app/controllers/api/v1/orders_controller.rb index c7494de..d37e410 100644 --- a/app/controllers/api/v1/orders_controller.rb +++ b/app/controllers/api/v1/orders_controller.rb @@ -4,9 +4,6 @@ module Api module V1 class OrdersController < ApiController - # Skip API key authentication for store_cart action (used by frontend forms) - skip_before_action :authenticate_api_key, only: [ :store_cart ] - before_action :set_order, only: [ :show, :checkout, :retry_payment, :increment_payment_attempt ] before_action :set_event, only: [ :new, :create ] diff --git a/app/controllers/api_controller.rb b/app/controllers/api_controller.rb index f7866d2..57998d3 100755 --- a/app/controllers/api_controller.rb +++ b/app/controllers/api_controller.rb @@ -2,7 +2,7 @@ # Provides authentication and common functionality for API controllers class ApiController < ApplicationController # Disable CSRF protection for API requests (token-based authentication instead) - protect_from_forgery with: :null_session + protect_from_forgery prepend: true # Authenticate all API requests using API key # Must be called before any API action diff --git a/app/javascript/controllers/ticket_selection_controller.js b/app/javascript/controllers/ticket_selection_controller.js index 4dfd365..7216ac1 100644 --- a/app/javascript/controllers/ticket_selection_controller.js +++ b/app/javascript/controllers/ticket_selection_controller.js @@ -10,7 +10,7 @@ export default class extends Controller { "checkoutButton", "form", ]; - static values = { eventSlug: String, eventId: String }; + static values = { eventSlug: String, eventId: String, orderNewUrl: String, storeCartUrl: String }; // Initialize the controller and update the cart summary connect() { @@ -117,9 +117,9 @@ export default class extends Controller { // Store cart data in session await this.storeCartInSession(cartData); - // Redirect to event-scoped orders/new page - const OrderNewUrl = `/orders/new/events/${this.eventSlugValue}.${this.eventIdValue}`; - window.location.href = OrderNewUrl; + // Redirect to event-scoped orders/new page + const orderNewUrl = this.orderNewUrlValue; + window.location.href = orderNewUrl; } catch (error) { console.error("Error storing cart:", error); alert("Une erreur est survenue. Veuillez réessayer."); @@ -145,7 +145,7 @@ export default class extends Controller { // Store cart data in session via AJAX async storeCartInSession(cartData) { - const storeCartUrl = `/api/v1/events/${this.eventIdValue}/store_cart`; + const storeCartUrl = this.storeCartUrlValue; const response = await fetch(storeCartUrl, { method: "POST", @@ -155,7 +155,7 @@ export default class extends Controller { .querySelector('meta[name="csrf-token"]') .getAttribute("content"), }, - body: JSON.stringify({ cart: cartData }), + body: JSON.stringify({ cart: cartData, event_id: this.eventIdValue }), }); if (!response.ok) { diff --git a/app/views/events/show.html.erb b/app/views/events/show.html.erb index e173880..02f8dd1 100755 --- a/app/views/events/show.html.erb +++ b/app/views/events/show.html.erb @@ -135,8 +135,12 @@ controller: "ticket-selection", ticket_selection_target: "form", ticket_selection_event_slug_value: @event.slug, - ticket_selection_event_id_value: @event.id - } do |form| %> + ticket_selection_event_id_value: @event.id, + ticket_selection_order_new_url_value: event_order_new_path(@event.slug, @event.id), + ticket_selection_store_cart_url_value: api_v1_store_cart_path, + ticket_selection_order_new_url_value: event_order_new_path(@event.slug, @event.id), + ticket_selection_store_cart_url_value: api_v1_store_cart_path + } do |form| %>
diff --git a/app/views/orders/checkout.html.erb b/app/views/orders/checkout.html.erb index 4b2b16b..33f666c 100644 --- a/app/views/orders/checkout.html.erb +++ b/app/views/orders/checkout.html.erb @@ -139,10 +139,13 @@
-