trackguard 0.28.0 → 0.30.0

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: c2629b3c673c2ac2c1f4e338dedc3abc7f9b572fb57e9b4f9091139809e362bc
4
- data.tar.gz: 6b4657343eecf9de2b48bea0df3f24c4463109dc6ae521dc83dad79741bbfe99
3
+ metadata.gz: 49c01824d558ae2b9416ed2451b7ecc9fd864a93979afb91ea1bdce297c198b2
4
+ data.tar.gz: 429ff2b09df7c9d9571ac7a863e3200a80b6ee4a50028284af0132d18b4b2b79
5
5
  SHA512:
6
- metadata.gz: d8b5fc84be6801c276f5d7cb7dd0007a7b1c7d5d19d5db9858f2412396e3c99fc604e9276cc34ee0a05b4b63cc01f72fac8e176b1c2e852405813e0e484b4733
7
- data.tar.gz: 96c791bce0d1cd72230e89e34a6335e4c7dfdbe818f90dccbd466bb3e908c2b9437e820dfe6afb9ced8a605e5293f341c65dd541e603a8bd9594210009c4dfe8
6
+ metadata.gz: 7da72f474a80e8b89b5549de51a3ebefa6cba99da4b9b1045b7d860b2628f10ac9950efcd7acdec94d438e1f72544e7c5189335285025c5c872a64b6e8d616ea
7
+ data.tar.gz: 7ce7519b8998ce10beab775c922e7c965cb017bfa4852a843173cbc586e0c8c3af95cf6df397a12b270869b6de04411c81c958fa53966f8185b75d47d5821a89
@@ -240,6 +240,7 @@
240
240
  /* ── Table ─────────────────────────────────────────────────────────── */
241
241
  .tg-table {
242
242
  width: 100%;
243
+ table-layout: fixed;
243
244
  border-collapse: collapse;
244
245
  font-size: 0.875rem;
245
246
  margin-bottom: 2rem;
@@ -269,6 +270,14 @@
269
270
  text-align: right;
270
271
  color: #9ca3af;
271
272
  font-weight: 400;
273
+ white-space: nowrap;
274
+ width: 3.5rem;
275
+ }
276
+
277
+ .tg-td--label {
278
+ overflow: hidden;
279
+ text-overflow: ellipsis;
280
+ white-space: nowrap;
272
281
  }
273
282
 
274
283
  .tg-td--break { word-break: break-all; }
@@ -15,10 +15,11 @@ module Trackguard
15
15
  private
16
16
 
17
17
  def set_trace_id
18
- @trace_id = SecureRandom.uuid
18
+ @trace_id = request.env["trackguard.trace_id"]
19
19
  end
20
20
 
21
21
  def track_page_view
22
+ return unless Trackguard.tracking_enabled?
22
23
  return unless request.get? || request.head?
23
24
  return unless request.format.html?
24
25
 
@@ -31,10 +32,17 @@ module Trackguard
31
32
  trace_id: @trace_id,
32
33
  source: extract_source,
33
34
  tracking_layer: "backend",
34
- http_method: request.request_method
35
+ http_method: request.request_method,
36
+ prefetch: turbo_prefetch?
35
37
  )
36
38
  end
37
39
 
40
+ def turbo_prefetch?
41
+ request.headers["Purpose"] == "prefetch" ||
42
+ request.headers["Sec-Purpose"]&.start_with?("prefetch") ||
43
+ request.headers["X-Sec-Purpose"]&.start_with?("prefetch")
44
+ end
45
+
38
46
  def extract_source
39
47
  raw = params[:ref].presence || params[:utm_source].presence
40
48
  raw && raw.strip.downcase.first(64)
@@ -1,10 +1,20 @@
1
1
  module Trackguard
2
2
  module ApplicationHelper
