Compare commits
3 Commits
819b84caf7
...
7a1b277ea7
Author | SHA1 | Date |
---|---|---|
Shadowfacts | 7a1b277ea7 | |
Shadowfacts | 97a06e7691 | |
Shadowfacts | 8024e27ebe |
|
@ -1,5 +1,5 @@
|
||||||
[
|
[
|
||||||
import_deps: [:ecto, :ecto_sql, :phoenix],
|
import_deps: [:phoenix],
|
||||||
subdirectories: ["priv/*/migrations"],
|
subdirectories: [],
|
||||||
inputs: ["*.{ex,exs}", "{config,lib,test}/**/*.{ex,exs}", "priv/*/seeds.exs"]
|
inputs: ["*.{ex,exs}", "{config,lib,test}/**/*.{ex,exs}"]
|
||||||
]
|
]
|
||||||
|
|
|
@ -8,7 +8,6 @@
|
||||||
import Config
|
import Config
|
||||||
|
|
||||||
config :tusker_push,
|
config :tusker_push,
|
||||||
ecto_repos: [TuskerPush.Repo],
|
|
||||||
generators: [timestamp_type: :utc_datetime]
|
generators: [timestamp_type: :utc_datetime]
|
||||||
|
|
||||||
# Configures the endpoint
|
# Configures the endpoint
|
||||||
|
@ -30,6 +29,12 @@ config :logger, :console,
|
||||||
# Use Jason for JSON parsing in Phoenix
|
# Use Jason for JSON parsing in Phoenix
|
||||||
config :phoenix, :json_library, Jason
|
config :phoenix, :json_library, Jason
|
||||||
|
|
||||||
|
config :sentry,
|
||||||
|
environment_name: Mix.env(),
|
||||||
|
enable_source_code_context: true,
|
||||||
|
root_source_code_paths: [File.cwd!()],
|
||||||
|
client: TuskerPush.SentryFinchClient
|
||||||
|
|
||||||
# Import environment specific config. This must remain at the bottom
|
# Import environment specific config. This must remain at the bottom
|
||||||
# of this file so it overrides the configuration defined above.
|
# of this file so it overrides the configuration defined above.
|
||||||
import_config "#{config_env()}.exs"
|
import_config "#{config_env()}.exs"
|
||||||
|
|
|
@ -1,15 +1,5 @@
|
||||||
import Config
|
import Config
|
||||||
|
|
||||||
# Configure your database
|
|
||||||
config :tusker_push, TuskerPush.Repo,
|
|
||||||
username: "postgres",
|
|
||||||
password: "postgres",
|
|
||||||
hostname: "localhost",
|
|
||||||
database: "tusker_push_dev",
|
|
||||||
stacktrace: true,
|
|
||||||
show_sensitive_data_on_connection_error: true,
|
|
||||||
pool_size: 10
|
|
||||||
|
|
||||||
# For development, we disable any cache and enable
|
# For development, we disable any cache and enable
|
||||||
# debugging and code reloading.
|
# debugging and code reloading.
|
||||||
#
|
#
|
||||||
|
|
|
@ -21,21 +21,6 @@ if System.get_env("PHX_SERVER") do
|
||||||
end
|
end
|
||||||
|
|
||||||
if config_env() == :prod do
|
if config_env() == :prod do
|
||||||
database_url =
|
|
||||||
System.get_env("DATABASE_URL") ||
|
|
||||||
raise """
|
|
||||||
environment variable DATABASE_URL is missing.
|
|
||||||
For example: ecto://USER:PASS@HOST/DATABASE
|
|
||||||
"""
|
|
||||||
|
|
||||||
maybe_ipv6 = if System.get_env("ECTO_IPV6") in ~w(true 1), do: [:inet6], else: []
|
|
||||||
|
|
||||||
config :tusker_push, TuskerPush.Repo,
|
|
||||||
# ssl: true,
|
|
||||||
url: database_url,
|
|
||||||
pool_size: String.to_integer(System.get_env("POOL_SIZE") || "10"),
|
|
||||||
socket_options: maybe_ipv6
|
|
||||||
|
|
||||||
# The secret key base is used to sign/encrypt cookies and other secrets.
|
# The secret key base is used to sign/encrypt cookies and other secrets.
|
||||||
# A default value is used in config/dev.exs and config/test.exs but you
|
# A default value is used in config/dev.exs and config/test.exs but you
|
||||||
# want to use a different value for prod and you most likely don't want
|
# want to use a different value for prod and you most likely don't want
|
||||||
|
@ -96,4 +81,6 @@ if config_env() == :prod do
|
||||||
# force_ssl: [hsts: true]
|
# force_ssl: [hsts: true]
|
||||||
#
|
#
|
||||||
# Check `Plug.SSL` for all available options in `force_ssl`.
|
# Check `Plug.SSL` for all available options in `force_ssl`.
|
||||||
|
|
||||||
|
config :sentry, dsn: System.fetch_env!("SENTRY_DSN")
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,18 +1,5 @@
|
||||||
import Config
|
import Config
|
||||||
|
|
||||||
# Configure your database
|
|
||||||
#
|
|
||||||
# The MIX_TEST_PARTITION environment variable can be used
|
|
||||||
# to provide built-in test partitioning in CI environment.
|
|
||||||
# Run `mix help test` for more information.
|
|
||||||
config :tusker_push, TuskerPush.Repo,
|
|
||||||
username: "postgres",
|
|
||||||
password: "postgres",
|
|
||||||
hostname: "localhost",
|
|
||||||
database: "tusker_push_test#{System.get_env("MIX_TEST_PARTITION")}",
|
|
||||||
pool: Ecto.Adapters.SQL.Sandbox,
|
|
||||||
pool_size: System.schedulers_online() * 2
|
|
||||||
|
|
||||||
# We don't run a server during test. If one is required,
|
# We don't run a server during test. If one is required,
|
||||||
# you can enable the server option below.
|
# you can enable the server option below.
|
||||||
config :tusker_push, TuskerPushWeb.Endpoint,
|
config :tusker_push, TuskerPushWeb.Endpoint,
|
||||||
|
|
|
@ -6,56 +6,4 @@ defmodule TuskerPush do
|
||||||
Contexts are also responsible for managing your data, regardless
|
Contexts are also responsible for managing your data, regardless
|
||||||
if it comes from the database, an external API or others.
|
if it comes from the database, an external API or others.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
alias TuskerPush.Registration
|
|
||||||
alias TuskerPush.Repo
|
|
||||||
|
|
||||||
@spec register(Map.t()) ::
|
|
||||||
{:ok, Registration.t()} | {:error, Ecto.Changeset.t()}
|
|
||||||
def register(params) do
|
|
||||||
params
|
|
||||||
|> Registration.create_changeset()
|
|
||||||
|> Repo.insert()
|
|
||||||
end
|
|
||||||
|
|
||||||
@spec update_registration(Registration.t(), Map.t()) ::
|
|
||||||
{:ok, Registration.t()} | {:error, Ecto.Changeset.t()}
|
|
||||||
def update_registration(registration, params) do
|
|
||||||
registration
|
|
||||||
|> Registration.update_changeset(params)
|
|
||||||
|> Repo.update()
|
|
||||||
end
|
|
||||||
|
|
||||||
@spec unregister(String.t()) :: :ok | {:error, :no_registration | Ecto.Changeset.t()}
|
|
||||||
def unregister(id) when is_binary(id) do
|
|
||||||
with registration when not is_nil(registration) <- Repo.get(Registration, id),
|
|
||||||
{:ok, _} <- Repo.delete(registration) do
|
|
||||||
:ok
|
|
||||||
else
|
|
||||||
nil ->
|
|
||||||
{:error, :no_registration}
|
|
||||||
|
|
||||||
{:error, reason} ->
|
|
||||||
{:error, reason}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
@spec unregister(Registration.t()) :: :ok | {:error, Ecto.Changeset.t()}
|
|
||||||
def unregister(%Registration{} = registration) do
|
|
||||||
case Repo.delete(registration) do
|
|
||||||
{:ok, _} -> :ok
|
|
||||||
{:error, reason} -> {:error, reason}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
@spec get_registration(String.t()) :: Registration.t() | nil
|
|
||||||
def get_registration(id) do
|
|
||||||
Repo.get(Registration, id)
|
|
||||||
end
|
|
||||||
|
|
||||||
@spec check_registration_expired(Registration.t()) :: :ok | {:expired, Registration.t()}
|
|
||||||
def check_registration_expired(_registration) do
|
|
||||||
# TODO: expiration & grace period
|
|
||||||
:ok
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,37 +1,37 @@
|
||||||
defmodule TuskerPush.Apns do
|
defmodule TuskerPush.Apns do
|
||||||
alias TuskerPush.Registration
|
|
||||||
|
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
@spec send(Registration.t(), Map.t()) :: :ok | {:error, term()}
|
@type environment() :: :development | :production
|
||||||
def send(registration, payload) do
|
|
||||||
|
@spec send(environment(), String.t(), Map.t()) :: :ok | {:error, term()}
|
||||||
|
def send(apns_env, apns_device_token, payload) do
|
||||||
with {:ok, body} <- Jason.encode(payload, pretty_print: false),
|
with {:ok, body} <- Jason.encode(payload, pretty_print: false),
|
||||||
req <- make_request(registration, body),
|
req <- make_request(apns_env, apns_device_token, body),
|
||||||
{:ok, resp} <- Finch.request(req, TuskerPush.Finch) do
|
{:ok, resp} <- Finch.request(req, TuskerPush.Finch) do
|
||||||
handle_response(resp, registration, payload)
|
handle_response(resp, apns_env, apns_device_token, payload)
|
||||||
else
|
else
|
||||||
{:error, %Finch.Error{reason: :connection_closed}} ->
|
{:error, %Finch.Error{reason: :connection_closed}} ->
|
||||||
Logger.warning("Apns Finch connection_closed, retrying in 1s")
|
Logger.warning("Apns Finch connection_closed, retrying in 1s")
|
||||||
Process.sleep(1000)
|
Process.sleep(1000)
|
||||||
__MODULE__.send(registration, payload)
|
__MODULE__.send(apns_env, apns_device_token, payload)
|
||||||
|
|
||||||
{:error, %Finch.Error{reason: :disconnected}} ->
|
{:error, %Finch.Error{reason: :disconnected}} ->
|
||||||
Logger.warning("Apns Finch disconnected, retrying in 1s")
|
Logger.warning("Apns Finch disconnected, retrying in 1s")
|
||||||
Process.sleep(1000)
|
Process.sleep(1000)
|
||||||
__MODULE__.send(registration, payload)
|
__MODULE__.send(apns_env, apns_device_token, payload)
|
||||||
|
|
||||||
{:error, %Mint.TransportError{reason: :closed}} ->
|
{:error, %Mint.TransportError{reason: :closed}} ->
|
||||||
Logger.warning("Apns Mint transport closed, retrying in 1s")
|
Logger.warning("Apns Mint transport closed, retrying in 1s")
|
||||||
Process.sleep(1000)
|
Process.sleep(1000)
|
||||||
__MODULE__.send(registration, payload)
|
__MODULE__.send(apns_env, apns_device_token, payload)
|
||||||
|
|
||||||
{:error, reason} ->
|
{:error, reason} ->
|
||||||
{:error, reason}
|
{:error, reason}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec make_request(Registration.t(), binary()) :: Finch.Request.t()
|
@spec make_request(environment(), String.t(), binary()) :: Finch.Request.t()
|
||||||
defp make_request(registration, body) do
|
defp make_request(apns_env, apns_device_token, body) do
|
||||||
bundle_id = Application.fetch_env!(:tusker_push, :apns)[:bundle_id]
|
bundle_id = Application.fetch_env!(:tusker_push, :apns)[:bundle_id]
|
||||||
|
|
||||||
headers = [
|
headers = [
|
||||||
|
@ -45,16 +45,17 @@ defmodule TuskerPush.Apns do
|
||||||
|
|
||||||
Finch.build(
|
Finch.build(
|
||||||
:post,
|
:post,
|
||||||
"https://#{host(registration.apns_environment)}/3/device/#{registration.apns_device_token}",
|
"https://#{host(apns_env)}/3/device/#{apns_device_token}",
|
||||||
headers,
|
headers,
|
||||||
body
|
body
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec handle_response(Finch.Response.t(), Registration.t(), Map.t()) :: :ok | {:error, term()}
|
@spec handle_response(Finch.Response.t(), environment(), String.t(), Map.t()) ::
|
||||||
|
:ok | {:error, term()}
|
||||||
|
|
||||||
defp handle_response(resp, registration, payload) do
|
defp handle_response(resp, apns_env, apns_device_token, payload) do
|
||||||
maybe_log_unique_id(resp, registration.apns_environment)
|
maybe_log_unique_id(resp, apns_env)
|
||||||
|
|
||||||
if resp.status in 200..299 do
|
if resp.status in 200..299 do
|
||||||
:ok
|
:ok
|
||||||
|
@ -71,7 +72,7 @@ defmodule TuskerPush.Apns do
|
||||||
case {resp.status, info} do
|
case {resp.status, info} do
|
||||||
{403, %{reason: "ExpiredProviderToken"}} ->
|
{403, %{reason: "ExpiredProviderToken"}} ->
|
||||||
Logger.warning("Expired provider token, retrying")
|
Logger.warning("Expired provider token, retrying")
|
||||||
__MODULE__.send(registration, payload)
|
__MODULE__.send(apns_env, apns_device_token, payload)
|
||||||
|
|
||||||
{410, %{reason: "Unregistered"}} ->
|
{410, %{reason: "Unregistered"}} ->
|
||||||
Logger.warning("Device token unregistered")
|
Logger.warning("Device token unregistered")
|
||||||
|
@ -106,7 +107,7 @@ defmodule TuskerPush.Apns do
|
||||||
|
|
||||||
defp maybe_log_unique_id(_resp, :production), do: :ok
|
defp maybe_log_unique_id(_resp, :production), do: :ok
|
||||||
|
|
||||||
@spec host(:development | :production) :: String.t()
|
@spec host(environment()) :: String.t()
|
||||||
|
|
||||||
def host(:development) do
|
def host(:development) do
|
||||||
"api.sandbox.push.apple.com"
|
"api.sandbox.push.apple.com"
|
||||||
|
|
|
@ -7,9 +7,12 @@ defmodule TuskerPush.Application do
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def start(_type, _args) do
|
def start(_type, _args) do
|
||||||
|
:logger.add_handler(:tusker_push_sentry_handler, Sentry.LoggerHandler, %{
|
||||||
|
config: %{metadata: [:file, :line]}
|
||||||
|
})
|
||||||
|
|
||||||
children = [
|
children = [
|
||||||
TuskerPushWeb.Telemetry,
|
TuskerPushWeb.Telemetry,
|
||||||
TuskerPush.Repo,
|
|
||||||
{DNSCluster, query: Application.get_env(:tusker_push, :dns_cluster_query) || :ignore},
|
{DNSCluster, query: Application.get_env(:tusker_push, :dns_cluster_query) || :ignore},
|
||||||
{Phoenix.PubSub, name: TuskerPush.PubSub},
|
{Phoenix.PubSub, name: TuskerPush.PubSub},
|
||||||
# Start a worker by calling: TuskerPush.Worker.start_link(arg)
|
# Start a worker by calling: TuskerPush.Worker.start_link(arg)
|
||||||
|
|
|
@ -1,12 +1,18 @@
|
||||||
defmodule TuskerPush.Forwarder do
|
defmodule TuskerPush.Forwarder do
|
||||||
alias TuskerPush.Apns
|
alias TuskerPush.Apns
|
||||||
alias TuskerPush.Registration
|
|
||||||
|
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
@spec forward(Registration.t(), binary(), String.t(), String.t(), String.t() | nil) ::
|
@spec forward(
|
||||||
|
Apns.environment(),
|
||||||
|
String.t(),
|
||||||
|
binary(),
|
||||||
|
String.t(),
|
||||||
|
String.t(),
|
||||||
|
String.t() | nil
|
||||||
|
) ::
|
||||||
:ok | {:error, term()}
|
:ok | {:error, term()}
|
||||||
def forward(%Registration{push_version: 1} = registration, body, salt, key, context) do
|
def forward(apns_env, apns_device_token, body, salt, key, context) do
|
||||||
payload = %{
|
payload = %{
|
||||||
"aps" => %{
|
"aps" => %{
|
||||||
"alert" => %{
|
"alert" => %{
|
||||||
|
@ -29,6 +35,6 @@ defmodule TuskerPush.Forwarder do
|
||||||
|
|
||||||
Logger.debug("Sending #{inspect(payload)}")
|
Logger.debug("Sending #{inspect(payload)}")
|
||||||
|
|
||||||
Apns.send(registration, payload)
|
Apns.send(apns_env, apns_device_token, payload)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,54 +0,0 @@
|
||||||
defmodule TuskerPush.Registration do
|
|
||||||
use Ecto.Schema
|
|
||||||
|
|
||||||
import Ecto.Changeset
|
|
||||||
|
|
||||||
@type t() :: %__MODULE__{
|
|
||||||
id: Ecto.UUID.t(),
|
|
||||||
storekit_original_transaction_id: String.t(),
|
|
||||||
apns_environment: String.t(),
|
|
||||||
apns_device_token: String.t(),
|
|
||||||
push_version: integer(),
|
|
||||||
inserted_at: NaiveDateTime.t(),
|
|
||||||
updated_at: NaiveDateTime.t()
|
|
||||||
}
|
|
||||||
|
|
||||||
@primary_key {:id, Ecto.UUID, autogenerate: true}
|
|
||||||
|
|
||||||
schema "registrations" do
|
|
||||||
field :storekit_original_transaction_id, :string
|
|
||||||
|
|
||||||
field :apns_environment, Ecto.Enum, values: [:production, :development]
|
|
||||||
# hex-encoded
|
|
||||||
field :apns_device_token, :string
|
|
||||||
|
|
||||||
field :push_version, :integer
|
|
||||||
|
|
||||||
timestamps()
|
|
||||||
end
|
|
||||||
|
|
||||||
@create_fields [
|
|
||||||
:storekit_original_transaction_id,
|
|
||||||
:apns_environment,
|
|
||||||
:apns_device_token,
|
|
||||||
:push_version
|
|
||||||
]
|
|
||||||
|
|
||||||
def create_changeset(registration \\ %__MODULE__{}, params) do
|
|
||||||
registration
|
|
||||||
|> cast(params, @create_fields)
|
|
||||||
|> validate_required(@create_fields)
|
|
||||||
end
|
|
||||||
|
|
||||||
@update_fields [
|
|
||||||
:apns_environment,
|
|
||||||
:apns_device_token,
|
|
||||||
:push_version
|
|
||||||
]
|
|
||||||
|
|
||||||
def update_changeset(registration, params) do
|
|
||||||
registration
|
|
||||||
|> cast(params, @update_fields)
|
|
||||||
|> validate_required(@update_fields)
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,5 +0,0 @@
|
||||||
defmodule TuskerPush.Repo do
|
|
||||||
use Ecto.Repo,
|
|
||||||
otp_app: :tusker_push,
|
|
||||||
adapter: Ecto.Adapters.Postgres
|
|
||||||
end
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
defmodule TuskerPush.SentryFinchClient do
|
||||||
|
@behaviour Sentry.HTTPClient
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def child_spec do
|
||||||
|
Supervisor.child_spec({Finch, name: __MODULE__}, id: __MODULE__)
|
||||||
|
end
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def post(url, headers, body) do
|
||||||
|
request = Finch.build(:post, url, headers, body)
|
||||||
|
|
||||||
|
case Finch.request(request, __MODULE__) do
|
||||||
|
{:ok, %Finch.Response{status: status, headers: resp_headers, body: resp_body}} ->
|
||||||
|
{:ok, status, resp_headers, resp_body}
|
||||||
|
|
||||||
|
{:error, error} ->
|
||||||
|
{:error, error}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,108 +0,0 @@
|
||||||
defmodule TuskerPushWeb.AppRegistrationsController do
|
|
||||||
alias TuskerPush.Registration
|
|
||||||
alias Ecto.Changeset
|
|
||||||
use TuskerPushWeb, :controller
|
|
||||||
|
|
||||||
require Logger
|
|
||||||
|
|
||||||
def create(conn, %{
|
|
||||||
"transaction_id" => transaction_id,
|
|
||||||
"environment" => env,
|
|
||||||
"device_token" => token,
|
|
||||||
"push_version" => version
|
|
||||||
}) do
|
|
||||||
with {:ok, %Registration{id: id}} <-
|
|
||||||
TuskerPush.register(%{
|
|
||||||
storekit_original_transaction_id: transaction_id,
|
|
||||||
apns_environment: env,
|
|
||||||
apns_device_token: token,
|
|
||||||
push_version: version
|
|
||||||
}) do
|
|
||||||
conn
|
|
||||||
|> json(%{
|
|
||||||
id: id,
|
|
||||||
endpoint: url(~p"/mastodon/v1/push/#{id}"),
|
|
||||||
device_token: token
|
|
||||||
})
|
|
||||||
else
|
|
||||||
{:error, %Changeset{valid?: false} = changeset} ->
|
|
||||||
errors =
|
|
||||||
changeset.errors
|
|
||||||
|> Enum.map(fn {k, {reason, _}} -> %{key: k, reason: reason} end)
|
|
||||||
|
|
||||||
conn
|
|
||||||
|> put_status(400)
|
|
||||||
|> json(%{error: "validation failed", fields: errors})
|
|
||||||
|
|
||||||
{:error, reason} ->
|
|
||||||
Logger.error("Failed creating registration: #{inspect(reason)}")
|
|
||||||
|
|
||||||
conn
|
|
||||||
|> put_status(500)
|
|
||||||
|> json(%{error: "unknown error", fields: []})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def create(conn, _) do
|
|
||||||
conn
|
|
||||||
|> put_status(400)
|
|
||||||
|> json(%{error: "missing required parameters"})
|
|
||||||
end
|
|
||||||
|
|
||||||
def update(conn, %{
|
|
||||||
"id" => id,
|
|
||||||
"environment" => env,
|
|
||||||
"device_token" => token,
|
|
||||||
"push_version" => version
|
|
||||||
}) do
|
|
||||||
params = %{
|
|
||||||
apns_environment: env,
|
|
||||||
apns_device_token: token,
|
|
||||||
push_version: version
|
|
||||||
}
|
|
||||||
|
|
||||||
with {:registration, registration} when not is_nil(registration) <-
|
|
||||||
{:registration, TuskerPush.get_registration(id)},
|
|
||||||
{:ok, _} <- TuskerPush.update_registration(registration, params) do
|
|
||||||
conn
|
|
||||||
|> json(%{
|
|
||||||
id: id,
|
|
||||||
endpoint: url(~p"/mastodon/v1/push/#{id}"),
|
|
||||||
device_token: token
|
|
||||||
})
|
|
||||||
else
|
|
||||||
{:registration, nil} ->
|
|
||||||
conn
|
|
||||||
|> put_status(404)
|
|
||||||
|> json(%{error: "not found", fields: []})
|
|
||||||
|
|
||||||
{:error, %Changeset{valid?: false} = changeset} ->
|
|
||||||
errors =
|
|
||||||
changeset.errors
|
|
||||||
|> Enum.map(fn {k, {reason, _}} -> %{key: k, reason: reason} end)
|
|
||||||
|
|
||||||
conn
|
|
||||||
|> put_status(400)
|
|
||||||
|> json(%{error: "validation failed", fields: errors})
|
|
||||||
|
|
||||||
{:error, reason} ->
|
|
||||||
Logger.error("Failed updating registration: #{inspect(reason)}")
|
|
||||||
|
|
||||||
conn
|
|
||||||
|> put_status(500)
|
|
||||||
|> json(%{error: "unknown error", fields: []})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def update(conn, _) do
|
|
||||||
conn
|
|
||||||
|> put_status(400)
|
|
||||||
|> json(%{error: "missing required parameters"})
|
|
||||||
end
|
|
||||||
|
|
||||||
def delete(conn, %{"id" => id}) do
|
|
||||||
TuskerPush.unregister(id)
|
|
||||||
|
|
||||||
json(conn, %{status: "ok"})
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -4,24 +4,19 @@ defmodule TuskerPushWeb.PushController do
|
||||||
|
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
def push(conn, %{"id" => id} = params) do
|
def push(conn, %{"env" => env, "apns_device_token" => apns_device_token, "ctx" => context}) do
|
||||||
with {:registration, registration} when not is_nil(registration) <-
|
with {:apns_env, apns_env} <- {:apns_env, get_apns_env(env)},
|
||||||
{:registration, TuskerPush.get_registration(id)},
|
|
||||||
:ok <- TuskerPush.check_registration_expired(registration),
|
|
||||||
{:encoding, ["aesgcm"]} <- {:encoding, get_req_header(conn, "content-encoding")},
|
{:encoding, ["aesgcm"]} <- {:encoding, get_req_header(conn, "content-encoding")},
|
||||||
{:body, {:ok, body, conn}} <- {:body, read_body(conn)},
|
{:body, {:ok, body, conn}} <- {:body, read_body(conn)},
|
||||||
{:salt, salt} when not is_nil(salt) <- get_salt(conn),
|
{:salt, salt} when not is_nil(salt) <- get_salt(conn),
|
||||||
{:key, key} when not is_nil(key) <- get_key(conn),
|
{:key, key} when not is_nil(key) <- get_key(conn),
|
||||||
context <- Map.get(params, "ctx"),
|
{:forward, :ok} <-
|
||||||
{:forward, :ok} <- {:forward, Forwarder.forward(registration, body, salt, key, context)} do
|
{:forward, Forwarder.forward(apns_env, apns_device_token, body, salt, key, context)} do
|
||||||
send_resp(conn, 200, "ok")
|
send_resp(conn, 200, "ok")
|
||||||
else
|
else
|
||||||
{:registration, nil} ->
|
{:apns_env, nil} ->
|
||||||
send_resp(conn, 400, "unregistered")
|
Logger.error("Bad environment: #{env}")
|
||||||
|
send_resp(conn, 400, "bad env")
|
||||||
{:expired, registration} ->
|
|
||||||
TuskerPush.unregister(registration)
|
|
||||||
send_resp(conn, 400, "expired")
|
|
||||||
|
|
||||||
{:encoding, encoding} ->
|
{:encoding, encoding} ->
|
||||||
Logger.warning("Unexpected encoding: #{inspect(encoding)}")
|
Logger.warning("Unexpected encoding: #{inspect(encoding)}")
|
||||||
|
@ -45,7 +40,6 @@ defmodule TuskerPushWeb.PushController do
|
||||||
|
|
||||||
{:forward, {:error, :device_token_unregistered}} ->
|
{:forward, {:error, :device_token_unregistered}} ->
|
||||||
Logger.debug("APNS device token unregistered, removing registration")
|
Logger.debug("APNS device token unregistered, removing registration")
|
||||||
TuskerPush.unregister(id)
|
|
||||||
send_resp(conn, 400, "apns unregistered")
|
send_resp(conn, 400, "apns unregistered")
|
||||||
|
|
||||||
{:forward, {:error, reason}} ->
|
{:forward, {:error, reason}} ->
|
||||||
|
@ -54,6 +48,10 @@ defmodule TuskerPushWeb.PushController do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp get_apns_env("development"), do: :development
|
||||||
|
defp get_apns_env("production"), do: :production
|
||||||
|
defp get_apns_env(_), do: nil
|
||||||
|
|
||||||
defp get_salt(conn) do
|
defp get_salt(conn) do
|
||||||
conn
|
conn
|
||||||
|> get_req_header("encryption")
|
|> get_req_header("encryption")
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
defmodule TuskerPushWeb.Endpoint do
|
defmodule TuskerPushWeb.Endpoint do
|
||||||
|
use Sentry.PlugCapture
|
||||||
use Phoenix.Endpoint, otp_app: :tusker_push
|
use Phoenix.Endpoint, otp_app: :tusker_push
|
||||||
|
|
||||||
# The session will be stored in the cookie and signed,
|
# The session will be stored in the cookie and signed,
|
||||||
|
@ -29,7 +30,6 @@ defmodule TuskerPushWeb.Endpoint do
|
||||||
# :code_reloader configuration of your endpoint.
|
# :code_reloader configuration of your endpoint.
|
||||||
if code_reloading? do
|
if code_reloading? do
|
||||||
plug Phoenix.CodeReloader
|
plug Phoenix.CodeReloader
|
||||||
plug Phoenix.Ecto.CheckRepoStatus, otp_app: :tusker_push
|
|
||||||
end
|
end
|
||||||
|
|
||||||
plug Plug.RequestId
|
plug Plug.RequestId
|
||||||
|
@ -40,6 +40,8 @@ defmodule TuskerPushWeb.Endpoint do
|
||||||
pass: ["*/*"],
|
pass: ["*/*"],
|
||||||
json_decoder: Phoenix.json_library()
|
json_decoder: Phoenix.json_library()
|
||||||
|
|
||||||
|
plug Sentry.PlugContext
|
||||||
|
|
||||||
plug Plug.MethodOverride
|
plug Plug.MethodOverride
|
||||||
plug Plug.Head
|
plug Plug.Head
|
||||||
plug Plug.Session, @session_options
|
plug Plug.Session, @session_options
|
||||||
|
|
|
@ -5,19 +5,11 @@ defmodule TuskerPushWeb.Router do
|
||||||
plug :accepts, ["json"]
|
plug :accepts, ["json"]
|
||||||
end
|
end
|
||||||
|
|
||||||
scope "/app", TuskerPushWeb do
|
scope "/push", TuskerPushWeb do
|
||||||
pipe_through :api
|
pipe_through :api
|
||||||
|
|
||||||
scope "/v1" do
|
scope "/v1" do
|
||||||
resources "/registrations", AppRegistrationsController, only: [:create, :update, :delete]
|
post "/:env/:apns_device_token/:ctx", PushController, :push
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
scope "/mastodon", TuskerPushWeb do
|
|
||||||
pipe_through :api
|
|
||||||
|
|
||||||
scope "/v1" do
|
|
||||||
post "/push/:id", PushController, :push
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
12
mix.exs
12
mix.exs
|
@ -33,9 +33,6 @@ defmodule TuskerPush.MixProject do
|
||||||
defp deps do
|
defp deps do
|
||||||
[
|
[
|
||||||
{:phoenix, "~> 1.7.11"},
|
{:phoenix, "~> 1.7.11"},
|
||||||
{:phoenix_ecto, "~> 4.4"},
|
|
||||||
{:ecto_sql, "~> 3.10"},
|
|
||||||
{:postgrex, ">= 0.0.0"},
|
|
||||||
{:telemetry_metrics, "~> 0.6"},
|
{:telemetry_metrics, "~> 0.6"},
|
||||||
{:telemetry_poller, "~> 1.0"},
|
{:telemetry_poller, "~> 1.0"},
|
||||||
{:jason, "~> 1.2"},
|
{:jason, "~> 1.2"},
|
||||||
|
@ -43,7 +40,8 @@ defmodule TuskerPush.MixProject do
|
||||||
{:bandit, "~> 1.2"},
|
{:bandit, "~> 1.2"},
|
||||||
{:dialyxir, "~> 1.4", only: [:dev, :test], runtime: false},
|
{:dialyxir, "~> 1.4", only: [:dev, :test], runtime: false},
|
||||||
{:finch, "~> 0.18"},
|
{:finch, "~> 0.18"},
|
||||||
{:jose, "~> 1.11"}
|
{:jose, "~> 1.11"},
|
||||||
|
{:sentry, "~> 10.3.0"}
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -55,10 +53,8 @@ defmodule TuskerPush.MixProject do
|
||||||
# See the documentation for `Mix` for more info on aliases.
|
# See the documentation for `Mix` for more info on aliases.
|
||||||
defp aliases do
|
defp aliases do
|
||||||
[
|
[
|
||||||
setup: ["deps.get", "ecto.setup"],
|
setup: ["deps.get"],
|
||||||
"ecto.setup": ["ecto.create", "ecto.migrate", "run priv/repo/seeds.exs"],
|
release: ["sentry.package_source_code", "release"]
|
||||||
"ecto.reset": ["ecto.drop", "ecto.setup"],
|
|
||||||
test: ["ecto.create --quiet", "ecto.migrate --quiet", "test"]
|
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
9
mix.lock
9
mix.lock
|
@ -1,29 +1,24 @@
|
||||||
%{
|
%{
|
||||||
"bandit": {:hex, :bandit, "1.4.2", "a1475c8dcbffd1f43002797f99487a64c8444753ff2b282b52409e279488e1f5", [:mix], [{:hpax, "~> 0.1.1", [hex: :hpax, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:thousand_island, "~> 1.0", [hex: :thousand_island, repo: "hexpm", optional: false]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "3db8bacea631bd926cc62ccad58edfee4252d1b4c5cccbbad9825df2722b884f"},
|
"bandit": {:hex, :bandit, "1.4.2", "a1475c8dcbffd1f43002797f99487a64c8444753ff2b282b52409e279488e1f5", [:mix], [{:hpax, "~> 0.1.1", [hex: :hpax, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:thousand_island, "~> 1.0", [hex: :thousand_island, repo: "hexpm", optional: false]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "3db8bacea631bd926cc62ccad58edfee4252d1b4c5cccbbad9825df2722b884f"},
|
||||||
"castore": {:hex, :castore, "1.0.6", "ffc42f110ebfdafab0ea159cd43d31365fa0af0ce4a02ecebf1707ae619ee727", [:mix], [], "hexpm", "374c6e7ca752296be3d6780a6d5b922854ffcc74123da90f2f328996b962d33a"},
|
"castore": {:hex, :castore, "1.0.6", "ffc42f110ebfdafab0ea159cd43d31365fa0af0ce4a02ecebf1707ae619ee727", [:mix], [], "hexpm", "374c6e7ca752296be3d6780a6d5b922854ffcc74123da90f2f328996b962d33a"},
|
||||||
"db_connection": {:hex, :db_connection, "2.6.0", "77d835c472b5b67fc4f29556dee74bf511bbafecdcaf98c27d27fa5918152086", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "c2f992d15725e721ec7fbc1189d4ecdb8afef76648c746a8e1cad35e3b8a35f3"},
|
|
||||||
"decimal": {:hex, :decimal, "2.1.1", "5611dca5d4b2c3dd497dec8f68751f1f1a54755e8ed2a966c2633cf885973ad6", [:mix], [], "hexpm", "53cfe5f497ed0e7771ae1a475575603d77425099ba5faef9394932b35020ffcc"},
|
|
||||||
"dialyxir": {:hex, :dialyxir, "1.4.3", "edd0124f358f0b9e95bfe53a9fcf806d615d8f838e2202a9f430d59566b6b53b", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "bf2cfb75cd5c5006bec30141b131663299c661a864ec7fbbc72dfa557487a986"},
|
"dialyxir": {:hex, :dialyxir, "1.4.3", "edd0124f358f0b9e95bfe53a9fcf806d615d8f838e2202a9f430d59566b6b53b", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "bf2cfb75cd5c5006bec30141b131663299c661a864ec7fbbc72dfa557487a986"},
|
||||||
"dns_cluster": {:hex, :dns_cluster, "0.1.3", "0bc20a2c88ed6cc494f2964075c359f8c2d00e1bf25518a6a6c7fd277c9b0c66", [:mix], [], "hexpm", "46cb7c4a1b3e52c7ad4cbe33ca5079fbde4840dedeafca2baf77996c2da1bc33"},
|
"dns_cluster": {:hex, :dns_cluster, "0.1.3", "0bc20a2c88ed6cc494f2964075c359f8c2d00e1bf25518a6a6c7fd277c9b0c66", [:mix], [], "hexpm", "46cb7c4a1b3e52c7ad4cbe33ca5079fbde4840dedeafca2baf77996c2da1bc33"},
|
||||||
"ecto": {:hex, :ecto, "3.11.2", "e1d26be989db350a633667c5cda9c3d115ae779b66da567c68c80cfb26a8c9ee", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "3c38bca2c6f8d8023f2145326cc8a80100c3ffe4dcbd9842ff867f7fc6156c65"},
|
|
||||||
"ecto_sql": {:hex, :ecto_sql, "3.11.1", "e9abf28ae27ef3916b43545f9578b4750956ccea444853606472089e7d169470", [:mix], [{:db_connection, "~> 2.4.1 or ~> 2.5", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.11.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.6.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.16.0 or ~> 0.17.0 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1 or ~> 2.2", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "ce14063ab3514424276e7e360108ad6c2308f6d88164a076aac8a387e1fea634"},
|
|
||||||
"erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"},
|
"erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"},
|
||||||
"finch": {:hex, :finch, "0.18.0", "944ac7d34d0bd2ac8998f79f7a811b21d87d911e77a786bc5810adb75632ada4", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: false]}, {:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.3", [hex: :mint, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.4 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 0.2.6 or ~> 1.0", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "69f5045b042e531e53edc2574f15e25e735b522c37e2ddb766e15b979e03aa65"},
|
"finch": {:hex, :finch, "0.18.0", "944ac7d34d0bd2ac8998f79f7a811b21d87d911e77a786bc5810adb75632ada4", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: false]}, {:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.3", [hex: :mint, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.4 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 0.2.6 or ~> 1.0", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "69f5045b042e531e53edc2574f15e25e735b522c37e2ddb766e15b979e03aa65"},
|
||||||
"hpax": {:hex, :hpax, "0.1.2", "09a75600d9d8bbd064cdd741f21fc06fc1f4cf3d0fcc335e5aa19be1a7235c84", [:mix], [], "hexpm", "2c87843d5a23f5f16748ebe77969880e29809580efdaccd615cd3bed628a8c13"},
|
"hpax": {:hex, :hpax, "0.1.2", "09a75600d9d8bbd064cdd741f21fc06fc1f4cf3d0fcc335e5aa19be1a7235c84", [:mix], [], "hexpm", "2c87843d5a23f5f16748ebe77969880e29809580efdaccd615cd3bed628a8c13"},
|
||||||
"jason": {:hex, :jason, "1.4.1", "af1504e35f629ddcdd6addb3513c3853991f694921b1b9368b0bd32beb9f1b63", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "fbb01ecdfd565b56261302f7e1fcc27c4fb8f32d56eab74db621fc154604a7a1"},
|
"jason": {:hex, :jason, "1.4.1", "af1504e35f629ddcdd6addb3513c3853991f694921b1b9368b0bd32beb9f1b63", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "fbb01ecdfd565b56261302f7e1fcc27c4fb8f32d56eab74db621fc154604a7a1"},
|
||||||
"joken": {:hex, :joken, "2.6.0", "b9dd9b6d52e3e6fcb6c65e151ad38bf4bc286382b5b6f97079c47ade6b1bcc6a", [:mix], [{:jose, "~> 1.11.5", [hex: :jose, repo: "hexpm", optional: false]}], "hexpm", "5a95b05a71cd0b54abd35378aeb1d487a23a52c324fa7efdffc512b655b5aaa7"},
|
|
||||||
"jose": {:hex, :jose, "1.11.6", "613fda82552128aa6fb804682e3a616f4bc15565a048dabd05b1ebd5827ed965", [:mix, :rebar3], [], "hexpm", "6275cb75504f9c1e60eeacb771adfeee4905a9e182103aa59b53fed651ff9738"},
|
"jose": {:hex, :jose, "1.11.6", "613fda82552128aa6fb804682e3a616f4bc15565a048dabd05b1ebd5827ed965", [:mix, :rebar3], [], "hexpm", "6275cb75504f9c1e60eeacb771adfeee4905a9e182103aa59b53fed651ff9738"},
|
||||||
"mime": {:hex, :mime, "2.0.5", "dc34c8efd439abe6ae0343edbb8556f4d63f178594894720607772a041b04b02", [:mix], [], "hexpm", "da0d64a365c45bc9935cc5c8a7fc5e49a0e0f9932a761c55d6c52b142780a05c"},
|
"mime": {:hex, :mime, "2.0.5", "dc34c8efd439abe6ae0343edbb8556f4d63f178594894720607772a041b04b02", [:mix], [], "hexpm", "da0d64a365c45bc9935cc5c8a7fc5e49a0e0f9932a761c55d6c52b142780a05c"},
|
||||||
"mint": {:hex, :mint, "1.5.2", "4805e059f96028948870d23d7783613b7e6b0e2fb4e98d720383852a760067fd", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 0.1.1", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "d77d9e9ce4eb35941907f1d3df38d8f750c357865353e21d335bdcdf6d892a02"},
|
"mint": {:hex, :mint, "1.5.2", "4805e059f96028948870d23d7783613b7e6b0e2fb4e98d720383852a760067fd", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 0.1.1", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "d77d9e9ce4eb35941907f1d3df38d8f750c357865353e21d335bdcdf6d892a02"},
|
||||||
"nimble_options": {:hex, :nimble_options, "1.1.0", "3b31a57ede9cb1502071fade751ab0c7b8dbe75a9a4c2b5bbb0943a690b63172", [:mix], [], "hexpm", "8bbbb3941af3ca9acc7835f5655ea062111c9c27bcac53e004460dfd19008a99"},
|
"nimble_options": {:hex, :nimble_options, "1.1.0", "3b31a57ede9cb1502071fade751ab0c7b8dbe75a9a4c2b5bbb0943a690b63172", [:mix], [], "hexpm", "8bbbb3941af3ca9acc7835f5655ea062111c9c27bcac53e004460dfd19008a99"},
|
||||||
|
"nimble_ownership": {:hex, :nimble_ownership, "0.3.1", "99d5244672fafdfac89bfad3d3ab8f0d367603ce1dc4855f86a1c75008bce56f", [:mix], [], "hexpm", "4bf510adedff0449a1d6e200e43e57a814794c8b5b6439071274d248d272a549"},
|
||||||
"nimble_pool": {:hex, :nimble_pool, "1.1.0", "bf9c29fbdcba3564a8b800d1eeb5a3c58f36e1e11d7b7fb2e084a643f645f06b", [:mix], [], "hexpm", "af2e4e6b34197db81f7aad230c1118eac993acc0dae6bc83bac0126d4ae0813a"},
|
"nimble_pool": {:hex, :nimble_pool, "1.1.0", "bf9c29fbdcba3564a8b800d1eeb5a3c58f36e1e11d7b7fb2e084a643f645f06b", [:mix], [], "hexpm", "af2e4e6b34197db81f7aad230c1118eac993acc0dae6bc83bac0126d4ae0813a"},
|
||||||
"phoenix": {:hex, :phoenix, "1.7.11", "1d88fc6b05ab0c735b250932c4e6e33bfa1c186f76dcf623d8dd52f07d6379c7", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.7", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:websock_adapter, "~> 0.5.3", [hex: :websock_adapter, repo: "hexpm", optional: false]}], "hexpm", "b1ec57f2e40316b306708fe59b92a16b9f6f4bf50ccfa41aa8c7feb79e0ec02a"},
|
"phoenix": {:hex, :phoenix, "1.7.11", "1d88fc6b05ab0c735b250932c4e6e33bfa1c186f76dcf623d8dd52f07d6379c7", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.7", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:websock_adapter, "~> 0.5.3", [hex: :websock_adapter, repo: "hexpm", optional: false]}], "hexpm", "b1ec57f2e40316b306708fe59b92a16b9f6f4bf50ccfa41aa8c7feb79e0ec02a"},
|
||||||
"phoenix_ecto": {:hex, :phoenix_ecto, "4.5.1", "6fdbc334ea53620e71655664df6f33f670747b3a7a6c4041cdda3e2c32df6257", [:mix], [{:ecto, "~> 3.5", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.1", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "ebe43aa580db129e54408e719fb9659b7f9e0d52b965c5be26cdca416ecead28"},
|
|
||||||
"phoenix_pubsub": {:hex, :phoenix_pubsub, "2.1.3", "3168d78ba41835aecad272d5e8cd51aa87a7ac9eb836eabc42f6e57538e3731d", [:mix], [], "hexpm", "bba06bc1dcfd8cb086759f0edc94a8ba2bc8896d5331a1e2c2902bf8e36ee502"},
|
"phoenix_pubsub": {:hex, :phoenix_pubsub, "2.1.3", "3168d78ba41835aecad272d5e8cd51aa87a7ac9eb836eabc42f6e57538e3731d", [:mix], [], "hexpm", "bba06bc1dcfd8cb086759f0edc94a8ba2bc8896d5331a1e2c2902bf8e36ee502"},
|
||||||
"phoenix_template": {:hex, :phoenix_template, "1.0.4", "e2092c132f3b5e5b2d49c96695342eb36d0ed514c5b252a77048d5969330d639", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "2c0c81f0e5c6753faf5cca2f229c9709919aba34fab866d3bc05060c9c444206"},
|
"phoenix_template": {:hex, :phoenix_template, "1.0.4", "e2092c132f3b5e5b2d49c96695342eb36d0ed514c5b252a77048d5969330d639", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "2c0c81f0e5c6753faf5cca2f229c9709919aba34fab866d3bc05060c9c444206"},
|
||||||
"plug": {:hex, :plug, "1.15.3", "712976f504418f6dff0a3e554c40d705a9bcf89a7ccef92fc6a5ef8f16a30a97", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "cc4365a3c010a56af402e0809208873d113e9c38c401cabd88027ef4f5c01fd2"},
|
"plug": {:hex, :plug, "1.15.3", "712976f504418f6dff0a3e554c40d705a9bcf89a7ccef92fc6a5ef8f16a30a97", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "cc4365a3c010a56af402e0809208873d113e9c38c401cabd88027ef4f5c01fd2"},
|
||||||
"plug_crypto": {:hex, :plug_crypto, "2.0.0", "77515cc10af06645abbfb5e6ad7a3e9714f805ae118fa1a70205f80d2d70fe73", [:mix], [], "hexpm", "53695bae57cc4e54566d993eb01074e4d894b65a3766f1c43e2c61a1b0f45ea9"},
|
"plug_crypto": {:hex, :plug_crypto, "2.0.0", "77515cc10af06645abbfb5e6ad7a3e9714f805ae118fa1a70205f80d2d70fe73", [:mix], [], "hexpm", "53695bae57cc4e54566d993eb01074e4d894b65a3766f1c43e2c61a1b0f45ea9"},
|
||||||
"postgrex": {:hex, :postgrex, "0.17.5", "0483d054938a8dc069b21bdd636bf56c487404c241ce6c319c1f43588246b281", [:mix], [{:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "50b8b11afbb2c4095a3ba675b4f055c416d0f3d7de6633a595fc131a828a67eb"},
|
"sentry": {:hex, :sentry, "10.3.0", "4b7543dfea5e59f3be6db28a032427884d55fbc828173b23115064e75dcb1eed", [:mix], [{:hackney, "~> 1.8", [hex: :hackney, repo: "hexpm", optional: true]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: true]}, {:nimble_options, "~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_ownership, "~> 0.3.0", [hex: :nimble_ownership, repo: "hexpm", optional: false]}, {:plug, "~> 1.6", [hex: :plug, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "c1c08ba57f0634b7fda92adb0818ea0677e043e2d28ea4464351a0e4e8e142e5"},
|
||||||
"telemetry": {:hex, :telemetry, "1.2.1", "68fdfe8d8f05a8428483a97d7aab2f268aaff24b49e0f599faa091f1d4e7f61c", [:rebar3], [], "hexpm", "dad9ce9d8effc621708f99eac538ef1cbe05d6a874dd741de2e689c47feafed5"},
|
"telemetry": {:hex, :telemetry, "1.2.1", "68fdfe8d8f05a8428483a97d7aab2f268aaff24b49e0f599faa091f1d4e7f61c", [:rebar3], [], "hexpm", "dad9ce9d8effc621708f99eac538ef1cbe05d6a874dd741de2e689c47feafed5"},
|
||||||
"telemetry_metrics": {:hex, :telemetry_metrics, "0.6.2", "2caabe9344ec17eafe5403304771c3539f3b6e2f7fb6a6f602558c825d0d0bfb", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "9b43db0dc33863930b9ef9d27137e78974756f5f198cae18409970ed6fa5b561"},
|
"telemetry_metrics": {:hex, :telemetry_metrics, "0.6.2", "2caabe9344ec17eafe5403304771c3539f3b6e2f7fb6a6f602558c825d0d0bfb", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "9b43db0dc33863930b9ef9d27137e78974756f5f198cae18409970ed6fa5b561"},
|
||||||
"telemetry_poller": {:hex, :telemetry_poller, "1.0.0", "db91bb424e07f2bb6e73926fcafbfcbcb295f0193e0a00e825e589a0a47e8453", [:rebar3], [{:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "b3a24eafd66c3f42da30fc3ca7dda1e9d546c12250a2d60d7b81d264fbec4f6e"},
|
"telemetry_poller": {:hex, :telemetry_poller, "1.0.0", "db91bb424e07f2bb6e73926fcafbfcbcb295f0193e0a00e825e589a0a47e8453", [:rebar3], [{:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "b3a24eafd66c3f42da30fc3ca7dda1e9d546c12250a2d60d7b81d264fbec4f6e"},
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
[
|
|
||||||
import_deps: [:ecto_sql],
|
|
||||||
inputs: ["*.exs"]
|
|
||||||
]
|
|
|
@ -1,18 +0,0 @@
|
||||||
defmodule TuskerPush.Repo.Migrations.CreateRegistrations do
|
|
||||||
use Ecto.Migration
|
|
||||||
|
|
||||||
def change do
|
|
||||||
create table(:registrations, primary_key: false) do
|
|
||||||
add :id, :uuid, primary_key: true, null: false
|
|
||||||
|
|
||||||
add :storekit_original_transaction_id, :string, null: false
|
|
||||||
|
|
||||||
add :apns_environment, :string, null: false
|
|
||||||
add :apns_device_token, :string, null: false
|
|
||||||
|
|
||||||
add :push_version, :integer, null: false
|
|
||||||
|
|
||||||
timestamps()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,11 +0,0 @@
|
||||||
# Script for populating the database. You can run it as:
|
|
||||||
#
|
|
||||||
# mix run priv/repo/seeds.exs
|
|
||||||
#
|
|
||||||
# Inside the script, you can read and write to any of your
|
|
||||||
# repositories directly:
|
|
||||||
#
|
|
||||||
# TuskerPush.Repo.insert!(%TuskerPush.SomeSchema{})
|
|
||||||
#
|
|
||||||
# We recommend using the bang functions (`insert!`, `update!`
|
|
||||||
# and so on) as they will fail if something goes wrong.
|
|
|
@ -1,58 +0,0 @@
|
||||||
defmodule TuskerPush.DataCase do
|
|
||||||
@moduledoc """
|
|
||||||
This module defines the setup for tests requiring
|
|
||||||
access to the application's data layer.
|
|
||||||
|
|
||||||
You may define functions here to be used as helpers in
|
|
||||||
your tests.
|
|
||||||
|
|
||||||
Finally, if the test case interacts with the database,
|
|
||||||
we enable the SQL sandbox, so changes done to the database
|
|
||||||
are reverted at the end of every test. If you are using
|
|
||||||
PostgreSQL, you can even run database tests asynchronously
|
|
||||||
by setting `use TuskerPush.DataCase, async: true`, although
|
|
||||||
this option is not recommended for other databases.
|
|
||||||
"""
|
|
||||||
|
|
||||||
use ExUnit.CaseTemplate
|
|
||||||
|
|
||||||
using do
|
|
||||||
quote do
|
|
||||||
alias TuskerPush.Repo
|
|
||||||
|
|
||||||
import Ecto
|
|
||||||
import Ecto.Changeset
|
|
||||||
import Ecto.Query
|
|
||||||
import TuskerPush.DataCase
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
setup tags do
|
|
||||||
TuskerPush.DataCase.setup_sandbox(tags)
|
|
||||||
:ok
|
|
||||||
end
|
|
||||||
|
|
||||||
@doc """
|
|
||||||
Sets up the sandbox based on the test tags.
|
|
||||||
"""
|
|
||||||
def setup_sandbox(tags) do
|
|
||||||
pid = Ecto.Adapters.SQL.Sandbox.start_owner!(TuskerPush.Repo, shared: not tags[:async])
|
|
||||||
on_exit(fn -> Ecto.Adapters.SQL.Sandbox.stop_owner(pid) end)
|
|
||||||
end
|
|
||||||
|
|
||||||
@doc """
|
|
||||||
A helper that transforms changeset errors into a map of messages.
|
|
||||||
|
|
||||||
assert {:error, changeset} = Accounts.create_user(%{password: "short"})
|
|
||||||
assert "password is too short" in errors_on(changeset).password
|
|
||||||
assert %{password: ["password is too short"]} = errors_on(changeset)
|
|
||||||
|
|
||||||
"""
|
|
||||||
def errors_on(changeset) do
|
|
||||||
Ecto.Changeset.traverse_errors(changeset, fn {message, opts} ->
|
|
||||||
Regex.replace(~r"%{(\w+)}", message, fn _, key ->
|
|
||||||
opts |> Keyword.get(String.to_existing_atom(key), key) |> to_string()
|
|
||||||
end)
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,2 +1 @@
|
||||||
ExUnit.start()
|
ExUnit.start()
|
||||||
Ecto.Adapters.SQL.Sandbox.mode(TuskerPush.Repo, :manual)
|
|
||||||
|
|
Loading…
Reference in New Issue