vpn-sdk 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.
- checksums.yaml +7 -0
- data/bin/vpn-sdk +235 -0
- data/lib/vpn_sdk.rb +81 -0
- metadata +43 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 53f60eddcb559c61baf0a67f2b7011a52d720bf8cb708bd8a342790db892011e
|
|
4
|
+
data.tar.gz: c62f2751492090d7a3e791c94b82724e09ee2986efef21b9f19ea6dc3bdcf475
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: b602b55b4ab8f28799a3fe3cfb54b2d8fb515cbb9fc15d1856050623639ccc3e9e630685be231eedf2f19302adeb93218699692f988f081c11bc3bc666b8e5e9
|
|
7
|
+
data.tar.gz: a1b4223a76959b160ef285f3df78e3ad2e38525ec96864349170a1af6e3f1d37bd3324d1a855a44830561ca30d79dc6e42d50ea7f9216719d56288c0cc292fb1
|
data/bin/vpn-sdk
ADDED
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
require 'io/console'
|
|
4
|
+
require 'json'
|
|
5
|
+
require 'digest'
|
|
6
|
+
require_relative '../lib/vpn_sdk'
|
|
7
|
+
|
|
8
|
+
class AuthManager
|
|
9
|
+
AUTH_FILE = File.join(Dir.home, '.vpn_sdk_auth.json')
|
|
10
|
+
MAX_ATTEMPTS = 3
|
|
11
|
+
LOCKOUT_TIME = 30
|
|
12
|
+
|
|
13
|
+
def self.load_data
|
|
14
|
+
return { 'pin_hash' => nil, 'attempts' => 0, 'lockout_until' => 0, 'session_active' => false } unless File.exist?(AUTH_FILE)
|
|
15
|
+
|
|
16
|
+
JSON.parse(File.read(AUTH_FILE))
|
|
17
|
+
rescue StandardError
|
|
18
|
+
{ 'pin_hash' => nil, 'attempts' => 0, 'lockout_until' => 0, 'session_active' => false }
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def self.save_data(data)
|
|
22
|
+
File.write(AUTH_FILE, JSON.pretty_generate(data))
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def self.hash_pin(pin)
|
|
26
|
+
Digest::SHA256.hexdigest(pin)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def self.check_lockout!
|
|
30
|
+
data = load_data
|
|
31
|
+
now = Time.now.to_i
|
|
32
|
+
|
|
33
|
+
if data['lockout_until'] > now
|
|
34
|
+
remaining = data['lockout_until'] - now
|
|
35
|
+
puts "\nā Access Denied: Too many failed PIN attempts."
|
|
36
|
+
puts "š System locked. Please wait #{remaining} seconds before trying again."
|
|
37
|
+
exit(1)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def self.authenticated?
|
|
42
|
+
data = load_data
|
|
43
|
+
data['session_active'] == true
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def self.register_pin(pin)
|
|
47
|
+
data = load_data
|
|
48
|
+
data['pin_hash'] = hash_pin(pin)
|
|
49
|
+
data['attempts'] = 0
|
|
50
|
+
data['lockout_until'] = 0
|
|
51
|
+
data['session_active'] = true
|
|
52
|
+
save_data(data)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def self.verify_pin(pin)
|
|
56
|
+
check_lockout!
|
|
57
|
+
data = load_data
|
|
58
|
+
|
|
59
|
+
if data['pin_hash'].nil?
|
|
60
|
+
puts "\nā Access Denied: No PIN registered. Run 'auth register' first."
|
|
61
|
+
exit(1)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
if hash_pin(pin) == data['pin_hash']
|
|
65
|
+
data['attempts'] = 0
|
|
66
|
+
data['session_active'] = true
|
|
67
|
+
save_data(data)
|
|
68
|
+
true
|
|
69
|
+
else
|
|
70
|
+
data['attempts'] += 1
|
|
71
|
+
if data['attempts'] >= MAX_ATTEMPTS
|
|
72
|
+
data['lockout_until'] = Time.now.to_i + LOCKOUT_TIME
|
|
73
|
+
data['attempts'] = 0
|
|
74
|
+
data['session_active'] = false
|
|
75
|
+
save_data(data)
|
|
76
|
+
puts "\nā Access Denied: Incorrect PIN."
|
|
77
|
+
puts "š Account locked due to #{MAX_ATTEMPTS} failed attempts. Try again in #{LOCKOUT_TIME}s."
|
|
78
|
+
else
|
|
79
|
+
remaining_attempts = MAX_ATTEMPTS - data['attempts']
|
|
80
|
+
save_data(data)
|
|
81
|
+
puts "\nā Access Denied: Incorrect PIN. Attempts remaining: #{remaining_attempts}"
|
|
82
|
+
end
|
|
83
|
+
false
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def self.logout
|
|
88
|
+
data = load_data
|
|
89
|
+
data['session_active'] = false
|
|
90
|
+
save_data(data)
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
client = VpnSdk::Client.new
|
|
95
|
+
|
|
96
|
+
def ask_password(prompt)
|
|
97
|
+
print prompt
|
|
98
|
+
$stdin.noecho(&:gets).chomp.tap { puts }
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def ask_question(prompt)
|
|
102
|
+
print prompt
|
|
103
|
+
$stdin.gets.chomp
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def require_authentication!
|
|
107
|
+
AuthManager.check_lockout!
|
|
108
|
+
unless AuthManager.authenticated?
|
|
109
|
+
puts "\nā Access Denied: PIN session expired or locked."
|
|
110
|
+
puts "Please authenticate using 'auth login' first."
|
|
111
|
+
exit(1)
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
command = ARGV[0]
|
|
116
|
+
subcommand = ARGV[1]
|
|
117
|
+
option = ARGV[2]
|
|
118
|
+
|
|
119
|
+
case command
|
|
120
|
+
when 'health'
|
|
121
|
+
require_authentication!
|
|
122
|
+
res = client.check_health
|
|
123
|
+
puts "\nHealth Check Response:"
|
|
124
|
+
puts JSON.pretty_generate(res)
|
|
125
|
+
|
|
126
|
+
when 'countries'
|
|
127
|
+
require_authentication!
|
|
128
|
+
countries = client.countries
|
|
129
|
+
puts "\nSupported Countries:"
|
|
130
|
+
puts '-' * 50
|
|
131
|
+
if countries.is_a?(Array)
|
|
132
|
+
countries.each do |c|
|
|
133
|
+
flag = c['flag'] || 'š'
|
|
134
|
+
name = c['name'] || c['code']
|
|
135
|
+
puts "#{flag} #{name} (#{c['code']}) - #{c['serverCount']} Servers [#{c['latency']}ms]"
|
|
136
|
+
end
|
|
137
|
+
else
|
|
138
|
+
puts JSON.pretty_generate(countries)
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
when 'servers'
|
|
142
|
+
require_authentication!
|
|
143
|
+
country_code = option || ARGV[3]
|
|
144
|
+
if country_code.nil? || country_code.empty?
|
|
145
|
+
country_code = ask_question('Enter country code (optional, press Enter for all): ')
|
|
146
|
+
end
|
|
147
|
+
res = client.servers(country_code.strip)
|
|
148
|
+
puts "\nServers Response:"
|
|
149
|
+
puts JSON.pretty_generate(res)
|
|
150
|
+
|
|
151
|
+
when 'status'
|
|
152
|
+
require_authentication!
|
|
153
|
+
res = client.status
|
|
154
|
+
puts "\nVPN Status:"
|
|
155
|
+
puts JSON.pretty_generate(res)
|
|
156
|
+
|
|
157
|
+
when 'create'
|
|
158
|
+
require_authentication!
|
|
159
|
+
country_code = option || ARGV[3]
|
|
160
|
+
res = client.create_session(country_code)
|
|
161
|
+
puts "\nCreate Session Result:"
|
|
162
|
+
puts JSON.pretty_generate(res)
|
|
163
|
+
|
|
164
|
+
when 'connect'
|
|
165
|
+
require_authentication!
|
|
166
|
+
country_code = option || ARGV[3]
|
|
167
|
+
if country_code.nil? || country_code.empty?
|
|
168
|
+
countries = client.countries
|
|
169
|
+
puts "\nSupported Countries:"
|
|
170
|
+
countries.each { |c| puts " - [#{c['code']}] #{c['flag']} #{c['name']}" } if countries.is_a?(Array)
|
|
171
|
+
country_code = ask_question("\nEnter country code to connect: ")
|
|
172
|
+
end
|
|
173
|
+
res = client.connect(country_code)
|
|
174
|
+
puts "\nConnecting to [#{country_code}] Response:"
|
|
175
|
+
puts JSON.pretty_generate(res)
|
|
176
|
+
|
|
177
|
+
when 'disconnect'
|
|
178
|
+
require_authentication!
|
|
179
|
+
res = client.disconnect
|
|
180
|
+
puts "\nDisconnect Response:"
|
|
181
|
+
puts JSON.pretty_generate(res)
|
|
182
|
+
|
|
183
|
+
when 'auth'
|
|
184
|
+
case subcommand
|
|
185
|
+
when 'status'
|
|
186
|
+
data = AuthManager.load_data
|
|
187
|
+
now = Time.now.to_i
|
|
188
|
+
puts "\nAuth Lock System Status:"
|
|
189
|
+
puts '-' * 25
|
|
190
|
+
if data['lockout_until'] > now
|
|
191
|
+
puts "Status: š LOCKED (Wait #{data['lockout_until'] - now}s)"
|
|
192
|
+
elsif AuthManager.authenticated?
|
|
193
|
+
puts "Status: ā
Authenticated"
|
|
194
|
+
else
|
|
195
|
+
puts "Status: ā Unauthenticated / Locked"
|
|
196
|
+
end
|
|
197
|
+
puts "Failed Attempts: #{data['attempts']}/#{AuthManager::MAX_ATTEMPTS}"
|
|
198
|
+
|
|
199
|
+
when 'login'
|
|
200
|
+
pin = ask_password('Enter PIN: ')
|
|
201
|
+
if AuthManager.verify_pin(pin)
|
|
202
|
+
puts 'ā
PIN authentication successful.'
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
when 'register'
|
|
206
|
+
new_pin = ask_password('Enter new PIN: ')
|
|
207
|
+
confirm_pin = ask_password('Confirm new PIN: ')
|
|
208
|
+
if !new_pin.empty? && new_pin == confirm_pin
|
|
209
|
+
AuthManager.register_pin(new_pin)
|
|
210
|
+
puts 'ā
New PIN registered and session authenticated successfully.'
|
|
211
|
+
else
|
|
212
|
+
$stderr.puts 'ā Error: PINs do not match or are empty!'
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
when 'logout'
|
|
216
|
+
AuthManager.logout
|
|
217
|
+
puts 'ā
Logged out successfully.'
|
|
218
|
+
|
|
219
|
+
else
|
|
220
|
+
puts "Unknown auth command. Available: status, login, register, logout"
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
else
|
|
224
|
+
puts "VPN Gateway CLI & SDK (Ruby)"
|
|
225
|
+
puts "Usage: vpn-sdk <command> [options]"
|
|
226
|
+
puts "\nCommands:"
|
|
227
|
+
puts " health"
|
|
228
|
+
puts " countries"
|
|
229
|
+
puts " servers"
|
|
230
|
+
puts " status"
|
|
231
|
+
puts " create"
|
|
232
|
+
puts " connect"
|
|
233
|
+
puts " disconnect"
|
|
234
|
+
puts " auth status | auth login | auth register | auth logout"
|
|
235
|
+
end
|
data/lib/vpn_sdk.rb
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
require 'net/http'
|
|
2
|
+
require 'json'
|
|
3
|
+
require 'uri'
|
|
4
|
+
|
|
5
|
+
module VpnSdk
|
|
6
|
+
class Client
|
|
7
|
+
BASE_URL = 'https://vpn-gateway-deploy--bxhxbb352.replit.app'.freeze
|
|
8
|
+
|
|
9
|
+
attr_accessor :base_url, :timeout
|
|
10
|
+
|
|
11
|
+
def initialize(base_url = BASE_URL, timeout: 10)
|
|
12
|
+
@base_url = base_url
|
|
13
|
+
@timeout = timeout
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def check_health
|
|
17
|
+
get('/api/healthz')
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def countries
|
|
21
|
+
get('/api/vpn/countries')
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def servers(country_code = nil)
|
|
25
|
+
path = if country_code && !country_code.to_s.empty?
|
|
26
|
+
"/api/vpn/servers?countryCode=#{URI.encode_www_form_component(country_code)}"
|
|
27
|
+
else
|
|
28
|
+
'/api/vpn/servers'
|
|
29
|
+
end
|
|
30
|
+
get(path)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def status
|
|
34
|
+
get('/api/vpn/status')
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def create_session(country_code = nil)
|
|
38
|
+
payload = country_code ? { countryCode: country_code } : {}
|
|
39
|
+
post('/api/vpn/create', payload)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def connect(country_code)
|
|
43
|
+
post('/api/vpn/connect', { countryCode: country_code })
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def disconnect
|
|
47
|
+
post('/api/vpn/disconnect', {})
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
private
|
|
51
|
+
|
|
52
|
+
def get(path)
|
|
53
|
+
uri = URI.parse("#{@base_url}#{path}")
|
|
54
|
+
req = Net::HTTP::Get.new(uri)
|
|
55
|
+
req['Content-Type'] = 'application/json'
|
|
56
|
+
perform_request(uri, req)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def post(path, payload)
|
|
60
|
+
uri = URI.parse("#{@base_url}#{path}")
|
|
61
|
+
req = Net::HTTP::Post.new(uri)
|
|
62
|
+
req['Content-Type'] = 'application/json'
|
|
63
|
+
req.body = payload.to_json
|
|
64
|
+
perform_request(uri, req)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def perform_request(uri, request)
|
|
68
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
69
|
+
http.use_ssl = (uri.scheme == 'https')
|
|
70
|
+
http.open_timeout = @timeout
|
|
71
|
+
http.read_timeout = @timeout
|
|
72
|
+
|
|
73
|
+
response = http.request(request)
|
|
74
|
+
JSON.parse(response.body)
|
|
75
|
+
rescue JSON::ParserError
|
|
76
|
+
{ 'status' => response.code, 'body' => response.body }
|
|
77
|
+
rescue StandardError => e
|
|
78
|
+
{ 'error' => e.message }
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: vpn-sdk
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Maintainer
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 2026-08-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies: []
|
|
12
|
+
description: A lightweight Ruby wrapper and CLI executable for the VPN Gateway backend
|
|
13
|
+
API with PIN authentication.
|
|
14
|
+
email:
|
|
15
|
+
- dodi66412@gmail.com
|
|
16
|
+
executables:
|
|
17
|
+
- vpn-sdk
|
|
18
|
+
extensions: []
|
|
19
|
+
extra_rdoc_files: []
|
|
20
|
+
files:
|
|
21
|
+
- bin/vpn-sdk
|
|
22
|
+
- lib/vpn_sdk.rb
|
|
23
|
+
licenses:
|
|
24
|
+
- MIT
|
|
25
|
+
metadata: {}
|
|
26
|
+
rdoc_options: []
|
|
27
|
+
require_paths:
|
|
28
|
+
- lib
|
|
29
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - ">="
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: 2.6.0
|
|
34
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
35
|
+
requirements:
|
|
36
|
+
- - ">="
|
|
37
|
+
- !ruby/object:Gem::Version
|
|
38
|
+
version: '0'
|
|
39
|
+
requirements: []
|
|
40
|
+
rubygems_version: 3.6.2
|
|
41
|
+
specification_version: 4
|
|
42
|
+
summary: Ruby SDK and CLI tool for VPN Gateway API
|
|
43
|
+
test_files: []
|