trackguard 0.17.0 → 0.19.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: 79ebac148a49b2b6df9f280c3a8e8e624fa82910ccf0e3d4c608a04ac7f73ced
4
- data.tar.gz: a0a0648dacb35df737e19413603d658b1c015f6e5fa60d0a6adc72da497082da
3
+ metadata.gz: 90c710c8d2149047c75e5b99151b708e9741e29385d17de7c4f101ad9db697cf
4
+ data.tar.gz: 49aaf485a932c241d9c18986120f639305485f4c316653923e9ad9a018566e01
5
5
  SHA512:
6
- metadata.gz: 59d2f2a7dde041da4cc0acf29249c77d9f911a76c498f9381293c838a29bd6ac5fec3c875645c7be60b686d77cb99e4731032b45eb7a2a93464dadaac81b04cb
7
- data.tar.gz: 5c1bf529774f1af5e8a7e83154e7e21b94e468c95702ac00bed7f0b133b85c092359e544e7f2f615d4e364d904e4512d16ad42d42e3c712d8f8dcb4b60cb81ae
6
+ metadata.gz: b1a17b99009baaacee17c4473bf33b811870df411541cee1c055807f2b8981b4ab09ba0c28eeee15adc0a8ab87081f6b463b494435618cafafb2aea424b95226
7
+ data.tar.gz: 8da17d592514268ced0ab53cb0c2331671eea10702deaac46264f90874f2af33e60f952a1c89b7c1f920b6ad35ad93297bfe6ab786c796816c3704d35a17424d
@@ -7,7 +7,7 @@
7
7
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Inter", sans-serif;
8
8
  font-size: 16px;
9
9
  line-height: 1.5;
10
- background-color: #07101f;
10
+ background-color: #0f0909;
11
11
  color: #e0e0e0;
12
12
  -webkit-font-smoothing: antialiased;
13
13
  min-height: 100vh;
@@ -22,8 +22,8 @@
22
22
 
23
23
  /* ── Header ────────────────────────────────────────────────────────── */
24
24
  .tg-header {
25
- background: #0d1829;
26
- border-bottom: 1px solid #1c2d4a;
25
+ background: #190e0e;
26
+ border-bottom: 1px solid #2e1515;
27
27
  padding: 0.875rem 0;
28
28
  }
29
29
 
@@ -40,16 +40,16 @@
40
40
  font-size: 0.9375rem;
41
41
  font-weight: 600;
42
42
  letter-spacing: 0.03em;
43
- color: #60a5fa;
43
+ color: #f87171;
44
44
  text-decoration: none;
45
45
  flex: 1;
46
46
  }
47
47
 
48
48
  .tg-brand__logo {
49
49
  width: 20px;
50
- height: 22px;
50
+ height: auto;
51
51
  flex-shrink: 0;
52
- filter: drop-shadow(0 0 4px rgba(59, 130, 246, 0.35));
52
+ filter: drop-shadow(0 0 4px rgba(239, 68, 68, 0.4));
53
53
  }
54
54
 
55
55
  /* ── Back link ─────────────────────────────────────────────────────── */
@@ -76,8 +76,8 @@
76
76
 
77
77
  /* ── Nav ───────────────────────────────────────────────────────────── */
78
78
  .tg-nav {
79
- background: #0d1829;
80
- border-bottom: 1px solid #1c2d4a;
79
+ background: #190e0e;
80
+ border-bottom: 1px solid #2e1515;
81
81
  }
82
82
 
83
83
  .tg-nav > .tg-container {
@@ -97,8 +97,8 @@
97
97
  .tg-nav__link:hover { color: #e0e0e0; }
98
98
 
99
99
  .tg-nav__link--active {
100
- color: #60a5fa;
101
- border-bottom-color: #60a5fa;
100
+ color: #f87171;
101
+ border-bottom-color: #e53e3e;
102
102
  }
103
103
 
104
104
  /* ── Main ──────────────────────────────────────────────────────────── */
@@ -128,8 +128,8 @@
128
128
  }
129
129
 
130
130
  .tg-stat {
131
- background: #0d1829;
132
- border: 1px solid #1c2d4a;
131
+ background: #190e0e;
132
+ border: 1px solid #2e1515;
133
133
  border-radius: 8px;
134
134
  padding: 1.25rem;
135
135
  }
@@ -167,8 +167,8 @@
167
167
  }
168
168
 
169
169
  .tg-panel {
170
- background: #0d1829;
171
- border: 1px solid #1c2d4a;
170
+ background: #190e0e;
171
+ border: 1px solid #2e1515;
172
172
  border-radius: 8px;
173
173
  padding: 1rem;
174
174
  margin-bottom: 1rem;
@@ -211,14 +211,14 @@
211
211
  font-size: 0.8125rem;
212
212
  color: #e0e0e0;
213
213
  text-decoration: none;
214
- background: #1c2d4a;
214
+ background: #2e1515;
215
215
  }