3
- def trackguard_meta_tags
4
- safe_join([
5
- tag.meta(name: "trackguard-url", content: trackguard.page_views_path),
6
- tag.meta(name: "trace-id", content: @trace_id)
7
- ], "\n")
3
+ def trackguard_header_tags
4
+ return "".html_safe unless Trackguard.tracking_enabled?
5
+
6
+ tags = [ tag.meta(name: "trace-id", content: @trace_id) ]
7
+
8
+ case Trackguard.adapter
9
+ when Trackguard::Adapters::Local
10
+ tags << tag.meta(name: "trackguard-url", content: trackguard.page_views_path)
11
+ when Trackguard::Adapters::Hub
12
+ if Rails.env.production?
13
+ tags << tag.script(src: "#{Trackguard.hub_url}/track.js", data: { api_key: Trackguard.hub_api_key })
14
+ end
15
+ end
16
+
17
+ safe_join(tags, "\n")
8
18
  end
9
19
 
10
20
  def trackguard_nav_links
@@ -40,7 +40,7 @@ module Trackguard
40
40
  def analyze_visitor(visitor, views)
41
41
  count = views.size
42
42
  return if count.zero?
43
- return if visitor.whitelisted_ip&.active?
43
+ return if whitelisted?(visitor)
44
44
 
45
45
  name = name_from_ua(visitor.user_agent)
46
46
 
@@ -183,6 +183,19 @@ module Trackguard
183
183
  )
184
184
  end
185
185
 
186
+ protected
187
+
188
+ def whitelisted?(visitor)
189
+ wl = visitor.whitelisted_ip
190
+ unless wl
191
+ wl = WhitelistedIp.active.find_by(ip: visitor.ip)
192
+ wl&.update!(visitor: visitor)
193
+ end
194
+ wl&.active?
195
+ end
196
+
197
+ private
198
+
186
199
  def name_from_ua(user_agent)
187
200
  BlockedUserAgent.matching_pattern(user_agent)
188
201
  end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "net/http"
4
+ require "json"
5
+
6
+ module Trackguard
7
+ module Hub
8
+ class SubmitBlockedRequestJob < ApplicationJob
9
+ queue_as :default
10
+
11
+ def perform(ip:, user_agent:, path:, http_method:, block_reason:)
12
+ uri = URI("#{Trackguard.hub_url}/api/backend/blocked_requests")
13
+ body = {
14
+ blocked_request: {
15
+ ip: ip, user_agent: user_agent, path: path,
16
+ http_method: http_method, block_reason: block_reason
17
+ }
18
+ }.to_json
19
+
20
+ req = Net::HTTP::Post.new(uri)
21
+ req["X-Api-Key"] = Trackguard.hub_api_key
22
+ req["Authorization"] = "Bearer #{Trackguard.hub_secret_key}"
23
+ req["Content-Type"] = "application/json"
24
+ req.body = body
25
+
26
+ opts = { use_ssl: uri.scheme == "https", open_timeout: 3, read_timeout: 5 }
27
+ response = Net::HTTP.start(uri.hostname, uri.port, **opts) { |http| http.request(req) }
28
+
29
+ unless response.is_a?(Net::HTTPSuccess)
30
+ Rails.logger.warn(
31
+ "[Trackguard::Hub::SubmitBlockedRequestJob] Unexpected response #{response.code} for path=#{path}"
32
+ )
33
+ end
34
+ rescue StandardError => e
35
+ Rails.logger.warn("[Trackguard::Hub::SubmitBlockedRequestJob] Failed to submit blocked request: #{e.message}")
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "net/http"
4
+ require "json"
5
+
6
+ module Trackguard
7
+ module Hub
8
+ class SubmitPageViewJob < ApplicationJob
9
+ queue_as :default
10
+
11
+ def perform(path:, ip:, user_agent:, referer:, session_id:, trace_id:, source:, http_method:, **)
12
+ uri = URI("#{Trackguard.hub_url}/api/backend/page_views")
13
+ body = {
14
+ page_view: {
15
+ path: path, referer: referer, session_id: session_id, trace_id: trace_id,
16
+ source: source, http_method: http_method, ip: ip, user_agent: user_agent
17
+ }
18
+ }.to_json
19
+
20
+ req = Net::HTTP::Post.new(uri)
21
+ req["X-Api-Key"] = Trackguard.hub_api_key
22
+ req["Authorization"] = "Bearer #{Trackguard.hub_secret_key}"
23
+ req["Content-Type"] = "application/json"
24
+ req.body = body
25
+
26
+ opts = { use_ssl: uri.scheme == "https", open_timeout: 3, read_timeout: 5 }
27
+ response = Net::HTTP.start(uri.hostname, uri.port, **opts) { |http| http.request(req) }
28
+
29
+ unless response.is_a?(Net::HTTPSuccess)
30
+ Rails.logger.warn(
31
+ "[Trackguard::Hub::SubmitPageViewJob] Unexpected response #{response.code} for path=#{path}"
32
+ )
33
+ end
34
+ rescue StandardError => e
35
+ Rails.logger.warn("[Trackguard::Hub::SubmitPageViewJob] Failed to submit page view: #{e.message}")
36
+ end
37
+ end
38
+ end
39
+ end
@@ -5,20 +5,7 @@ module Trackguard
5
5
  queue_as :default
