spree_api 5.4.0.beta7 → 5.4.0.beta8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6f27a3d636c9e7fdac4e297ba4820d97f91831b4124a1b6aafcc734b4f3906bc
4
- data.tar.gz: a436eac6eee920191894f256c587c12f852b5b86f3a978d6af43f032cff1908b
3
+ metadata.gz: d59f56e428252697b4360183c26b92d20fe7607630b84589a5203ad766d6a467
4
+ data.tar.gz: 2807c39fa35138ae1db42e5fb184998dfc8e9c25d7309f11b43edc0af1751877
5
5
  SHA512:
6
- metadata.gz: adce2c15462e5c368813c95419047c83e686254e93af752832e370a2ed19aa7f5893659ed8b79c982bbe37294695b245d87671d196d93e92d759b9d8fe79caa9
7
- data.tar.gz: 1cdec354c04bd65e731b2ee0309170e597012047ede52184ca0cbe2f8323d9af4c0f5ed967cb13fade6c74c3a1206aa058a803992c983b1ae351fc887f77b934
6
+ metadata.gz: ed23df8b1714945bb9b4718f0c81443fb140fb3ddbaf48e72fbc4de1f9b6b0f9b41373587cf57f69fcc4e928aa75fc1636c7dc57b700e24c9958a3bd70c42bc0
7
+ data.tar.gz: bc499f9d970dc0fc0d24de5c711070b267e103d60c82704e5de1f48c7bcd635183cf024e1cfef4b0f9d0841f5e2ea150e74618f7ec3283dd59ee73f20059682f
@@ -13,6 +13,8 @@ module Spree
13
13
  invalid_token: 'invalid_token',
14
14
  invalid_provider: 'invalid_provider',
15
15
  current_password_invalid: 'current_password_invalid',
16
+ password_reset_token_invalid: 'password_reset_token_invalid',
17
+ redirect_url_not_allowed: 'redirect_url_not_allowed',
16
18
 
17
19
  # Resource errors
18
20
  record_not_found: 'record_not_found',
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Spree
4
+ module Api
5
+ module V3
6
+ module Store
7
+ module Customer
8
+ class PasswordResetsController < Store::BaseController
9
+ rate_limit to: Spree::Api::Config[:rate_limit_password_reset],
10
+ within: Spree::Api::Config[:rate_limit_window].seconds,
11
+ store: Rails.cache,
12
+ with: RATE_LIMIT_RESPONSE
13
+
14
+ skip_before_action :authenticate_user
15
+
16
+ # POST /api/v3/store/customer/password_resets
17
+ def create
18
+ redirect_url = params[:redirect_url]
19
+
20
+ # Validate redirect_url against allowed origins (secure by default).
21
+ # If no allowed origins are configured, redirect_url is silently ignored
22
+ # to prevent open redirect / token exfiltration attacks.
23
+ if redirect_url.present?
24
+ if current_store.allowed_origins.exists? && current_store.allowed_origin?(redirect_url)
25
+ # redirect_url is valid — keep it
26
+ else
27
+ redirect_url = nil
28
+ end
29
+ end
30
+
31
+ user = Spree.user_class.find_by(email: params[:email])
32
+
33
+ if user
34
+ token = user.generate_token_for(:password_reset)
35
+ event_payload = { reset_token: token, email: user.email }
36
+ event_payload[:redirect_url] = redirect_url if redirect_url.present?
37
+ user.publish_event('customer.password_reset_requested', event_payload)
38
+ end
39
+
40
+ # Always return 202 to prevent email enumeration
41
+ render json: { message: Spree.t(:password_reset_requested, scope: :api) }, status: :accepted
42
+ end
43
+
44
+ # PATCH /api/v3/store/customer/password_resets/:id
45
+ def update
46
+ user = Spree.user_class.find_by_password_reset_token(params[:id])
47
+
48
+ unless user
49
+ return render_error(
50
+ code: ERROR_CODES[:password_reset_token_invalid],
51
+ message: Spree.t(:password_reset_token_invalid, scope: :api),
52
+ status: :unprocessable_content
53
+ )
54
+ end
55
+
56
+ if user.update(password: params[:password], password_confirmation: params[:password_confirmation])
57
+ jwt = generate_jwt(user)
58
+ user.publish_event('customer.password_reset')
59
+
60
+ render json: {
61
+ token: jwt,
62
+ user: serializer_class.new(user, params: serializer_params).to_h
63
+ }
64
+ else
65
+ render_errors(user.errors)
66
+ end
67
+ end
68
+
69
+ protected
70
+
71
+ def serializer_class
72
+ Spree.api.customer_serializer
73
+ end
74
+
75
+ def serializer_params
76
+ {
77
+ store: current_store,
78
+ locale: current_locale,
79
+ currency: current_currency,
80
+ user: nil,
81
+ includes: []
82
+ }
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
@@ -41,7 +41,7 @@ module Spree
41
41
  # these scopes are not automatically picked by ar_lazy_preload gem and we need to explicitly include them
