th7-clerk-sdk-ruby 4.2.2

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.
Files changed (195) hide show
  1. checksums.yaml +7 -0
  2. data/.env.example +3 -0
  3. data/.github/workflows/main.yml +30 -0
  4. data/.github/workflows/semgrep.yml +24 -0
  5. data/.gitignore +21 -0
  6. data/.rspec +3 -0
  7. data/.ruby-version +1 -0
  8. data/CHANGELOG.md +212 -0
  9. data/Gemfile +33 -0
  10. data/Gemfile.lock +300 -0
  11. data/Guardfile +14 -0
  12. data/LICENSE.txt +21 -0
  13. data/README.md +278 -0
  14. data/Rakefile +56 -0
  15. data/apps/rack/app.rb +67 -0
  16. data/apps/rack/config.ru +17 -0
  17. data/apps/rack/middleware/disable_paths.rb +13 -0
  18. data/apps/rails-api/.dockerignore +41 -0
  19. data/apps/rails-api/.gitattributes +9 -0
  20. data/apps/rails-api/.gitignore +32 -0
  21. data/apps/rails-api/.kamal/hooks/docker-setup.sample +3 -0
  22. data/apps/rails-api/.kamal/hooks/post-deploy.sample +14 -0
  23. data/apps/rails-api/.kamal/hooks/post-proxy-reboot.sample +3 -0
  24. data/apps/rails-api/.kamal/hooks/pre-build.sample +51 -0
  25. data/apps/rails-api/.kamal/hooks/pre-connect.sample +47 -0
  26. data/apps/rails-api/.kamal/hooks/pre-deploy.sample +109 -0
  27. data/apps/rails-api/.kamal/hooks/pre-proxy-reboot.sample +3 -0
  28. data/apps/rails-api/.kamal/secrets +17 -0
  29. data/apps/rails-api/.rubocop.yml +8 -0
  30. data/apps/rails-api/.ruby-version +1 -0
  31. data/apps/rails-api/Dockerfile +69 -0
  32. data/apps/rails-api/Gemfile +54 -0
  33. data/apps/rails-api/Gemfile.lock +374 -0
  34. data/apps/rails-api/README.md +24 -0
  35. data/apps/rails-api/Rakefile +6 -0
  36. data/apps/rails-api/app/controllers/application_controller.rb +3 -0
  37. data/apps/rails-api/app/controllers/home_controller.rb +5 -0
  38. data/apps/rails-api/app/jobs/application_job.rb +7 -0
  39. data/apps/rails-api/app/mailers/application_mailer.rb +4 -0
  40. data/apps/rails-api/app/models/application_record.rb +3 -0
  41. data/apps/rails-api/app/views/layouts/mailer.html.erb +13 -0
  42. data/apps/rails-api/app/views/layouts/mailer.text.erb +1 -0
  43. data/apps/rails-api/bin/brakeman +7 -0
  44. data/apps/rails-api/bin/bundle +109 -0
  45. data/apps/rails-api/bin/dev +2 -0
  46. data/apps/rails-api/bin/docker-entrypoint +14 -0
  47. data/apps/rails-api/bin/jobs +6 -0
  48. data/apps/rails-api/bin/kamal +27 -0
  49. data/apps/rails-api/bin/rails +4 -0
  50. data/apps/rails-api/bin/rake +4 -0
  51. data/apps/rails-api/bin/rubocop +8 -0
  52. data/apps/rails-api/bin/setup +34 -0
  53. data/apps/rails-api/bin/thrust +5 -0
  54. data/apps/rails-api/config/application.rb +36 -0
  55. data/apps/rails-api/config/boot.rb +4 -0
  56. data/apps/rails-api/config/cable.yml +17 -0
  57. data/apps/rails-api/config/cache.yml +16 -0
  58. data/apps/rails-api/config/credentials.yml.enc +1 -0
  59. data/apps/rails-api/config/database.yml +41 -0
  60. data/apps/rails-api/config/deploy.yml +116 -0
  61. data/apps/rails-api/config/environment.rb +5 -0
  62. data/apps/rails-api/config/environments/development.rb +70 -0
  63. data/apps/rails-api/config/environments/production.rb +88 -0
  64. data/apps/rails-api/config/environments/test.rb +53 -0
  65. data/apps/rails-api/config/initializers/cors.rb +16 -0
  66. data/apps/rails-api/config/initializers/filter_parameter_logging.rb +8 -0
  67. data/apps/rails-api/config/initializers/inflections.rb +16 -0
  68. data/apps/rails-api/config/locales/en.yml +31 -0
  69. data/apps/rails-api/config/puma.rb +41 -0
  70. data/apps/rails-api/config/queue.yml +18 -0
  71. data/apps/rails-api/config/recurring.yml +10 -0
  72. data/apps/rails-api/config/routes.rb +10 -0
  73. data/apps/rails-api/config/storage.yml +34 -0
  74. data/apps/rails-api/config.ru +6 -0
  75. data/apps/rails-api/db/cable_schema.rb +11 -0
  76. data/apps/rails-api/db/cache_schema.rb +14 -0
  77. data/apps/rails-api/db/queue_schema.rb +129 -0
  78. data/apps/rails-api/db/seeds.rb +9 -0
  79. data/apps/rails-api/public/robots.txt +1 -0
  80. data/apps/rails-api/test/controllers/home_controller_test.rb +7 -0
  81. data/apps/rails-api/test/test_helper.rb +15 -0
  82. data/apps/rails-full/.dockerignore +47 -0
  83. data/apps/rails-full/.gitattributes +9 -0
  84. data/apps/rails-full/.gitignore +34 -0
  85. data/apps/rails-full/.kamal/hooks/docker-setup.sample +3 -0
  86. data/apps/rails-full/.kamal/hooks/post-deploy.sample +14 -0
  87. data/apps/rails-full/.kamal/hooks/post-proxy-reboot.sample +3 -0
  88. data/apps/rails-full/.kamal/hooks/pre-build.sample +51 -0
  89. data/apps/rails-full/.kamal/hooks/pre-connect.sample +47 -0
  90. data/apps/rails-full/.kamal/hooks/pre-deploy.sample +109 -0
  91. data/apps/rails-full/.kamal/hooks/pre-proxy-reboot.sample +3 -0
  92. data/apps/rails-full/.kamal/secrets +17 -0
  93. data/apps/rails-full/.rubocop.yml +8 -0
  94. data/apps/rails-full/.ruby-version +1 -0
  95. data/apps/rails-full/Dockerfile +72 -0
  96. data/apps/rails-full/Gemfile +70 -0
  97. data/apps/rails-full/Gemfile.lock +429 -0
  98. data/apps/rails-full/README.md +24 -0
  99. data/apps/rails-full/Rakefile +6 -0
  100. data/apps/rails-full/app/assets/stylesheets/application.css +10 -0
  101. data/apps/rails-full/app/controllers/application_controller.rb +6 -0
  102. data/apps/rails-full/app/controllers/home_controller.rb +11 -0
  103. data/apps/rails-full/app/helpers/application_helper.rb +2 -0
  104. data/apps/rails-full/app/helpers/home_helper.rb +2 -0
  105. data/apps/rails-full/app/javascript/application.js +3 -0
  106. data/apps/rails-full/app/javascript/controllers/application.js +9 -0
  107. data/apps/rails-full/app/javascript/controllers/hello_controller.js +7 -0
  108. data/apps/rails-full/app/javascript/controllers/index.js +4 -0
  109. data/apps/rails-full/app/jobs/application_job.rb +7 -0
  110. data/apps/rails-full/app/mailers/application_mailer.rb +4 -0
  111. data/apps/rails-full/app/models/application_record.rb +3 -0
  112. data/apps/rails-full/app/views/home/index.html.erb +7 -0
  113. data/apps/rails-full/app/views/layouts/application.html.erb +60 -0
  114. data/apps/rails-full/app/views/layouts/mailer.html.erb +13 -0
  115. data/apps/rails-full/app/views/layouts/mailer.text.erb +1 -0
  116. data/apps/rails-full/app/views/pwa/manifest.json.erb +22 -0
  117. data/apps/rails-full/app/views/pwa/service-worker.js +26 -0
  118. data/apps/rails-full/bin/brakeman +7 -0
  119. data/apps/rails-full/bin/bundle +109 -0
  120. data/apps/rails-full/bin/dev +2 -0
  121. data/apps/rails-full/bin/docker-entrypoint +14 -0
  122. data/apps/rails-full/bin/importmap +4 -0
  123. data/apps/rails-full/bin/jobs +6 -0
  124. data/apps/rails-full/bin/kamal +27 -0
  125. data/apps/rails-full/bin/rails +4 -0
  126. data/apps/rails-full/bin/rake +4 -0
  127. data/apps/rails-full/bin/rubocop +8 -0
  128. data/apps/rails-full/bin/setup +34 -0
  129. data/apps/rails-full/bin/thrust +5 -0
  130. data/apps/rails-full/config/application.rb +31 -0
  131. data/apps/rails-full/config/boot.rb +4 -0
  132. data/apps/rails-full/config/cable.yml +17 -0
  133. data/apps/rails-full/config/cache.yml +16 -0
  134. data/apps/rails-full/config/credentials.yml.enc +1 -0
  135. data/apps/rails-full/config/database.yml +41 -0
  136. data/apps/rails-full/config/deploy.yml +116 -0
  137. data/apps/rails-full/config/environment.rb +5 -0
  138. data/apps/rails-full/config/environments/development.rb +72 -0
  139. data/apps/rails-full/config/environments/production.rb +91 -0
  140. data/apps/rails-full/config/environments/test.rb +53 -0
  141. data/apps/rails-full/config/importmap.rb +7 -0
  142. data/apps/rails-full/config/initializers/assets.rb +7 -0
  143. data/apps/rails-full/config/initializers/clerk.rb +4 -0
  144. data/apps/rails-full/config/initializers/content_security_policy.rb +25 -0
  145. data/apps/rails-full/config/initializers/filter_parameter_logging.rb +8 -0
  146. data/apps/rails-full/config/initializers/inflections.rb +16 -0
  147. data/apps/rails-full/config/locales/en.yml +31 -0
  148. data/apps/rails-full/config/puma.rb +41 -0
  149. data/apps/rails-full/config/queue.yml +18 -0
  150. data/apps/rails-full/config/recurring.yml +10 -0
  151. data/apps/rails-full/config/routes.rb +15 -0
  152. data/apps/rails-full/config/storage.yml +34 -0
  153. data/apps/rails-full/config.ru +6 -0
  154. data/apps/rails-full/db/cable_schema.rb +11 -0
  155. data/apps/rails-full/db/cache_schema.rb +14 -0
  156. data/apps/rails-full/db/queue_schema.rb +129 -0
  157. data/apps/rails-full/db/seeds.rb +9 -0
  158. data/apps/rails-full/public/400.html +114 -0
  159. data/apps/rails-full/public/404.html +114 -0
  160. data/apps/rails-full/public/406-unsupported-browser.html +114 -0
  161. data/apps/rails-full/public/422.html +114 -0
  162. data/apps/rails-full/public/500.html +114 -0
  163. data/apps/rails-full/public/icon.png +0 -0
  164. data/apps/rails-full/public/icon.svg +3 -0
  165. data/apps/rails-full/public/robots.txt +1 -0
  166. data/apps/rails-full/test/application_system_test_case.rb +5 -0
  167. data/apps/rails-full/test/controllers/home_controller_test.rb +7 -0
  168. data/apps/rails-full/test/test_helper.rb +15 -0
  169. data/apps/sinatra/app.rb +29 -0
  170. data/apps/sinatra/config.ru +8 -0
  171. data/apps/sinatra/views/index.erb +44 -0
  172. data/bin/console +16 -0
  173. data/bin/release +21 -0
  174. data/bin/setup +8 -0
  175. data/clerk-sdk-ruby.gemspec +38 -0
  176. data/docs/clerk-logo-dark.png +0 -0
  177. data/docs/clerk-logo-light.png +0 -0
  178. data/lib/clerk/authenticatable.rb +32 -0
  179. data/lib/clerk/authenticate_context.rb +168 -0
  180. data/lib/clerk/authenticate_request.rb +261 -0
  181. data/lib/clerk/configuration.rb +84 -0
  182. data/lib/clerk/constants.rb +74 -0
  183. data/lib/clerk/error.rb +17 -0
  184. data/lib/clerk/jwks_cache.rb +37 -0
  185. data/lib/clerk/proxy.rb +135 -0
  186. data/lib/clerk/rack.rb +2 -0
  187. data/lib/clerk/rack_middleware.rb +112 -0
  188. data/lib/clerk/rails.rb +3 -0
  189. data/lib/clerk/railtie.rb +15 -0
  190. data/lib/clerk/sdk.rb +84 -0
  191. data/lib/clerk/sinatra.rb +52 -0
  192. data/lib/clerk/utils.rb +73 -0
  193. data/lib/clerk/version.rb +5 -0
  194. data/lib/clerk.rb +27 -0
  195. metadata +340 -0