6
6
 
7
7
  def perform(ip:, user_agent:, path:, http_method:, block_reason:)
8
- visitor = Visitor.find_or_create_by!(ip: ip) do |v|
9
- v.user_agent = user_agent
10
- v.first_seen_at = Time.current
11
- v.last_seen_at = Time.current
12
- end
13
- visitor.update!(last_seen_at: Time.current, user_agent: user_agent)
14
-
15
- BlockedRequest.create!(
16
- path: path,
17
- user_agent: user_agent,
18
- http_method: http_method,
19
- block_reason: block_reason,
20
- visitor: visitor
21
- )
8
+ TrackBlockedRequest.call(ip:, user_agent:, path:, http_method:, block_reason:)
22
9
  end
23
10
  end
24
11
  end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Trackguard
4
+ class TrackBlockedRequest < ApplicationService
5
+ def initialize(ip:, user_agent:, path:, http_method:, block_reason:, visitor_scope: {})
6
+ @ip = ip
7
+ @user_agent = user_agent
8
+ @path = path
9
+ @http_method = http_method
10
+ @block_reason = block_reason
11
+ @visitor_scope = visitor_scope
12
+ end
13
+
14
+ def call
15
+ visitor = Visitor.find_or_create_by!(ip: @ip, **@visitor_scope) do |v|
16
+ v.user_agent = @user_agent
17
+ v.first_seen_at = Time.current
18
+ v.last_seen_at = Time.current
19
+ end
20
+ visitor.update!(last_seen_at: Time.current, user_agent: @user_agent)
21
+
22
+ BlockedRequest.create!(
23
+ path: @path,
24
+ user_agent: @user_agent,
25
+ http_method: @http_method,
26
+ block_reason: @block_reason,
27
+ visitor: visitor,
28
+ **@visitor_scope
29
+ )
30
+ end
31
+ end
32
+ end
@@ -15,7 +15,7 @@
15
15
  <tbody>
16
16
  <% rows.each do |label, count| %>
17
17
  <tr>
18
- <td class="tg-td"><%= label %></td>
18
+ <td class="tg-td tg-td--label"><%= label %></td>
19
19
  <td class="tg-td tg-td--num"><%= count %></td>
20
20
  </tr>
21
21
  <% end %>
data/config/importmap.rb CHANGED
@@ -1 +1,3 @@
1
- pin_all_from File.expand_path("../app/assets/javascripts/controllers", __dir__), under: "controllers"
1
+ if Trackguard.adapter.is_a?(Trackguard::Adapters::Local)
2
+ pin_all_from File.expand_path("../app/assets/javascripts/controllers", __dir__), under: "controllers"
3
+ end
data/config/routes.rb CHANGED
@@ -1,4 +1,6 @@
1
1
  Trackguard::Engine.routes.draw do
