traffic_orchestrator 1.0.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.
Files changed (4) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +135 -0
  3. data/lib/traffic_orchestrator.rb +227 -0
  4. metadata +88 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 70635b2e17549aeb91e0aec3d144c254fe07a81978324dfaa62e5df697e3e0b1
4
+ data.tar.gz: df7c26f54a757112be4886a6b7d88aa195f1de32aa8be1cd4a29ea84990c6db8
5
+ SHA512:
6
+ metadata.gz: 36da1c9cbe288d412e06fbc205198de3ed801f8d1bedfab2a651ab4c3d9273e7ebb13bb294cc8d4f275052a4f484f824992401995bad84999eb23a7663cae44a
7
+ data.tar.gz: 9dccaf4ab4690ff0c2e43370afbd080ceed638367fe5fa5eb94cb3d7e8be66a381a54445911b7c4d56d8be57d43735f248f741322b69c903942b93ba570a2283
data/README.md ADDED
@@ -0,0 +1,135 @@
1
+ # traffic_orchestrator (Ruby)
2
+
3
+ Official Ruby gem for [Traffic Orchestrator](https://trafficorchestrator.com) — enterprise-grade software license management.
4
+
5
+ ## Install
6
+
7
+ ```ruby
8
+ # Gemfile
9
+ gem 'traffic_orchestrator'
10
+ ```
11
+
12
+ ```bash
13
+ bundle install
14
+ # or
15
+ gem install traffic_orchestrator
16
+ ```
17
+
18
+ ## Quick Start
19
+
20
+ ```ruby
21
+ require 'traffic_orchestrator'
22
+
23
+ client = TrafficOrchestrator::Client.new(
24
+ api_key: ENV['TO_API_KEY'],
25
+ timeout: 5,
26
+ retries: 3
27
+ )
28
+
29
+ result = client.validate_license('LK-xxxx', domain: 'example.com')
30
+ puts "Valid: #{result.valid}, Plan: #{result.plan_id}" if result.valid
31
+ ```
32
+
33
+ ## API Methods
34
+
35
+ ### Core License Operations
36
+
37
+ | Method | Description |
38
+ |--------|-------------|
39
+ | `validate_license(token, domain:)` | Validate a license key against a domain |
40
+ | `verify_offline(token)` | Verify license locally using Ed25519 signatures |
41
+ | `list_licenses` | List all licenses for the authenticated user |
42
+ | `create_license(plan_id:, domains:)` | Create a new license |
43
+ | `rotate_license(license_id)` | Rotate a license key (revoke old, generate new) |
44
+ | `add_domain(license_id, domain)` | Add a domain to a license |
45
+ | `remove_domain(license_id, domain)` | Remove a domain from a license |
46
+ | `delete_license(license_id)` | Delete (revoke) a license |
47
+ | `get_usage` | Get current usage statistics |
48
+
49
+ ### Portal & Enterprise Methods
50
+
51
+ | Method | Description |
52
+ |--------|-------------|
53
+ | `get_analytics(days: 30)` | Detailed analytics for the specified period |
54
+ | `get_dashboard` | Full dashboard overview |
55
+ | `get_sla` | SLA compliance data |
56
+ | `export_audit_logs(format: 'json')` | Export audit logs as JSON or CSV |
57
+ | `get_webhook_deliveries(page: 1)` | Webhook delivery history |
58
+ | `batch_license_operation(op:, ids:)` | Batch suspend/activate/extend licenses |
59
+ | `get_ip_allowlist(license_id)` | Get IP allowlist for a license |
60
+ | `set_ip_allowlist(license_id, ips:)` | Set IP allowlist for a license |
61
+ | `health_check` | Check API health status |
62
+
63
+ ## Error Handling
64
+
65
+ ```ruby
66
+ begin
67
+ client.validate_license(token, domain: 'example.com')
68
+ rescue TrafficOrchestrator::ApiError => e
69
+ puts "#{e.code}: #{e.message} (HTTP #{e.status})"
70
+ rescue TrafficOrchestrator::NetworkError => e
71
+ puts "Network: #{e.message}"
72
+ end
73
+ ```
74
+
75
+ ## Rails Integration
76
+
77
+ ```ruby
78
+ # config/initializers/traffic_orchestrator.rb
79
+ TrafficOrchestrator.configure do |config|
80
+ config.api_key = Rails.application.credentials.dig(:traffic_orchestrator, :api_key)
81
+ config.timeout = 5
82
+ config.retries = 3
83
+ end
84
+
85
+ # In a controller
86
+ class LicensesController < ApplicationController
87
+ def validate
88
+ result = TrafficOrchestrator.client.validate_license(
89
+ params[:license_key],
90
+ domain: request.host
91
+ )
92
+ render json: { valid: result.valid, plan: result.plan_id }
93
+ end
94
+ end
95
+ ```
96
+
97
+ ## Multi-Environment Keys
98
+
99
+ ```ruby
100
+ # Development
101
+ dev_client = TrafficOrchestrator::Client.new(
102
+ api_key: ENV['TO_API_KEY_DEV'],
103
+ base_url: 'https://api-staging.trafficorchestrator.com'
104
+ )
105
+
106
+ # Production
107
+ prod_client = TrafficOrchestrator::Client.new(
108
+ api_key: ENV['TO_API_KEY']
109
+ )
110
+ ```
111
+
112
+ ## Offline Verification (Enterprise)
113
+
114
+ ```ruby
115
+ client = TrafficOrchestrator::Client.new(
116
+ public_key: ENV['TO_PUBLIC_KEY']
117
+ )
118
+ result = client.verify_offline(license_token)
119
+ puts "Plan: #{result.plan_id}" if result.valid
120
+ ```
121
+
122
+ ## Requirements
123
+
124
+ - Ruby 3.0+
125
+ - Faraday 2.x
126
+
127
+ ## Documentation
128
+
129
+ - [API Reference](https://trafficorchestrator.com/docs)
130
+ - [Ruby SDK Guide](https://trafficorchestrator.com/docs/sdk/ruby)
131
+ - [OpenAPI Spec](https://api.trafficorchestrator.com/api/v1/openapi.json)
132
+
133
+ ## License
134
+
135
+ MIT
@@ -0,0 +1,227 @@
1
+ require 'net/http'
2
+ require 'json'
3
+ require 'uri'
4
+
5
+ module TrafficOrchestrator
6
+ VERSION = '2.0.0'
7
+
8
+ class Client
9
+ attr_reader :base_url, :api_key, :timeout, :retries
10
+
11
+ def initialize(base_url = 'https://api.trafficorchestrator.com/api/v1', api_key: nil, timeout: 10, retries: 2)
12
+ @base_url = base_url.chomp('/')
13
+ @api_key = api_key
14
+ @timeout = timeout
15
+ @retries = retries
16
+ end
17
+
18
+ # ── Core: License Validation ──────────────────────────────────────────
19
+
20
+ # Validate a license key against the API server.
21
+ def validate_license(token, domain = nil)
22
+ request('POST', '/validate', { token: token, domain: domain })
23
+ end
24
+
25
+ # Verify offline using Ed25519.
26
+ # Requires 'ed25519' and 'base64' gems.
27
+ def verify_offline(token, public_key_base64, domain = nil)
28
+ require 'ed25519'
29
+ require 'base64'
30
+
31
+ parts = token.split('.')
32
+ return { 'valid' => false, 'error' => 'Invalid token format' } unless parts.length == 3
33
+
34
+ header = JSON.parse(Base64.urlsafe_decode64(parts[0]))
35
+ payload = JSON.parse(Base64.urlsafe_decode64(parts[1]))
36
+ signature = Base64.urlsafe_decode64(parts[2])
37
+
38
+ return { 'valid' => false, 'error' => 'Algorithm not supported' } unless header['alg'] == 'EdDSA'
39
+
40
+ # Verify Signature
41
+ public_key_bytes = Base64.strict_decode64(public_key_base64)
42
+ verify_key = Ed25519::VerifyKey.new(public_key_bytes)
43
+
44
+ message = "#{parts[0]}.#{parts[1]}"
45
+
46
+ begin
47
+ verify_key.verify(signature, message)
48
+ rescue Ed25519::VerifyError
49
+ return { 'valid' => false, 'error' => 'Invalid signature' }
50
+ end
51
+
52
+ # Verify Expiration
53
+ if payload['exp'] && payload['exp'] < Time.now.to_i
54
+ return { 'valid' => false, 'error' => 'Token expired' }
55
+ end
56
+
57
+ # Verify Domain
58
+ if domain && payload['dom'].is_a?(Array)
59
+ match = payload['dom'].any? { |d| domain.include?(d) }
60
+ return { 'valid' => false, 'error' => 'Domain mismatch' } unless match
61
+ end
62
+
63
+ { 'valid' => true, 'payload' => payload }
64
+ rescue => e
65
+ { 'valid' => false, 'error' => e.message }
66
+ end
67
+
68
+ # ── License Management (requires API key) ────────────────────────────
69
+
70
+ # List all licenses for the authenticated user.
71
+ def list_licenses
72
+ data = request('GET', '/portal/licenses')
73
+ data['licenses'] || []
74
+ end
75
+
76
+ # Create a new license.
77
+ def create_license(app_name, domain: nil, plan_id: nil)
78
+ body = { appName: app_name }
79
+ body[:domain] = domain if domain
80
+ body[:planId] = plan_id if plan_id
81
+ request('POST', '/portal/licenses', body)
82
+ end
83
+
84
+ # Rotate a license key (revoke old, generate new).
85
+ def rotate_license(license_id)
86
+ request('POST', "/portal/licenses/#{license_id}/rotate")
87
+ end
88
+
89
+ # Add a domain to a license.
90
+ def add_domain(license_id, domain)
91
+ request('POST', "/portal/licenses/#{license_id}/domains", { domain: domain })
92
+ end
93
+
94
+ # Remove a domain from a license.
95
+ def remove_domain(license_id, domain)
96
+ request('DELETE', "/portal/licenses/#{license_id}/domains", { domain: domain })
97
+ end
98
+
99
+ # Delete (revoke) a license.
100
+ def delete_license(license_id)
101
+ request('DELETE', "/portal/licenses/#{license_id}")
102
+ end
103
+
104
+ # ── Usage & Analytics ────────────────────────────────────────────────
105
+
106
+ # Get current usage statistics.
107
+ def get_usage
108
+ request('GET', '/portal/stats')
109
+ end
110
+
111
+ # ── Health ───────────────────────────────────────────────────────────
112
+
113
+ # Check API health status.
114
+ def health_check
115
+ request('GET', '/health')
116
+ end
117
+
118
+ # ── Analytics & SLA ──────────────────────────────────────────────────
119
+
120
+ # Get detailed analytics for the specified number of days.
121
+ def get_analytics(days: 30)
122
+ request('GET', "/portal/analytics?days=#{days}")
123
+ end
124
+
125
+ # Get a full dashboard overview.
126
+ def get_dashboard
127
+ request('GET', '/portal/dashboard')
128
+ end
129
+
130
+ # Get SLA compliance data.
131
+ def get_sla(days: 30)
132
+ request('GET', "/portal/sla?days=#{days}")
133
+ end
134
+
135
+ # ── Audit & Webhooks ─────────────────────────────────────────────────
136
+
137
+ # Export audit logs in the specified format.
138
+ def export_audit_logs(format: 'json', since: nil)
139
+ path = "/portal/audit-logs/export?format=#{format}"
140
+ path += "&since=#{since}" if since
141
+ request('GET', path)
142
+ end
143
+
144
+ # Get webhook delivery history.
145
+ def get_webhook_deliveries(limit: 50, status: nil)
146
+ path = "/portal/webhooks/deliveries?limit=#{limit}"
147
+ path += "&status=#{status}" if status
148
+ request('GET', path)
149
+ end
150
+
151
+ # ── Batch Operations ─────────────────────────────────────────────────
152
+
153
+ # Perform a batch operation on multiple licenses.
154
+ def batch_license_operation(action, license_ids, days: nil)
155
+ body = { action: action, licenseIds: license_ids }
156
+ body[:days] = days if days
157
+ request('POST', '/portal/licenses/batch', body)
158
+ end
159
+
160
+ # ── IP Allowlist ─────────────────────────────────────────────────────
161
+
162
+ # Get the IP allowlist for a license.
163
+ def get_ip_allowlist(license_id)
164
+ request('GET', "/portal/licenses/#{license_id}/ip-allowlist")
165
+ end
166
+
167
+ # Set the IP allowlist for a license.
168
+ def set_ip_allowlist(license_id, allowed_ips)
169
+ request('PUT', "/portal/licenses/#{license_id}/ip-allowlist", { allowedIps: allowed_ips })
170
+ end
171
+
172
+ private
173
+
174
+ # ── Internal ─────────────────────────────────────────────────────────
175
+
176
+ def request(method, path, body = nil)
177
+ uri = URI.parse("#{@base_url}#{path}")
178
+ last_error = nil
179
+
180
+ (0..@retries).each do |attempt|
181
+ begin
182
+ http = Net::HTTP.new(uri.host, uri.port)
183
+ http.use_ssl = true if uri.scheme == 'https'
184
+ http.open_timeout = @timeout
185
+ http.read_timeout = @timeout
186
+
187
+ headers = {
188
+ 'Content-Type' => 'application/json',
189
+ 'User-Agent' => "TrafficOrchestrator-Ruby/#{VERSION}"
190
+ }
191
+ headers['Authorization'] = "Bearer #{@api_key}" if @api_key
192
+
193
+ case method
194
+ when 'POST'
195
+ req = Net::HTTP::Post.new(uri.request_uri, headers)
196
+ req.body = body.to_json if body
197
+ when 'PUT'
198
+ req = Net::HTTP::Put.new(uri.request_uri, headers)
199
+ req.body = body.to_json if body
200
+ when 'DELETE'
201
+ req = Net::HTTP::Delete.new(uri.request_uri, headers)
202
+ req.body = body.to_json if body
203
+ when 'GET'
204
+ req = Net::HTTP::Get.new(uri.request_uri, headers)
205
+ end
206
+
207
+ response = http.request(req)
208
+ data = JSON.parse(response.body)
209
+
210
+ code = response.code.to_i
211
+ return data if code >= 200 && code < 300
212
+ return data if code >= 400 && code < 500
213
+
214
+ last_error = "HTTP #{code}"
215
+ rescue => e
216
+ last_error = e.message
217
+ end
218
+
219
+ if attempt < @retries
220
+ sleep([1.0 * (2 ** attempt), 5.0].min)
221
+ end
222
+ end
223
+
224
+ { 'valid' => false, 'error' => last_error || 'Request failed' }
225
+ end
226
+ end
227
+ end
metadata ADDED
@@ -0,0 +1,88 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: traffic_orchestrator
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Traffic Orchestrator
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2026-03-27 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: ed25519
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.3'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: jwt
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '2.7'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '2.7'
41
+ - !ruby/object:Gem::Dependency
42
+ name: faraday
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '2.7'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '2.7'
55
+ description: Ruby SDK for Traffic Orchestrator license validation with offline verification
56
+ support
57
+ email:
58
+ - contact@trafficorchestrator.com
59
+ executables: []
60
+ extensions: []
61
+ extra_rdoc_files: []
62
+ files:
63
+ - README.md
64
+ - lib/traffic_orchestrator.rb
65
+ homepage: https://trafficorchestrator.com
66
+ licenses:
67
+ - MIT
68
+ metadata: {}
69
+ post_install_message:
70
+ rdoc_options: []
71
+ require_paths:
72
+ - lib
73
+ required_ruby_version: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ version: 2.7.0
78
+ required_rubygems_version: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ requirements: []
84
+ rubygems_version: 3.4.19
85
+ signing_key:
86
+ specification_version: 4
87
+ summary: Traffic Orchestrator License Validation SDK
88
+ test_files: []