spree_cm_commissioner 2.6.2.pre.pre.4 → 2.6.2.pre.pre.5
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 +4 -4
- data/.github/workflows/test_and_build_gem.yml +0 -2
- data/.gitignore +1 -0
- data/Gemfile.lock +1 -1
- data/README.md +36 -0
- data/Rakefile +9 -0
- data/app/controllers/concerns/spree/admin/service_calendars_concern.rb +3 -1
- data/app/controllers/concerns/spree_cm_commissioner/adaptive_turnstile.rb +37 -0
- data/app/controllers/concerns/spree_cm_commissioner/turnstile_protectable.rb +55 -0
- data/app/controllers/spree/admin/inventory_items_controller.rb +2 -2
- data/app/controllers/spree/admin/inventory_monitorings_controller.rb +1 -1
- data/app/controllers/spree/admin/product_service_calendars_controller.rb +1 -0
- data/app/controllers/spree/admin/system/cache_controller.rb +30 -0
- data/app/controllers/spree/admin/system/waiting_room_controller.rb +35 -0
- data/app/controllers/spree/api/v2/organizer/invite_guests_controller.rb +91 -0
- data/app/controllers/spree/api/v2/storefront/access_tokens_controller.rb +3 -0
- data/app/controllers/spree/api/v2/storefront/pin_code_generators_controller.rb +4 -0
- data/app/controllers/spree/api/v2/storefront/pin_code_otp_generators_controller.rb +4 -0
- data/app/controllers/spree/api/v2/storefront/reset_passwords_controller.rb +4 -0
- data/app/controllers/spree/api/v2/storefront/saved_guests_controller.rb +77 -0
- data/app/controllers/spree/api/v2/storefront/trip_search_controller.rb +12 -2
- data/app/controllers/spree/api/v2/storefront/user_registration_with_pin_codes_controller.rb +3 -0
- data/app/controllers/spree/api/v2/tenant/popular_route_places_controller.rb +8 -2
- data/app/controllers/spree/api/v2/tenant/routes_controller.rb +8 -2
- data/app/controllers/spree/api/v2/tenant/trip_search_controller.rb +11 -2
- data/app/controllers/spree_cm_commissioner/api/v2/storefront/checkout_controller_decorator.rb +3 -0
- data/app/errors/spree_cm_commissioner/reserved_blocks/on_hold_by_other_guest_error.rb +21 -0
- data/app/errors/spree_cm_commissioner/reserved_blocks/reserved_by_other_guest_error.rb +10 -0
- data/app/errors/spree_cm_commissioner/reserved_blocks/reserved_by_same_guest_error.rb +10 -0
- data/app/errors/spree_cm_commissioner/reserved_blocks/unable_to_save_reserved_block_error.rb +10 -0
- data/app/finders/spree_cm_commissioner/routes/find.rb +8 -3
- data/app/finders/spree_cm_commissioner/routes/find_popular.rb +17 -3
- data/app/helpers/spree/base_helper_decorator.rb +2 -1
- data/app/helpers/spree_cm_commissioner/transit/trip_helper.rb +35 -0
- data/app/interactors/spree_cm_commissioner/create_vendor.rb +2 -0
- data/app/interactors/spree_cm_commissioner/pin_code_sender.rb +2 -1
- data/app/interactors/spree_cm_commissioner/sms.rb +3 -2
- data/app/interactors/spree_cm_commissioner/stock/stock_movement_creator.rb +1 -1
- data/app/interactors/spree_cm_commissioner/turnstile_token_validator.rb +62 -0
- data/app/jobs/spree_cm_commissioner/cleanup_expired_access_tokens_job.rb +9 -0
- data/app/jobs/spree_cm_commissioner/{stock/inventory_items_adjuster_job.rb → inventory_items/bulk_adjust_quantities_by_variant_job.rb} +3 -3
- data/app/jobs/spree_cm_commissioner/inventory_items/bulk_adjust_quantities_job.rb +15 -0
- data/app/jobs/spree_cm_commissioner/inventory_items/bulk_generate_permanent_items_job.rb +12 -0
- data/app/jobs/spree_cm_commissioner/inventory_items/generate_inventory_items_job.rb +15 -0
- data/app/jobs/spree_cm_commissioner/route_prices/update_price_range_job.rb +12 -0
- data/app/jobs/spree_cm_commissioner/sms_pin_code_job.rb +1 -1
- data/app/models/concerns/spree_cm_commissioner/order_seatable.rb +6 -8
- data/app/models/spree_cm_commissioner/agency.rb +15 -0
- data/app/models/spree_cm_commissioner/distribution_agreement.rb +17 -0
- data/app/models/spree_cm_commissioner/order_decorator.rb +3 -2
- data/app/models/spree_cm_commissioner/pin_code.rb +1 -1
- data/app/models/spree_cm_commissioner/price_list.rb +17 -0
- data/app/models/spree_cm_commissioner/role_decorator.rb +3 -0
- data/app/models/spree_cm_commissioner/route.rb +16 -0
- data/app/models/spree_cm_commissioner/route_price.rb +12 -0
- data/app/models/spree_cm_commissioner/stock_item_decorator.rb +2 -2
- data/app/models/spree_cm_commissioner/taxon_decorator.rb +2 -0
- data/app/models/spree_cm_commissioner/taxonomy_decorator.rb +3 -0
- data/app/models/spree_cm_commissioner/tenant.rb +1 -0
- data/app/models/spree_cm_commissioner/trip.rb +37 -4
- data/app/models/spree_cm_commissioner/trip_stop.rb +4 -1
- data/app/models/spree_cm_commissioner/user_agency.rb +10 -0
- data/app/models/spree_cm_commissioner/user_decorator.rb +8 -0
- data/app/models/spree_cm_commissioner/vehicle_type.rb +1 -0
- data/app/models/spree_cm_commissioner/vendor_decorator.rb +20 -2
- data/app/queries/spree_cm_commissioner/multi_leg_trips_query.rb +269 -0
- data/app/queries/spree_cm_commissioner/single_leg_trips_query.rb +134 -0
- data/app/queries/spree_cm_commissioner/trip_query.rb +33 -133
- data/app/serializers/spree/v2/organizer/invite_guest_serializer.rb +13 -0
- data/app/serializers/spree/v2/tenant/line_item_serializer.rb +1 -1
- data/app/serializers/spree_cm_commissioner/v2/storefront/price_serializer.rb +21 -0
- data/app/serializers/spree_cm_commissioner/v2/storefront/route_price_serializer.rb +11 -0
- data/app/serializers/spree_cm_commissioner/v2/storefront/route_serializer.rb +2 -0
- data/app/serializers/spree_cm_commissioner/v2/storefront/trip_result_serializer.rb +1 -1
- data/app/serializers/spree_cm_commissioner/v2/storefront/trip_stop_serializer.rb +7 -1
- data/app/serializers/spree_cm_commissioner/v2/storefront/trip_vehicle_type_serializer.rb +3 -1
- data/app/services/spree_cm_commissioner/agency_categories/create.rb +1 -4
- data/app/services/spree_cm_commissioner/agency_users/add.rb +80 -0
- data/app/services/spree_cm_commissioner/agency_users/create.rb +104 -0
- data/app/services/spree_cm_commissioner/agency_users/destroy.rb +20 -0
- data/app/services/spree_cm_commissioner/api_caches/invalidate.rb +51 -33
- data/app/services/spree_cm_commissioner/cleanup_expired_access_tokens.rb +52 -0
- data/app/services/spree_cm_commissioner/distribution_agreements/create.rb +28 -0
- data/app/services/spree_cm_commissioner/distribution_agreements/update.rb +28 -0
- data/app/services/spree_cm_commissioner/integrations/stadium_x_v1/polling/sync_zones.rb +3 -3
- data/app/services/spree_cm_commissioner/inventory_items/bulk_adjust_quantities.rb +40 -0
- data/app/services/spree_cm_commissioner/inventory_items/bulk_adjust_quantities_by_variant.rb +16 -0
- data/app/services/spree_cm_commissioner/inventory_items/bulk_adjust_quantities_on_hold.rb +39 -0
- data/app/{interactors/spree_cm_commissioner/stock/permanent_inventory_items_generator.rb → services/spree_cm_commissioner/inventory_items/bulk_generate_permanent_items.rb} +12 -7
- data/app/services/spree_cm_commissioner/inventory_items/generate_non_permanent_item.rb +13 -0
- data/app/{interactors/spree_cm_commissioner/stock/inventory_item_resetter.rb → services/spree_cm_commissioner/inventory_items/reset.rb} +17 -12
- data/app/services/spree_cm_commissioner/open_dated_trips/redeem.rb +1 -1
- data/app/services/spree_cm_commissioner/price_lists/create.rb +58 -0
- data/app/services/spree_cm_commissioner/redis_stock/base.rb +39 -0
- data/app/services/spree_cm_commissioner/redis_stock/restock.rb +71 -0
- data/app/services/spree_cm_commissioner/redis_stock/unstock.rb +65 -0
- data/app/services/spree_cm_commissioner/reserved_blocks/cancel.rb +29 -0
- data/app/services/spree_cm_commissioner/reserved_blocks/hold.rb +56 -0
- data/app/services/spree_cm_commissioner/reserved_blocks/reserve.rb +50 -0
- data/app/services/spree_cm_commissioner/route_prices/update_price_range.rb +36 -0
- data/app/services/spree_cm_commissioner/routes/create.rb +1 -1
- data/app/services/spree_cm_commissioner/transit/legs_builder_service.rb +13 -13
- data/app/services/spree_cm_commissioner/transit_order/create.rb +79 -20
- data/app/services/spree_cm_commissioner/trips/clone.rb +25 -31
- data/app/services/spree_cm_commissioner/trips/create_multi_leg.rb +355 -0
- data/app/services/spree_cm_commissioner/trips/create_open_dated_trip.rb +1 -1
- data/app/services/spree_cm_commissioner/trips/create_single_leg.rb +16 -5
- data/app/services/spree_cm_commissioner/trips/search.rb +12 -8
- data/app/views/spree/admin/exports/index.html.erb +0 -10
- data/app/views/spree/admin/product_service_calendars/edit.html.erb +9 -0
- data/app/views/spree/admin/product_service_calendars/index.html.erb +1 -0
- data/app/views/spree/admin/shared/_system_tabs.html.erb +19 -0
- data/app/views/spree/admin/shared/service_calendars/_exception_rules.html.erb +20 -7
- data/app/views/spree/admin/system/cache/show.html.erb +18 -0
- data/app/views/spree/admin/system/{show.html.erb → waiting_room/show.html.erb} +5 -7
- data/config/locales/en.yml +18 -0
- data/config/locales/km.yml +8 -0
- data/config/routes.rb +18 -6
- data/db/migrate/20260226105108_add_trip_type_to_cm_trip.rb +5 -0
- data/db/migrate/20260302062116_add_offset_days_to_cm_trip_stops.rb +5 -0
- data/db/migrate/20260311105437_add_tenant_to_cm_sms_logs.rb +5 -0
- data/db/migrate/20260318081516_create_cm_agencies.rb +18 -0
- data/db/migrate/20260319090000_add_role_type_to_spree_roles.rb +5 -0
- data/db/migrate/20260320103313_add_index_to_spree_oauth_access_tokens_created_at.rb +7 -0
- data/db/migrate/20260325102409_create_cm_user_agencies.rb +16 -0
- data/db/migrate/20260326100000_create_cm_price_lists.rb +36 -0
- data/db/migrate/20260330032107_create_cm_distribution_agreements.rb +22 -0
- data/db/migrate/20260330032108_add_vendor_id_to_spree_taxonomies.rb +9 -0
- data/db/migrate/20260401080000_create_cm_route_prices.rb +22 -0
- data/lib/spree_cm_commissioner/service_module_throwable.rb +39 -0
- data/lib/spree_cm_commissioner/test_helper/factories/agency_factory.rb +7 -0
- data/lib/spree_cm_commissioner/test_helper/factories/distribution_agreement_factory.rb +33 -0
- data/lib/spree_cm_commissioner/test_helper/factories/price_list_factory.rb +15 -0
- data/lib/spree_cm_commissioner/test_helper/factories/user_agency_factory.rb +6 -0
- data/lib/spree_cm_commissioner/test_helper/factories/variant_factory.rb +1 -1
- data/lib/spree_cm_commissioner/transit/route_stop_collection.rb +18 -5
- data/lib/spree_cm_commissioner/transit/service_calendar_form.rb +19 -0
- data/lib/spree_cm_commissioner/transit/trip_form.rb +4 -3
- data/lib/spree_cm_commissioner/transit/trip_stop_form.rb +20 -5
- data/lib/spree_cm_commissioner/trip_query_result.rb +37 -8
- data/lib/spree_cm_commissioner/trip_result.rb +71 -8
- data/lib/spree_cm_commissioner/version.rb +1 -1
- data/lib/spree_cm_commissioner.rb +1 -0
- data/lib/tasks/generate_inventory_items.rake +1 -1
- metadata +70 -21
- data/app/controllers/spree/admin/system_controller.rb +0 -44
- data/app/errors/spree_cm_commissioner/seats/blocks_are_on_hold_by_other_guest_error.rb +0 -19
- data/app/errors/spree_cm_commissioner/seats/blocks_are_reserved_by_other_guest_error.rb +0 -19
- data/app/errors/spree_cm_commissioner/seats/blocks_are_reserved_by_same_guest_error.rb +0 -8
- data/app/errors/spree_cm_commissioner/seats/unable_to_save_reserved_block_record_error.rb +0 -19
- data/app/interactors/spree_cm_commissioner/inventory_item_syncer.rb +0 -49
- data/app/interactors/spree_cm_commissioner/stock/inventory_items_adjuster.rb +0 -13
- data/app/interactors/spree_cm_commissioner/stock/inventory_items_generator.rb +0 -15
- data/app/jobs/spree_cm_commissioner/inventory_item_syncer_job.rb +0 -13
- data/app/jobs/spree_cm_commissioner/stock/inventory_items_generator_job.rb +0 -11
- data/app/jobs/spree_cm_commissioner/stock/permanent_inventory_items_generator_job.rb +0 -9
- data/app/models/spree_cm_commissioner/redis_stock/inventory_updater.rb +0 -143
- data/app/models/spree_cm_commissioner/seats/blocks_canceler.rb +0 -31
- data/app/models/spree_cm_commissioner/seats/blocks_holder.rb +0 -66
- data/app/models/spree_cm_commissioner/seats/blocks_reserver.rb +0 -56
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: '0319481a07b3f09bd6c82a11919c71d903c5fba255975a9b8c2125b0d34b8c56'
|
|
4
|
+
data.tar.gz: 4bdb018d9d105aa5a0afb1105f24d4dd5870654528f34c1928256ac4232c61de
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 323de4c2e82ca9f643216ca00a8da7f7de5985ffb38da8388ba09549b1cf1b2878634c1ab61c1178f77f9728fd40442b062784db3af7a1ffa694f9308c50b874
|
|
7
|
+
data.tar.gz: 67ade5a70dcbfd1faf99bb89f41ab68db83e7cc6bf51ec997ba16f233c16d696fa92534c15c2d6d8c98d9f68078ef826264f72d9f6a815e99fb375b8676f6702
|
|
@@ -244,8 +244,6 @@ jobs:
|
|
|
244
244
|
with:
|
|
245
245
|
path: spec/dummy
|
|
246
246
|
key: test-app-${{ runner.os }}-${{ hashFiles('Gemfile.lock', 'db/migrate/*.rb') }}
|
|
247
|
-
restore-keys: |
|
|
248
|
-
test-app-${{ runner.os }}-
|
|
249
247
|
|
|
250
248
|
# Generate dummy Rails app for testing (cacheable part)
|
|
251
249
|
# This step checks if spec/dummy exists and skips generation if found
|
data/.gitignore
CHANGED
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
|
@@ -153,8 +153,38 @@ PIN_CODE_DEBUG_NOTIFIY_TELEGRAM_ENABLE="yes"
|
|
|
153
153
|
EXCEPTION_NOTIFY_ENABLE="yes" # yes or no
|
|
154
154
|
EXCEPTION_TELEGRAM_BOT_TOKEN=""
|
|
155
155
|
EXCEPTION_NOTIFIER_TELEGRAM_CHANNEL_ID=""
|
|
156
|
+
|
|
157
|
+
TURNSTILE_SECRET_KEY="" # Cloudflare Turnstile secret key (server-side verification)
|
|
158
|
+
TURNSTILE_ENABLED="no" # Kill switch: `yes` to enforce Turnstile validation, `no` to bypass all validation without a code deploy (e.g. during a Cloudflare outage). Default: `no` (disabled)
|
|
156
159
|
```
|
|
157
160
|
|
|
161
|
+
### Cloudflare Turnstile
|
|
162
|
+
|
|
163
|
+
Cloudflare Turnstile is used to protect sensitive API endpoints from bots. The server-side validation is handled by `SpreeCmCommissioner::TurnstileTokenValidator` and the concern `SpreeCmCommissioner::TurnstileProtectable`.
|
|
164
|
+
|
|
165
|
+
**Protected endpoints:**
|
|
166
|
+
|
|
167
|
+
- `POST /api/v2/storefront/pin_code_generators`
|
|
168
|
+
- `POST /api/v2/storefront/pin_code_otp_generators`
|
|
169
|
+
- `POST /api/v2/storefront/user_registration_with_pin_codes`
|
|
170
|
+
- `PUT /api/v2/storefront/reset_passwords`
|
|
171
|
+
- `PATCH /api/v2/storefront/checkout/complete`
|
|
172
|
+
|
|
173
|
+
**How it works:**
|
|
174
|
+
|
|
175
|
+
1. The client sends a Turnstile token via the `X-Turnstile-Token` HTTP header
|
|
176
|
+
2. `TurnstileProtectable` reads the header and calls `TurnstileTokenValidator`
|
|
177
|
+
3. The validator POSTs to Cloudflare's siteverify API with the token and `TURNSTILE_SECRET_KEY`
|
|
178
|
+
4. Returns 403 if verification fails
|
|
179
|
+
|
|
180
|
+
**Configuration:**
|
|
181
|
+
|
|
182
|
+
1. Set `TURNSTILE_SECRET_KEY` in `.env` (obtained from [Cloudflare Turnstile dashboard](https://dash.cloudflare.com/))
|
|
183
|
+
2. For local development testing, use Cloudflare's test keys:
|
|
184
|
+
- Always pass: `1x0000000000000000000000000000000AA`
|
|
185
|
+
- Always fail: `2x0000000000000000000000000000000AA`
|
|
186
|
+
- Token spent: `3x0000000000000000000000000000000AA`
|
|
187
|
+
|
|
158
188
|
## JSON Format Examples For Store and Tenant Preferences
|
|
159
189
|
|
|
160
190
|
### Asset Links Format
|
|
@@ -234,6 +264,12 @@ bundle update
|
|
|
234
264
|
bundle exec rake test_app
|
|
235
265
|
```
|
|
236
266
|
|
|
267
|
+
To completely remove the dummy app and rebuild it from scratch, you can use:
|
|
268
|
+
|
|
269
|
+
```sh
|
|
270
|
+
bundle exec rake test_app:reset
|
|
271
|
+
```
|
|
272
|
+
|
|
237
273
|
When testing your applications integration with this extension you may use it's factories.
|
|
238
274
|
Simply add this require statement to your spec_helper:
|
|
239
275
|
|
data/Rakefile
CHANGED
|
@@ -88,6 +88,15 @@ namespace :test_app do
|
|
|
88
88
|
puts '✓ Database setup complete'
|
|
89
89
|
end
|
|
90
90
|
end
|
|
91
|
+
|
|
92
|
+
desc 'Remove spec/dummy and run test_app'
|
|
93
|
+
task :reset do
|
|
94
|
+
puts 'Removing spec/dummy...'
|
|
95
|
+
FileUtils.rm_rf('spec/dummy')
|
|
96
|
+
|
|
97
|
+
puts 'Rebuilding test app...'
|
|
98
|
+
Rake::Task['test_app'].invoke
|
|
99
|
+
end
|
|
91
100
|
end
|
|
92
101
|
|
|
93
102
|
desc 'Generates a dummy app for testing'
|
|
@@ -44,6 +44,8 @@ module Spree
|
|
|
44
44
|
private
|
|
45
45
|
|
|
46
46
|
def build_exception_rules(exception_rules)
|
|
47
|
+
return [] if exception_rules.nil?
|
|
48
|
+
|
|
47
49
|
exception_rules.values.reject! { |rule| rule['from'].blank? || rule['to'].blank? || rule['type'].blank? } || exception_rules.values
|
|
48
50
|
end
|
|
49
51
|
|
|
@@ -57,7 +59,7 @@ module Spree
|
|
|
57
59
|
end
|
|
58
60
|
|
|
59
61
|
def set_exception_rules
|
|
60
|
-
@exception_rules = [{ from: DateTime.now, to: DateTime.now, type: 'exclusion', reason: nil }]
|
|
62
|
+
@exception_rules = (@object.exception_rules.presence || [{ from: DateTime.now, to: DateTime.now, type: 'exclusion', reason: nil }])
|
|
61
63
|
end
|
|
62
64
|
|
|
63
65
|
def build_resource
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
module SpreeCmCommissioner
|
|
2
|
+
module AdaptiveTurnstile
|
|
3
|
+
extend ActiveSupport::Concern
|
|
4
|
+
include TurnstileProtectable
|
|
5
|
+
|
|
6
|
+
FAILED_ATTEMPT_THRESHOLD = 3
|
|
7
|
+
|
|
8
|
+
private
|
|
9
|
+
|
|
10
|
+
# Only require Turnstile if the user has exceeded the failed attempt threshold.
|
|
11
|
+
# This reduces latency for normal logins while catching brute force attacks.
|
|
12
|
+
# Only applies to password grant; other OAuth flows (refresh_token, client_credentials) are skipped.
|
|
13
|
+
def verify_turnstile_if_suspicious!
|
|
14
|
+
return unless params[:grant_type] == 'password'
|
|
15
|
+
|
|
16
|
+
user = Spree::User.find_by(email: params[:username]&.downcase&.strip)
|
|
17
|
+
|
|
18
|
+
# No user found -- require Turnstile to prevent email enumeration
|
|
19
|
+
if user.nil?
|
|
20
|
+
verify_turnstile!
|
|
21
|
+
return
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# User has too many failed attempts -- require Turnstile
|
|
25
|
+
if user.respond_to?(:failed_attempts) && user.failed_attempts >= FAILED_ATTEMPT_THRESHOLD
|
|
26
|
+
verify_turnstile!
|
|
27
|
+
return
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Also check Redis for IP-level failed attempt tracking
|
|
31
|
+
return unless defined?(::Rack::Attack) && ::Rack::Attack.respond_to?(:cache)
|
|
32
|
+
|
|
33
|
+
ip_attempts = ::Rack::Attack.cache.count("failed_auth/ip:#{request.remote_ip}", 15.minutes)
|
|
34
|
+
verify_turnstile! if ip_attempts >= FAILED_ATTEMPT_THRESHOLD
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
module SpreeCmCommissioner
|
|
2
|
+
module TurnstileProtectable
|
|
3
|
+
extend ActiveSupport::Concern
|
|
4
|
+
|
|
5
|
+
private
|
|
6
|
+
|
|
7
|
+
# Verify the Turnstile token from the X-Turnstile-Token header.
|
|
8
|
+
# Renders 403 and halts the request if verification fails.
|
|
9
|
+
#
|
|
10
|
+
# Usage:
|
|
11
|
+
# before_action -> { verify_turnstile! }, only: :create
|
|
12
|
+
#
|
|
13
|
+
def verify_turnstile!
|
|
14
|
+
# Kill switch: set TURNSTILE_ENABLED='no' env var to disable globally.
|
|
15
|
+
return unless turnstile_feature_enabled?
|
|
16
|
+
|
|
17
|
+
token = request.headers['X-Turnstile-Token']
|
|
18
|
+
|
|
19
|
+
# Skip verification if no token and Turnstile is not enforced
|
|
20
|
+
return if token.blank? && turnstile_optional?
|
|
21
|
+
|
|
22
|
+
result = TurnstileTokenValidator.call(
|
|
23
|
+
token: token,
|
|
24
|
+
remote_ip: request.remote_ip
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
return unless result.failure?
|
|
28
|
+
|
|
29
|
+
render json: {
|
|
30
|
+
errors: [{
|
|
31
|
+
status: '403',
|
|
32
|
+
error_code: 'human_verification_failed',
|
|
33
|
+
title: I18n.t('turnstile.verification_failed'),
|
|
34
|
+
detail: result.message
|
|
35
|
+
}
|
|
36
|
+
]
|
|
37
|
+
}, status: :forbidden
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Override in controllers to make Turnstile optional for specific actions.
|
|
41
|
+
# Default: required (returns false).
|
|
42
|
+
def turnstile_optional?
|
|
43
|
+
false
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Turnstile verification is enabled only when both conditions are met:
|
|
47
|
+
# 1. TURNSTILE_ENABLED environment variable is set to 'yes'
|
|
48
|
+
# 2. TURNSTILE_SECRET_KEY is present
|
|
49
|
+
# Default: disabled (no). Set TURNSTILE_ENABLED='yes' and provide
|
|
50
|
+
# TURNSTILE_SECRET_KEY to enable verification.
|
|
51
|
+
def turnstile_feature_enabled?
|
|
52
|
+
ENV.fetch('TURNSTILE_ENABLED', 'no') == 'yes' && ENV.fetch('TURNSTILE_SECRET_KEY', '').present?
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -66,8 +66,8 @@ module Spree
|
|
|
66
66
|
|
|
67
67
|
errors = []
|
|
68
68
|
inventory_items.each do |inventory_item|
|
|
69
|
-
result = SpreeCmCommissioner::
|
|
70
|
-
errors << result.
|
|
69
|
+
result = SpreeCmCommissioner::InventoryItems::Reset.call(inventory_item: inventory_item)
|
|
70
|
+
errors << result.error.to_s unless result.success?
|
|
71
71
|
end
|
|
72
72
|
|
|
73
73
|
if errors.empty?
|
|
@@ -24,7 +24,7 @@ module Spree
|
|
|
24
24
|
authorize! :manage, SpreeCmCommissioner::InventoryItem
|
|
25
25
|
|
|
26
26
|
inventory_item = SpreeCmCommissioner::InventoryItem.find(params[:id])
|
|
27
|
-
result = SpreeCmCommissioner::
|
|
27
|
+
result = SpreeCmCommissioner::InventoryItems::Reset.call(inventory_item: inventory_item)
|
|
28
28
|
|
|
29
29
|
if result.success?
|
|
30
30
|
flash[:success] = "Successfully reset inventory for #{inventory_item.variant.product.name}"
|
|
@@ -6,6 +6,7 @@ module Spree
|
|
|
6
6
|
before_action :load_product
|
|
7
7
|
before_action :ensure_product_type_supports_calendar
|
|
8
8
|
before_action :ensure_product_has_no_calendar, only: %i[new create]
|
|
9
|
+
before_action :set_exception_rules, only: %i[edit update]
|
|
9
10
|
|
|
10
11
|
create.before :set_calendarable
|
|
11
12
|
update.before :set_calendarable
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module Admin
|
|
3
|
+
module System
|
|
4
|
+
class CacheController < Spree::Admin::BaseController
|
|
5
|
+
def show; end
|
|
6
|
+
|
|
7
|
+
def invalidate
|
|
8
|
+
@patterns = params[:patterns].to_s
|
|
9
|
+
|
|
10
|
+
patterns = @patterns.split("\n").map(&:strip).compact_blank
|
|
11
|
+
|
|
12
|
+
if patterns.empty?
|
|
13
|
+
flash.now[:error] = 'Please enter at least one pattern.' # rubocop:disable Rails/I18nLocaleTexts
|
|
14
|
+
render :show and return
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
result = SpreeCmCommissioner::InvalidateCacheRequest.call(patterns: patterns)
|
|
18
|
+
|
|
19
|
+
if result.success?
|
|
20
|
+
flash[:success] = "Cache invalidated successfully for #{patterns.size} pattern(s)."
|
|
21
|
+
redirect_to admin_system_cache_path
|
|
22
|
+
else
|
|
23
|
+
flash.now[:error] = result.message
|
|
24
|
+
render :show
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module Admin
|
|
3
|
+
module System
|
|
4
|
+
class WaitingRoomController < Spree::Admin::BaseController
|
|
5
|
+
def show
|
|
6
|
+
@fetcher = SpreeCmCommissioner::WaitingRoomSystemMetadataFetcher.new
|
|
7
|
+
@fetcher.load_document_data
|
|
8
|
+
|
|
9
|
+
@active_sesions_count = SpreeCmCommissioner::WaitingRoomSession.active.count
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def modify_multiplier
|
|
13
|
+
modifier = params[:multiplier]&.to_i || 0
|
|
14
|
+
SpreeCmCommissioner::WaitingRoomSystemMetadataSetter.new.modify_multiplier(modifier)
|
|
15
|
+
|
|
16
|
+
redirect_back fallback_location: admin_system_waiting_room_path
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def modify_max_thread_count
|
|
20
|
+
modifier = params[:max_thread_count]&.to_i || 0
|
|
21
|
+
SpreeCmCommissioner::WaitingRoomSystemMetadataSetter.new.modify_max_thread_count(modifier)
|
|
22
|
+
|
|
23
|
+
redirect_back fallback_location: admin_system_waiting_room_path
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def force_pull
|
|
27
|
+
SpreeCmCommissioner::WaitingRoomLatestSystemMetadataPullerJob.perform_now
|
|
28
|
+
SpreeCmCommissioner::WaitingGuestsCallerJob.perform_now
|
|
29
|
+
|
|
30
|
+
redirect_back fallback_location: admin_system_waiting_room_path
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module Api
|
|
3
|
+
module V2
|
|
4
|
+
module Organizer
|
|
5
|
+
class InviteGuestsController < ::Spree::Api::V2::Organizer::BaseController
|
|
6
|
+
before_action :load_invite_guest_by_token, only: :show
|
|
7
|
+
before_action :load_invite_guest, :assign_line_item_data, only: :update
|
|
8
|
+
|
|
9
|
+
def show
|
|
10
|
+
render_serialized_payload { serialize_resource(@invite_guest) }
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def update
|
|
14
|
+
return render_error(:revoked) if @invite_guest.revoked?
|
|
15
|
+
return render_error(:closed) if @invite_guest.expired?
|
|
16
|
+
return render_error(:fully_claimed) if @invite_guest.fully_claimed?
|
|
17
|
+
|
|
18
|
+
guest = @line_item.guests.new(guest_params)
|
|
19
|
+
guest.event_id = @invite_guest.event_id
|
|
20
|
+
|
|
21
|
+
if guest.save
|
|
22
|
+
@invite_guest.update(claimed_status: :claimed) if @line_item.guests.count == @invite_guest.quantity
|
|
23
|
+
send_guest_claimed_invitation_telegram_alert_to_vendor(guest) if guest.event.vendor.preferred_telegram_chat_id.present?
|
|
24
|
+
|
|
25
|
+
render json: SpreeCmCommissioner::V2::Storefront::GuestSerializer.new(guest.reload).serializable_hash
|
|
26
|
+
else
|
|
27
|
+
render_error_payload(guest.errors.full_messages.to_sentence)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
private
|
|
32
|
+
|
|
33
|
+
def send_guest_claimed_invitation_telegram_alert_to_vendor(guest)
|
|
34
|
+
title = '📣 --- [NEW GUEST CLAIMED INVITATION] ---' # Style/StringLiterals
|
|
35
|
+
chat_id = guest.event.vendor.preferred_telegram_chat_id
|
|
36
|
+
return if chat_id.blank?
|
|
37
|
+
|
|
38
|
+
factory = SpreeCmCommissioner::InviteGuestClaimedTelegramMessageFactory.new(
|
|
39
|
+
title: title,
|
|
40
|
+
order: @invite_guest.order,
|
|
41
|
+
guest: guest,
|
|
42
|
+
vendor: guest.event.vendor
|
|
43
|
+
)
|
|
44
|
+
SpreeCmCommissioner::TelegramNotificationSenderJob.perform_later(
|
|
45
|
+
chat_id: chat_id,
|
|
46
|
+
message: factory.message,
|
|
47
|
+
parse_mode: factory.parse_mode
|
|
48
|
+
)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def load_invite_guest_by_token
|
|
52
|
+
@invite_guest = SpreeCmCommissioner::InviteGuest.find_by(token: params[:id])
|
|
53
|
+
rescue ActiveRecord::RecordNotFound
|
|
54
|
+
render_error_payload(I18n.t('invite.url_not_found'))
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def load_invite_guest
|
|
58
|
+
@invite_guest = SpreeCmCommissioner::InviteGuest.find(params[:id])
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def assign_line_item_data
|
|
62
|
+
@line_item = @invite_guest.order.line_items.first
|
|
63
|
+
@guests = @line_item.guests
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def render_error(message)
|
|
67
|
+
render json: { errors: message }
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def guest_params
|
|
71
|
+
params.require(:invite_guest).permit(
|
|
72
|
+
:first_name,
|
|
73
|
+
:last_name,
|
|
74
|
+
:dob,
|
|
75
|
+
:gender,
|
|
76
|
+
:age,
|
|
77
|
+
:emergency_contact,
|
|
78
|
+
:phone_number,
|
|
79
|
+
:address,
|
|
80
|
+
:other_organization
|
|
81
|
+
)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def resource_serializer
|
|
85
|
+
::Spree::V2::Organizer::InviteGuestSerializer
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
@@ -3,6 +3,10 @@ module Spree
|
|
|
3
3
|
module V2
|
|
4
4
|
module Storefront
|
|
5
5
|
class PinCodeGeneratorsController < ::Spree::Api::V2::ResourceController
|
|
6
|
+
include SpreeCmCommissioner::TurnstileProtectable
|
|
7
|
+
|
|
8
|
+
before_action :verify_turnstile!, only: :create
|
|
9
|
+
|
|
6
10
|
# :phone_number, :email, :type, :tenant
|
|
7
11
|
def create
|
|
8
12
|
context = SpreeCmCommissioner::PinCodeGenerator.call(pin_code_attrs)
|
|
@@ -3,6 +3,10 @@ module Spree
|
|
|
3
3
|
module V2
|
|
4
4
|
module Storefront
|
|
5
5
|
class PinCodeOtpGeneratorsController < ::Spree::Api::V2::ResourceController
|
|
6
|
+
include SpreeCmCommissioner::TurnstileProtectable
|
|
7
|
+
|
|
8
|
+
before_action :verify_turnstile!, only: :create
|
|
9
|
+
|
|
6
10
|
# :phone_number, :email, :type, :recaptcha_token, :recaptcha_action, :recaptcah_site_key
|
|
7
11
|
def create
|
|
8
12
|
options = otp_attrs
|
|
@@ -3,6 +3,10 @@ module Spree
|
|
|
3
3
|
module V2
|
|
4
4
|
module Storefront
|
|
5
5
|
class ResetPasswordsController < Spree::Api::V2::ResourceController
|
|
6
|
+
include SpreeCmCommissioner::TurnstileProtectable
|
|
7
|
+
|
|
8
|
+
before_action :verify_turnstile!, only: :update
|
|
9
|
+
|
|
6
10
|
def update
|
|
7
11
|
context = SpreeCmCommissioner::UserForgottenPasswordUpdater.call(update_params)
|
|
8
12
|
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module Api
|
|
3
|
+
module V2
|
|
4
|
+
module Storefront
|
|
5
|
+
class SavedGuestsController < ::Spree::Api::V2::ResourceController
|
|
6
|
+
before_action :require_spree_current_user
|
|
7
|
+
before_action :load_saved_guest, only: %i[show update destroy]
|
|
8
|
+
|
|
9
|
+
def collection
|
|
10
|
+
spree_current_user.saved_guests.order(created_at: :desc)
|
|
11
|
+
.page(params[:page])
|
|
12
|
+
.per(params[:per_page])
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def create
|
|
16
|
+
saved_guest = spree_current_user.saved_guests.new(saved_guest_params)
|
|
17
|
+
|
|
18
|
+
if saved_guest.save
|
|
19
|
+
render_serialized_payload(201) { serialize_resource(saved_guest) }
|
|
20
|
+
else
|
|
21
|
+
render_error_payload(saved_guest.errors.full_messages.to_sentence, 422)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def update
|
|
26
|
+
if @saved_guest.update(saved_guest_params)
|
|
27
|
+
render_serialized_payload { serialize_resource(@saved_guest) }
|
|
28
|
+
else
|
|
29
|
+
render_error_payload(@saved_guest.errors.full_messages.to_sentence, 400)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def destroy
|
|
34
|
+
@saved_guest.destroy
|
|
35
|
+
head :no_content
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
private
|
|
39
|
+
|
|
40
|
+
def model_class
|
|
41
|
+
SpreeCmCommissioner::SavedGuest
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def collection_serializer
|
|
45
|
+
resource_serializer
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def resource_serializer
|
|
49
|
+
SpreeCmCommissioner::V2::Storefront::SavedGuestSerializer
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def load_saved_guest
|
|
53
|
+
@saved_guest = spree_current_user.saved_guests.find(params[:id])
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def saved_guest_params
|
|
57
|
+
params.require(:saved_guest).permit(
|
|
58
|
+
:first_name,
|
|
59
|
+
:last_name,
|
|
60
|
+
:dob,
|
|
61
|
+
:age,
|
|
62
|
+
:gender,
|
|
63
|
+
:email,
|
|
64
|
+
:country_code,
|
|
65
|
+
:phone_number,
|
|
66
|
+
:intel_phone_number,
|
|
67
|
+
:nationality_id,
|
|
68
|
+
:occupation_id,
|
|
69
|
+
:nationality_group,
|
|
70
|
+
:age_group
|
|
71
|
+
)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
@@ -15,7 +15,7 @@ module Spree
|
|
|
15
15
|
route_type: params[:route_type],
|
|
16
16
|
vendor_id: params[:vendor_id],
|
|
17
17
|
number_of_guests: params[:number_of_guests],
|
|
18
|
-
|
|
18
|
+
options: params
|
|
19
19
|
).call
|
|
20
20
|
|
|
21
21
|
serialize_collection(trips)
|
|
@@ -72,7 +72,7 @@ module Spree
|
|
|
72
72
|
def serialize_collection(collection)
|
|
73
73
|
serialized_data = SpreeCmCommissioner::V2::Storefront::TripQueryResultSerializer.new(
|
|
74
74
|
collection,
|
|
75
|
-
include:
|
|
75
|
+
include: resource_includes,
|
|
76
76
|
params: serializer_params
|
|
77
77
|
).serializable_hash
|
|
78
78
|
serialized_data[:meta] = {
|
|
@@ -87,6 +87,16 @@ module Spree
|
|
|
87
87
|
serialized_data
|
|
88
88
|
end
|
|
89
89
|
|
|
90
|
+
# From BaseController to always include defaults and merge request includes
|
|
91
|
+
# override
|
|
92
|
+
|
|
93
|
+
def resource_includes
|
|
94
|
+
(Array(super) + default_resource_includes)
|
|
95
|
+
.map { |path| path.to_s.strip }
|
|
96
|
+
.compact_blank
|
|
97
|
+
.uniq
|
|
98
|
+
end
|
|
99
|
+
|
|
90
100
|
# override
|
|
91
101
|
def serializer_params
|
|
92
102
|
params.permit(:include).to_hash
|
|
@@ -3,6 +3,9 @@ module Spree
|
|
|
3
3
|
module V2
|
|
4
4
|
module Storefront
|
|
5
5
|
class UserRegistrationWithPinCodesController < Spree::Api::V2::ResourceController
|
|
6
|
+
include SpreeCmCommissioner::TurnstileProtectable
|
|
7
|
+
|
|
8
|
+
before_action :verify_turnstile!, only: :create
|
|
6
9
|
before_action :validate_token_client!
|
|
7
10
|
|
|
8
11
|
def validate_token_client!
|
|
@@ -9,7 +9,8 @@ module Spree
|
|
|
9
9
|
def collection
|
|
10
10
|
@collection ||= collection_finder.new(
|
|
11
11
|
tenant: @tenant,
|
|
12
|
-
route_type: params[:route_type]
|
|
12
|
+
route_type: params[:route_type],
|
|
13
|
+
include_route_prices: include_route_prices?
|
|
13
14
|
).execute
|
|
14
15
|
end
|
|
15
16
|
|
|
@@ -37,7 +38,8 @@ module Spree
|
|
|
37
38
|
def serializer_params
|
|
38
39
|
super.merge(
|
|
39
40
|
include_vendors: include_vendors?,
|
|
40
|
-
include_nearby_places: include_nearby_places
|
|
41
|
+
include_nearby_places: include_nearby_places?,
|
|
42
|
+
include_route_prices: include_route_prices?
|
|
41
43
|
)
|
|
42
44
|
end
|
|
43
45
|
|
|
@@ -53,6 +55,10 @@ module Spree
|
|
|
53
55
|
def include_nearby_places?
|
|
54
56
|
resource_includes.include?(:nearby_places) || false
|
|
55
57
|
end
|
|
58
|
+
|
|
59
|
+
def include_route_prices?
|
|
60
|
+
resource_includes.include?(:route_prices) || false
|
|
61
|
+
end
|
|
56
62
|
end
|
|
57
63
|
end
|
|
58
64
|
end
|
|
@@ -9,7 +9,8 @@ module Spree
|
|
|
9
9
|
def collection
|
|
10
10
|
@collection ||= collection_finder.new(
|
|
11
11
|
tenant: @tenant,
|
|
12
|
-
params: params
|
|
12
|
+
params: params,
|
|
13
|
+
include_route_prices: include_route_prices?
|
|
13
14
|
).execute
|
|
14
15
|
end
|
|
15
16
|
|
|
@@ -27,7 +28,8 @@ module Spree
|
|
|
27
28
|
def serializer_params
|
|
28
29
|
super.merge(
|
|
29
30
|
include_vendors: include_vendors?,
|
|
30
|
-
include_nearby_places: include_nearby_places
|
|
31
|
+
include_nearby_places: include_nearby_places?,
|
|
32
|
+
include_route_prices: include_route_prices?
|
|
31
33
|
)
|
|
32
34
|
end
|
|
33
35
|
|
|
@@ -43,6 +45,10 @@ module Spree
|
|
|
43
45
|
def include_nearby_places?
|
|
44
46
|
resource_includes.include?(:nearby_places) || false
|
|
45
47
|
end
|
|
48
|
+
|
|
49
|
+
def include_route_prices?
|
|
50
|
+
resource_includes.include?(:route_prices) || false
|
|
51
|
+
end
|
|
46
52
|
end
|
|
47
53
|
end
|
|
48
54
|
end
|
|
@@ -15,7 +15,7 @@ module Spree
|
|
|
15
15
|
route_type: params[:route_type],
|
|
16
16
|
number_of_guests: params[:number_of_guests],
|
|
17
17
|
tenant_id: @tenant.id,
|
|
18
|
-
|
|
18
|
+
options: params
|
|
19
19
|
).call
|
|
20
20
|
|
|
21
21
|
serialize_collection(trips)
|
|
@@ -72,7 +72,7 @@ module Spree
|
|
|
72
72
|
def serialize_collection(collection)
|
|
73
73
|
serialized_data = Spree::V2::Tenant::TripQueryResultSerializer.new(
|
|
74
74
|
collection,
|
|
75
|
-
include:
|
|
75
|
+
include: resource_includes,
|
|
76
76
|
params: serializer_params
|
|
77
77
|
).serializable_hash
|
|
78
78
|
serialized_data[:meta] = {
|
|
@@ -87,6 +87,15 @@ module Spree
|
|
|
87
87
|
serialized_data
|
|
88
88
|
end
|
|
89
89
|
|
|
90
|
+
# From BaseController to always include defaults and merge request includes
|
|
91
|
+
# override
|
|
92
|
+
def resource_includes
|
|
93
|
+
(Array(super) + default_resource_includes)
|
|
94
|
+
.map { |path| path.to_s.strip }
|
|
95
|
+
.compact_blank
|
|
96
|
+
.uniq
|
|
97
|
+
end
|
|
98
|
+
|
|
90
99
|
# override
|
|
91
100
|
def serializer_params
|
|
92
101
|
params.permit(:include).to_hash
|