data/README.md ADDED
@@ -0,0 +1,278 @@
1
+ <p align="center">
2
+ <a href="https://www.clerk.com/?utm_source=github&utm_medium=starter_repos&utm_campaign=sdk_ruby" target="_blank" align="center">
3
+ <picture>
4
+ <source media="(prefers-color-scheme: dark)" srcset="./docs/clerk-logo-dark.png">
5
+ <img src="./docs/clerk-logo-light.png" height="64">
6
+ </picture>
7
+ </a>
8
+ <br />
9
+ </p>
10
+
11
+ # Clerk Ruby SDK
12
+
13
+ This SDK allows you to call the [Clerk](https://www.clerk.com/?utm_source=github&utm_medium=starter_repos&utm_campaign=sdk_ruby) Backend API from Ruby code without having to implement the calls yourself.
14
+
15
+ [![chat on Discord](https://img.shields.io/discord/856971667393609759.svg?logo=discord)](https://discord.com/invite/b5rXHjAg7A)
16
+ [![documentation](https://img.shields.io/badge/documentation-clerk-green.svg)](https://clerk.com/docs)
17
+ [![twitter](https://img.shields.io/twitter/follow/ClerkDev?style=social)](https://twitter.com/intent/follow?screen_name=ClerkDev)
18
+
19
+ ---
20
+
21
+ **Clerk is Hiring!**
22
+
23
+ Would you like to work on Open Source software and help maintain this repository? [Apply today!](https://apply.workable.com/clerk-dev/)
24
+
25
+ ---
26
+
27
+ **Note**: You're looking at the main branch, which requires that you use [Auth
28
+ v2](https://clerk.com/docs/upgrade-guides/auth-v2).
29
+
30
+ If you're looking for the legacy authentication scheme, refer to the
31
+ [`v1`](https://github.com/clerkinc/clerk-sdk-ruby/tree/v1) branch.
32
+
33
+ ## Installation
34
+
35
+ Add this line to your application's Gemfile:
36
+
37
+ ```ruby
38
+ gem 'clerk-sdk-ruby', require: "clerk"
39
+ ```
40
+
41
+ And then execute:
42
+
43
+ $ bundle install
44
+
45
+ Or install it yourself as:
46
+
47
+ $ gem install clerk-sdk-ruby
48
+
49
+ ## Quick Start
50
+
51
+ First, you need to get an API key for a Clerk instance. This is done via the
52
+ [Clerk dashboard](https://dashboard.clerk.com/applications).
53
+
54
+ Then you can instantiate a `Clerk::SDK` instance and access all
55
+ [Backend API](https://clerk.com/docs/reference/backend-api) endpoints.
56
+ Here's a quick example:
57
+
58
+ ```ruby
59
+ clerk = Clerk::SDK.new(api_key: "your_api_key")
60
+ # List all users
61
+ clerk.users.get_user_list
62
+ # Get your first user
63
+ user = clerk.users.get_user_list(limit: 1).first
64
+ # Extract their primary email address ID
65
+ email_id = user["primary_email_address_id"]
66
+
67
+ # Create an organization
68
+ p Clerk::SDK.organizations.create_organization({
69
+ create_organization_request: ClerkHttpClient::CreateOrganizationRequest.new({
70
+ name: 'example'
71
+ })
72
+ })
73
+ ```
74
+
75
+ ## Configuration
76
+
77
+ The SDK can be configured in three ways: environment variables, configuration
78
+ singleton and constructor arguments. The priority goes like this:
79
+
80
+ 1. Constructor arguments
81
+ 2. Configuration object
82
+ 3. Environment variables
83
+
84
+ If an argument is not provided, the configuration object is looked up, which
85
+ falls back to the associated environment variable. Here's an example with all
86
+ supported configuration settings their environment variable equivalents:
87
+
88
+ ```ruby
89
+ Clerk.configure do |c|
90
+ c.secret_key = "sk_(test|live)_...." # if omitted: ENV["CLERK_SECRET_KEY"] - API calls will fail if unset
91
+ c.publishable_key = "pk_(test|live)_...." # if omitted: ENV["CLERK_PUBLISHABLE_KEY"] - Handshake mechanism (check section below) will fail if unset
92
+ c.logger = Logger.new(STDOUT) # if omitted, no logging
93
+ c.middleware_cache_store = ActiveSupport::Cache::FileStore.new("/tmp/clerk_middleware_cache") # if omitted: no caching
94
+ c.excluded_routes ["/foo", "/bar/*"]
95
+ end
96
+ ```
97
+
98
+ You can customize each instance of the `Clerk::SDK` object by passing keyword
99
+ arguments to the constructor:
100
+
101
+ ```ruby
102
+ clerk = Clerk::SDK.new(
103
+ logger: Logger.new()
104
+ secret_key: "X",
105
+ )
106
+ ```
107
+
108
+ For full customization, you can instead pass a `Faraday` object directly, which
109
+ will ignore all the other arguments, if passed:
110
+
111
+ ```ruby
112
+ faraday = Faraday.new()
113
+ clerk = Clerk::SDK.new(connection: faraday)
114
+ ```
115
+
116
+ Refer to the [Faraday documentation](https://lostisland.github.io/faraday/usage/#customizing-faradayconnection)
117
+ for details.
118
+
119
+ ## Rack middleware
120
+
121
+ The SDK comes with a Rack middleware which lazily loads the Clerk session and
122
+ user. It inserts a `clerk` key in the Rack environment, which is an instance
123
+ of `Clerk::Proxy`. To get the session or the user of the session, you call
124
+ `session` or `user` respectively. In case there is no session, you can retrieve
125
+ the API error with the `error` getter method.
126
+
127
+ ```ruby
128
+ use Clerk::Rack::Middleware
129
+ ```
130
+
131
+ ### Reverification middleware
132
+
133
+ The SDK comes with a revalidation middleware which will automatically revalidate the session when the user navigates to a protected route.
134
+
135
+ ```ruby
136
+ use Clerk::Rack::Reverification,
137
+ preset: Clerk::StepUp::Preset::LAX,
138
+ routes: ["/*"]
139
+ ```
140
+
141
+
142
+
143
+ ## Rails integration
144
+
145
+ The SDK will automatically add the [Rack middleware](#rack-middleware) to the
146
+ middleware stack. For easier access to the Clerk session and user, include the
147
+ `Clerk::Authenticatable` concern in your controller:
148
+
149
+ ```ruby
150
+ class ApplicationController < ActionController::Base
151
+ include Clerk::Authenticatable
152
+ end
153
+
154
+ class AdminController < ApplicationController
155
+ before_action :require_reverification!, only: [:protected]
156
+
157
+ def index
158
+ @user = clerk.user
159
+ end
160
+
161
+ def protected
162
+ render json: {message: clerk.user? ? "Valid session" : "Not logged in"}
163
+ end
164
+ end
165
+ ```
166
+
167
+ This gives your controller and views access to the following methods and more:
168
+
169
+ - `clerk.sdk.*`
170
+ - `clerk.user?`
171
+ - `clerk.user`: NOTE: This makes an additional request and attempts to cache it.
172
+ - `clerk.user_id`
173
+ - `clerk.organization?`
174
+ - `clerk.organization` NOTE: This makes an additional request and attempts to cache it.
175
+ - `clerk.organization_id`
176
+ - `clerk.organization_role`
177
+ - `clerk.organization_permissions`
178
+
179
+ ### Skipping the Railtie
180
+
181
+ There are cases where you might not want to use the Railtie, for example, only using the SDK in a Rails application. To accomplish this, you can set the `CLERK_SKIP_RAILTIE` environment variable to `true`.
182
+
183
+ This will prevent the Railtie from being loaded and the Rack middleware from being added to the middleware stack.
184
+
185
+ You can still configure the SDK as normal, but you will need to call the SDK using `Clerk::SDK.new` instead of the `clerk.sdk` helper.
186
+
187
+ ## Sinatra integration
188
+
189
+ The SDK enables the use of Extensions to add Clerk support to your Sinatra application.
190
+
191
+ `Sinatra::Clerk` will automatically add the [Rack middleware](#rack-middleware)to the
192
+ middleware stack and enable easy access to the Clerk session and user helper methods.
193
+
194
+ ```ruby
195
+ class App < Sinatra::Base
196
+ register Sinatra::Clerk
197
+
198
+ get "/" do
199
+ erb :index, format: :html5
200
+ end
201
+
202
+ get "/admin" do
203
+ @user = clerk.user
204
+ erb :index, format: :html5
205
+ end
206
+
207
+ get "/protected" do
208
+ require_reverification!
209
+ {message: clerk.user? ? "Valid session" : "Not logged in"}.to_json
210
+ end
211
+ end
212
+ ```
213
+
214
+ ## Internals
215
+
216
+ The API client depends on the excellent [Faraday](https://rubygems.org/gems/faraday)
217
+ gem for HTTP requests. You can swap out the original implementation with your
218
+ own customized instance.
219
+
220
+ The API client sends all requests as `application/x-www-form-urlencoded`. The
221
+ API then responds with JSON which is then converted and returned as a Ruby
222
+ `Hash`, or `Array` of hashes. Errors are also returned as a JSON object, with a
223
+ single key (`errors`) containing an array of error objects.
224
+
225
+ Read the [API documentation](https://clerk.com/docs/reference/backend-api)
226
+ for details on expected parameters and response formats.
227
+
228
+ <a name="handshake"></a>
229
+
230
+ ### Handshake
231
+
232
+ The Client Handshake is a mechanism that is used to resolve a request’s authentication state from “unknown” to definitively signed in or signed out. Clerk’s session management architecture relies on a short-lived session JWT to validate requests, along with a long-lived session that is used to keep the session JWT fresh by interacting with the Frontend API. The long-lived session token is stored in an HttpOnly cookie associated with the Frontend API domain. If a short-lived session JWT is expired on a request to an application’s backend, the SDK doesn’t know if the session has ended, or if a new short-lived JWT needs to be issued. When an SDK gets into this state, it triggers the handshake.
233
+
234
+ With the handshake, we can resolve the authentication state on the backend and ensure the request is properly handled as signed in or out, instead of being in a potentially “unknown” state. The handshake flow relies on redirects to exchange session information between FAPI and the application, ensuring the resolution of unknown authentication states minimizes performance impact and behaves consistently across different framework and language implementations.
235
+
236
+ ## Development
237
+
238
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run
239
+ `bundle exec rake spec` to run the tests. You can also run `bin/console` for an
240
+ interactive prompt that will allow you to experiment.
241
+
242
+ To install this gem onto your local machine, run `bundle exec rake install`.
243
+
244
+ To run the example applications, run:
245
+
246
+ ```bash
247
+ rake app:rack
248
+ rake app:rails
249
+ rake app:rails:api
250
+ rake app:sinatra
251
+ ```
252
+
253
+ ## Release
254
+
255
+ To release a new version:
256
+ - update the version number in `version.rb`
257
+ - update `CHANGELOG.md` to include information about the changes
258
+ - merge changes into main
259
+ - run `bundle exec rake release`
260
+
261
+ If gem publishing is NOT executed automatically:
262
+ - run `gem push pkg/clerk-sdk-ruby-{version}.gem` to push the `.gem` file to [rubygems.org](https://rubygems.org)
263
+
264
+ The `bundle exec rake release` command:
265
+ - creates a git tag with the version found in `version.rb`
266
+ - pushes the git tag
267
+
268
+ ## Yank release
269
+
270
+ We should avoid yanking a releasing but if it's necessary execute `gem yank clerk-sdk-ruby -v {version}`
271
+
272
+ ## Contributing
273
+
274
+ Bug reports and pull requests are welcome on GitHub at https://github.com/clerkinc/clerk-sdk-ruby.
275
+
276
+ ## License
277
+
278
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "standard/rake"
5
+
6
+ ################################
7
+ # COVERAGE
8
+ ################################
9
+
10
+ desc "Open coverage report"
11
+ task(:cov) { system "open coverage/index.html" }
12
+
13
+ ################################
14
+ # TESTING
15
+ ################################
16
+
17
+ desc "Run all tests"
18
+ task spec: "spec:all"
19
+
20
+ namespace :spec do
21
+ task(:all) { system "bundle exec rspec" }
22
+
23
+ desc "Run tests on file changes"
24
+ task(:watch) { system "bundle exec guard -g spec" }
25
+
26
+ desc "Run failed tests only"
27
+ task(:failed) { system "bundle exec rspec --only-failures" }
28
+ end
29
+
30
+ ################################
31
+ # PLAYGROUND APPLICATIONS
32
+ ################################
33
+
34
+ namespace :app do
35
+ desc "Run Rails (full stack) application"
36
+ task rails: "rails:full"
37
+
38
+ namespace :rails do
39
+ task :full do
40
+ cd ("apps/rails-full") { system "bin/rails server" }
41
+ end
42
+
43
+ desc "Run Rails (API only) application"
44
+ task :api do
45
+ cd ("apps/rails-api") { system "bin/rails server" }
46
+ end
47
+ end
48
+
49
+ desc "Run Rack application"
50
+ task(:rack) { system "rerun --dir lib,apps/rack --pattern '**/*.{rb,ru}' -- bundle exec puma apps/rack/config.ru -p 3000" }
51
+
52
+ desc "Run Sinatra application"
53
+ task(:sinatra) { system "rerun --dir lib,apps/sinatra --pattern '**/*.{erb,rb,ru}' -- bundle exec puma apps/sinatra/config.ru -p 3000 -v" }
54
+ end
55
+
56
+
data/apps/rack/app.rb ADDED
@@ -0,0 +1,67 @@
1
+ require "erb"
2
+ require "clerk"
3
+
4
+ class App
5
+ def call(env)
6
+ # # Example: Without using `Clerk::Rack::Reverification` Middleware
7
+ # preset = Clerk::StepUp::Preset::LAX
8
+ # if env["clerk"].user_needs_reverification?(preset)
9
+ # return env["clerk"].user_reverification_rack_response(preset)
10
+ # end
11
+
12
+ respond_with(200) do
13
+ user = env["clerk"].user
14
+ user ? "Authenticated User: #{user.first_name} (#{user.id})" : "Not Authenticated"
15
+ end
16
+ end
17
+
18
+ private
19
+
20
+ def respond_with(status, plain_body = nil, &html_body)
21
+ return [status, {"Content-Type" => "text/plain; charset=utf-8"}, [plain_body]] unless block_given?
22
+
23
+ compiled = <<-HTML
24
+ <html>
25
+ <head>
26
+ <title>Rack</title>
27
+ <style>
28
+ html { font-family: monospace; }
29
+ @media (prefers-color-scheme: dark) {
30
+ html {
31
+ color: #FFE6E6FF;
32
+ background-color: #201D1E;
33
+ }
34
+ }
35
+ </style>
36
+ <script
37
+ async
38
+ crossorigin="anonymous"
39
+ data-clerk-publishable-key="#{ENV["CLERK_PUBLISHABLE_KEY"]}"
40
+ src="#{ENV["CLERK_JS_URL"]}"
41
+ type="text/javascript"
42
+ ></script>
43
+ <script>
44
+ window.addEventListener('load', async function () {
45
+ await Clerk.load()
46
+ const container = document.getElementById('auth-container')
47
+ if (Clerk.user) {
48
+ container.innerHTML = `<div id="user-button"></div>`
49
+ Clerk.mountUserButton(document.getElementById('user-button'))
50
+ } else {
51
+ container.innerHTML = `<div id="sign-in"></div>`
52
+ Clerk.mountSignIn(document.getElementById('sign-in'))
53
+ }
54
+ })
55
+ </script>
56
+ </head>
57
+ <body>
58
+ <h1>Rack</h1>
59
+ <h2>#{yield}</h2>
60
+ <div id="auth-container"></div>
61
+ </body>
62
+ </html>
63
+ HTML
64
+
65
+ [status, {"Content-Type" => "text/html; charset=utf-8"}, [compiled]]
66
+ end
67
+ end
@@ -0,0 +1,17 @@
1
+ require "active_support"
2
+ require "rack"
3
+ require "clerk/rack"
4
+ require "dotenv"
5
+
6
+ require_relative "app"
7
+ require_relative "middleware/disable_paths"
8
+
9
+ Dotenv.load(".env")
10
+
11
+ use DisablePaths, paths: ["/favicon.ico"]
12
+ use Clerk::Rack::Middleware
13
+ use Clerk::Rack::Reverification,
14
+ preset: Clerk::StepUp::Preset::LAX,
15
+ routes: ["/*"]
16
+
17
+ run App.new
@@ -0,0 +1,13 @@
1
+ class DisablePaths
2
+ def initialize(app, paths: [])
3
+ @app = app
4
+ @paths = {}
5
+
6
+ paths.each { |p| @paths[p] = true }
7
+ end
8
+
9
+ def call(env)
10
+ return [404, {}, []] if @paths[env["PATH_INFO"]]
11
+ @app.call(env)
12
+ end
13
+ end
@@ -0,0 +1,41 @@
1
+ # See https://docs.docker.com/engine/reference/builder/#dockerignore-file for more about ignoring files.
2
+
3
+ # Ignore git directory.
4
+ /.git/
5
+ /.gitignore
6
+
7
+ # Ignore bundler config.
8
+ /.bundle
9
+
10
+ # Ignore all environment files.
11
+ /.env*
12
+
13
+ # Ignore all default key files.
14
+ /config/master.key
15
+ /config/credentials/*.key
16
+
17
+ # Ignore all logfiles and tempfiles.
18
+ /log/*
19
+ /tmp/*
20
+ !/log/.keep
21
+ !/tmp/.keep
22
+
23
+ # Ignore pidfiles, but keep the directory.
24
+ /tmp/pids/*
25
+ !/tmp/pids/.keep
26
+
27
+ # Ignore storage (uploaded files in development and any SQLite databases).
28
+ /storage/*
29
+ !/storage/.keep
30
+ /tmp/storage/*
31
+ !/tmp/storage/.keep
32
+
33
+ # Ignore CI service files.
34
+ /.github
35
+
36
+ # Ignore development files
37
+ /.devcontainer
38
+
39
+ # Ignore Docker-related files
40
+ /.dockerignore
41
+ /Dockerfile*
@@ -0,0 +1,9 @@
1
+ # See https://git-scm.com/docs/gitattributes for more about git attribute files.
2
+
3
+ # Mark the database schema as having been generated.
4
+ db/schema.rb linguist-generated
5
+
6
+ # Mark any vendored files as having been vendored.
7
+ vendor/* linguist-vendored
8
+ config/credentials/*.yml.enc diff=rails_credentials
9
+ config/credentials.yml.enc diff=rails_credentials
@@ -0,0 +1,32 @@
1
+ # See https://help.github.com/articles/ignoring-files for more about ignoring files.
2
+ #
3
+ # Temporary files generated by your text editor or operating system
4
+ # belong in git's global ignore instead:
5
+ # `$XDG_CONFIG_HOME/git/ignore` or `~/.config/git/ignore`
6
+
7
+ # Ignore bundler config.
8
+ /.bundle
9
+
10
+ # Ignore all environment files.
11
+ /.env*
12
+
13
+ # Ignore all logfiles and tempfiles.
14
+ /log/*
15
+ /tmp/*
16
+ !/log/.keep
17
+ !/tmp/.keep
18
+
19
+ # Ignore pidfiles, but keep the directory.
20
+ /tmp/pids/*
21
+ !/tmp/pids/
22
+ !/tmp/pids/.keep
23
+
24
+ # Ignore storage (uploaded files in development and any SQLite databases).
25
+ /storage/*
26
+ !/storage/.keep
27
+ /tmp/storage/*
28
+ !/tmp/storage/
29
+ !/tmp/storage/.keep
30
+
31
+ # Ignore master key for decrypting credentials and more.
32
+ /config/master.key
@@ -0,0 +1,3 @@
1
+ #!/bin/sh
2
+
3
+ echo "Docker set up on $KAMAL_HOSTS..."
@@ -0,0 +1,14 @@
1
+ #!/bin/sh
2
+
3
+ # A sample post-deploy hook
4
+ #
5
+ # These environment variables are available:
6
+ # KAMAL_RECORDED_AT
7
+ # KAMAL_PERFORMER
8
+ # KAMAL_VERSION
9
+ # KAMAL_HOSTS
10
+ # KAMAL_ROLE (if set)
11
+ # KAMAL_DESTINATION (if set)
12
+ # KAMAL_RUNTIME
13
+
14
+ echo "$KAMAL_PERFORMER deployed $KAMAL_VERSION to $KAMAL_DESTINATION in $KAMAL_RUNTIME seconds"
@@ -0,0 +1,3 @@
1
+ #!/bin/sh
2
+
3
+ echo "Rebooted kamal-proxy on $KAMAL_HOSTS"
@@ -0,0 +1,51 @@
1
+ #!/bin/sh
2
+
3
+ # A sample pre-build hook
4
+ #
5
+ # Checks:
6
+ # 1. We have a clean checkout
7
+ # 2. A remote is configured
8
+ # 3. The branch has been pushed to the remote
9
+ # 4. The version we are deploying matches the remote
10
+ #
11
+ # These environment variables are available:
12
+ # KAMAL_RECORDED_AT
13
+ # KAMAL_PERFORMER
14
+ # KAMAL_VERSION
15
+ # KAMAL_HOSTS
16
+ # KAMAL_ROLE (if set)
17
+ # KAMAL_DESTINATION (if set)
18
+
19
+ if [ -n "$(git status --porcelain)" ]; then
20
+ echo "Git checkout is not clean, aborting..." >&2
21
+ git status --porcelain >&2
22
+ exit 1
23
+ fi
24
+
25
+ first_remote=$(git remote)
26
+
27
+ if [ -z "$first_remote" ]; then
28
+ echo "No git remote set, aborting..." >&2
29
+ exit 1
30
+ fi
31
+
32
+ current_branch=$(git branch --show-current)
33
+
34
+ if [ -z "$current_branch" ]; then
35
+ echo "Not on a git branch, aborting..." >&2
36
+ exit 1
37
+ fi
38
+
39
+ remote_head=$(git ls-remote $first_remote --tags $current_branch | cut -f1)
40
+
41
+ if [ -z "$remote_head" ]; then
42
+ echo "Branch not pushed to remote, aborting..." >&2
43
+ exit 1
44
+ fi
45
+
46
+ if [ "$KAMAL_VERSION" != "$remote_head" ]; then
47
+ echo "Version ($KAMAL_VERSION) does not match remote HEAD ($remote_head), aborting..." >&2
48
+ exit 1
49
+ fi
50
+
51
+ exit 0
@@ -0,0 +1,47 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # A sample pre-connect check
4
+ #
5
+ # Warms DNS before connecting to hosts in parallel
6
+ #
7
+ # These environment variables are available:
8
+ # KAMAL_RECORDED_AT
9
+ # KAMAL_PERFORMER
10
+ # KAMAL_VERSION
11
+ # KAMAL_HOSTS
12
+ # KAMAL_ROLE (if set)
13
+ # KAMAL_DESTINATION (if set)
14
+ # KAMAL_RUNTIME
15
+
16
+ hosts = ENV["KAMAL_HOSTS"].split(",")
17
+ results = nil
18
+ max = 3
19
+
20
+ elapsed = Benchmark.realtime do
21
+ results = hosts.map do |host|
22
+ Thread.new do
23
+ tries = 1
24
+
25
+ begin
26
+ Socket.getaddrinfo(host, 0, Socket::AF_UNSPEC, Socket::SOCK_STREAM, nil, Socket::AI_CANONNAME)
27
+ rescue SocketError
28
+ if tries < max
29
+ puts "Retrying DNS warmup: #{host}"
30
+ tries += 1
31
+ sleep rand
32
+ retry
33
+ else
34
+ puts "DNS warmup failed: #{host}"
35
+ host
36
+ end
37
+ end
38
+
39
+ tries
40
+ end
41
+ end.map(&:value)
42
+ end
43
+
44
+ retries = results.sum - hosts.size
45
+ nopes = results.count { |r| r == max }
46
+
47
+ puts "Prewarmed %d DNS lookups in %.2f sec: %d retries, %d failures" % [ hosts.size, elapsed, retries, nopes ]