2
+ next if Trackguard.adapter.is_a?(Trackguard::Adapters::Hub)
3
+
2
4
  post "/page_views", to: "page_views#create"
3
5
 
4
6
  scope "/admin", module: "admin" do
@@ -3,13 +3,16 @@
3
3
  module Trackguard
4
4
  module Adapters
5
5
  class Base
6
+ def validate! = nil
7
+
6
8
  def blocked_user_agent?(user_agent) = raise NotImplementedError, "#{self.class}#blocked_user_agent?"
7
9
  def blocked_path?(path) = raise NotImplementedError, "#{self.class}#blocked_path?"
8
10
  def whitelisted_ip?(ip) = raise NotImplementedError, "#{self.class}#whitelisted_ip?"
9
11
  def flagged_visitor?(ip) = raise NotImplementedError, "#{self.class}#flagged_visitor?"
10
12
 
11
13
  def track_page_view(path:, ip:, user_agent:, referer:, session_id:, trace_id:, source:, tracking_layer:,
12
- http_method:)
14
+ http_method:, prefetch: false)
15
+ return unless Trackguard.tracking_enabled?
13
16
  return if blocked_user_agent?(user_agent)
14
17
  return if blocked_path?(path)
15
18
  return if path.blank? || path.start_with?(Trackguard.admin_path)
@@ -17,20 +20,27 @@ module Trackguard
17
20
  perform_track_page_view(
18
21
  path: path, ip: ip, user_agent: user_agent, referer: referer,
19
22
  session_id: session_id, trace_id: trace_id, source: source,
20
- tracking_layer: tracking_layer, http_method: http_method
23
+ tracking_layer: tracking_layer, http_method: http_method, prefetch: prefetch
21
24
  )
22
25
  end
23
26
 
24
27
  def track_blocked_request(ip:, user_agent:, path:, http_method:, block_reason:)
25
- raise NotImplementedError, "#{self.class}#track_blocked_request"
28
+ return unless Trackguard.tracking_enabled?
29
+
30
+ perform_track_blocked_request(ip: ip, user_agent: user_agent, path: path,
31
+ http_method: http_method, block_reason: block_reason)
26
32
  end
27
33
 
28
34
  protected
29
35
 
30
36
  def perform_track_page_view(path:, ip:, user_agent:, referer:, session_id:, trace_id:, source:,
31
- tracking_layer:, http_method:)
37
+ tracking_layer:, http_method:, prefetch: false)
32
38
  raise NotImplementedError, "#{self.class}#perform_track_page_view"
33
39
  end
40
+
41
+ def perform_track_blocked_request(ip:, user_agent:, path:, http_method:, block_reason:)
42
+ raise NotImplementedError, "#{self.class}#perform_track_blocked_request"
43
+ end
34
44
  end
35
45
  end
36
46
  end
@@ -10,6 +10,33 @@ module Trackguard
10
10
  STALE_KEY = "trackguard/hub_rules/stale"
11
11
  ETAG_KEY = "trackguard/hub_rules/etag"
12
12
 
13
+ def validate!
14
+ return unless Trackguard.tracking_enabled?
15
+
16
+ missing = []
17
+ missing << "hub_api_key" if Trackguard.hub_api_key.blank?
18
+ missing << "hub_secret_key" if Trackguard.hub_secret_key.blank?
19
+ return if missing.empty?
20
+
21
+ raise Trackguard::ConfigurationError, <<~MSG.strip
22
+ Trackguard Hub adapter is missing required credentials: #{missing.join(', ')}.
23
+
24
+ Set them in your initializer:
25
+
26
+ Trackguard.configure do |c|
27
+ c.adapter = :hub
28
+ c.hub_api_key = ENV["TRACKGUARD_API_KEY"]
29
+ c.hub_secret_key = ENV["TRACKGUARD_SECRET_KEY"]
30
+ end
31
+
32
+ Sign up at https://trackguard.dev to create a project and generate credentials.
33
+
34
+ To use local tracking instead (no external service needed):
35
+
36
+ Trackguard.configure { |c| c.adapter = :local }
37
+ MSG
38
+ end
39
+
13
40
  def blocked_user_agent?(user_agent)
