Class: InfraForge::Client

Inherits:
Object
  • Object
show all
Defined in:
lib/infraforge.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(url:, project_slug:, api_key:, timeout: 30) ⇒ Client

Returns a new instance of Client.



36
37
38
39
40
41
42
# File 'lib/infraforge.rb', line 36

def initialize(url:, project_slug:, api_key:, timeout: 30)
  @url = url.chomp("/")
  @project_slug = project_slug
  @api_key = api_key
  @timeout = timeout
  @session = nil
end

Instance Attribute Details

#sessionObject (readonly)

Returns the value of attribute session.



34
35
36
# File 'lib/infraforge.rb', line 34

def session
  @session
end

Instance Method Details

#get_userObject

Fetch current user from server.

Raises:



96
97
98
99
100
101
102
103
104
105
106
107
108
109
# File 'lib/infraforge.rb', line 96

def get_user
  raise AuthError, "Not authenticated" if @session.nil?

  status, data = request(:get, auth_url("/me"),
    headers: { "Authorization" => "Bearer #{@session.token}" })
  if status >= 400
    @session = nil if status == 401
    raise AuthError, error_msg(data, status)
  end

  user = user_from_hash(data.fetch("user"))
  @session = Session.new(user: user, token: @session.token)
  user
end

#query(sql, params = []) ⇒ Object

Run a SQL query — RLS enforced via current JWT.

Raises:



132
133
134
135
136
137
138
139
140
141
142
143
144
# File 'lib/infraforge.rb', line 132

def query(sql, params = [])
  raise QueryError, "Not authenticated. Call sign_in first." if @session.nil?

  status, data = request(:post, "#{@url}/api/query/#{@project_slug}",
    headers: {
      "x-infraforge-key" => @api_key,
      "Authorization" => "Bearer #{@session.token}",
    },
    body: { query: sql, params: params })
  raise QueryError, error_msg(data, status) if status >= 400

  data["data"] || []
end

#request_password_reset(email) ⇒ Object

Request password reset email (silent, no enumeration).

Raises:



112
113
114
115
116
117
# File 'lib/infraforge.rb', line 112

def request_password_reset(email)
  status, data = request(:post, auth_url("/forgot-password"), body: { email: email })
  raise AuthError, error_msg(data, status) if status >= 400

  true
end

#set_token(token) ⇒ Object

Manually set a JWT (e.g., received from external OAuth callback).



49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# File 'lib/infraforge.rb', line 49

def set_token(token)
  parts = token.split(".")
  user = User.new(id: "", email: "", role: "authenticated")
  if parts.size == 3
    begin
      payload = JSON.parse(base64url_decode(parts[1]))
      user = User.new(
        id: payload["sub"].to_s,
        email: payload["email"].to_s,
        role: (payload["role"] || "authenticated").to_s,
      )
    rescue StandardError
      # leave default user
    end
  end
  @session = Session.new(user: user, token: token)
end

#sign_in(email:, password:) ⇒ Object

Sign in. Stores session in memory.

Raises:



83
84
85
86
87
88
89
90
91
92
93
# File 'lib/infraforge.rb', line 83

def (email:, password:)
  status, data = request(:post, auth_url("/login"), body: { email: email, password: password })
  if status >= 400
    pending = status == 403
    raise AuthError.new(error_msg(data, status), pending: pending)
  end

  raise AuthError, "Malformed login response" unless data["user"] && data["token"]

  @session = Session.new(user: user_from_hash(data["user"]), token: data["token"])
end

#sign_outObject

Clear the in-memory session.



120
121
122
# File 'lib/infraforge.rb', line 120

def sign_out
  @session = nil
end

#sign_up(email:, password:, name: nil) ⇒ Object

Register a new user. Returns { user:, pending:, message: }. If admin approval is required, status is 202 and pending is true.

Raises:



69
70
71
72
73
74
75
76
77
78
79
80
# File 'lib/infraforge.rb', line 69

def (email:, password:, name: nil)
  body = { email: email, password: password }
  body[:name] = name unless name.nil?
  status, data = request(:post, auth_url("/register"), body: body)
  raise AuthError, error_msg(data, status) if status >= 400

  {
    user: data["user"] ? user_from_hash(data["user"]) : nil,
    pending: status == 202,
    message: data["message"],
  }
end

#social_login_url(provider:, redirect_to:) ⇒ Object

Build the OAuth start URL for a browser flow.

Raises:

  • (ArgumentError)


125
126
127
128
129
# File 'lib/infraforge.rb', line 125

def (provider:, redirect_to:)
  raise ArgumentError, "provider must be 'google' or 'github'" unless %w[google github].include?(provider)

  "#{auth_url("/oauth/#{provider}/start")}?redirectTo=#{URI.encode_www_form_component(redirect_to)}"
end

#tokenObject



44
45
46
# File 'lib/infraforge.rb', line 44

def token
  @session&.token
end