42
42
  def scope_includes
43
43
  [
44
- thumbnail: [attachment_attachment: :blob],
44
+ primary_media: [attachment_attachment: :blob],
45
45
  master: [:prices, stock_items: :stock_location],
46
46
  variants: [:prices, stock_items: :stock_location]
47
47
  ]
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Spree
4
+ module Api
5
+ module V3
6
+ module Admin
7
+ class AllowedOriginSerializer < V3::BaseSerializer
8
+ typelize store_id: :string
9
+
10
+ attributes :id, :origin
11
+
12
+ attribute :store_id do |allowed_origin|
13
+ allowed_origin.store&.prefixed_id
14
+ end
15
+
16
+ attributes created_at: :iso8601, updated_at: :iso8601
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,17 @@
1
+ module Spree
2
+ module Api
3
+ module V3
4
+ module Admin
5
+ class MediaSerializer < V3::MediaSerializer
6
+ typelize viewable_type: :string, viewable_id: :string
7
+
8
+ attribute :viewable_id do |asset|
9
+ asset.viewable&.prefixed_id
10
+ end
11
+
12
+ attributes :viewable_type
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -37,10 +37,14 @@ module Spree
37
37
  resource: Spree.api.admin_variant_serializer,
38
38
  if: proc { expand?('master_variant') }
39
39
 
40
- many :variant_images,
41
- key: :images,
42
- resource: Spree.api.admin_image_serializer,
43
- if: proc { expand?('images') }
40
+ one :primary_media,
41
+ resource: Spree.api.admin_media_serializer,
42
+ if: proc { expand?('primary_media') }
43
+
44
+ many :gallery_media,
45
+ key: :media,
46
+ resource: Spree.api.admin_media_serializer,
47
+ if: proc { expand?('media') }
44
48
 
45
49
  many :option_types,
46
50
  resource: Spree.api.admin_option_type_serializer,
@@ -20,9 +20,14 @@ module Spree
20
20
  end
21
21
 
22
22
  # Override inherited associations to use admin serializers
23
- many :images,
24
- resource: Spree.api.admin_image_serializer,
25
- if: proc { expand?('images') }
23
+ one :primary_media,
24
+ resource: Spree.api.admin_media_serializer,
25
+ if: proc { expand?('primary_media') }
26
+
27
+ many :gallery_media,
28
+ key: :media,
29
+ resource: Spree.api.admin_media_serializer,
30
+ if: proc { expand?('media') }
26
31
 
27
32
  many :option_values, resource: Spree.api.admin_option_value_serializer
28
33
 
@@ -38,7 +38,7 @@ module Spree
38
38
  end
39
39
 
40
40
  # Check if an association should be expanded