216
216
 
217
- .tg-pagination__link:hover { background: #243a5e; }
217
+ .tg-pagination__link:hover { background: #3d1c1c; }
218
218
 
219
219
  .tg-pagination__link--active {
220
- background: #60a5fa;
221
- color: #07101f;
220
+ background: #e53e3e;
221
+ color: #ffffff;
222
222
  font-weight: 600;
223
223
  }
224
224
 
@@ -249,14 +249,14 @@
249
249
  text-transform: uppercase;
250
250
  letter-spacing: 0.08em;
251
251
  color: #666666;
252
- border-bottom: 1px solid #1c2d4a;
252
+ border-bottom: 1px solid #2e1515;
253
253
  }
254
254
 
255
255
  .tg-th--right { text-align: right; }
256
256
 
257
257
  .tg-td {
258
258
  padding: 0.75rem 0.875rem;
259
- border-bottom: 1px solid #1c2d4a;
259
+ border-bottom: 1px solid #2e1515;
260
260
  color: #f0f0f0;
261
261
  font-weight: 500;
262
262
  }
@@ -271,7 +271,7 @@
271
271
 
272
272
  .tg-td--bare {
273
273
  padding: 0;
274
- border-bottom: 1px solid #1c2d4a;
274
+ border-bottom: 1px solid #2e1515;
275
275
  }
276
276
 
277
277
  /* ── Empty state ───────────────────────────────────────────────────── */
@@ -361,7 +361,7 @@
361
361
  /* ── Detail panel ──────────────────────────────────────────────────── */
362
362
  .tg-detail {
363
363
  padding: 0 1rem 1rem 1rem;
364
- background: #0d1829;
364
+ background: #190e0e;
365
365
  }
366
366
 
367
367
  .tg-detail__grid {
@@ -423,7 +423,7 @@
423
423
  align-items: center;
424
424
  gap: 0.5rem;
425
425
  padding: 0.75rem 0 0;
426
- border-top: 1px solid #1c2d4a;
426
+ border-top: 1px solid #2e1515;
427
427
  margin-top: 0.75rem;
428
428
  }
429
429
 
@@ -436,8 +436,8 @@
436
436
 
437
437
  .tg-input {
438
438
  flex: 1;
439
- background: #07101f;
440
- border: 1px solid #1c2d4a;
439
+ background: #0f0909;
440
+ border: 1px solid #2e1515;
441
441
  border-radius: 4px;
442
442
  color: #e0e0e0;
443
443
  font-size: 0.8125rem;
@@ -445,7 +445,7 @@
445
445
  outline: none;
446
446
  }
447
447
 
448
- .tg-input:focus { border-color: #60a5fa; }
448
+ .tg-input:focus { border-color: #e53e3e; }
449
449
  .tg-input::placeholder { color: #444; }
450
450
 
451
451
  .tg-btn {
@@ -461,8 +461,8 @@
461
461
  .tg-btn--danger { background: #7f1d1d; color: #fca5a5; }
462
462
  .tg-btn--danger:hover { background: #991b1b; }
463
463
 
464
- .tg-btn--ghost { background: #1c2d4a; color: #e0e0e0; }
465
- .tg-btn--ghost:hover { background: #243a5e; }
464
+ .tg-btn--ghost { background: #2e1515; color: #e0e0e0; }
465
+ .tg-btn--ghost:hover { background: #3d1c1c; }
466
466
 
467
467
  .tg-btn--whitelist { background: #064e3b; color: #6ee7b7; }
468
468
  .tg-btn--whitelist:hover { background: #065f46; }
@@ -476,4 +476,4 @@
476
476
  .tg-stats { grid-template-columns: 1fr; }
477
477
  .tg-detail__grid { grid-template-columns: 1fr; }
478
478
  .tg-summary__ip { display: none; }
479
- }
479
+ }
@@ -1,13 +1,5 @@
1
1
  module Trackguard
2
2
  class PageViewRecorder < ApplicationService
3
- BOT_REGEX = /
4
- Googlebot|Bingbot|Slurp|DuckDuckBot|Baidu|YandexBot|
5
- facebookexternalhit|Twitterbot|LinkedInBot|
6
- curl|wget|python-requests|python-urllib|
7
- Go-http-client|libwww|Java|Ruby|
8
- bot|crawl|spider
9
- /ix
10
-
11
3
  def initialize(path:, ip:, user_agent:, referer:, session_id:, trace_id:, source: nil, initial: false,
12
4
  http_method: nil)
13
5
  @path = path.to_s
@@ -22,11 +14,11 @@ module Trackguard
22
14
  end
23
15
 
24
16
  def call
25
- return if BOT_REGEX.match?(@user_agent)
26
- return if BlockedUserAgent.blocked?(@user_agent)
17
+ adapter = Trackguard.adapter
18
+ return if adapter.blocked_user_agent?(@user_agent)
27
19
  return if @path.blank? || @path.start_with?("/admin")
28
20
 
29
- TrackPageViewJob.perform_later(
21
+ adapter.track_page_view(
30
22
  path: @path,
31
23
  ip: @ip,
32
24
  user_agent: @user_agent,
@@ -11,27 +11,7 @@
11
11
  <div class="tg-container">
12
12
  <div class="tg-header__inner">
13
13
  <span class="tg-brand">
14
- <svg class="tg-brand__logo" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 22" aria-hidden="true">
15
- <defs>
16
- <linearGradient id="tg-shield-grad" x1="0" y1="0" x2="0" y2="1">
17
- <stop offset="0%" stop-color="#1e3a5f"/>
18
- <stop offset="100%" stop-color="#0d1f3c"/>
19
- </linearGradient>
20
- </defs>
21
- <!-- Shield -->
22
- <path d="M10 1L19 4.5V11.5C19 16.4 15 20.3 10 21.5C5 20.3 1 16.4 1 11.5V4.5Z"
23
- fill="url(#tg-shield-grad)" stroke="#3b82f6" stroke-width="1.25" stroke-linejoin="round"/>
24
- <!-- Left rail -->
25
- <line x1="7.5" y1="6" x2="7.5" y2="17" stroke="#60a5fa" stroke-width="1.6" stroke-linecap="round"/>
26
- <!-- Right rail -->
27
- <line x1="12.5" y1="6" x2="12.5" y2="17" stroke="#60a5fa" stroke-width="1.6" stroke-linecap="round"/>
28
- <!-- Cross-tie 1 -->
29
- <line x1="6.25" y1="8" x2="13.75" y2="8" stroke="#3b82f6" stroke-width="1.25" stroke-linecap="round"/>
30
- <!-- Cross-tie 2 -->
31
- <line x1="6.25" y1="11.5" x2="13.75" y2="11.5" stroke="#3b82f6" stroke-width="1.25" stroke-linecap="round"/>
32
- <!-- Cross-tie 3 -->
33
- <line x1="6.25" y1="15" x2="13.75" y2="15" stroke="#3b82f6" stroke-width="1.25" stroke-linecap="round"/>
34
- </svg>
14
+ <%= image_tag "trackguard/logo.png", class: "tg-brand__logo", alt: "Trackguard" %>
35
15
  Trackguard
36
16
  </span>
37
17
 
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Trackguard
4
+ module Adapters
5
+ class Base
6
+ def blocked_user_agent?(user_agent) = raise NotImplementedError, "#{self.class}#blocked_user_agent?"
7
+ def whitelisted_ip?(ip) = raise NotImplementedError, "#{self.class}#whitelisted_ip?"
8
+ def flagged_visitor?(ip) = raise NotImplementedError, "#{self.class}#flagged_visitor?"
9
+
10
+ def track_page_view(path:, ip:, user_agent:, referer:, session_id:, trace_id:, source:, initial:, http_method:)
11
+ raise NotImplementedError, "#{self.class}#track_page_view"
12
+ end
13
+
14
+ def track_blocked_request(ip:, user_agent:, path:, http_method:, block_reason:)
15
+ raise NotImplementedError, "#{self.class}#track_blocked_request"
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Trackguard
4
+ module Adapters
5
+ class Local < Base
6
+ def blocked_user_agent?(user_agent)
7
+ BlockedUserAgent.blocked?(user_agent)
8
+ end
9
+
10
+ def whitelisted_ip?(ip)
11
+ WhitelistedIp.whitelisted?(ip)
12
+ end
13
+
14
+ def flagged_visitor?(ip)
15
+ Visitor.flagged?(ip)
16
+ end
17
+
18
+ def track_page_view(path:, ip:, user_agent:, referer:, session_id:, trace_id:, source:, initial:, http_method:)
19
+ TrackPageViewJob.perform_later(
20
+ path: path,
21
+ ip: ip,
22
+ user_agent: user_agent,
23
+ referer: referer,
24
+ session_id: session_id,
25
+ trace_id: trace_id,
26
+ source: source,
27
+ initial: initial,
28
+ http_method: http_method
29
+ )
30
+ end
31
+
32
+ def track_blocked_request(ip:, user_agent:, path:, http_method:, block_reason:)
33
+ TrackBlockedRequestJob.perform_later(
34
+ ip: ip,
35
+ user_agent: user_agent,
36
+ path: path,
37
+ http_method: http_method,
38
+ block_reason: block_reason
39
+ )
40
+ end
41
+ end
42
+ end
43
+ end
@@ -5,20 +5,22 @@ require "rack/attack"
5
5
  module Trackguard
6
6
  module RackAttack
7
7
  def self.configure
8
+ adapter = Trackguard.adapter
9
+
8
10
  ::Rack::Attack.safelist("trackguard/allow local") do |req|
9
11
  [ "127.0.0.1", "::1" ].include?(req.ip)
10
12
  end
11
13
 
12
14
  ::Rack::Attack.safelist("trackguard/allow whitelisted ips") do |req|
13
- Trackguard::WhitelistedIp.whitelisted?(req.ip)
15
+ adapter.whitelisted_ip?(req.ip)
14
16
  end
15
17
 
16
18
  ::Rack::Attack.blocklist("trackguard/block known scanners") do |req|
17
- Trackguard::BlockedUserAgent.blocked?(req.user_agent)
19
+ adapter.blocked_user_agent?(req.user_agent)
18
20
  end
19
21
 
20
22
  ::Rack::Attack.blocklist("trackguard/flagged visitors") do |req|
21
- Trackguard::Visitor.flagged?(req.ip)
23
+ adapter.flagged_visitor?(req.ip)
22
24
  end
23
25
 
24
26
  ::Rack::Attack.throttle(
@@ -27,15 +29,15 @@ module Trackguard
27
29
  period: Trackguard.throttle_period, &:ip
28
30
  )
29
31
 
30
- subscribe_to_blocked_requests
32
+ subscribe_to_blocked_requests(adapter)
31
33
  end
32
34
 
33
- def self.subscribe_to_blocked_requests
35
+ def self.subscribe_to_blocked_requests(adapter)
34
36
  @subscribe_to_blocked_requests ||= ActiveSupport::Notifications.subscribe("rack.attack") do |*, payload|
35
37
  req = payload[:request]
36
38
  next unless req.env["rack.attack.match_type"] == :blocklist
37
39
 
38
- Trackguard::TrackBlockedRequestJob.perform_later(
40
+ adapter.track_blocked_request(
39
41
  ip: req.ip,
40
42
  user_agent: req.user_agent.to_s,
41
43
  path: req.path,
@@ -1,3 +1,3 @@
1
1
  module Trackguard
2
- VERSION = "0.17.0".freeze
2
+ VERSION = "0.19.0".freeze
3
3
  end
data/lib/trackguard.rb CHANGED
@@ -3,6 +3,8 @@
3
3
  require "trackguard/version"
4
4
  require "trackguard/engine"
5
5
  require "trackguard/rack_attack"
6
+ require "trackguard/adapters/base"
7
+ require "trackguard/adapters/local"
6
8
 
7
9
  module Trackguard
8
10
  class << self
@@ -36,5 +38,23 @@ module Trackguard
36
38
  def throttle_period
37
39
  @throttle_period ||= 60
38
40
  end
41
+
42
+ def adapter
43
+ @adapter ||= Trackguard::Adapters::Local.new
44
+ end
45
+
46
+ def adapter=(value)
47
+ @adapter = resolve_adapter(value)
48
+ end
49
+
50
+ private
51
+
52
+ def resolve_adapter(value)
53
+ case value
54
+ when Symbol then Trackguard::Adapters.const_get(value.to_s.camelize).new
55
+ when Class then value.new
56
+ else value
57
+ end
58
+ end
39
59
  end
40
60
  end
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.17.0
4
+ version: 0.19.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Krzysztof Rygielski
@@ -43,6 +43,7 @@ executables: []
43
43
  extensions: []
44
44
  extra_rdoc_files: []
45
45
  files:
46
+ - app/assets/images/trackguard/logo.png
46
47
  - app/assets/javascripts/controllers/page_tracker_controller.js
47
48
  - app/assets/stylesheets/trackguard/admin.css
48
49
  - app/controllers/concerns/trackguard/page_tracker.rb
@@ -80,6 +81,8 @@ files:
80
81
  - lib/generators/trackguard/upgrade_generator.rb
81
82
  - lib/tasks/trackguard.rake
82
83
  - lib/trackguard.rb
84
+ - lib/trackguard/adapters/base.rb
85
+ - lib/trackguard/adapters/local.rb
83
86
  - lib/trackguard/engine.rb
84
87
  - lib/trackguard/rack_attack.rb
85
88
  - lib/trackguard/version.rb