reputable 0.1.21 → 0.1.22
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/README.md +50 -4
- data/lib/reputable/configuration.rb +3 -1
- data/lib/reputable/middleware.rb +110 -22
- data/lib/reputable/reputation.rb +11 -3
- data/lib/reputable/version.rb +1 -1
- data/lib/reputable.rb +6 -0
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 594ed67d80dc9ae5bd493bbe7c3322c4ed79f1d4e661b7e36ddc57d46d350c71
|
|
4
|
+
data.tar.gz: e9b6093d3cc1a23fe9f881d292984c92e7a32c86ccb52c1581f60a12eae94078
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c921b3e37a852a8226551cd43c4787760a3c41230dbfaf7535b93c62599185b7e9438c4421cf7e03533325036b184b4a6c817cedee69606f057dec27abb93b79
|
|
7
|
+
data.tar.gz: 6da6fc589dd71010685ef7e02d68b5c0b6c4a97fd457187f1c432fc9a5c992a8821d49bda29cd2f169ed95262e28c63a0f4be08b41d811555f166c5a14717c92
|
data/README.md
CHANGED
|
@@ -29,10 +29,13 @@ bundle install
|
|
|
29
29
|
Reputable.configure do |config|
|
|
30
30
|
# Required: Redis/Dragonfly URL (TLS supported via rediss://)
|
|
31
31
|
config.redis_url = ENV['REPUTABLE_REDIS_URL']
|
|
32
|
-
|
|
33
|
-
# Optional:
|
|
34
|
-
|
|
32
|
+
|
|
33
|
+
# Optional: Verbose logging (logs every middleware decision)
|
|
34
|
+
# config.verbose = true
|
|
35
35
|
end
|
|
36
|
+
|
|
37
|
+
# Optional: Enable logging
|
|
38
|
+
Reputable.logger = Rails.logger
|
|
36
39
|
```
|
|
37
40
|
|
|
38
41
|
### 2. Add Middleware
|
|
@@ -100,6 +103,9 @@ REPUTABLE_WRITE_TIMEOUT=0.5 # Redis write timeout (seconds)
|
|
|
100
103
|
REPUTABLE_POOL_SIZE=5 # Connection pool size
|
|
101
104
|
REPUTABLE_POOL_TIMEOUT=1.0 # Pool checkout timeout (seconds)
|
|
102
105
|
|
|
106
|
+
# Optional: Verbose logging (logs every middleware decision at info level)
|
|
107
|
+
REPUTABLE_VERBOSE=true
|
|
108
|
+
|
|
103
109
|
# Optional: SSL (for custom certificates)
|
|
104
110
|
REPUTABLE_SSL_VERIFY=false # Disable SSL verification (NOT recommended for production)
|
|
105
111
|
```
|
|
@@ -155,6 +161,10 @@ Reputable.configure do |config|
|
|
|
155
161
|
config.support_email = ENV['REPUTABLE_SUPPORT_EMAIL']
|
|
156
162
|
config.support_url = ENV['REPUTABLE_SUPPORT_URL']
|
|
157
163
|
|
|
164
|
+
# Verbose logging — logs every middleware decision at info level
|
|
165
|
+
# Also available via REPUTABLE_VERBOSE=true
|
|
166
|
+
config.verbose = true
|
|
167
|
+
|
|
158
168
|
# Error callback (optional)
|
|
159
169
|
config.on_error = ->(error, context) {
|
|
160
170
|
# Report to your error tracking service
|
|
@@ -162,7 +172,7 @@ Reputable.configure do |config|
|
|
|
162
172
|
}
|
|
163
173
|
end
|
|
164
174
|
|
|
165
|
-
# Enable logging
|
|
175
|
+
# Enable logging (required for verbose output)
|
|
166
176
|
Reputable.logger = Rails.logger
|
|
167
177
|
```
|
|
168
178
|
|
|
@@ -719,6 +729,42 @@ config.on_error = ->(error, context) {
|
|
|
719
729
|
|
|
720
730
|
---
|
|
721
731
|
|
|
732
|
+
## Verbose Logging / Debugging
|
|
733
|
+
|
|
734
|
+
When troubleshooting middleware behavior (e.g., unexpected redirects or blocked requests), enable verbose mode to see every decision the gem makes:
|
|
735
|
+
|
|
736
|
+
```ruby
|
|
737
|
+
# config/initializers/reputable.rb
|
|
738
|
+
Reputable.configure do |config|
|
|
739
|
+
config.verbose = true
|
|
740
|
+
# ...
|
|
741
|
+
end
|
|
742
|
+
Reputable.logger = Rails.logger
|
|
743
|
+
```
|
|
744
|
+
|
|
745
|
+
Or via environment variable:
|
|
746
|
+
|
|
747
|
+
```bash
|
|
748
|
+
REPUTABLE_VERBOSE=true
|
|
749
|
+
```
|
|
750
|
+
|
|
751
|
+
When enabled, the middleware logs at `info` level with a `[Reputable]` prefix. Example output for a challenged request:
|
|
752
|
+
|
|
753
|
+
```
|
|
754
|
+
[Reputable] request: GET / ip=68.6.118.12 rid=abc-123
|
|
755
|
+
[Reputable] state: enabled=true operational=true gate=true track=false
|
|
756
|
+
[Reputable] extract_ip: 68.6.118.12 (from HTTP_X_FORWARDED_FOR: 68.6.118.12, 10.0.0.1)
|
|
757
|
+
[Reputable] lookup: reputation:ip:68.6.118.12 → status=untrusted_challenge reason=inherited:asn:7922 source=inherited
|
|
758
|
+
[Reputable] gate: ip=68.6.118.12 status="untrusted_challenge"
|
|
759
|
+
[Reputable] verified_session?: false (reputable_verified_at= reputable_verified=)
|
|
760
|
+
[Reputable] gate: CHALLENGING ip=68.6.118.12 action=verify
|
|
761
|
+
[Reputable] challenge: REDIRECTING 302 → https://api.reputable.click/_reputable/verify?token=...
|
|
762
|
+
```
|
|
763
|
+
|
|
764
|
+
When verbose is `false` (the default), none of these logs are emitted.
|
|
765
|
+
|
|
766
|
+
---
|
|
767
|
+
|
|
722
768
|
## Tags for Classification
|
|
723
769
|
|
|
724
770
|
Use tags to classify requests for behavioral analysis:
|
|
@@ -15,7 +15,8 @@ module Reputable
|
|
|
15
15
|
:connect_timeout, :read_timeout, :write_timeout,
|
|
16
16
|
:ssl_params, :trusted_proxies, :ip_header_priority,
|
|
17
17
|
:on_error, :trusted_keys, :base_url,
|
|
18
|
-
:site_name, :support_email, :support_url
|
|
18
|
+
:site_name, :support_email, :support_url,
|
|
19
|
+
:verbose
|
|
19
20
|
|
|
20
21
|
# Alias for backward compatibility
|
|
21
22
|
alias_method :verification_base_url, :base_url
|
|
@@ -82,6 +83,7 @@ module Reputable
|
|
|
82
83
|
@site_name = ENV["REPUTABLE_SITE_NAME"]
|
|
83
84
|
@support_email = ENV["REPUTABLE_SUPPORT_EMAIL"]
|
|
84
85
|
@support_url = ENV["REPUTABLE_SUPPORT_URL"]
|
|
86
|
+
@verbose = %w[1 true yes on].include?(ENV.fetch("REPUTABLE_VERBOSE", "").downcase)
|
|
85
87
|
end
|
|
86
88
|
|
|
87
89
|
# Alias for backward compatibility
|
data/lib/reputable/middleware.rb
CHANGED
|
@@ -67,6 +67,15 @@ module Reputable
|
|
|
67
67
|
# This ID is exposed to views so it can be included in the JS snippet
|
|
68
68
|
env['reputable.request_id'] = SecureRandom.uuid
|
|
69
69
|
|
|
70
|
+
ip = extract_ip(env)
|
|
71
|
+
env["reputable.ip"] = ip
|
|
72
|
+
path = env["PATH_INFO"] || "/"
|
|
73
|
+
|
|
74
|
+
if Reputable.verbose?
|
|
75
|
+
Reputable.logger&.info("[Reputable] request: #{env['REQUEST_METHOD']} #{path} ip=#{ip} rid=#{env['reputable.request_id']}")
|
|
76
|
+
Reputable.logger&.info("[Reputable] state: enabled=#{Reputable.enabled?} operational=#{Reputable.operational?} gate=#{@reputation_gate} track=#{track_request_enabled?}")
|
|
77
|
+
end
|
|
78
|
+
|
|
70
79
|
# Check for verification return parameters and verify signature if present
|
|
71
80
|
handle_verification_return(env)
|
|
72
81
|
|
|
@@ -96,29 +105,53 @@ module Reputable
|
|
|
96
105
|
private
|
|
97
106
|
|
|
98
107
|
def safe_reputation_gate(env)
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
108
|
+
unless @reputation_gate
|
|
109
|
+
Reputable.logger&.info("[Reputable] gate: skipped (reputation_gate=false)") if Reputable.verbose?
|
|
110
|
+
return nil
|
|
111
|
+
end
|
|
112
|
+
unless Reputable.enabled?
|
|
113
|
+
Reputable.logger&.info("[Reputable] gate: skipped (disabled)") if Reputable.verbose?
|
|
114
|
+
return nil
|
|
115
|
+
end
|
|
116
|
+
unless Reputable.operational?
|
|
117
|
+
Reputable.logger&.info("[Reputable] gate: skipped (not operational, circuit_breaker=#{Connection.circuit_open? ? 'open' : 'closed'} failures=#{Connection.failure_count})") if Reputable.verbose?
|
|
118
|
+
return nil
|
|
119
|
+
end
|
|
120
|
+
if skip_request?(env)
|
|
121
|
+
Reputable.logger&.info("[Reputable] gate: skipped (path excluded)") if Reputable.verbose?
|
|
122
|
+
return nil
|
|
123
|
+
end
|
|
103
124
|
|
|
104
125
|
enforce_reputation_gate(env)
|
|
105
126
|
rescue StandardError => e
|
|
106
|
-
Reputable.logger&.
|
|
127
|
+
Reputable.logger&.warn("[Reputable] gate: exception #{e.class} - #{e.message}")
|
|
128
|
+
Reputable.logger&.debug(e.backtrace&.first(5)&.join("\n")) if Reputable.verbose?
|
|
107
129
|
nil
|
|
108
130
|
end
|
|
109
131
|
|
|
110
132
|
def enforce_reputation_gate(env)
|
|
111
133
|
status = reputation_status(env)
|
|
112
|
-
return nil if status.nil?
|
|
113
134
|
ip = env["reputable.ip"] || extract_ip(env)
|
|
114
135
|
|
|
136
|
+
if Reputable.verbose?
|
|
137
|
+
Reputable.logger&.info("[Reputable] gate: ip=#{ip} status=#{status.inspect}")
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
return nil if status.nil?
|
|
141
|
+
|
|
115
142
|
case status
|
|
116
143
|
when "untrusted_block"
|
|
144
|
+
Reputable.logger&.info("[Reputable] gate: BLOCKING ip=#{ip} action=#{@block_action}") if Reputable.verbose?
|
|
117
145
|
handle_block_action(env, ip)
|
|
118
146
|
when "untrusted_challenge"
|
|
119
|
-
|
|
147
|
+
if verified_session?(env)
|
|
148
|
+
Reputable.logger&.info("[Reputable] gate: ip=#{ip} has untrusted_challenge but session is verified, passing through") if Reputable.verbose?
|
|
149
|
+
return nil
|
|
150
|
+
end
|
|
151
|
+
Reputable.logger&.info("[Reputable] gate: CHALLENGING ip=#{ip} action=#{@challenge_action}") if Reputable.verbose?
|
|
120
152
|
handle_challenge_action(env)
|
|
121
153
|
else
|
|
154
|
+
Reputable.logger&.info("[Reputable] gate: ip=#{ip} status=#{status} — allowing") if Reputable.verbose?
|
|
122
155
|
nil
|
|
123
156
|
end
|
|
124
157
|
end
|
|
@@ -156,19 +189,29 @@ module Reputable
|
|
|
156
189
|
query = request.query_string
|
|
157
190
|
return unless query.include?("reputable_r") || query.include?("reputable_status")
|
|
158
191
|
|
|
192
|
+
Reputable.logger&.info("[Reputable] verification return detected in query string") if Reputable.verbose?
|
|
193
|
+
|
|
159
194
|
params = request.params
|
|
160
195
|
|
|
161
196
|
# Determine status from new format or legacy format
|
|
162
197
|
status = if params["reputable_r"]
|
|
163
198
|
decoded = Reputable.decode_reputable_response(params)
|
|
199
|
+
Reputable.logger&.info("[Reputable] verification: new format decoded=#{decoded.inspect}") if Reputable.verbose?
|
|
164
200
|
decoded&.dig("status")
|
|
165
201
|
else
|
|
202
|
+
Reputable.logger&.info("[Reputable] verification: legacy format status=#{params['reputable_status']}") if Reputable.verbose?
|
|
166
203
|
params["reputable_status"]
|
|
167
204
|
end
|
|
168
205
|
|
|
169
|
-
|
|
206
|
+
unless status == "pass"
|
|
207
|
+
Reputable.logger&.info("[Reputable] verification: status=#{status.inspect} (not 'pass'), ignoring") if Reputable.verbose?
|
|
208
|
+
return
|
|
209
|
+
end
|
|
170
210
|
|
|
171
|
-
|
|
211
|
+
sig_valid = Reputable.verify_redirect_return(params)
|
|
212
|
+
Reputable.logger&.info("[Reputable] verification: signature_valid=#{sig_valid}") if Reputable.verbose?
|
|
213
|
+
|
|
214
|
+
if sig_valid
|
|
172
215
|
env["reputable.verified"] = true
|
|
173
216
|
|
|
174
217
|
# Extract ignore_analytics from new format or legacy format
|
|
@@ -182,15 +225,21 @@ module Reputable
|
|
|
182
225
|
unless ignore_analytics.nil?
|
|
183
226
|
env["reputable.ignore_analytics"] = ignore_analytics == true || ignore_analytics.to_s == "true"
|
|
184
227
|
end
|
|
185
|
-
|
|
228
|
+
|
|
186
229
|
# Store in session if available
|
|
187
230
|
if env["rack.session"]
|
|
188
231
|
env["rack.session"]["reputable_verified"] = true
|
|
189
232
|
env["rack.session"]["reputable_verified_at"] = Time.now.to_i
|
|
233
|
+
Reputable.logger&.info("[Reputable] verification: stored in session, verified=true") if Reputable.verbose?
|
|
234
|
+
else
|
|
235
|
+
Reputable.logger&.info("[Reputable] verification: no rack.session available, cannot persist") if Reputable.verbose?
|
|
190
236
|
end
|
|
237
|
+
else
|
|
238
|
+
Reputable.logger&.warn("[Reputable] verification: INVALID signature, rejecting")
|
|
191
239
|
end
|
|
192
240
|
rescue StandardError => e
|
|
193
|
-
Reputable.logger&.
|
|
241
|
+
Reputable.logger&.warn("[Reputable] verification: exception #{e.class} - #{e.message}")
|
|
242
|
+
Reputable.logger&.debug(e.backtrace&.first(5)&.join("\n")) if Reputable.verbose?
|
|
194
243
|
end
|
|
195
244
|
|
|
196
245
|
def handle_blocked_page_request(env)
|
|
@@ -209,15 +258,20 @@ module Reputable
|
|
|
209
258
|
def handle_block_action(env, ip)
|
|
210
259
|
case @block_action
|
|
211
260
|
when :blocked_page
|
|
261
|
+
Reputable.logger&.info("[Reputable] block: rendering inline blocked page for ip=#{ip}") if Reputable.verbose?
|
|
212
262
|
build_blocked_page_response(ip)
|
|
213
263
|
when :blocked_page_remote
|
|
214
264
|
url = resolve_blocked_redirect_url(env) || Reputable.blocked_page_url
|
|
265
|
+
Reputable.logger&.info("[Reputable] block: REDIRECTING #{@blocked_redirect_status} → #{url}") if Reputable.verbose?
|
|
215
266
|
redirect_response(url, @blocked_redirect_status)
|
|
216
267
|
when :forbidden
|
|
268
|
+
Reputable.logger&.info("[Reputable] block: returning 403 for ip=#{ip}") if Reputable.verbose?
|
|
217
269
|
[403, { "Content-Type" => "text/plain; charset=utf-8" }, ["Forbidden"]]
|
|
218
270
|
when Proc
|
|
271
|
+
Reputable.logger&.info("[Reputable] block: calling custom proc for ip=#{ip}") if Reputable.verbose?
|
|
219
272
|
@block_action.call(env, ip)
|
|
220
273
|
else
|
|
274
|
+
Reputable.logger&.info("[Reputable] block: rendering default blocked page for ip=#{ip}") if Reputable.verbose?
|
|
221
275
|
build_blocked_page_response(ip)
|
|
222
276
|
end
|
|
223
277
|
end
|
|
@@ -226,13 +280,20 @@ module Reputable
|
|
|
226
280
|
case @challenge_action
|
|
227
281
|
when :verify
|
|
228
282
|
keys = Reputable.configuration.trusted_keys
|
|
229
|
-
|
|
283
|
+
if keys.nil? || keys.empty?
|
|
284
|
+
Reputable.logger&.info("[Reputable] challenge: no trusted_keys configured, cannot redirect to verification") if Reputable.verbose?
|
|
285
|
+
return nil
|
|
286
|
+
end
|
|
230
287
|
|
|
231
288
|
request = Rack::Request.new(env)
|
|
232
289
|
return_url = request.url
|
|
233
290
|
failure_url = build_verification_failure_url(request)
|
|
234
291
|
session_id = resolve_session_id(env)
|
|
235
292
|
|
|
293
|
+
if Reputable.verbose?
|
|
294
|
+
Reputable.logger&.info("[Reputable] challenge: building verification URL return_url=#{return_url} failure_url=#{failure_url} session_id=#{session_id}")
|
|
295
|
+
end
|
|
296
|
+
|
|
236
297
|
redirect_url = Reputable.verification_url(
|
|
237
298
|
return_url: return_url,
|
|
238
299
|
failure_url: failure_url,
|
|
@@ -240,10 +301,15 @@ module Reputable
|
|
|
240
301
|
force_challenge: @verification_force_challenge
|
|
241
302
|
)
|
|
242
303
|
|
|
243
|
-
|
|
304
|
+
if redirect_url == return_url
|
|
305
|
+
Reputable.logger&.info("[Reputable] challenge: verification_url == return_url, skipping redirect") if Reputable.verbose?
|
|
306
|
+
return nil
|
|
307
|
+
end
|
|
244
308
|
|
|
309
|
+
Reputable.logger&.info("[Reputable] challenge: REDIRECTING #{@challenge_redirect_status} → #{redirect_url}") if Reputable.verbose?
|
|
245
310
|
redirect_response(redirect_url, @challenge_redirect_status)
|
|
246
311
|
when Proc
|
|
312
|
+
Reputable.logger&.info("[Reputable] challenge: calling custom proc") if Reputable.verbose?
|
|
247
313
|
@challenge_action.call(env)
|
|
248
314
|
else
|
|
249
315
|
nil
|
|
@@ -287,14 +353,27 @@ module Reputable
|
|
|
287
353
|
end
|
|
288
354
|
|
|
289
355
|
def verified_session?(env)
|
|
290
|
-
|
|
356
|
+
if env["reputable.verified"]
|
|
357
|
+
Reputable.logger&.info("[Reputable] verified_session?: true (env flag set this request)") if Reputable.verbose?
|
|
358
|
+
return true
|
|
359
|
+
end
|
|
291
360
|
|
|
292
361
|
session = env["rack.session"]
|
|
293
|
-
|
|
362
|
+
unless session
|
|
363
|
+
Reputable.logger&.info("[Reputable] verified_session?: false (no rack.session)") if Reputable.verbose?
|
|
364
|
+
return false
|
|
365
|
+
end
|
|
294
366
|
|
|
295
|
-
@verified_session_keys.any? do |key|
|
|
367
|
+
found = @verified_session_keys.any? do |key|
|
|
296
368
|
session[key] || session[key.to_s]
|
|
297
369
|
end
|
|
370
|
+
|
|
371
|
+
if Reputable.verbose?
|
|
372
|
+
keys_state = @verified_session_keys.map { |k| "#{k}=#{session[k] || session[k.to_s]}" }.join(" ")
|
|
373
|
+
Reputable.logger&.info("[Reputable] verified_session?: #{found} (#{keys_state})")
|
|
374
|
+
end
|
|
375
|
+
|
|
376
|
+
found
|
|
298
377
|
rescue StandardError
|
|
299
378
|
false
|
|
300
379
|
end
|
|
@@ -408,27 +487,36 @@ module Reputable
|
|
|
408
487
|
|
|
409
488
|
def extract_ip(env)
|
|
410
489
|
config = Reputable.configuration
|
|
411
|
-
|
|
490
|
+
|
|
412
491
|
# Try each header in priority order
|
|
413
492
|
config.ip_header_priority.each do |header|
|
|
414
493
|
value = env[header]
|
|
415
494
|
next if value.nil? || value.empty?
|
|
416
|
-
|
|
495
|
+
|
|
417
496
|
# X-Forwarded-For and Forwarded can contain multiple IPs
|
|
418
497
|
if header == "HTTP_X_FORWARDED_FOR"
|
|
419
498
|
ip = extract_from_xff(value)
|
|
420
|
-
|
|
499
|
+
if ip
|
|
500
|
+
Reputable.logger&.info("[Reputable] extract_ip: #{ip} (from #{header}: #{value})") if Reputable.verbose?
|
|
501
|
+
return ip
|
|
502
|
+
end
|
|
421
503
|
elsif header == "HTTP_FORWARDED"
|
|
422
504
|
ip = extract_from_forwarded(value)
|
|
423
|
-
|
|
505
|
+
if ip
|
|
506
|
+
Reputable.logger&.info("[Reputable] extract_ip: #{ip} (from #{header}: #{value})") if Reputable.verbose?
|
|
507
|
+
return ip
|
|
508
|
+
end
|
|
424
509
|
else
|
|
425
510
|
# Single IP headers
|
|
426
511
|
ip = value.to_s.strip
|
|
427
|
-
|
|
512
|
+
unless ip.empty?
|
|
513
|
+
Reputable.logger&.info("[Reputable] extract_ip: #{ip} (from #{header})") if Reputable.verbose?
|
|
514
|
+
return ip
|
|
515
|
+
end
|
|
428
516
|
end
|
|
429
517
|
end
|
|
430
518
|
|
|
431
|
-
|
|
519
|
+
Reputable.logger&.info("[Reputable] extract_ip: 0.0.0.0 (no headers matched)") if Reputable.verbose?
|
|
432
520
|
"0.0.0.0"
|
|
433
521
|
rescue StandardError
|
|
434
522
|
"0.0.0.0"
|
data/lib/reputable/reputation.rb
CHANGED
|
@@ -133,20 +133,25 @@ module Reputable
|
|
|
133
133
|
return nil unless valid_entity_type?(entity_type)
|
|
134
134
|
|
|
135
135
|
key = "reputation:#{entity_type}:#{entity_id}"
|
|
136
|
+
Reputable.logger&.info("[Reputable] lookup: key=#{key}") if Reputable.verbose?
|
|
136
137
|
|
|
137
138
|
data = Connection.safe_with(default: nil, context: "reputation_lookup") do |redis|
|
|
138
139
|
redis.hgetall(key)
|
|
139
140
|
end
|
|
140
141
|
|
|
141
|
-
|
|
142
|
+
if data.nil? || data.empty?
|
|
143
|
+
Reputable.logger&.info("[Reputable] lookup: #{key} → (none)") if Reputable.verbose?
|
|
144
|
+
return nil
|
|
145
|
+
end
|
|
142
146
|
|
|
143
147
|
# Check if expired (Redis TTL should handle this, but double-check)
|
|
144
148
|
expires_at = data["expires_at"].to_i
|
|
145
149
|
if expires_at > 0 && expires_at < (Time.now.to_f * 1000).to_i
|
|
150
|
+
Reputable.logger&.info("[Reputable] lookup: #{key} → expired (expires_at=#{expires_at})") if Reputable.verbose?
|
|
146
151
|
return nil
|
|
147
152
|
end
|
|
148
153
|
|
|
149
|
-
{
|
|
154
|
+
result = {
|
|
150
155
|
status: data["status"],
|
|
151
156
|
reason: data["reason"],
|
|
152
157
|
source: data["source"],
|
|
@@ -154,8 +159,11 @@ module Reputable
|
|
|
154
159
|
expires_at: expires_at,
|
|
155
160
|
metadata: safe_parse_json(data["metadata"])
|
|
156
161
|
}
|
|
162
|
+
|
|
163
|
+
Reputable.logger&.info("[Reputable] lookup: #{key} → status=#{result[:status]} reason=#{result[:reason]} source=#{result[:source]}") if Reputable.verbose?
|
|
164
|
+
result
|
|
157
165
|
rescue StandardError => e
|
|
158
|
-
Reputable.logger&.
|
|
166
|
+
Reputable.logger&.warn("[Reputable] lookup: exception #{e.class} - #{e.message}")
|
|
159
167
|
nil
|
|
160
168
|
end
|
|
161
169
|
|
data/lib/reputable/version.rb
CHANGED
data/lib/reputable.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: reputable
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.22
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Reputable
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-02-
|
|
11
|
+
date: 2026-02-08 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: redis
|