41
- # Supports dot notation: expand?('variants') matches both 'variants' and 'variants.images'
41
+ # Supports dot notation: expand?('variants') matches both 'variants' and 'variants.media'
42
42
  def expand?(name)
43
43
  name = name.to_s
44
44
  expands.any? { |e| e == name || e.start_with?("#{name}.") }
@@ -0,0 +1,60 @@
1
+ module Spree
2
+ module Api
3
+ module V3
4
+ class MediaSerializer < BaseSerializer
5
+ typelize position: :number, alt: [:string, nullable: true],
6
+ product_id: [:string, nullable: true],
7
+ variant_ids: [:string, multi: true],
8
+ media_type: :string,
9
+ focal_point_x: [:number, nullable: true],
10
+ focal_point_y: [:number, nullable: true],
11
+ external_video_url: [:string, nullable: true],
12
+ original_url: [:string, nullable: true], mini_url: [:string, nullable: true],
13
+ small_url: [:string, nullable: true], medium_url: [:string, nullable: true],
14
+ large_url: [:string, nullable: true], xlarge_url: [:string, nullable: true],
15
+ og_image_url: [:string, nullable: true]
16
+
17
+ attribute :product_id do |asset|
18
+ asset.product&.prefixed_id
19
+ end
20
+
21
+ # Returns prefixed IDs of variants this media is associated with.
22
+ # Currently: single variant via viewable (legacy). In 6.0: multiple via VariantMedia join table.
23
+ attribute :variant_ids do |asset|
24
+ if asset.viewable_type == 'Spree::Variant'
25
+ [asset.viewable&.prefixed_id].compact
26
+ else
27
+ []
28
+ end
29
+ end
30
+
31
+ attributes :position, :alt, :media_type,
32
+ :focal_point_x, :focal_point_y, :external_video_url,
33
+ created_at: :iso8601, updated_at: :iso8601
34
+
35
+ attribute :original_url do |asset|
36
+ image_url_for(asset)
37
+ end
38
+
39
+ # Dynamically define attributes for each configured image variant
40
+ # Uses named variants from Spree::Config.product_image_variant_sizes
41
+ # (e.g., mini, small, medium, large, xlarge)
42
+ Spree::Config.product_image_variant_sizes.each_key do |variant_name|
43
+ attribute :"#{variant_name}_url" do |asset|
44
+ variant_url(asset, variant_name)
45
+ end
46
+ end
47
+
48
+ private
49
+
50
+ def variant_url(asset, variant_name)
51
+ return nil unless asset&.attachment&.attached?
52
+
53
+ Rails.application.routes.url_helpers.cdn_image_url(
54
+ asset.attachment.variant(variant_name)
55
+ )
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -40,9 +40,9 @@ module Spree
40
40
  product.default_variant&.prefixed_id
41
41
  end
42
42
 
43
- # Main product image URL for listings (cached thumbnail)
43
+ # Main product image URL for listings (cached primary_media)
44
44
  attribute :thumbnail_url do |product|
45
- image_url_for(product.thumbnail)
45
+ image_url_for(product.primary_media)
46
46
  end
47
47
 
48
48
  attribute :tags do |product|
@@ -68,10 +68,14 @@ module Spree
68
68
  end
69
69
 
70
70
  # Conditional associations
71
- many :variant_images,
72
- key: :images,
73
- resource: Spree.api.image_serializer,
74
- if: proc { expand?('images') }
71
+ one :primary_media,
72
+ resource: Spree.api.media_serializer,
73
+ if: proc { expand?('primary_media') }
74
+
75
+ many :gallery_media,
76
+ key: :media,
77
+ resource: Spree.api.media_serializer,
78
+ if: proc { expand?('media') }
75
79
 
76
80
  many :variants,
77
81
  resource: Spree.api.variant_serializer,
@@ -5,8 +5,8 @@ module Spree
5
5
  # Customer-facing variant data with limited fields
6
6
  class VariantSerializer < BaseSerializer
