Current section

Files

Jump to
shopifex lib shopifex_web controllers auth_controller.ex
Raw

lib/shopifex_web/controllers/auth_controller.ex

defmodule ShopifexWeb.AuthController do
@moduledoc """
You can use this module inside of another controller to handle initial iFrame load and shop installation
Example:
```elixir
defmodule MyAppWeb.AuthController do
use MyAppWeb, :controller
use ShopifexWeb.AuthController
# Thats it! Validation, installation are now handled for you :)
end
```
"""
@type shop :: %{access_token: String.t(), scope: String.t(), url: String.t()}
@doc """
An optional callback called after the installation is completed, the shop is
persisted in the database and webhooks are registered. By default, this function
redirects the user to the app within their Shopify admin panel.
## Example
@impl true
def after_install(conn, shop) do
# send yourself an e-mail about shop installation
# follow default behaviour.
super(conn, shop)
end
"""
@callback after_install(Plug.Conn.t(), shop()) :: Plug.Conn.t()
@doc """
An optional callback which is called after the shop data has been retrieved from
Shopify API. This function should persist the shop data and return a shop record.
## Example
@impl true
def insert_shop(shop) do
# make sure there is only one store in the database because we don't have
# a unique index on the url column for some reason.
case Shopifex.Shops.get_shop_by_url(shop.url) do
nil -> super(shop)
shop -> shop
end
end
"""
@callback insert_shop(shop()) :: shop()
@doc """
An optional callback which you can use to override how your app is rendered on
initial load. If you are building a server-rendered app, you might just want
to redirect to your index page. If you are building an externally hosted SPA,
you probably want to redirect to the Shopify admin link for your app.
Externally hosted SPA's will likely only hit this route on install.
"""
@callback auth(conn :: Plug.Conn.t(), params :: Plug.Conn.params()) :: Plug.Conn.t()
@optional_callbacks after_install: 2, insert_shop: 1, auth: 2
defmacro __using__(_opts) do
quote do
@behaviour ShopifexWeb.AuthController
require Logger
@impl ShopifexWeb.AuthController
def auth(conn, _) do
path_prefix = Application.get_env(:shopifex, :path_prefix, "")
conn
|> redirect(to: path_prefix <> "/?token=" <> Guardian.Plug.current_token(conn))
end
def initialize_installation(conn, %{"shop" => shop_url}) do
if Regex.match?(~r/^.*\.myshopify\.com/, shop_url) do
# check if store is in the system already:
case Shopifex.Shops.get_shop_by_url(shop_url) do
nil ->
Logger.info("Initiating shop installation for #{shop_url}")
install_url =
"https://#{shop_url}/admin/oauth/authorize?client_id=#{
Application.fetch_env!(:shopifex, :api_key)
}&scope=#{Application.fetch_env!(:shopifex, :scopes)}&redirect_uri=#{
Application.fetch_env!(:shopifex, :redirect_uri)
}"
conn
|> redirect(external: install_url)
shop ->
Logger.info("Initiating shop reinstallation for #{shop_url}")
reinstall_url =
"https://#{shop_url}/admin/oauth/request_grant?client_id=#{
Application.fetch_env!(:shopifex, :api_key)
}&scope=#{Application.fetch_env!(:shopifex, :scopes)}&redirect_uri=#{
Application.fetch_env!(:shopifex, :reinstall_uri)
}"
conn
|> redirect(external: reinstall_url)
end
else
conn
|> put_view(ShopifexWeb.AuthView)
|> put_layout({ShopifexWeb.LayoutView, "app.html"})
|> put_flash(:error, "Invalid shop URL")
|> render("select-store.html")
end
end
@impl ShopifexWeb.AuthController
def after_install(conn, shop) do
redirect(conn,
external:
"https://#{shop.url}/admin/apps/#{Application.fetch_env!(:shopifex, :api_key)}"
)
end
@impl ShopifexWeb.AuthController
def insert_shop(shop) do
Shopifex.Shops.create_shop(shop)
end
def install(conn, %{"code" => code, "shop" => shop_url}) do
url = "https://#{shop_url}/admin/oauth/access_token"
case(
HTTPoison.post(
url,
Jason.encode!(%{
client_id: Application.fetch_env!(:shopifex, :api_key),
client_secret: Application.fetch_env!(:shopifex, :secret),
code: code
}),
"Content-Type": "application/json",
Accept: "application/json"
)
) do
{:ok, response} ->
shop =
Jason.decode!(response.body, keys: :atoms)
|> Map.put(:url, shop_url)
|> insert_shop()
Shopifex.Shops.configure_webhooks(shop)
after_install(conn, shop)
error ->
raise(Shopifex.InstallError, message: "Installation failed for shop #{shop_url}")
end
end
def update(conn, %{"code" => code, "shop" => shop_url}) do
url = "https://#{shop_url}/admin/oauth/access_token"
case(
HTTPoison.post(
url,
Jason.encode!(%{
client_id: Application.fetch_env!(:shopifex, :api_key),
client_secret: Application.fetch_env!(:shopifex, :secret),
code: code
}),
"Content-Type": "application/json",
Accept: "application/json"
)
) do
{:ok, response} ->
params = Jason.decode!(response.body, keys: :atoms)
shop =
shop_url
|> Shopifex.Shops.get_shop_by_url()
|> Shopifex.Shops.update_shop(params)
|> Shopifex.Shops.configure_webhooks()
redirect(conn,
external:
"https://#{shop_url}/admin/apps/#{Application.fetch_env!(:shopifex, :api_key)}"
)
error ->
raise(Shopifex.UpdateError, message: "Update failed for shop #{shop_url}")
end
end
defoverridable after_install: 2, insert_shop: 1, auth: 2
end
end
end