screenshotfreeapi 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.
@@ -0,0 +1,96 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ScreenshotFreeAPI
4
+ module Resources
5
+ # Team workspace management.
6
+ #
7
+ # Workspaces allow multiple users to share API keys and collaborate.
8
+ # All methods require a management JWT (from Auth#token) passed as `jwt:`.
9
+ #
10
+ # Member roles: "owner" | "admin" | "member" | "viewer"
11
+ class Workspaces
12
+ def initialize(http)
13
+ @http = http
14
+ end
15
+
16
+ # Create a new workspace.
17
+ #
18
+ # @param jwt [String] Management JWT
19
+ # @param name [String] Workspace display name
20
+ #
21
+ # @return [Hash] Workspace object
22
+ def create(jwt:, name:, **options)
23
+ body = { name: name }.merge(options)
24
+ @http.request(:post, "/workspaces", body: body, auth_header: "Bearer #{jwt}")
25
+ end
26
+
27
+ # List workspaces the authenticated user belongs to.
28
+ #
29
+ # @param jwt [String] Management JWT
30
+ #
31
+ # @return [Array<Hash>] Array of workspace objects
32
+ def list(jwt:)
33
+ @http.request(:get, "/workspaces", auth_header: "Bearer #{jwt}")
34
+ end
35
+
36
+ # Get a single workspace with its members list.
37
+ #
38
+ # @param jwt [String] Management JWT
39
+ # @param workspace_id [String] Workspace ID
40
+ #
41
+ # @return [Hash] Workspace object including "members" array
42
+ def get(jwt:, workspace_id:)
43
+ @http.request(:get, "/workspaces/#{workspace_id}", auth_header: "Bearer #{jwt}")
44
+ end
45
+
46
+ # Invite a user to the workspace by email address.
47
+ #
48
+ # @param jwt [String]
49
+ # @param workspace_id [String]
50
+ # @param email [String] Email of the person to invite
51
+ # @param role [String] "admin" | "member" | "viewer"
52
+ #
53
+ # @return [Hash] Invitation object
54
+ def invite(jwt:, workspace_id:, email:, role: "member")
55
+ @http.request(
56
+ :post,
57
+ "/workspaces/#{workspace_id}/invite",
58
+ body: { email: email, role: role },
59
+ auth_header: "Bearer #{jwt}"
60
+ )
61
+ end
62
+
63
+ # Remove a member from the workspace.
64
+ #
65
+ # @param jwt [String]
66
+ # @param workspace_id [String]
67
+ # @param user_id [String] ID of the member to remove
68
+ #
69
+ # @return [Hash] Confirmation object
70
+ def remove_member(jwt:, workspace_id:, user_id:)
71
+ @http.request(
72
+ :delete,
73
+ "/workspaces/#{workspace_id}/members/#{user_id}",
74
+ auth_header: "Bearer #{jwt}"
75
+ )
76
+ end
77
+
78
+ # Change a member's role within the workspace.
79
+ #
80
+ # @param jwt [String]
81
+ # @param workspace_id [String]
82
+ # @param user_id [String]
83
+ # @param role [String] New role: "admin" | "member" | "viewer"
84
+ #
85
+ # @return [Hash] Updated member object
86
+ def update_member(jwt:, workspace_id:, user_id:, role:)
87
+ @http.request(
88
+ :patch,
89
+ "/workspaces/#{workspace_id}/members/#{user_id}",
90
+ body: { role: role },
91
+ auth_header: "Bearer #{jwt}"
92
+ )
93
+ end
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ScreenshotFreeAPI
4
+ VERSION = "1.0.0"
5
+ end
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "openssl"
4
+
5
+ module ScreenshotFreeAPI
6
+ # Utilities for verifying incoming ScreenshotFreeAPI webhook signatures.
7
+ #
8
+ # The API signs webhook payloads with HMAC-SHA256 using your webhook secret.
9
+ # The signature is delivered in the `X-ScreenshotFree-Signature` header as a hex digest.
10
+ #
11
+ # Example (Rack / Rails):
12
+ #
13
+ # raw_body = request.body.read
14
+ # signature = request.headers["X-ScreenshotFree-Signature"]
15
+ #
16
+ # unless ScreenshotFreeAPI::Webhooks.verify_signature(raw_body, signature, ENV["WEBHOOK_SECRET"])
17
+ # render plain: "Bad signature", status: :forbidden and return
18
+ # end
19
+ #
20
+ module Webhooks
21
+ # Verify that a webhook payload was signed by the ScreenshotFreeAPI.
22
+ #
23
+ # Uses a constant-time comparison to prevent timing attacks.
24
+ #
25
+ # @param raw_body [String] Raw request body bytes (before any parsing)
26
+ # @param signature [String] Value of the `X-ScreenshotFree-Signature` header
27
+ # @param secret [String] Your webhook signing secret
28
+ #
29
+ # @return [Boolean] `true` if the signature is valid, `false` otherwise
30
+ def self.verify_signature(raw_body, signature, secret)
31
+ return false if raw_body.nil? || signature.nil? || secret.nil?
32
+ return false if signature.empty? || secret.empty?
33
+
34
+ expected = OpenSSL::HMAC.hexdigest("SHA256", secret.to_s, raw_body.to_s)
35
+ secure_compare(expected, signature.to_s)
36
+ end
37
+
38
+ # Parse a verified webhook payload into a plain hash.
39
+ #
40
+ # Raises ArgumentError if the signature is invalid.
41
+ #
42
+ # @param raw_body [String] Raw request body
43
+ # @param signature [String] X-ScreenshotFree-Signature header value
44
+ # @param secret [String] Webhook signing secret
45
+ #
46
+ # @return [Hash] Parsed event payload
47
+ def self.construct_event(raw_body, signature, secret)
48
+ unless verify_signature(raw_body, signature, secret)
49
+ raise ArgumentError, "Invalid webhook signature"
50
+ end
51
+
52
+ require "json"
53
+ JSON.parse(raw_body)
54
+ end
55
+
56
+ # Constant-time string comparison to mitigate timing side-channel attacks.
57
+ #
58
+ # XORs each byte of both strings and accumulates any differences.
59
+ # Returns true only when the accumulated diff is zero (all bytes equal)
60
+ # and both strings have the same length.
61
+ #
62
+ # @param a [String] First string (expected)
63
+ # @param b [String] Second string (provided)
64
+ # @return [Boolean]
65
+ def self.secure_compare(a, b)
66
+ return false unless a.bytesize == b.bytesize
67
+
68
+ l = a.unpack("C#{a.bytesize}")
69
+ r = b.unpack("C#{b.bytesize}")
70
+
71
+ diff = 0
72
+ l.zip(r) { |x, y| diff |= (x ^ y) }
73
+ diff.zero?
74
+ end
75
+
76
+ private_class_method :secure_compare
77
+ end
78
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ # screenshotfreeapi — Official Ruby SDK for ScreenshotFreeAPI
4
+ #
5
+ # ScreenshotFreeAPI is a screenshot-as-a-service platform. This SDK wraps the
6
+ # REST API and provides a clean Ruby interface for capturing web pages,
7
+ # mobile app store listings, and HTML strings.
8
+ #
9
+ # Quick start:
10
+ #
11
+ # require "screenshotfreeapi"
12
+ #
13
+ # client = ScreenshotFreeAPI.new(api_key: ENV["SCREENSHOTFREEAPI_KEY"])
14
+ # result = client.capture("https://example.com")
15
+ # puts result["screenshots"].first["url"]
16
+ #
17
+
18
+ require_relative "screenshotfreeapi/version"
19
+ require_relative "screenshotfreeapi/errors"
20
+ require_relative "screenshotfreeapi/http_client"
21
+ require_relative "screenshotfreeapi/webhooks"
22
+ require_relative "screenshotfreeapi/resources/auth"
23
+ require_relative "screenshotfreeapi/resources/jobs"
24
+ require_relative "screenshotfreeapi/resources/screenshots"
25
+ require_relative "screenshotfreeapi/resources/billing"
26
+ require_relative "screenshotfreeapi/resources/workspaces"
27
+ require_relative "screenshotfreeapi/resources/monitors"
28
+ require_relative "screenshotfreeapi/resources/integrations"
29
+ require_relative "screenshotfreeapi/client"
30
+
31
+ module ScreenshotFreeAPI
32
+ # Convenience constructor — creates a Client without needing to reference
33
+ # the full class path.
34
+ #
35
+ # @param api_key [String]
36
+ # @param base_url [String] (optional)
37
+ # @param timeout [Integer] (optional)
38
+ # @param max_retries [Integer] (optional)
39
+ #
40
+ # @return [ScreenshotFreeAPI::Client]
41
+ #
42
+ # @example
43
+ # client = ScreenshotFreeAPI.new(api_key: "sfa_...")
44
+ def self.new(api_key:, **opts)
45
+ Client.new(api_key: api_key, **opts)
46
+ end
47
+ end
metadata ADDED
@@ -0,0 +1,64 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: screenshotfreeapi
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - ScreenshotFreeAPI
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2026-06-20 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Ruby client for ScreenshotFreeAPI — screenshot-as-a-service. Capture
14
+ web pages, mobile app store listings, and HTML strings via a simple HTTP API. Supports
15
+ async job polling, webhook signature verification, and all billing/workspace endpoints.
16
+ email:
17
+ - support@screenshotfreeapi.com
18
+ executables: []
19
+ extensions: []
20
+ extra_rdoc_files: []
21
+ files:
22
+ - LICENSE
23
+ - README.md
24
+ - lib/screenshotfreeapi.rb
25
+ - lib/screenshotfreeapi/client.rb
26
+ - lib/screenshotfreeapi/errors.rb
27
+ - lib/screenshotfreeapi/http_client.rb
28
+ - lib/screenshotfreeapi/resources/auth.rb
29
+ - lib/screenshotfreeapi/resources/billing.rb
30
+ - lib/screenshotfreeapi/resources/integrations.rb
31
+ - lib/screenshotfreeapi/resources/jobs.rb
32
+ - lib/screenshotfreeapi/resources/monitors.rb
33
+ - lib/screenshotfreeapi/resources/screenshots.rb
34
+ - lib/screenshotfreeapi/resources/workspaces.rb
35
+ - lib/screenshotfreeapi/version.rb
36
+ - lib/screenshotfreeapi/webhooks.rb
37
+ homepage: https://api.screenshotfreeapi.com
38
+ licenses:
39
+ - MIT
40
+ metadata:
41
+ homepage_uri: https://api.screenshotfreeapi.com
42
+ source_code_uri: https://github.com/chinkauchenna2021/screenshotfreeapi-ruby
43
+ changelog_uri: https://github.com/chinkauchenna2021/screenshotfreeapi-ruby/blob/main/CHANGELOG.md
44
+ bug_tracker_uri: https://github.com/chinkauchenna2021/screenshotfreeapi-ruby/issues
45
+ post_install_message:
46
+ rdoc_options: []
47
+ require_paths:
48
+ - lib
49
+ required_ruby_version: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '2.7'
54
+ required_rubygems_version: !ruby/object:Gem::Requirement
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ version: '0'
59
+ requirements: []
60
+ rubygems_version: 3.4.19
61
+ signing_key:
62
+ specification_version: 4
63
+ summary: Official Ruby SDK for ScreenshotFreeAPI
64
+ test_files: []