7
7
  typelize product_id: :string, sku: [:string, nullable: true],
8
- is_master: :boolean, options_text: :string, track_inventory: :boolean, image_count: :number,
9
- thumbnail: [:string, nullable: true],
8
+ is_master: :boolean, options_text: :string, track_inventory: :boolean, media_count: :number,
9
+ thumbnail_url: [:string, nullable: true],
10
10
  purchasable: :boolean, in_stock: :boolean, backorderable: :boolean,
11
11
  weight: [:number, nullable: true], height: [:number, nullable: true], width: [:number, nullable: true], depth: [:number, nullable: true],
12
12
  price: 'Price',
@@ -16,12 +16,12 @@ module Spree
16
16
  variant.product&.prefixed_id
17
17
  end
18
18
 
19
- attributes :sku, :is_master, :options_text, :track_inventory, :image_count,
19
+ attributes :sku, :is_master, :options_text, :track_inventory, :media_count,
20
20
  created_at: :iso8601, updated_at: :iso8601
21
21
 
22
- # Main variant image URL for listings (cached thumbnail)
23
- attribute :thumbnail do |variant|
24
- image_url_for(variant.thumbnail)
22
+ # Main variant image URL for listings (cached primary_media)
23
+ attribute :thumbnail_url do |variant|
24
+ image_url_for(variant.primary_media)
25
25
  end
26
26
 
27
27
  attribute :purchasable do |variant|
@@ -70,9 +70,14 @@ module Spree
70
70
  end
71
71
 
72
72
  # Conditional associations
73
- many :images,
74
- resource: Spree.api.image_serializer,
75
- if: proc { expand?('images') }
73
+ one :primary_media,
74
+ resource: Spree.api.media_serializer,
75
+ if: proc { expand?('primary_media') }
76
+
77
+ many :gallery_media,
78
+ key: :media,
79
+ resource: Spree.api.media_serializer,
80
+ if: proc { expand?('media') }
76
81
 
77
82
  many :option_values, resource: Spree.api.option_value_serializer
78
83
 
@@ -7,6 +7,9 @@ en:
7
7
  invalid_resource: Invalid resource. Please fix errors and try again.
8
8
  invalid_taxonomy_id: Invalid taxonomy id.
9
9
  current_password_invalid: Current password is invalid or missing
10
+ password_reset_requested: If an account exists for that email, password reset instructions have been sent.
11
+ password_reset_token_invalid: Password reset token is invalid or has expired.
12
+ redirect_url_not_allowed: The redirect URL is not from an allowed origin for this store.
10
13
  must_specify_api_key: You must specify an API key.
11
14
  negative_quantity: quantity is negative
12
15
  order:
data/config/routes.rb CHANGED
@@ -59,6 +59,8 @@ Spree::Core::Engine.add_routes do
59
59
 
60
60
  # Customer nested resources
61
61
  namespace :customer, path: 'customer' do
62
+ resources :password_resets, only: [:create, :update]
63
+
62
64
  resources :orders, only: [:index, :show]
63
65
  resources :addresses, only: [:index, :show, :create, :update, :destroy] do
64
66
  member do
@@ -19,6 +19,7 @@ module Spree
19
19
  preference :rate_limit_register, :integer, default: 3 # per IP
20
20
  preference :rate_limit_refresh, :integer, default: 10 # per IP
21
21
  preference :rate_limit_oauth, :integer, default: 5 # per IP
22
+ preference :rate_limit_password_reset, :integer, default: 3 # per IP
22
23
 
23
24
  # Request body size limit in bytes
24
25
  preference :max_request_body_size, :integer, default: 102_400 # 100KB
@@ -96,7 +96,7 @@ module Spree
96
96
  price_serializer: 'Spree::Api::V3::PriceSerializer',
97
97
  product_serializer: 'Spree::Api::V3::ProductSerializer',
98
98
  variant_serializer: 'Spree::Api::V3::VariantSerializer',