14
41
  rules.fetch(:blocked_user_agents, []).any? do |p|
15
42
  user_agent.to_s.downcase.include?(p.downcase)
@@ -30,15 +57,23 @@ module Trackguard
30
57
  rules.fetch(:flagged_ips, []).include?(ip)
31
58
  end
32
59
 
33
- def track_blocked_request(ip:, user_agent:, path:, http_method:, block_reason:)
34
- # placeholder: hub adapter does not persist blocked requests locally
35
- end
36
-
37
60
  protected
38
61
 
62
+ def perform_track_blocked_request(ip:, user_agent:, path:, http_method:, block_reason:)
63
+ Trackguard::Hub::SubmitBlockedRequestJob.perform_later(
64
+ ip: ip, user_agent: user_agent, path: path, http_method: http_method, block_reason: block_reason
65
+ )
66
+ end
67
+
39
68
  def perform_track_page_view(path:, ip:, user_agent:, referer:, session_id:, trace_id:, source:,
40
- tracking_layer:, http_method:)
41
- # placeholder: hub tracking not yet implemented
69
+ tracking_layer:, http_method:, prefetch: false)
70
+ return if prefetch
71
+
72
+ Trackguard::Hub::SubmitPageViewJob.perform_later(
73
+ path: path, ip: ip, user_agent: user_agent, referer: referer,
74
+ session_id: session_id, trace_id: trace_id, source: source,
75
+ tracking_layer: tracking_layer, http_method: http_method
76
+ )
42
77
  end
43
78
 
44
79
  private
@@ -55,22 +90,8 @@ module Trackguard
55
90
  end
56
91
 
57
92
  def fetch_rules_from_hub
58
- uri = URI("#{Trackguard.hub_url}/api/rules")
59
- request = Net::HTTP::Get.new(uri)
60
- request["Authorization"] = "Bearer #{Trackguard.hub_secret_key}"
61
- request["Accept"] = "application/json"
62
-
63
- etag = Rails.cache.read(ETAG_KEY)
64
- request["If-None-Match"] = etag if etag
65
-
66
- response = Net::HTTP.start(
67
- uri.hostname, uri.port,
68
- use_ssl: uri.scheme == "https",
69
- open_timeout: 3,
70
- read_timeout: 5
71
- ) do |http|
72
- http.request(request)
73
- end
93
+ uri = URI("#{Trackguard.hub_url}/api/backend/rules")
94
+ response = execute_http(uri, build_request(uri))
74
95
 
75
96
  return Rails.cache.read(STALE_KEY) || {} if response.is_a?(Net::HTTPNotModified)
76
97
 
@@ -80,6 +101,21 @@ module Trackguard
80
101
  Rails.cache.write(ETAG_KEY, response["ETag"], expires_in: 24.hours) if response["ETag"]
81
102
  fresh
82
103
  end
104
+
105
+ def build_request(uri)
106
+ req = Net::HTTP::Get.new(uri)
107
+ req["Authorization"] = "Bearer #{Trackguard.hub_secret_key}"
108
+ req["X-Api-Key"] = Trackguard.hub_api_key
109
+ req["Accept"] = "application/json"
110
+ etag = Rails.cache.read(ETAG_KEY)
111
+ req["If-None-Match"] = etag if etag
112
+ req
113
+ end
114
+
115
+ def execute_http(uri, request)
116
+ opts = { use_ssl: uri.scheme == "https", open_timeout: 3, read_timeout: 5 }
117
+ Net::HTTP.start(uri.hostname, uri.port, **opts) { |http| http.request(request) }
118
+ end
83
119
  end
