synor/sdk/ruby/lib/synor_hosting/client.rb
Gulshan Yadav a874faef13 feat: complete Phase 3 SDKs for Swift, C, C++, C#, and Ruby
Implements Database, Hosting, and Bridge SDKs for remaining languages:

Swift SDKs:
- SynorDatabase with KV, Document, Vector, TimeSeries stores
- SynorHosting with domain, DNS, deployment, SSL operations
- SynorBridge with lock-mint and burn-unlock cross-chain flows

C SDKs:
- database.h/c - multi-model database client
- hosting.h/c - hosting and domain management
- bridge.h/c - cross-chain asset transfers

C++ SDKs:
- database.hpp - modern C++17 with std::future async
- hosting.hpp - domain and deployment operations
- bridge.hpp - cross-chain bridge with wait operations

C# SDKs:
- SynorDatabase.cs - async/await with inner store classes
- SynorHosting.cs - domain management and analytics
- SynorBridge.cs - cross-chain with BridgeException handling

Ruby SDKs:
- synor_database - Struct-based types with Faraday HTTP
- synor_hosting - domain, DNS, SSL, analytics
- synor_bridge - lock-mint/burn-unlock with retry logic

Phase 3 complete: Database/Hosting/Bridge now available in all 12 languages.
2026-01-27 02:23:07 +05:30

357 lines
10 KiB
Ruby