99
- image_serializer: 'Spree::Api::V3::ImageSerializer',
99
+ media_serializer: 'Spree::Api::V3::MediaSerializer',
100
100
  option_type_serializer: 'Spree::Api::V3::OptionTypeSerializer',
101
101
  option_value_serializer: 'Spree::Api::V3::OptionValueSerializer',
102
102
  cart_serializer: 'Spree::Api::V3::CartSerializer',
@@ -161,7 +161,7 @@ module Spree
161
161
  admin_line_item_serializer: 'Spree::Api::V3::Admin::LineItemSerializer',
162
162
  admin_option_type_serializer: 'Spree::Api::V3::Admin::OptionTypeSerializer',
163
163
  admin_option_value_serializer: 'Spree::Api::V3::Admin::OptionValueSerializer',
164
- admin_image_serializer: 'Spree::Api::V3::Admin::ImageSerializer',
164
+ admin_media_serializer: 'Spree::Api::V3::Admin::MediaSerializer',
165
165
  admin_asset_serializer: 'Spree::Api::V3::Admin::AssetSerializer',
166
166
  admin_stock_item_serializer: 'Spree::Api::V3::Admin::StockItemSerializer',
167
167
  admin_shipment_serializer: 'Spree::Api::V3::Admin::ShipmentSerializer',
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: spree_api
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.4.0.beta7
4
+ version: 5.4.0.beta8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Vendo Connect Inc.
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-03-15 00:00:00.000000000 Z
11
+ date: 2026-03-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rswag-specs
@@ -86,14 +86,14 @@ dependencies:
86
86
  requirements:
87
87
  - - '='
88
88
  - !ruby/object:Gem::Version
89
- version: 5.4.0.beta7
89
+ version: 5.4.0.beta8
90
90
  type: :runtime
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
94
  - - '='
95
95
  - !ruby/object:Gem::Version
96
- version: 5.4.0.beta7
96
+ version: 5.4.0.beta8
97
97
  description: Spree's API
98
98
  email:
99
99
  - hello@spreecommerce.org
@@ -136,6 +136,7 @@ files:
136
136
  - app/controllers/spree/api/v3/store/customer/credit_cards_controller.rb
137
137
  - app/controllers/spree/api/v3/store/customer/gift_cards_controller.rb
138
138
  - app/controllers/spree/api/v3/store/customer/orders_controller.rb
139
+ - app/controllers/spree/api/v3/store/customer/password_resets_controller.rb
139
140
  - app/controllers/spree/api/v3/store/customer/payment_setup_sessions_controller.rb
140
141
  - app/controllers/spree/api/v3/store/customers_controller.rb
141
142
  - app/controllers/spree/api/v3/store/digitals_controller.rb
@@ -155,13 +156,14 @@ files:
155
156
  - app/serializers/spree/api/v3/admin/address_serializer.rb
156
157
  - app/serializers/spree/api/v3/admin/adjustment_serializer.rb
157
158
  - app/serializers/spree/api/v3/admin/admin_user_serializer.rb
159
+ - app/serializers/spree/api/v3/admin/allowed_origin_serializer.rb
158
160
  - app/serializers/spree/api/v3/admin/asset_serializer.rb
159
161
  - app/serializers/spree/api/v3/admin/category_serializer.rb
160
162
  - app/serializers/spree/api/v3/admin/credit_card_serializer.rb
161
163
  - app/serializers/spree/api/v3/admin/customer_serializer.rb
162
164
  - app/serializers/spree/api/v3/admin/digital_link_serializer.rb
163
- - app/serializers/spree/api/v3/admin/image_serializer.rb
164
165
  - app/serializers/spree/api/v3/admin/line_item_serializer.rb
166
+ - app/serializers/spree/api/v3/admin/media_serializer.rb
165
167
  - app/serializers/spree/api/v3/admin/metafield_serializer.rb