84
120
  end
85
121
  end
@@ -19,7 +19,9 @@ module Trackguard
19
19
  Visitor.flagged?(ip)
20
20
  end
21
21
 
22
- def track_blocked_request(ip:, user_agent:, path:, http_method:, block_reason:)
22
+ protected
23
+
24
+ def perform_track_blocked_request(ip:, user_agent:, path:, http_method:, block_reason:)
23
25
  TrackBlockedRequestJob.perform_later(
24
26
  ip: ip,
25
27
  user_agent: user_agent,
@@ -29,10 +31,8 @@ module Trackguard
29
31
  )
30
32
  end
31
33
 
32
- protected
33
-
34
34
  def perform_track_page_view(path:, ip:, user_agent:, referer:, session_id:, trace_id:, source:,
35
- tracking_layer:, http_method:)
35
+ tracking_layer:, http_method:, prefetch: false)
36
36
  TrackPageViewJob.perform_later(
37
37
  path: path,
38
38
  ip: ip,
@@ -6,7 +6,6 @@ module Trackguard
6
6
 
7
7
  config.to_prepare do
8
8
  ActionController::Base.helper Trackguard::ApplicationHelper
9
- ActionController::Base.helper Trackguard::HubHelper
10
9
  end
11
10
 
12
11
  initializer "trackguard.assets" do |app|
@@ -17,7 +16,12 @@ module Trackguard
17
16
  app.config.importmap.paths << root.join("config/importmap.rb") if app.config.respond_to?(:importmap)
18
17
  end
19
18
 
19
+ initializer "trackguard.trace_id_middleware" do |app|
20
+ app.middleware.use Trackguard::TraceIdMiddleware
21
+ end
22
+
20
23
  config.after_initialize do
24
+ Trackguard.adapter.validate!
21
25
  Trackguard::RackAttack.configure
22
26
  end
23
27
  end
@@ -4,28 +4,25 @@ require "rack/attack"
4
4
 
5
5
  module Trackguard
6
6
  module RackAttack
7
- # rubocop:disable Metrics/MethodLength
8
7
  def self.configure
9
- adapter = Trackguard.adapter
10
-
11
8
  ::Rack::Attack.safelist("trackguard/allow local") do |req|
12
9
  [ "127.0.0.1", "::1" ].include?(req.ip)
13
10
  end
14
11
 
15
12
  ::Rack::Attack.safelist("trackguard/allow whitelisted ips") do |req|
16
- adapter.whitelisted_ip?(req.ip)
13
+ Trackguard.adapter.whitelisted_ip?(req.ip)
17
14
  end
18
15
 
19
16
  ::Rack::Attack.blocklist("trackguard/block known scanners") do |req|
20
- adapter.blocked_user_agent?(req.user_agent)
17
+ Trackguard.adapter.blocked_user_agent?(req.user_agent)
21
18
  end
22
19
 
23
20
  ::Rack::Attack.blocklist("trackguard/block known paths") do |req|
24
- adapter.blocked_path?(req.path)
21
+ Trackguard.adapter.blocked_path?(req.path)
25
22
  end
26
23
 
27
24
  ::Rack::Attack.blocklist("trackguard/flagged visitors") do |req|
28
- adapter.flagged_visitor?(req.ip)
25
+ Trackguard.adapter.flagged_visitor?(req.ip)
29
26
  end
30
27
 
31
28
  ::Rack::Attack.throttle(
@@ -34,16 +31,15 @@ module Trackguard
34
31
  period: Trackguard.throttle_period, &:ip
35
32
  )
36
33
 
37
- subscribe_to_blocked_requests(adapter)
34
+ subscribe_to_blocked_requests
38
35
  end
39
- # rubocop:enable Metrics/MethodLength
40
36
 
41
- def self.subscribe_to_blocked_requests(adapter)
37
+ def self.subscribe_to_blocked_requests
42
38
  @subscribe_to_blocked_requests ||= ActiveSupport::Notifications.subscribe("rack.attack") do |*, payload|
43
39
  req = payload[:request]
44
40
  next unless req.env["rack.attack.match_type"] == :blocklist
45
41
 
46
- adapter.track_blocked_request(
42
+ Trackguard.adapter.track_blocked_request(
47
43
  ip: req.ip,
48
44
  user_agent: req.user_agent.to_s,
49
45
  path: req.path,
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Trackguard
4
+ class TraceIdMiddleware
5
+ def initialize(app)
6
+ @app = app
7
+ end
8
+
9
+ def call(env)
10
+ env["trackguard.trace_id"] = SecureRandom.uuid
11
+ @app.call(env)
12
+ end
13
+ end
14
+ end
@@ -1,3 +1,3 @@
1
1
  module Trackguard
2
- VERSION = "0.28.0".freeze
2
+ VERSION = "0.30.0".freeze
3
3
  end
data/lib/trackguard.rb CHANGED
@@ -3,15 +3,90 @@
3
3
  require "trackguard/version"
4
4
  require "trackguard/engine"
5
5
  require "trackguard/rack_attack"
6
+ require "trackguard/trace_id_middleware"
6
7
  require "trackguard/adapters/base"
7
8
  require "trackguard/adapters/local"
8
9
  require "trackguard/adapters/hub"
9
10
 
10
11
  module Trackguard
12
+ class ConfigurationError < StandardError; end
13
+
11
14
  class << self
12
- attr_writer :authenticate_admin_with, :admin_layout, :admin_path, :back_label, :local_api_token, :throttle_limit,
13
- :throttle_period, :hub_secret_key, :hub_api_key, :hub_rules_ttl
14
- attr_accessor :hub_url
15
+ def configure
16
+ @_configuring = true
17
+ yield self
18
+ ensure
19
+ @_configuring = false
20
+ end
21
+
22
+ # Setters — use configure block; direct assignment is deprecated.
23
+
24
+ def authenticate_admin_with=(value)
25
+ deprecated_direct_setter(:authenticate_admin_with)
26
+ @authenticate_admin_with = value
27
+ end
28
+
29
+ def admin_layout=(value)
30
+ deprecated_direct_setter(:admin_layout)
31
+ @admin_layout = value
32
+ end
33
+
34
+ def admin_path=(value)
35
+ deprecated_direct_setter(:admin_path)
36
+ @admin_path = value
37
+ end
38
+
39
+ def back_label=(value)
40
+ deprecated_direct_setter(:back_label)
41
+ @back_label = value
42
+ end
43
+
44
+ def local_api_token=(value)
45
+ deprecated_direct_setter(:local_api_token)
46
+ @local_api_token = value
47
+ end
48
+
49
+ def throttle_limit=(value)
50
+ deprecated_direct_setter(:throttle_limit)
51
+ @throttle_limit = value
52
+ end
53
+
54
+ def throttle_period=(value)
55
+ deprecated_direct_setter(:throttle_period)
56
+ @throttle_period = value
57
+ end
58
+
59
+ def hub_url=(value)
60
+ deprecated_direct_setter(:hub_url)
61
+ @hub_url = value
62
+ end
63
+
64
+ def hub_secret_key=(value)
65
+ deprecated_direct_setter(:hub_secret_key)
66
+ @hub_secret_key = value
67
+ end
68
+
69
+ def hub_api_key=(value)
70
+ deprecated_direct_setter(:hub_api_key)
71
+ @hub_api_key = value
72
+ end
73
+
74
+ def hub_rules_ttl=(value)
75
+ deprecated_direct_setter(:hub_rules_ttl)
76
+ @hub_rules_ttl = value
77
+ end
78
+
79
+ def adapter=(value)
80
+ deprecated_direct_setter(:adapter)
81
+ @adapter = resolve_adapter(value)
82
+ end
83
+
84
+ def disable_on_development=(value)
85
+ deprecated_direct_setter(:disable_on_development)
86
+ @disable_on_development = value
87
+ end
88
+
89
+ # Readers
15
90
 
16
91
  def authenticate_admin_with
17
92
  @authenticate_admin_with ||= proc {}
@@ -41,20 +116,36 @@ module Trackguard
41
116
  @throttle_period ||= 60
42
117
  end
43
118
 
119
+ def hub_url
120
+ @hub_url ||= "https://app.trackguard.dev"
121
+ end
122
+
44
123
  def hub_secret_key = @hub_secret_key.to_s
45
124
  def hub_api_key = @hub_api_key.to_s
46
- def hub_rules_ttl = @hub_rules_ttl ||= 5.minutes
125
+ def hub_rules_ttl = @hub_rules_ttl ||= 5.minutes
47
126
 
48
127
  def adapter
49
128
  @adapter ||= Trackguard::Adapters::Local.new
50
129
  end
51
130
 
52
- def adapter=(value)
53
- @adapter = resolve_adapter(value)
131
+ def disable_on_development
132
+ @disable_on_development.nil? || @disable_on_development
133
+ end
134
+
135
+ def tracking_enabled?
136
+ !disable_on_development || !Rails.env.development?
54
137
  end
55
138
 
56
139
  private
57
140
 
141
+ def deprecated_direct_setter(attr)
142
+ return if @_configuring
143
+
144
+ warn "[DEPRECATED] Trackguard.#{attr}= called directly. " \
145
+ "Use `Trackguard.configure { |c| c.#{attr} = ... }` instead. " \
146
+ "Direct assignment will be removed in a future version."
147
+ end
148
+
58
149
  def resolve_adapter(value)
59
150
  case value
60
151
  when Symbol then Trackguard::Adapters.const_get(value.to_s.camelize).new
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: trackguard
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.28.0
4
+ version: 0.30.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Krzysztof Rygielski
@@ -58,8 +58,9 @@ files:
58
58
  - app/controllers/trackguard/admin/whitelisted_ips_controller.rb
59
59
  - app/controllers/trackguard/page_views_controller.rb
60
60
  - app/helpers/trackguard/application_helper.rb
61
- - app/helpers/trackguard/hub_helper.rb
62
61
  - app/jobs/trackguard/detect_suspicious_visitors_job.rb
62
+ - app/jobs/trackguard/hub/submit_blocked_request_job.rb
63
+ - app/jobs/trackguard/hub/submit_page_view_job.rb
63
64
  - app/jobs/trackguard/track_blocked_request_job.rb
64
65
  - app/jobs/trackguard/track_page_view_job.rb
65
66
  - app/models/trackguard/blocked_path.rb
@@ -71,6 +72,7 @@ files:
71
72
  - app/models/trackguard/whitelisted_ip.rb
72
73
  - app/services/trackguard/analytics_query.rb
73
74
  - app/services/trackguard/application_service.rb
75
+ - app/services/trackguard/track_blocked_request.rb
74
76
  - app/services/trackguard/track_page_view.rb
75
77
  - app/views/layouts/trackguard/admin.html.erb
76
78
  - app/views/trackguard/admin/_nav.html.erb
@@ -98,6 +100,7 @@ files:
98
100
  - lib/trackguard/adapters/local.rb
99
101
  - lib/trackguard/engine.rb
100
102
  - lib/trackguard/rack_attack.rb
103
+ - lib/trackguard/trace_id_middleware.rb
101
104
  - lib/trackguard/version.rb
102
105
  - trackguard.gemspec
103
106
  homepage: https://github.com/riggy/trackguard
@@ -1,11 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Trackguard
4
- module HubHelper
5
- def trackguard_hub_js_tag
6
- return unless Trackguard.adapter.is_a?(Trackguard::Adapters::Hub)
7
-
8
- tag.script(src: "https://app.trackguard.dev/track.js", data: { api_key: Trackguard.hub_api_key })
9
- end
10
- end
11
- end