# frozen_string_literal: true
require "faraday"
require "json"
require "uri"
module SynorHosting
# Synor Hosting SDK client for Ruby.
# Provides domain management, DNS, deployments, SSL, and analytics.
class Client
attr_reader :closed
def initialize(config)
@config = config
@closed = false
@conn = Faraday.new(url: config.endpoint) do |f|
f.request :json
f.response :json
f.options.timeout = config.timeout
f.headers["Authorization"] = "Bearer #{config.api_key}"
f.headers["Content-Type"] = "application/json"
f.headers["X-SDK-Version"] = "ruby/#{VERSION}"
end
end
# ==================== Domain Operations ====================
# Register a new domain
def register_domain(name, auto_renew: true)
body = { name: name, auto_renew: auto_renew }
response = post("/domains", body)
parse_domain(response)
end
# Get domain details
def get_domain(name)
response = get("/domains/#{encode(name)}")
parse_domain(response)
end
# Resolve domain to its record
def resolve_domain(name)
response = get("/domains/#{encode(name)}/resolve")
DomainRecord.new(
cid: response["cid"],
ttl: response["ttl"],
updated_at: response["updated_at"]
)
end
# Update domain record
def update_domain(name, cid:, ttl: nil)
body = { cid: cid }
body[:ttl] = ttl if ttl
response = put("/domains/#{encode(name)}", body)
parse_domain(response)
end
# Transfer domain ownership
def transfer_domain(name, new_owner)
body = { new_owner: new_owner }
response = post("/domains/#{encode(name)}/transfer", body)
parse_domain(response)
end
# List domains
def list_domains(limit: nil, offset: nil)
params = {}
params[:limit] = limit if limit
params[:offset] = offset if offset
response = get("/domains", params)
(response["domains"] || []).map { |d| parse_domain(d) }
end
# ==================== DNS Operations ====================
# Set DNS records for a domain
def set_dns_records(domain, records)
body = { records: records.map { |r| record_to_hash(r) } }
response = put("/domains/#{encode(domain)}/dns", body)
(response["records"] || []).map { |r| parse_dns_record(r) }
end
# Get DNS records for a domain
def get_dns_records(domain)
response = get("/domains/#{encode(domain)}/dns")
(response["records"] || []).map { |r| parse_dns_record(r) }
end
# Add a single DNS record
def add_dns_record(domain, record)
body = record_to_hash(record)
response = post("/domains/#{encode(domain)}/dns", body)
parse_dns_record(response)
end
# Delete a DNS record
def delete_dns_record(domain, record_type, name)
delete("/domains/#{encode(domain)}/dns/#{record_type}/#{encode(name)}")
nil
end
# ==================== Deployment Operations ====================
# Deploy content to a domain
def deploy(cid, domain, options = {})
body = { cid: cid, domain: domain }
body[:build_command] = options[:build_command] if options[:build_command]
body[:env_vars] = options[:env_vars] if options[:env_vars]
response = post("/deployments", body)
parse_deployment(response)
end
# Get deployment details
def get_deployment(id)
response = get("/deployments/#{encode(id)}")
parse_deployment(response)
end
# List deployments
def list_deployments(domain: nil, limit: nil, offset: nil)
params = {}
params[:domain] = domain if domain
params[:limit] = limit if limit
params[:offset] = offset if offset
response = get("/deployments", params)
(response["deployments"] || []).map { |d| parse_deployment(d) }
end
# Wait for deployment to complete
def wait_for_deployment(id, poll_interval: 5, max_wait: 600)
deadline = Time.now + max_wait
final_statuses = [DeploymentStatus::ACTIVE, DeploymentStatus::FAILED, DeploymentStatus::ROLLED_BACK]
while Time.now < deadline
deployment = get_deployment(id)
return deployment if final_statuses.include?(deployment.status)
sleep(poll_interval)
end
raise Error, "Timeout waiting for deployment"
end
# Rollback deployment
def rollback(domain, deployment_id)
body = { deployment_id: deployment_id }
response = post("/domains/#{encode(domain)}/rollback", body)
parse_deployment(response)
end
# ==================== SSL Operations ====================
# Provision SSL certificate for a domain
def provision_ssl(domain)
response = post("/domains/#{encode(domain)}/ssl", {})
parse_certificate(response)
end
# Get SSL certificate for a domain
def get_certificate(domain)
response = get("/domains/#{encode(domain)}/ssl")
parse_certificate(response)
end
# Renew SSL certificate
def renew_certificate(domain)
response = post("/domains/#{encode(domain)}/ssl/renew", {})
parse_certificate(response)
end
# ==================== Analytics Operations ====================
# Get analytics for a domain
def get_analytics(domain, start_time:, end_time:)
params = { start: start_time.to_i, end: end_time.to_i }
response = get("/domains/#{encode(domain)}/analytics", params)
parse_analytics(response)
end
# Get bandwidth stats
def get_bandwidth(domain, start_time:, end_time:)
params = { start: start_time.to_i, end: end_time.to_i }
response = get("/domains/#{encode(domain)}/analytics/bandwidth", params)
BandwidthStats.new(
total_bytes: response["total_bytes"],
cached_bytes: response["cached_bytes"],
uncached_bytes: response["uncached_bytes"]
)
end
# ==================== Lifecycle ====================
# Health check
def health_check
response = get("/health")
response["status"] == "healthy"
rescue StandardError
false
end
# Close the client
def close
@closed = true
@conn.close if @conn.respond_to?(:close)
end
private
def get(path, params = {})
execute { @conn.get(path, params).body }
end
def post(path, body)
execute { @conn.post(path, body).body }
end
def put(path, body)
execute { @conn.put(path, body).body }
end
def delete(path)
execute { @conn.delete(path).body }
end
def execute
raise ClientClosedError, "Client has been closed" if @closed
last_error = nil
@config.retries.times do |attempt|
begin
response = yield
check_error(response) if response.is_a?(Hash)
return response
rescue StandardError => e
last_error = e
sleep(2**attempt) if attempt < @config.retries - 1
end
end
raise last_error
end
def check_error(response)
return unless response["error"] || response["code"]
message = response["message"] || response["error"] || "Unknown error"
code = response["code"]
status = response["status_code"] || 0
raise HttpError.new(message, status_code: status, code: code)
end
def encode(str)
URI.encode_www_form_component(str)
end
def record_to_hash(record)
hash = {
type: record.type,
name: record.name,
value: record.value,
ttl: record.ttl
}
hash[:priority] = record.priority if record.priority
hash
end
def parse_domain(data)
return nil unless data
Domain.new(
name: data["name"],
owner: data["owner"],
status: data["status"],
record: data["record"] ? DomainRecord.new(
cid: data["record"]["cid"],
ttl: data["record"]["ttl"],
updated_at: data["record"]["updated_at"]
) : nil,
expires_at: data["expires_at"],
created_at: data["created_at"],
auto_renew: data["auto_renew"]
)
end
def parse_dns_record(data)
return nil unless data
DnsRecord.new(
type: data["type"],
name: data["name"],
value: data["value"],
ttl: data["ttl"],
priority: data["priority"]
)
end
def parse_deployment(data)
return nil unless data
Deployment.new(
id: data["id"],
domain: data["domain"],
cid: data["cid"],
status: data["status"],
url: data["url"],
created_at: data["created_at"],
deployed_at: data["deployed_at"],
build_logs: data["build_logs"],
error_message: data["error_message"]
)
end
def parse_certificate(data)
return nil unless data
Certificate.new(
domain: data["domain"],
status: data["status"],
issuer: data["issuer"],
issued_at: data["issued_at"],
expires_at: data["expires_at"],
auto_renew: data["auto_renew"],
fingerprint: data["fingerprint"]
)
end
def parse_analytics(data)
return nil unless data
Analytics.new(
domain: data["domain"],
time_range: AnalyticsTimeRange.new(
start_time: data["time_range"]&.dig("start"),
end_time: data["time_range"]&.dig("end")
),
requests: data["requests"] ? RequestStats.new(
total: data["requests"]["total"],
success: data["requests"]["success"],
error: data["requests"]["error"],
cached: data["requests"]["cached"]
) : nil,
bandwidth: data["bandwidth"] ? BandwidthStats.new(
total_bytes: data["bandwidth"]["total_bytes"],
cached_bytes: data["bandwidth"]["cached_bytes"],
uncached_bytes: data["bandwidth"]["uncached_bytes"]
) : nil,
visitors: data["visitors"],
page_views: data["page_views"],
top_paths: (data["top_paths"] || []).map do |p|
PathStats.new(path: p["path"], requests: p["requests"], bandwidth: p["bandwidth"])
end,
geo_distribution: (data["geo_distribution"] || []).map do |g|
GeoStats.new(country: g["country"], requests: g["requests"], bandwidth: g["bandwidth"])
end
)
end
end
end