166
168
  - app/serializers/spree/api/v3/admin/option_type_serializer.rb
167
169
  - app/serializers/spree/api/v3/admin/option_value_serializer.rb
@@ -199,13 +201,13 @@ files:
199
201
  - app/serializers/spree/api/v3/export_serializer.rb
200
202
  - app/serializers/spree/api/v3/gift_card_batch_serializer.rb
201
203
  - app/serializers/spree/api/v3/gift_card_serializer.rb
202
- - app/serializers/spree/api/v3/image_serializer.rb
203
204
  - app/serializers/spree/api/v3/import_row_serializer.rb
204
205
  - app/serializers/spree/api/v3/import_serializer.rb
205
206
  - app/serializers/spree/api/v3/invitation_serializer.rb
206
207
  - app/serializers/spree/api/v3/line_item_serializer.rb
207
208
  - app/serializers/spree/api/v3/locale_serializer.rb
208
209
  - app/serializers/spree/api/v3/market_serializer.rb
210
+ - app/serializers/spree/api/v3/media_serializer.rb
209
211
  - app/serializers/spree/api/v3/metafield_serializer.rb
210
212
  - app/serializers/spree/api/v3/newsletter_subscriber_serializer.rb
211
213
  - app/serializers/spree/api/v3/option_type_serializer.rb
@@ -266,9 +268,9 @@ licenses:
266
268
  - BSD-3-Clause
267
269
  metadata:
268
270
  bug_tracker_uri: https://github.com/spree/spree/issues
269
- changelog_uri: https://github.com/spree/spree/releases/tag/v5.4.0.beta7
271
+ changelog_uri: https://github.com/spree/spree/releases/tag/v5.4.0.beta8
270
272
  documentation_uri: https://docs.spreecommerce.org/
271
- source_code_uri: https://github.com/spree/spree/tree/v5.4.0.beta7
273
+ source_code_uri: https://github.com/spree/spree/tree/v5.4.0.beta8
272
274
  post_install_message:
273
275
  rdoc_options: []
274
276
  require_paths:
@@ -1,10 +0,0 @@
1
- module Spree
2
- module Api
3
- module V3
4
- module Admin
5
- class ImageSerializer < V3::ImageSerializer
6
- end
7
- end
8
- end
9
- end
10
- end
@@ -1,43 +0,0 @@
1
- module Spree
2
- module Api
3
- module V3
4
- class ImageSerializer < AssetSerializer
5
- typelize position: :number, alt: [:string, nullable: true], viewable_type: :string, viewable_id: :string,
6
- original_url: [:string, nullable: true], mini_url: [:string, nullable: true],
7
- small_url: [:string, nullable: true], medium_url: [:string, nullable: true],
8
- large_url: [:string, nullable: true], xlarge_url: [:string, nullable: true],
9
- og_image_url: [:string, nullable: true]
10
-
11
- attribute :viewable_id do |image|
12
- image.viewable&.prefixed_id
13
- end
14
-
15
- attributes :position, :alt, :viewable_type,
16
- created_at: :iso8601, updated_at: :iso8601
17
-
18
- attribute :original_url do |image|
19
- image_url_for(image)
20
- end
21
-
22
- # Dynamically define attributes for each configured image variant
23
- # Uses named variants from Spree::Config.product_image_variant_sizes
24
- # (e.g., mini, small, medium, large, xlarge)
25
- Spree::Config.product_image_variant_sizes.each_key do |variant_name|
26
- attribute :"#{variant_name}_url" do |image|
27
- variant_url(image, variant_name)
28
- end
29
- end
30
-
31
- private
32
-
33
- def variant_url(image, variant_name)
34
- return nil unless image&.attachment&.attached?
35
-
36
- Rails.application.routes.url_helpers.cdn_image_url(
37
- image.attachment.variant(variant_name)
38
- )
39
- end
40
- end
41
- end
42
- end
43
- end