shopify-sinatra-app 0.12.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.circleci/config.yml +15 -0
- data/.github/dependabot.yml +14 -0
- data/CHANGELOG +9 -0
- data/README.md +13 -69
- data/{test.sh → bin/ci.sh} +2 -5
- data/example/Gemfile +1 -3
- data/example/Rakefile +0 -18
- data/example/config.ru +1 -5
- data/example/db/schema.rb +2 -2
- data/example/src/app.rb +2 -4
- data/example/test/app_test.rb +3 -3
- data/example/views/home.erb +2 -0
- data/example/views/{install.erb → login.erb} +10 -8
- data/lib/sinatra/shopify-sinatra-app.rb +70 -109
- data/shopify-sinatra-app.gemspec +4 -4
- metadata +20 -13
- data/.travis.yml +0 -13
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 46049e2afcd7dce3a1d3f115ba665a731d4fcb1b5344dcc9ff84649cfd30beda
|
4
|
+
data.tar.gz: 2ba425471b1cbb5e40f91178d646d8c0dd4da456321518a0772afb7ec4359058
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c7223a2223ba1a71865bdbf05ef3d20585723da858cc99b2ac3872c13c29a3cb6db4e03502a8412f329393e6458e4d2c8038355abcd81247992bd44cf4ff8826
|
7
|
+
data.tar.gz: 81f547ba807a56f66802482746ff9a7d6d4ccacf7c9c5d3650491e3cabde7280fed22ce576b42a901f0ce5a57f36cef355108ac12f82223aeca33b9dc8ca1ac3
|
data/CHANGELOG
CHANGED
@@ -1,3 +1,12 @@
|
|
1
|
+
1.0.0
|
2
|
+
-----
|
3
|
+
* Update to omniauth 2.0.4 and add rack protection authenticity token
|
4
|
+
* All forms will need an authenticity_token param added like in the login view
|
5
|
+
* Refactored the provided routes to be simpler. Now only login and logout (no more install)
|
6
|
+
* shopify_webhook now defines the route because it needs to also configure ignoring csrf for the route
|
7
|
+
* refactored other internal methods to simplify and reduce surface area
|
8
|
+
* Removed heroku rake tasks. They are out of date and don't encourage best practises
|
9
|
+
|
1
10
|
0.12.0
|
2
11
|
------
|
3
12
|
* Update to use the Shopify AppBridge instead of the ESDK
|
data/README.md
CHANGED
@@ -58,7 +58,10 @@ This will create a new skeleton shopify-sinatra-app. The generator will create s
|
|
58
58
|
|
59
59
|
### Setting the app to use your Shopify API credentials
|
60
60
|
|
61
|
-
You'll need to create a Shopify Partner Account and a new application. You can make an account [here](http://www.shopify.ca/partners) and see this [tutorial](http://docs.shopify.com/api/the-basics/getting-started) for creating a new application.
|
61
|
+
You'll need to create a Shopify Partner Account and a new application. You can make an account [here](http://www.shopify.ca/partners) and see this [tutorial](http://docs.shopify.com/api/the-basics/getting-started) for creating a new application. The requires 2 redirects configured:
|
62
|
+
|
63
|
+
* the default redirect_uri from omniauth `<your domain>/auth/shopify/callback`
|
64
|
+
* and `<your domain>/login`
|
62
65
|
|
63
66
|
Note - The shopify-sinatra-app creates an embedded app! You need change the embedded setting to `enabled` in the [Shopify Partner area](https://app.shopify.com/services/partners/api_clients) for your app. If you don't want your app to be embedded then remove the related code in `layout/application.erb` and delete the `layout/_top_bar.erb` file and the references to it in the other views.
|
64
67
|
|
@@ -87,13 +90,11 @@ get '/products.json' do
|
|
87
90
|
end
|
88
91
|
```
|
89
92
|
|
90
|
-
**shopify_webhook** - This method
|
93
|
+
**shopify_webhook** - This method defines a `post` endpoint that receives a webhook from Shopify. Webhooks are a great way to keep your app in sync with a shop's data without polling. You can read more about webhooks [here](http://docs.shopify.com/api/tutorials/using-webhooks). This method also takes a block and yields the `shop_name` and `webhook_body` as a hash (note only works for json webhooks, don't use xml). Here is an example that listens to an order creation webhook:
|
91
94
|
|
92
95
|
```ruby
|
93
|
-
|
94
|
-
|
95
|
-
# do something with the data
|
96
|
-
end
|
96
|
+
shopify_webhook('/order.json') do |shop_name, webhook_data|
|
97
|
+
# do something with the data
|
97
98
|
end
|
98
99
|
```
|
99
100
|
|
@@ -107,14 +108,11 @@ ShopifyAPI::Base.activate_session(api_session)
|
|
107
108
|
|
108
109
|
It's impossible to control the flow of webhooks to your app from Shopify especially if a larger store installs your app or if a shop has a flash sale. To prevent your app from getting overloaded with webhook requests it is best practise to process webhooks in a background queue and return a `200` to Shopify immediately. Ruby has several good background job frameworks that work with Sinatra including [Sidekiq](https://github.com/mperham/sidekiq) and [Resque](https://github.com/resque/resque).
|
109
110
|
|
110
|
-
|
111
111
|
**after_shopify_auth** - This is a private method provided with the framework that gets called whenever the app is authorized. You should fill this method in with anything you need to initialize, for example webhooks and services on Shopify or any other database models you have created specific to a shop. Note that this method will be called anytime the auth flow is completed so this method should be idempotent (running it twice has the same effect as running it once).
|
112
112
|
|
113
|
-
**logout** - This method clears the current session
|
114
|
-
|
115
113
|
shopify-sinatra-app includes sinatra/activerecord for creating models that can be persisted in the database. You might want to read more about sinatra/activerecord and the methods it makes available to you: [https://github.com/janko-m/sinatra-activerecord](https://github.com/janko-m/sinatra-activerecord)
|
116
114
|
|
117
|
-
shopify-sinatra-app also includes `
|
115
|
+
shopify-sinatra-app also includes `sinatra-flash` and the flash messages are forwarded to the Shopify Embedded App SDK (see the code in `views/layouts/application.erb`). Flash messages are useful for signalling to your users that a request was successful without changing the page. The following is an example of how to use a flash message in a route:
|
118
116
|
|
119
117
|
```ruby
|
120
118
|
post '/flash_message' do
|
@@ -128,17 +126,17 @@ note - a flash must be followed by a redirect or it won't work!
|
|
128
126
|
|
129
127
|
Developing
|
130
128
|
----------
|
131
|
-
The embedded app sdk won't load non https content so you'll need to use a forwarding service like [ngrok](https://ngrok.com/)
|
129
|
+
The embedded app sdk won't load non https content so you'll need to use a real domain or a forwarding service like [ngrok](https://ngrok.com/). Set your application url in the [Shopify Partner area](https://app.shopify.com/services/partners/api_clients) to your forwarded url and set the redirect_uri to your forwarded url + `/auth/shopify/callback` which will allow you to install your app on a live shop while running it locally.
|
132
130
|
|
133
|
-
To run the app locally we use
|
131
|
+
To run the app locally we use [overmind](https://github.com/DarthSim/overmind) a tool for running multiple process and setting our credentials as environment variables. To run the application run:
|
134
132
|
|
135
133
|
```
|
136
|
-
|
134
|
+
overmind start
|
137
135
|
```
|
138
136
|
|
139
|
-
|
137
|
+
To connect to a single process to use a debugger/break point use `overmind connect <process>`
|
140
138
|
|
141
|
-
To debug your app
|
139
|
+
To debug your app add `require 'byebug'` at the top and then add `byebug` to your code where you would like to drop into an interactive session. You may also want to try out [Pry](http://pryrepl.org/).
|
142
140
|
|
143
141
|
If you are testing webhooks locally make sure they also go through the forwarded url and not `localhost`.
|
144
142
|
|
@@ -158,60 +156,6 @@ bundle exec rake test
|
|
158
156
|
Checkout the contents of the `app_test.rb` file and the `test_helper.rb` and modify them as you add functionality to your app. You can also check the tests of other apps using this framework to see more about how to write tests for your own app.
|
159
157
|
|
160
158
|
|
161
|
-
Deploying
|
162
|
-
---------
|
163
|
-
|
164
|
-
This template was created with deploying to Heroku in mind. Heroku is a cloud based app hosting provider that makes it easy to get an application into a product environment.
|
165
|
-
|
166
|
-
Before you can get started with Heroku you need to create a git repo for you application:
|
167
|
-
|
168
|
-
```
|
169
|
-
git init
|
170
|
-
git add .
|
171
|
-
git commit -m "initial commit"
|
172
|
-
```
|
173
|
-
|
174
|
-
Now you can create a new heroku application. Download the [Heroku Toolbelt](https://devcenter.heroku.com/articles/quickstart) and run the following command to create a new application:
|
175
|
-
|
176
|
-
```
|
177
|
-
heroku apps:create <your new app name>
|
178
|
-
```
|
179
|
-
|
180
|
-
You will also need to add the following (free) add-ons to your new Heroku app:
|
181
|
-
|
182
|
-
```
|
183
|
-
heroku addons:add heroku-postgresql
|
184
|
-
```
|
185
|
-
|
186
|
-
Now we can deploy the new application to Heroku. Deploying to Heroku is as simple as pushing the code using git:
|
187
|
-
|
188
|
-
```
|
189
|
-
git push heroku master
|
190
|
-
```
|
191
|
-
|
192
|
-
A `rake deploy2heroku` command is included in the generated Rakefile which does just this.
|
193
|
-
|
194
|
-
Now that our application is deployed we need to run `rake db:migrate` to initialize our database on Heroku. To do this run:
|
195
|
-
|
196
|
-
```
|
197
|
-
heroku run rake db:migrate
|
198
|
-
```
|
199
|
-
|
200
|
-
We also need to set our environment variables on Heroku. The environment variables are stored in `.env` and are not tracked by git. This is to protect your credentials in the case of a source control breach. Heroku provides a command to set environment variables: `heroku config:set VAR=foo`. In the generated Rakefile there is a helper method that will properly set all the variables in your `.env` file:
|
201
|
-
|
202
|
-
```
|
203
|
-
rake creds2heroku
|
204
|
-
```
|
205
|
-
|
206
|
-
and make sure you have at least 1 dyno for web:
|
207
|
-
|
208
|
-
```
|
209
|
-
heroku scale web=1
|
210
|
-
```
|
211
|
-
|
212
|
-
Make sure you set your shopify apps url to your Heroku app url (and make sure to use the `https` version or else the Embedded App SDK won't work) in the Shopify Partner area https://app.shopify.com/services/partners/api_clients.
|
213
|
-
|
214
|
-
|
215
159
|
Apps using this framework
|
216
160
|
-------------------------
|
217
161
|
|
data/{test.sh → bin/ci.sh}
RENAMED
data/example/Gemfile
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
source 'https://rubygems.org'
|
2
|
-
ruby '2.6.
|
2
|
+
ruby '2.6.6'
|
3
3
|
|
4
4
|
gem 'shopify-sinatra-app', path: '../'
|
5
5
|
gem 'sinatra-activerecord'
|
@@ -16,8 +16,6 @@ end
|
|
16
16
|
|
17
17
|
group :development do
|
18
18
|
gem 'rake', '>= 12.3.3'
|
19
|
-
gem 'foreman'
|
20
|
-
gem 'dotenv'
|
21
19
|
end
|
22
20
|
|
23
21
|
group :test do
|
data/example/Rakefile
CHANGED
@@ -2,24 +2,6 @@ require 'sinatra/activerecord/rake'
|
|
2
2
|
require 'rake/testtask'
|
3
3
|
require './src/app'
|
4
4
|
|
5
|
-
task :creds2heroku do
|
6
|
-
Bundler.with_clean_env do
|
7
|
-
File.readlines('.env').each do |var|
|
8
|
-
pipe = IO.popen("heroku config:set #{var}")
|
9
|
-
while (line = pipe.gets)
|
10
|
-
print line
|
11
|
-
end
|
12
|
-
end
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
task :deploy2heroku do
|
17
|
-
pipe = IO.popen('git push heroku master --force')
|
18
|
-
while (line = pipe.gets)
|
19
|
-
print line
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
5
|
namespace :test do
|
24
6
|
task :prepare do
|
25
7
|
`RACK_ENV=test rake db:create`
|
data/example/config.ru
CHANGED
data/example/db/schema.rb
CHANGED
@@ -2,8 +2,8 @@
|
|
2
2
|
# of editing this file, please use the migrations feature of Active Record to
|
3
3
|
# incrementally modify your database, and then regenerate this schema definition.
|
4
4
|
#
|
5
|
-
# This file is the source Rails uses to define your schema when running `rails
|
6
|
-
# db:schema:load`. When creating a new database, `rails db:schema:load` tends to
|
5
|
+
# This file is the source Rails uses to define your schema when running `bin/rails
|
6
|
+
# db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to
|
7
7
|
# be faster and is potentially less error prone than running all of your
|
8
8
|
# migrations from scratch. Old migrations may fail to apply correctly if those
|
9
9
|
# migrations use external dependencies or application code.
|
data/example/src/app.rb
CHANGED
@@ -23,10 +23,8 @@ class SinatraApp < Sinatra::Base
|
|
23
23
|
# this endpoint recieves the uninstall webhook
|
24
24
|
# and cleans up data, add to this endpoint as your app
|
25
25
|
# stores more data.
|
26
|
-
|
27
|
-
|
28
|
-
Shop.find_by(name: shop_name).destroy
|
29
|
-
end
|
26
|
+
shopify_webhook '/uninstall' do |shop_name, params|
|
27
|
+
Shop.find_by(name: shop_name).destroy
|
30
28
|
end
|
31
29
|
|
32
30
|
private
|
data/example/test/app_test.rb
CHANGED
@@ -42,18 +42,18 @@ class AppTest < Minitest::Test
|
|
42
42
|
def test_root_without_session_redirects_to_install
|
43
43
|
get '/'
|
44
44
|
assert_equal 302, last_response.status
|
45
|
-
assert_equal 'http://example.org/
|
45
|
+
assert_equal 'http://example.org/login', last_response.location
|
46
46
|
end
|
47
47
|
|
48
48
|
def test_root_with_shop_redirects_to_auth
|
49
49
|
get '/?shop=othertestshop.myshopify.com'
|
50
|
-
|
50
|
+
assert_equal 'http://example.org/login?shop=othertestshop.myshopify.com', last_response.location
|
51
51
|
end
|
52
52
|
|
53
53
|
def test_root_with_session_and_new_shop_redirects_to_auth
|
54
54
|
set_session
|
55
55
|
get '/?shop=othertestshop.myshopify.com'
|
56
|
-
|
56
|
+
assert_equal 'http://example.org/login?shop=othertestshop.myshopify.com', last_response.location
|
57
57
|
end
|
58
58
|
|
59
59
|
def test_root_rescues_UnauthorizedAccess_clears_session_and_redirects
|
data/example/views/home.erb
CHANGED
@@ -28,13 +28,15 @@
|
|
28
28
|
<div style="margin-top: 30px"></div>
|
29
29
|
|
30
30
|
<div class="container">
|
31
|
-
<form role="form" class="form-large" action="/
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
</
|
31
|
+
<form id="login" role="form" class="form-large" action="/auth/shopify" method="post">
|
32
|
+
<input type="hidden" name="authenticity_token" value="<%= env['rack.session'][:csrf] %>" />
|
33
|
+
<div class="input-group form-wide">
|
34
|
+
<input class="form-control" name="shop" value="<%= params['shop'] %>" placeholder="your-shop-url.myshopify.com">
|
35
|
+
<span class="input-group-btn">
|
36
|
+
<input class="btn" type="submit" value="Login" />
|
37
|
+
</span>
|
38
|
+
</div>
|
38
39
|
</form>
|
39
40
|
</div>
|
40
|
-
</body>
|
41
|
+
</body>
|
42
|
+
|
@@ -11,15 +11,10 @@ module Sinatra
|
|
11
11
|
module Shopify
|
12
12
|
module Methods
|
13
13
|
|
14
|
-
# designed to be
|
14
|
+
# designed to be overridden
|
15
15
|
def after_shopify_auth
|
16
16
|
end
|
17
17
|
|
18
|
-
def logout
|
19
|
-
session.delete(:shopify)
|
20
|
-
session.clear
|
21
|
-
end
|
22
|
-
|
23
18
|
# for the app bridge initializer
|
24
19
|
def shop_origin
|
25
20
|
"#{session[:shopify][:shop]}"
|
@@ -31,35 +26,42 @@ module Sinatra
|
|
31
26
|
|
32
27
|
if no_session?
|
33
28
|
authenticate(return_to, return_params)
|
29
|
+
|
34
30
|
elsif different_shop?
|
35
|
-
|
31
|
+
clear_session
|
36
32
|
authenticate(return_to, return_params)
|
33
|
+
|
37
34
|
else
|
38
35
|
shop_name = session[:shopify][:shop]
|
39
36
|
token = session[:shopify][:token]
|
40
37
|
activate_shopify_api(shop_name, token)
|
41
38
|
yield shop_name
|
42
39
|
end
|
40
|
+
|
43
41
|
rescue ActiveResource::UnauthorizedAccess
|
44
|
-
clear_session
|
45
|
-
redirect request.path
|
46
|
-
end
|
42
|
+
clear_session
|
47
43
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
status 200
|
44
|
+
shop = Shop.find_by(name: shop_name)
|
45
|
+
shop.token = nil
|
46
|
+
shop.save
|
47
|
+
|
48
|
+
redirect request.path
|
54
49
|
end
|
55
50
|
|
56
51
|
private
|
57
52
|
|
58
|
-
def
|
59
|
-
|
53
|
+
def authenticate(return_to = '/', return_params = nil)
|
54
|
+
session[:return_params] = return_params if return_params
|
55
|
+
|
56
|
+
if shop_name = sanitized_shop_param(params)
|
57
|
+
redirect "/login?shop=#{shop_name}"
|
58
|
+
else
|
59
|
+
redirect '/login'
|
60
|
+
end
|
60
61
|
end
|
61
62
|
|
62
63
|
def base_url
|
64
|
+
request_protocol = request.secure? ? 'https' : 'http'
|
63
65
|
"#{request_protocol}://#{request.env['HTTP_HOST']}"
|
64
66
|
end
|
65
67
|
|
@@ -68,17 +70,12 @@ module Sinatra
|
|
68
70
|
end
|
69
71
|
|
70
72
|
def different_shop?
|
71
|
-
params[:shop].present? && session[:shopify][:shop] !=
|
73
|
+
params[:shop].present? && session[:shopify][:shop] != sanitized_shop_param(params)
|
72
74
|
end
|
73
75
|
|
74
|
-
def
|
75
|
-
|
76
|
-
|
77
|
-
redirect_url = "#{base_url}/auth/shopify"
|
78
|
-
redirect_javascript redirect_url
|
79
|
-
else
|
80
|
-
redirect '/install'
|
81
|
-
end
|
76
|
+
def clear_session
|
77
|
+
session.delete(:shopify)
|
78
|
+
session.clear
|
82
79
|
end
|
83
80
|
|
84
81
|
def activate_shopify_api(shop_name, token)
|
@@ -86,66 +83,15 @@ module Sinatra
|
|
86
83
|
ShopifyAPI::Base.activate_session(api_session)
|
87
84
|
end
|
88
85
|
|
89
|
-
def
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
def redirect_javascript(url)
|
97
|
-
erb %(
|
98
|
-
<!DOCTYPE html>
|
99
|
-
<html lang="en">
|
100
|
-
<head>
|
101
|
-
<meta charset="utf-8" />
|
102
|
-
<base target="_top">
|
103
|
-
<title>Redirecting…</title>
|
104
|
-
<script src="https://unpkg.com/@shopify/app-bridge"></script>
|
105
|
-
<script type='text/javascript'>
|
106
|
-
var AppBridge = window['app-bridge'];
|
107
|
-
var createApp = AppBridge.createApp;
|
108
|
-
var actions = AppBridge.actions;
|
109
|
-
var Redirect = actions.Redirect;
|
110
|
-
|
111
|
-
var apiKey = '#{settings.api_key}';
|
112
|
-
var redirectUri = '#{url}';
|
113
|
-
var shopOrigin = '#{sanitized_shop_name}';
|
114
|
-
|
115
|
-
var permissionUrl = 'https://'+
|
116
|
-
shopOrigin+
|
117
|
-
'/admin'+
|
118
|
-
'/oauth/authorize?client_id='+
|
119
|
-
apiKey+
|
120
|
-
'&scope=#{settings.scope}&redirect_uri='+
|
121
|
-
redirectUri;
|
122
|
-
|
123
|
-
// If the current window is the 'parent', change the URL by setting location.href
|
124
|
-
if (window.top == window.self) {
|
125
|
-
window.location.assign(permissionUrl);
|
126
|
-
|
127
|
-
// If the current window is the 'child', change the parent's URL with Shopify App Bridge's Redirect action
|
128
|
-
} else {
|
129
|
-
var app = createApp({
|
130
|
-
apiKey: apiKey,
|
131
|
-
shopOrigin: shopOrigin
|
132
|
-
});
|
133
|
-
|
134
|
-
Redirect.create(app).dispatch(Redirect.Action.REMOTE, permissionUrl);
|
135
|
-
}
|
136
|
-
</script>
|
137
|
-
</head>
|
138
|
-
<body>
|
139
|
-
</body>
|
140
|
-
</html>
|
141
|
-
), layout: false
|
142
|
-
end
|
143
|
-
|
144
|
-
def sanitized_shop_name
|
145
|
-
@sanitized_shop_name ||= sanitize_shop_param(params)
|
86
|
+
def receive_webhook(&blk)
|
87
|
+
return unless verify_shopify_webhook
|
88
|
+
shop_name = request.env['HTTP_X_SHOPIFY_SHOP_DOMAIN']
|
89
|
+
webhook_body = ActiveSupport::JSON.decode(request.body.read.to_s)
|
90
|
+
yield shop_name, webhook_body
|
91
|
+
status 200
|
146
92
|
end
|
147
93
|
|
148
|
-
def
|
94
|
+
def sanitized_shop_param(params)
|
149
95
|
return unless params[:shop].present?
|
150
96
|
name = params[:shop].to_s.strip
|
151
97
|
name += '.myshopify.com' if !name.include?('myshopify.com') && !name.include?('.')
|
@@ -165,21 +111,30 @@ module Sinatra
|
|
165
111
|
if calculated_hmac == request.env['HTTP_X_SHOPIFY_HMAC_SHA256']
|
166
112
|
true
|
167
113
|
else
|
168
|
-
puts 'Shopify Webhook
|
114
|
+
puts 'Shopify Webhook verification failed!'
|
169
115
|
false
|
170
116
|
end
|
171
117
|
end
|
172
118
|
end
|
173
119
|
|
120
|
+
def shopify_webhook(route, &blk)
|
121
|
+
settings.webhook_routes << route
|
122
|
+
post(route) do
|
123
|
+
receive_webhook(&blk)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
174
127
|
def self.registered(app)
|
175
128
|
app.helpers Shopify::Methods
|
176
129
|
app.register Sinatra::ActiveRecordExtension
|
177
|
-
app.enable :inline_templates
|
178
130
|
|
179
131
|
app.set :database_file, File.expand_path('config/database.yml')
|
132
|
+
|
133
|
+
app.set :erb, layout: :'layouts/application'
|
180
134
|
app.set :views, File.expand_path('views')
|
181
135
|
app.set :public_folder, File.expand_path('public')
|
182
|
-
app.
|
136
|
+
app.enable :inline_templates
|
137
|
+
|
183
138
|
app.set :protection, except: :frame_options
|
184
139
|
|
185
140
|
app.set :api_version, '2019-07'
|
@@ -189,7 +144,12 @@ module Sinatra
|
|
189
144
|
app.set :shared_secret, ENV['SHOPIFY_SHARED_SECRET']
|
190
145
|
app.set :secret, ENV['SECRET']
|
191
146
|
|
147
|
+
# csrf needs to be disabled for webhook routes
|
148
|
+
app.set :webhook_routes, ['/uninstall']
|
149
|
+
|
150
|
+
# add support for put/patch/delete
|
192
151
|
app.use Rack::MethodOverride
|
152
|
+
|
193
153
|
app.use Rack::Session::Cookie, key: 'rack.session',
|
194
154
|
path: '/',
|
195
155
|
secure: true,
|
@@ -197,18 +157,27 @@ module Sinatra
|
|
197
157
|
secret: app.settings.secret,
|
198
158
|
expire_after: 60 * 30 # half an hour in seconds
|
199
159
|
|
160
|
+
app.use Rack::Protection::AuthenticityToken, allow_if: lambda { |env|
|
161
|
+
app.settings.webhook_routes.include?(env["PATH_INFO"])
|
162
|
+
}
|
163
|
+
|
164
|
+
OmniAuth.config.allowed_request_methods = [:post]
|
165
|
+
|
200
166
|
app.use OmniAuth::Builder do
|
201
167
|
provider :shopify,
|
202
|
-
|
203
|
-
|
168
|
+
app.settings.api_key,
|
169
|
+
app.settings.shared_secret,
|
170
|
+
scope: app.settings.scope,
|
171
|
+
setup: lambda { |env|
|
172
|
+
shop = if env['REQUEST_METHOD'] == 'POST'
|
173
|
+
env['rack.request.form_hash']['shop']
|
174
|
+
else
|
175
|
+
Rack::Utils.parse_query(env['QUERY_STRING'])['shop']
|
176
|
+
end
|
204
177
|
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
params = Rack::Utils.parse_query(env['QUERY_STRING'])
|
209
|
-
site_url = "https://#{params['shop']}"
|
210
|
-
env['omniauth.strategy'].options[:client_options][:site] = site_url
|
211
|
-
}
|
178
|
+
site_url = "https://#{shop}"
|
179
|
+
env['omniauth.strategy'].options[:client_options][:site] = site_url
|
180
|
+
}
|
212
181
|
end
|
213
182
|
|
214
183
|
ShopifyAPI::Session.setup(
|
@@ -216,21 +185,13 @@ module Sinatra
|
|
216
185
|
secret: app.settings.shared_secret
|
217
186
|
)
|
218
187
|
|
219
|
-
app.get '/
|
220
|
-
|
221
|
-
authenticate
|
222
|
-
else
|
223
|
-
erb :install, layout: false
|
224
|
-
end
|
225
|
-
end
|
226
|
-
|
227
|
-
app.post '/login' do
|
228
|
-
authenticate
|
188
|
+
app.get '/login' do
|
189
|
+
erb :login, layout: false
|
229
190
|
end
|
230
191
|
|
231
192
|
app.get '/logout' do
|
232
|
-
|
233
|
-
redirect '/
|
193
|
+
clear_session
|
194
|
+
redirect '/login'
|
234
195
|
end
|
235
196
|
|
236
197
|
app.get '/auth/shopify/callback' do
|
data/shopify-sinatra-app.gemspec
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = 'shopify-sinatra-app'
|
3
|
-
s.version = '0.
|
3
|
+
s.version = '1.0.0'
|
4
4
|
|
5
5
|
s.summary = 'A classy shopify app'
|
6
6
|
s.description = 'A Sinatra extension for building Shopify Apps. Akin to the shopify_app gem but for Sinatra'
|
@@ -13,14 +13,14 @@ Gem::Specification.new do |s|
|
|
13
13
|
s.files = `git ls-files`.split("\n")
|
14
14
|
s.executables << 'shopify-sinatra-app-generator'
|
15
15
|
|
16
|
-
s.add_runtime_dependency 'sinatra', '
|
16
|
+
s.add_runtime_dependency 'sinatra', '>= 2.0.2', '< 2.2.0'
|
17
17
|
s.add_runtime_dependency 'sinatra-activerecord', '~> 2.0.9'
|
18
18
|
s.add_runtime_dependency 'activesupport'
|
19
19
|
s.add_runtime_dependency 'attr_encrypted', '~> 3.1.0'
|
20
20
|
|
21
|
-
s.add_runtime_dependency 'shopify_api', '>= 7.0.1', '< 9.
|
21
|
+
s.add_runtime_dependency 'shopify_api', '>= 7.0.1', '< 9.4.0'
|
22
22
|
s.add_runtime_dependency 'omniauth-shopify-oauth2', '>= 2.3.2'
|
23
|
-
s.add_runtime_dependency 'omniauth', '
|
23
|
+
s.add_runtime_dependency 'omniauth', '>= 2.0.4'
|
24
24
|
|
25
25
|
s.add_development_dependency 'rake', '>= 12.3.3'
|
26
26
|
s.add_development_dependency 'sqlite3'
|
metadata
CHANGED
@@ -1,29 +1,35 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: shopify-sinatra-app
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kevin Hughes
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-06-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: sinatra
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - "
|
17
|
+
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: 2.0.2
|
20
|
+
- - "<"
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 2.2.0
|
20
23
|
type: :runtime
|
21
24
|
prerelease: false
|
22
25
|
version_requirements: !ruby/object:Gem::Requirement
|
23
26
|
requirements:
|
24
|
-
- - "
|
27
|
+
- - ">="
|
25
28
|
- !ruby/object:Gem::Version
|
26
29
|
version: 2.0.2
|
30
|
+
- - "<"
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 2.2.0
|
27
33
|
- !ruby/object:Gem::Dependency
|
28
34
|
name: sinatra-activerecord
|
29
35
|
requirement: !ruby/object:Gem::Requirement
|
@@ -75,7 +81,7 @@ dependencies:
|
|
75
81
|
version: 7.0.1
|
76
82
|
- - "<"
|
77
83
|
- !ruby/object:Gem::Version
|
78
|
-
version: 9.
|
84
|
+
version: 9.4.0
|
79
85
|
type: :runtime
|
80
86
|
prerelease: false
|
81
87
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -85,7 +91,7 @@ dependencies:
|
|
85
91
|
version: 7.0.1
|
86
92
|
- - "<"
|
87
93
|
- !ruby/object:Gem::Version
|
88
|
-
version: 9.
|
94
|
+
version: 9.4.0
|
89
95
|
- !ruby/object:Gem::Dependency
|
90
96
|
name: omniauth-shopify-oauth2
|
91
97
|
requirement: !ruby/object:Gem::Requirement
|
@@ -104,16 +110,16 @@ dependencies:
|
|
104
110
|
name: omniauth
|
105
111
|
requirement: !ruby/object:Gem::Requirement
|
106
112
|
requirements:
|
107
|
-
- -
|
113
|
+
- - ">="
|
108
114
|
- !ruby/object:Gem::Version
|
109
|
-
version:
|
115
|
+
version: 2.0.4
|
110
116
|
type: :runtime
|
111
117
|
prerelease: false
|
112
118
|
version_requirements: !ruby/object:Gem::Requirement
|
113
119
|
requirements:
|
114
|
-
- -
|
120
|
+
- - ">="
|
115
121
|
- !ruby/object:Gem::Version
|
116
|
-
version:
|
122
|
+
version: 2.0.4
|
117
123
|
- !ruby/object:Gem::Dependency
|
118
124
|
name: rake
|
119
125
|
requirement: !ruby/object:Gem::Requirement
|
@@ -206,12 +212,14 @@ executables:
|
|
206
212
|
extensions: []
|
207
213
|
extra_rdoc_files: []
|
208
214
|
files:
|
215
|
+
- ".circleci/config.yml"
|
216
|
+
- ".github/dependabot.yml"
|
209
217
|
- ".gitignore"
|
210
|
-
- ".travis.yml"
|
211
218
|
- CHANGELOG
|
212
219
|
- Gemfile
|
213
220
|
- LICENSE
|
214
221
|
- README.md
|
222
|
+
- bin/ci.sh
|
215
223
|
- bin/shopify-sinatra-app-generator
|
216
224
|
- example/.gitignore
|
217
225
|
- example/Gemfile
|
@@ -232,11 +240,10 @@ files:
|
|
232
240
|
- example/views/_flash_messages.erb
|
233
241
|
- example/views/_top_bar.erb
|
234
242
|
- example/views/home.erb
|
235
|
-
- example/views/install.erb
|
236
243
|
- example/views/layouts/application.erb
|
244
|
+
- example/views/login.erb
|
237
245
|
- lib/sinatra/shopify-sinatra-app.rb
|
238
246
|
- shopify-sinatra-app.gemspec
|
239
|
-
- test.sh
|
240
247
|
homepage: https://github.com/kevinhughes27/shopify-sinatra-app/
|
241
248
|
licenses:
|
242
249
|
- MIT
|