linkio 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/CHANGELOG.md +47 -0
- data/LICENSE +21 -0
- data/README.md +295 -0
- data/app/controllers/linkio/application_controller.rb +50 -0
- data/app/controllers/linkio/deep_links_controller.rb +11 -0
- data/app/controllers/linkio/pending_links_controller.rb +33 -0
- data/app/controllers/linkio/referrals_controller.rb +23 -0
- data/app/controllers/linkio/well_known_controller.rb +16 -0
- data/config/routes.rb +18 -0
- data/lib/generators/linkio/install/install_generator.rb +29 -0
- data/lib/generators/linkio/install/templates/linkio.rb +63 -0
- data/lib/linkio/app_target.rb +79 -0
- data/lib/linkio/client.rb +213 -0
- data/lib/linkio/configuration.rb +152 -0
- data/lib/linkio/deep_link_data.rb +30 -0
- data/lib/linkio/engine.rb +24 -0
- data/lib/linkio/errors.rb +12 -0
- data/lib/linkio/pending_link_data.rb +57 -0
- data/lib/linkio/platform.rb +31 -0
- data/lib/linkio/rack/middleware.rb +105 -0
- data/lib/linkio/referral_data.rb +48 -0
- data/lib/linkio/request.rb +76 -0
- data/lib/linkio/result.rb +74 -0
- data/lib/linkio/smart_redirect.rb +135 -0
- data/lib/linkio/storage/base.rb +69 -0
- data/lib/linkio/storage/in_memory_storage.rb +79 -0
- data/lib/linkio/storage/redis_storage.rb +110 -0
- data/lib/linkio/utils.rb +125 -0
- data/lib/linkio/version.rb +5 -0
- data/lib/linkio.rb +71 -0
- metadata +136 -0
data/lib/linkio/utils.rb
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "digest"
|
|
4
|
+
require "uri"
|
|
5
|
+
|
|
6
|
+
module LinkIO
|
|
7
|
+
# Stateless helpers for fingerprinting, platform detection and deep link
|
|
8
|
+
# building. Mirrors the utilities from the Node.js LinkIO backend.
|
|
9
|
+
module Utils
|
|
10
|
+
# Characters left unescaped by JavaScript's encodeURIComponent.
|
|
11
|
+
UNRESERVED = /[^A-Za-z0-9\-_.!~*'()]/n
|
|
12
|
+
|
|
13
|
+
module_function
|
|
14
|
+
|
|
15
|
+
# Generate a fingerprint from IP + User-Agent for deferred deep linking.
|
|
16
|
+
# This allows matching users before and after app install.
|
|
17
|
+
#
|
|
18
|
+
# @param ip [String]
|
|
19
|
+
# @param user_agent [String]
|
|
20
|
+
# @return [String] 32-character hex digest
|
|
21
|
+
def generate_fingerprint(ip, user_agent)
|
|
22
|
+
Digest::SHA256.hexdigest("#{ip}|#{user_agent}")[0, 32]
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Generate an IP-only fingerprint for cross-browser/app matching.
|
|
26
|
+
# Less precise but works when User-Agent differs between browser and app.
|
|
27
|
+
#
|
|
28
|
+
# @param ip [String]
|
|
29
|
+
# @return [String] 32-character hex digest
|
|
30
|
+
def generate_ip_fingerprint(ip)
|
|
31
|
+
Digest::SHA256.hexdigest(ip.to_s)[0, 32]
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Extract the client IP from request data, honouring the X-Forwarded-For
|
|
35
|
+
# header set by proxies.
|
|
36
|
+
#
|
|
37
|
+
# @param forwarded_for [String, nil] value of the X-Forwarded-For header
|
|
38
|
+
# @param ip [String, nil] framework-resolved remote IP
|
|
39
|
+
# @param remote_address [String, nil] raw socket remote address
|
|
40
|
+
# @return [String] the resolved client IP, or "unknown"
|
|
41
|
+
def client_ip(forwarded_for: nil, ip: nil, remote_address: nil)
|
|
42
|
+
return forwarded_for.to_s.split(",").first.to_s.strip if forwarded_for && !forwarded_for.to_s.empty?
|
|
43
|
+
|
|
44
|
+
value = ip || remote_address
|
|
45
|
+
value.nil? || value.to_s.empty? ? "unknown" : value.to_s
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Detect the platform from a User-Agent string.
|
|
49
|
+
#
|
|
50
|
+
# @param user_agent [String, nil]
|
|
51
|
+
# @return [String]
|
|
52
|
+
def detect_platform(user_agent)
|
|
53
|
+
Platform.detect(user_agent)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Parse the query parameters of a URL into a string-keyed hash.
|
|
57
|
+
#
|
|
58
|
+
# @param url [String]
|
|
59
|
+
# @return [Hash{String => String}]
|
|
60
|
+
def parse_query_params(url)
|
|
61
|
+
query = URI.parse(url).query
|
|
62
|
+
return {} if query.nil? || query.empty?
|
|
63
|
+
|
|
64
|
+
params = {}
|
|
65
|
+
URI.decode_www_form(query).each do |key, value|
|
|
66
|
+
params[key] = if params.key?(key)
|
|
67
|
+
Array(params[key]) << value
|
|
68
|
+
else
|
|
69
|
+
value
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
params
|
|
73
|
+
rescue URI::InvalidURIError, ArgumentError
|
|
74
|
+
{}
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Build a custom-scheme deep link URI.
|
|
78
|
+
#
|
|
79
|
+
# @param scheme [String] e.g. "rokart"
|
|
80
|
+
# @param path [String] e.g. "link"
|
|
81
|
+
# @param params [Hash]
|
|
82
|
+
# @return [String] e.g. "rokart://link?foo=bar"
|
|
83
|
+
def build_deep_link(scheme, path, params)
|
|
84
|
+
query = encode_query(params)
|
|
85
|
+
"#{scheme}://#{path}#{"?#{query}" unless query.empty?}"
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# URL-encode a params hash into a query string, matching the Node backend's
|
|
89
|
+
# `encodeURIComponent` behaviour.
|
|
90
|
+
#
|
|
91
|
+
# @param params [Hash]
|
|
92
|
+
# @return [String]
|
|
93
|
+
def encode_query(params)
|
|
94
|
+
params.map do |key, value|
|
|
95
|
+
"#{escape_component(key)}=#{escape_component(value)}"
|
|
96
|
+
end.join("&")
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# Percent-encode a single component the way JavaScript's
|
|
100
|
+
# `encodeURIComponent` does (spaces become "%20", not "+").
|
|
101
|
+
#
|
|
102
|
+
# @param value [#to_s]
|
|
103
|
+
# @return [String]
|
|
104
|
+
def escape_component(value)
|
|
105
|
+
value.to_s.b.gsub(UNRESERVED) { |byte| format("%%%02X", byte.ord) }
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# Substitute `{name}` placeholders in a template with URL-encoded values
|
|
109
|
+
# from +params+ (string or symbol keys). Missing keys become empty strings.
|
|
110
|
+
# Used to build role-specific fallback URLs, e.g.
|
|
111
|
+
# "https://example.com/refer?code={code}&role={role}"
|
|
112
|
+
#
|
|
113
|
+
# @param template [String]
|
|
114
|
+
# @param params [Hash]
|
|
115
|
+
# @return [String]
|
|
116
|
+
def interpolate(template, params)
|
|
117
|
+
template.to_s.gsub(/\{(\w+)\}/) do
|
|
118
|
+
key = Regexp.last_match(1)
|
|
119
|
+
value = params[key]
|
|
120
|
+
value = params[key.to_sym] if value.nil? && params.respond_to?(:key?)
|
|
121
|
+
escape_component(value)
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
data/lib/linkio.rb
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "linkio/version"
|
|
4
|
+
require "linkio/errors"
|
|
5
|
+
require "linkio/platform"
|
|
6
|
+
require "linkio/utils"
|
|
7
|
+
require "linkio/pending_link_data"
|
|
8
|
+
require "linkio/referral_data"
|
|
9
|
+
require "linkio/deep_link_data"
|
|
10
|
+
require "linkio/request"
|
|
11
|
+
require "linkio/result"
|
|
12
|
+
require "linkio/app_target"
|
|
13
|
+
require "linkio/configuration"
|
|
14
|
+
require "linkio/storage/base"
|
|
15
|
+
require "linkio/storage/in_memory_storage"
|
|
16
|
+
require "linkio/storage/redis_storage"
|
|
17
|
+
require "linkio/smart_redirect"
|
|
18
|
+
require "linkio/client"
|
|
19
|
+
|
|
20
|
+
# LinkIO is a self-hosted deep linking backend for mobile apps -
|
|
21
|
+
# an open source alternative to Branch.io.
|
|
22
|
+
#
|
|
23
|
+
# The module also exposes a global configuration/client convenience API used by
|
|
24
|
+
# the Rack middleware and Rails engine:
|
|
25
|
+
#
|
|
26
|
+
# LinkIO.configure do |config|
|
|
27
|
+
# config.domain = "example.com"
|
|
28
|
+
# config.ios_app_id = "123456789"
|
|
29
|
+
# # ...
|
|
30
|
+
# config.storage = LinkIO::Storage::InMemoryStorage.new
|
|
31
|
+
# end
|
|
32
|
+
#
|
|
33
|
+
# LinkIO.client # => memoized LinkIO::Client
|
|
34
|
+
module LinkIO
|
|
35
|
+
class << self
|
|
36
|
+
# @return [LinkIO::Configuration]
|
|
37
|
+
def configuration
|
|
38
|
+
@configuration ||= Configuration.new
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
attr_writer :configuration
|
|
42
|
+
|
|
43
|
+
# Configure the global LinkIO instance.
|
|
44
|
+
#
|
|
45
|
+
# @yieldparam config [LinkIO::Configuration]
|
|
46
|
+
# @return [LinkIO::Configuration]
|
|
47
|
+
def configure
|
|
48
|
+
yield(configuration) if block_given?
|
|
49
|
+
configuration
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# A memoized client built from the global configuration.
|
|
53
|
+
# Reset with {reset!} after changing configuration.
|
|
54
|
+
#
|
|
55
|
+
# @return [LinkIO::Client]
|
|
56
|
+
def client
|
|
57
|
+
@client ||= Client.new(configuration)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Reset global configuration and client. Primarily useful in tests.
|
|
61
|
+
#
|
|
62
|
+
# @return [void]
|
|
63
|
+
def reset!
|
|
64
|
+
@configuration = nil
|
|
65
|
+
@client = nil
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
require "linkio/rack/middleware"
|
|
71
|
+
require "linkio/engine" if defined?(Rails::Engine)
|
metadata
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: linkio
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Nakul Sharma
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: rack
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '2.0'
|
|
19
|
+
type: :development
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '2.0'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: rake
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - "~>"
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '13.0'
|
|
33
|
+
type: :development
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - "~>"
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '13.0'
|
|
40
|
+
- !ruby/object:Gem::Dependency
|
|
41
|
+
name: rspec
|
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - "~>"
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '3.12'
|
|
47
|
+
type: :development
|
|
48
|
+
prerelease: false
|
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - "~>"
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: '3.12'
|
|
54
|
+
- !ruby/object:Gem::Dependency
|
|
55
|
+
name: rubocop
|
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
|
57
|
+
requirements:
|
|
58
|
+
- - "~>"
|
|
59
|
+
- !ruby/object:Gem::Version
|
|
60
|
+
version: '1.60'
|
|
61
|
+
type: :development
|
|
62
|
+
prerelease: false
|
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
64
|
+
requirements:
|
|
65
|
+
- - "~>"
|
|
66
|
+
- !ruby/object:Gem::Version
|
|
67
|
+
version: '1.60'
|
|
68
|
+
description: |
|
|
69
|
+
LinkIO is a self-hosted deep linking solution for mobile applications, an open
|
|
70
|
+
source alternative to Branch.io. It generates Apple App Site Association and
|
|
71
|
+
Android assetlinks.json files, performs smart app-or-store redirects, supports
|
|
72
|
+
fingerprint-based deferred deep linking, and tracks referrals. It ships a
|
|
73
|
+
framework-agnostic core, a mountable Rails engine, and Rack middleware.
|
|
74
|
+
email:
|
|
75
|
+
- nakul@tecorb.co
|
|
76
|
+
executables: []
|
|
77
|
+
extensions: []
|
|
78
|
+
extra_rdoc_files: []
|
|
79
|
+
files:
|
|
80
|
+
- CHANGELOG.md
|
|
81
|
+
- LICENSE
|
|
82
|
+
- README.md
|
|
83
|
+
- app/controllers/linkio/application_controller.rb
|
|
84
|
+
- app/controllers/linkio/deep_links_controller.rb
|
|
85
|
+
- app/controllers/linkio/pending_links_controller.rb
|
|
86
|
+
- app/controllers/linkio/referrals_controller.rb
|
|
87
|
+
- app/controllers/linkio/well_known_controller.rb
|
|
88
|
+
- config/routes.rb
|
|
89
|
+
- lib/generators/linkio/install/install_generator.rb
|
|
90
|
+
- lib/generators/linkio/install/templates/linkio.rb
|
|
91
|
+
- lib/linkio.rb
|
|
92
|
+
- lib/linkio/app_target.rb
|
|
93
|
+
- lib/linkio/client.rb
|
|
94
|
+
- lib/linkio/configuration.rb
|
|
95
|
+
- lib/linkio/deep_link_data.rb
|
|
96
|
+
- lib/linkio/engine.rb
|
|
97
|
+
- lib/linkio/errors.rb
|
|
98
|
+
- lib/linkio/pending_link_data.rb
|
|
99
|
+
- lib/linkio/platform.rb
|
|
100
|
+
- lib/linkio/rack/middleware.rb
|
|
101
|
+
- lib/linkio/referral_data.rb
|
|
102
|
+
- lib/linkio/request.rb
|
|
103
|
+
- lib/linkio/result.rb
|
|
104
|
+
- lib/linkio/smart_redirect.rb
|
|
105
|
+
- lib/linkio/storage/base.rb
|
|
106
|
+
- lib/linkio/storage/in_memory_storage.rb
|
|
107
|
+
- lib/linkio/storage/redis_storage.rb
|
|
108
|
+
- lib/linkio/utils.rb
|
|
109
|
+
- lib/linkio/version.rb
|
|
110
|
+
homepage: https://github.com/pt-nakul-sharma/linkio-rails
|
|
111
|
+
licenses:
|
|
112
|
+
- MIT
|
|
113
|
+
metadata:
|
|
114
|
+
source_code_uri: https://github.com/pt-nakul-sharma/linkio-rails
|
|
115
|
+
bug_tracker_uri: https://github.com/pt-nakul-sharma/linkio-rails/issues
|
|
116
|
+
changelog_uri: https://github.com/pt-nakul-sharma/linkio-rails/blob/main/CHANGELOG.md
|
|
117
|
+
rubygems_mfa_required: 'true'
|
|
118
|
+
rdoc_options: []
|
|
119
|
+
require_paths:
|
|
120
|
+
- lib
|
|
121
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
122
|
+
requirements:
|
|
123
|
+
- - ">="
|
|
124
|
+
- !ruby/object:Gem::Version
|
|
125
|
+
version: 3.0.0
|
|
126
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
127
|
+
requirements:
|
|
128
|
+
- - ">="
|
|
129
|
+
- !ruby/object:Gem::Version
|
|
130
|
+
version: '0'
|
|
131
|
+
requirements: []
|
|
132
|
+
rubygems_version: 4.0.10
|
|
133
|
+
specification_version: 4
|
|
134
|
+
summary: Self-hosted deep linking backend for mobile apps - open source alternative
|
|
135
|
+
to Branch.io
|
|
136
|
+
test_files: []
|