Compare commits
No commits in common. "b8b58b14a4bfd42aa7ac2968b30cd88279181165" and "f7a64e742ad78334989c6b02769257d4c6feb32b" have entirely different histories.
b8b58b14a4
...
f7a64e742a
|
@ -18,14 +18,6 @@ defmodule TuskerPush do
|
||||||
|> Repo.insert()
|
|> Repo.insert()
|
||||||
end
|
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()}
|
@spec unregister(String.t()) :: :ok | {:error, :no_registration | Ecto.Changeset.t()}
|
||||||
def unregister(id) when is_binary(id) do
|
def unregister(id) when is_binary(id) do
|
||||||
with registration when not is_nil(registration) <- Repo.get(Registration, id),
|
with registration when not is_nil(registration) <- Repo.get(Registration, id),
|
||||||
|
|
|
@ -1,118 +0,0 @@
|
||||||
defmodule TuskerPush.Apns do
|
|
||||||
alias TuskerPush.Registration
|
|
||||||
|
|
||||||
require Logger
|
|
||||||
|
|
||||||
@spec send(Registration.t(), Map.t()) :: :ok | {:error, term()}
|
|
||||||
def send(registration, payload) do
|
|
||||||
with {:ok, body} <- Jason.encode(payload, pretty_print: false),
|
|
||||||
req <- make_request(registration, body),
|
|
||||||
{:ok, resp} <- Finch.request(req, TuskerPush.Finch) do
|
|
||||||
handle_response(resp, registration, payload)
|
|
||||||
else
|
|
||||||
{:error, %Finch.Error{reason: :connection_closed}} ->
|
|
||||||
Logger.warning("Apns Finch connection_closed, retrying in 1s")
|
|
||||||
Process.sleep(1000)
|
|
||||||
__MODULE__.send(registration, payload)
|
|
||||||
|
|
||||||
{:error, %Finch.Error{reason: :disconnected}} ->
|
|
||||||
Logger.warning("Apns Finch disconnected, retrying in 1s")
|
|
||||||
Process.sleep(1000)
|
|
||||||
__MODULE__.send(registration, payload)
|
|
||||||
|
|
||||||
{:error, %Mint.TransportError{reason: :closed}} ->
|
|
||||||
Logger.warning("Apns Mint transport closed, retrying in 1s")
|
|
||||||
Process.sleep(1000)
|
|
||||||
__MODULE__.send(registration, payload)
|
|
||||||
|
|
||||||
{:error, reason} ->
|
|
||||||
{:error, reason}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
@spec make_request(Registration.t(), binary()) :: Finch.Request.t()
|
|
||||||
defp make_request(registration, body) do
|
|
||||||
bundle_id = Application.fetch_env!(:tusker_push, :apns)[:bundle_id]
|
|
||||||
|
|
||||||
headers = [
|
|
||||||
{"authorization", "bearer #{TuskerPush.Apns.Token.current()}"},
|
|
||||||
{"apns-push-type", "alert"},
|
|
||||||
{"apns-bundle-id", bundle_id},
|
|
||||||
{"apns-topic", bundle_id},
|
|
||||||
{"apns-expiration",
|
|
||||||
DateTime.utc_now() |> DateTime.add(1, :day) |> DateTime.to_unix() |> Integer.to_string()}
|
|
||||||
]
|
|
||||||
|
|
||||||
Finch.build(
|
|
||||||
:post,
|
|
||||||
"https://#{host(registration.apns_environment)}/3/device/#{registration.apns_device_token}",
|
|
||||||
headers,
|
|
||||||
body
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
@spec handle_response(Finch.Response.t(), Registration.t(), Map.t()) :: :ok | {:error, term()}
|
|
||||||
|
|
||||||
defp handle_response(resp, registration, payload) do
|
|
||||||
maybe_log_unique_id(resp, registration.apns_environment)
|
|
||||||
|
|
||||||
if resp.status in 200..299 do
|
|
||||||
:ok
|
|
||||||
else
|
|
||||||
info =
|
|
||||||
case Jason.decode(resp.body) do
|
|
||||||
{:ok, data} ->
|
|
||||||
inspect(data)
|
|
||||||
|
|
||||||
{:error, _} ->
|
|
||||||
resp.body
|
|
||||||
end
|
|
||||||
|
|
||||||
case {resp.status, info} do
|
|
||||||
{403, %{reason: "ExpiredProviderToken"}} ->
|
|
||||||
Logger.warning("Expired provider token, retrying")
|
|
||||||
__MODULE__.send(registration, payload)
|
|
||||||
|
|
||||||
{410, %{reason: "Unregistered"}} ->
|
|
||||||
Logger.warning("Device token unregistered")
|
|
||||||
{:error, :device_token_unregistered}
|
|
||||||
|
|
||||||
_ ->
|
|
||||||
Logger.error("Received #{resp.status} with #{inspect(info)}")
|
|
||||||
|
|
||||||
{:error, "unexpected status #{resp.status}"}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
@spec maybe_log_unique_id(Finch.Response.t(), :development | :production) :: :ok
|
|
||||||
|
|
||||||
defp maybe_log_unique_id(resp, :development) do
|
|
||||||
resp.headers
|
|
||||||
|> Enum.find(fn
|
|
||||||
{"apns-unique-id", _} -> true
|
|
||||||
_ -> false
|
|
||||||
end)
|
|
||||||
|> case do
|
|
||||||
{_, id} ->
|
|
||||||
Logger.debug("APNS unique id: #{id}")
|
|
||||||
|
|
||||||
_ ->
|
|
||||||
nil
|
|
||||||
end
|
|
||||||
|
|
||||||
:ok
|
|
||||||
end
|
|
||||||
|
|
||||||
defp maybe_log_unique_id(_resp, :production), do: :ok
|
|
||||||
|
|
||||||
@spec host(:development | :production) :: String.t()
|
|
||||||
|
|
||||||
def host(:development) do
|
|
||||||
"api.sandbox.push.apple.com"
|
|
||||||
end
|
|
||||||
|
|
||||||
def host(:production) do
|
|
||||||
"api.push.apple.com"
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,57 +0,0 @@
|
||||||
defmodule TuskerPush.Apns.Token do
|
|
||||||
use GenServer
|
|
||||||
|
|
||||||
require Logger
|
|
||||||
|
|
||||||
def start_link(_) do
|
|
||||||
GenServer.start_link(__MODULE__, nil, name: __MODULE__)
|
|
||||||
end
|
|
||||||
|
|
||||||
def current do
|
|
||||||
GenServer.call(__MODULE__, :current)
|
|
||||||
end
|
|
||||||
|
|
||||||
@impl true
|
|
||||||
def init(_) do
|
|
||||||
Logger.debug("Apns.Token initialized, scheduling update")
|
|
||||||
Process.send_after(self(), :refresh, 1000 * 60 * 45)
|
|
||||||
{:ok, generate_token()}
|
|
||||||
end
|
|
||||||
|
|
||||||
@impl true
|
|
||||||
def handle_info(:refresh, _old_token) do
|
|
||||||
Logger.debug("Apns.Token regenerated")
|
|
||||||
Process.send_after(self(), :refresh, 1000 * 60 * 45)
|
|
||||||
{:noreply, generate_token()}
|
|
||||||
end
|
|
||||||
|
|
||||||
@impl true
|
|
||||||
def handle_call(:current, _from, token) do
|
|
||||||
{:reply, token, token}
|
|
||||||
end
|
|
||||||
|
|
||||||
defp generate_token do
|
|
||||||
# can't use Joken because it always inserts "typ": "JWT" which APNS doesn't like
|
|
||||||
|
|
||||||
config = Application.fetch_env!(:tusker_push, :apns)
|
|
||||||
|
|
||||||
jwk = JOSE.JWK.from_pem(config[:key_pem])
|
|
||||||
|
|
||||||
jws =
|
|
||||||
JOSE.JWS.from_map(%{
|
|
||||||
"kid" => config[:key_id],
|
|
||||||
"alg" => "ES256"
|
|
||||||
})
|
|
||||||
|
|
||||||
claims = %{
|
|
||||||
"iss" => config[:team_id],
|
|
||||||
"iat" => DateTime.utc_now() |> DateTime.to_unix()
|
|
||||||
}
|
|
||||||
|
|
||||||
{_, result} =
|
|
||||||
JOSE.JWT.sign(jwk, jws, claims)
|
|
||||||
|> JOSE.JWS.compact()
|
|
||||||
|
|
||||||
result
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -14,13 +14,6 @@ defmodule TuskerPush.Application do
|
||||||
{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)
|
||||||
# {TuskerPush.Worker, arg},
|
# {TuskerPush.Worker, arg},
|
||||||
{Finch,
|
|
||||||
name: TuskerPush.Finch,
|
|
||||||
pools: %{
|
|
||||||
("https://" <> TuskerPush.Apns.host(:development)) => [protocols: [:http2], count: 1],
|
|
||||||
("https://" <> TuskerPush.Apns.host(:production)) => [protocols: [:http2], count: 1]
|
|
||||||
}},
|
|
||||||
TuskerPush.Apns.Token,
|
|
||||||
# Start to serve requests, typically the last entry
|
# Start to serve requests, typically the last entry
|
||||||
TuskerPushWeb.Endpoint
|
TuskerPushWeb.Endpoint
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,35 +0,0 @@
|
||||||
defmodule TuskerPush.Forwarder do
|
|
||||||
alias TuskerPush.Apns
|
|
||||||
alias TuskerPush.Registration
|
|
||||||
|
|
||||||
require Logger
|
|
||||||
|
|
||||||
@spec forward(Registration.t(), binary(), String.t(), String.t(), String.t() | nil) ::
|
|
||||||
:ok | {:error, term()}
|
|
||||||
def forward(%Registration{push_version: 1} = registration, body, salt, key, context) do
|
|
||||||
payload = %{
|
|
||||||
"aps" => %{
|
|
||||||
"alert" => %{
|
|
||||||
"loc-key" => "apns_enc"
|
|
||||||
},
|
|
||||||
"mutable-content" => 1
|
|
||||||
},
|
|
||||||
"reg_id" => registration.id,
|
|
||||||
"data" => Base.encode64(body),
|
|
||||||
"salt" => salt,
|
|
||||||
"pk" => key,
|
|
||||||
"v" => 1
|
|
||||||
}
|
|
||||||
|
|
||||||
payload =
|
|
||||||
unless is_nil(context) do
|
|
||||||
Map.put(payload, "ctx", context)
|
|
||||||
else
|
|
||||||
payload
|
|
||||||
end
|
|
||||||
|
|
||||||
Logger.debug("Sending #{inspect(payload)}")
|
|
||||||
|
|
||||||
Apns.send(registration, payload)
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -8,7 +8,6 @@ defmodule TuskerPush.Registration do
|
||||||
storekit_original_transaction_id: String.t(),
|
storekit_original_transaction_id: String.t(),
|
||||||
apns_environment: String.t(),
|
apns_environment: String.t(),
|
||||||
apns_device_token: String.t(),
|
apns_device_token: String.t(),
|
||||||
push_version: integer(),
|
|
||||||
inserted_at: NaiveDateTime.t(),
|
inserted_at: NaiveDateTime.t(),
|
||||||
updated_at: NaiveDateTime.t()
|
updated_at: NaiveDateTime.t()
|
||||||
}
|
}
|
||||||
|
@ -19,36 +18,16 @@ defmodule TuskerPush.Registration do
|
||||||
field :storekit_original_transaction_id, :string
|
field :storekit_original_transaction_id, :string
|
||||||
|
|
||||||
field :apns_environment, Ecto.Enum, values: [:production, :development]
|
field :apns_environment, Ecto.Enum, values: [:production, :development]
|
||||||
# hex-encoded
|
|
||||||
field :apns_device_token, :string
|
field :apns_device_token, :string
|
||||||
|
|
||||||
field :push_version, :integer
|
|
||||||
|
|
||||||
timestamps()
|
timestamps()
|
||||||
end
|
end
|
||||||
|
|
||||||
@create_fields [
|
@required_fields [:storekit_original_transaction_id, :apns_environment, :apns_device_token]
|
||||||
:storekit_original_transaction_id,
|
|
||||||
:apns_environment,
|
|
||||||
:apns_device_token,
|
|
||||||
:push_version
|
|
||||||
]
|
|
||||||
|
|
||||||
def create_changeset(registration \\ %__MODULE__{}, params) do
|
def create_changeset(registration \\ %__MODULE__{}, params) do
|
||||||
registration
|
registration
|
||||||
|> cast(params, @create_fields)
|
|> cast(params, @required_fields)
|
||||||
|> validate_required(@create_fields)
|
|> validate_required(@required_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
|
||||||
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
|
|
|
@ -1,19 +1,14 @@
|
||||||
defmodule TuskerPushWeb.PushController do
|
defmodule TuskerPushWeb.PushController do
|
||||||
alias TuskerPush.Forwarder
|
|
||||||
use TuskerPushWeb, :controller
|
use TuskerPushWeb, :controller
|
||||||
|
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
def push(conn, %{"id" => id} = params) do
|
def push(conn, %{"id" => id}) do
|
||||||
with {:registration, registration} when not is_nil(registration) <-
|
with {:registration, registration} when not is_nil(registration) <-
|
||||||
{:registration, TuskerPush.get_registration(id)},
|
{:registration, TuskerPush.get_registration(id)},
|
||||||
:ok <- TuskerPush.check_registration_expired(registration),
|
:ok <- TuskerPush.check_registration_expired(registration),
|
||||||
{:encoding, ["aesgcm"]} <- {:encoding, get_req_header(conn, "content-encoding")},
|
{:ok, body, conn} <- read_body(conn) do
|
||||||
{:body, {:ok, body, conn}} <- {:body, read_body(conn)},
|
IO.inspect(body |> byte_size())
|
||||||
{:salt, salt} when not is_nil(salt) <- get_salt(conn),
|
|
||||||
{:key, key} when not is_nil(key) <- get_key(conn),
|
|
||||||
context <- Map.get(params, "context"),
|
|
||||||
{:forward, :ok} <- {:forward, Forwarder.forward(registration, body, salt, key, context)} do
|
|
||||||
send_resp(conn, 200, "ok")
|
send_resp(conn, 200, "ok")
|
||||||
else
|
else
|
||||||
{:registration, nil} ->
|
{:registration, nil} ->
|
||||||
|
@ -21,68 +16,18 @@ defmodule TuskerPushWeb.PushController do
|
||||||
|
|
||||||
{:expired, registration} ->
|
{:expired, registration} ->
|
||||||
TuskerPush.unregister(registration)
|
TuskerPush.unregister(registration)
|
||||||
send_resp(conn, 400, "expired")
|
|
||||||
|
|
||||||
{:encoding, encoding} ->
|
send_resp(conn, 400, "unregistered")
|
||||||
Logger.warning("Unexpected encoding: #{inspect(encoding)}")
|
|
||||||
send_resp(conn, 400, "bad encoding")
|
|
||||||
|
|
||||||
{:body, {:more, _, conn}} ->
|
{:more, _, conn} ->
|
||||||
Logger.error("Didn't finish reading")
|
Logger.error("Didn't finish reading")
|
||||||
|
|
||||||
send_resp(conn, 500, "failed to read body")
|
send_resp(conn, 500, "failed to read body")
|
||||||
|
|
||||||
{:body, {:error, reason}} ->
|
{:error, reason} ->
|
||||||
Logger.error("Reading body: #{inspect(reason)}")
|
Logger.error("Reading body: #{inspect(reason)}")
|
||||||
|
|
||||||
send_resp(conn, 500, "failed to read body")
|
send_resp(conn, 500, "failed to read body")
|
||||||
|
|
||||||
{:salt, nil} ->
|
|
||||||
Logger.warning("Missing salt")
|
|
||||||
send_resp(conn, 400, "missing salt")
|
|
||||||
|
|
||||||
{:key, nil} ->
|
|
||||||
Logger.warning("Missing key")
|
|
||||||
send_resp(conn, 400, "missing key")
|
|
||||||
|
|
||||||
{:forward, {:error, :device_token_unregistered}} ->
|
|
||||||
Logger.debug("APNS device token unregistered, removing registration")
|
|
||||||
TuskerPush.unregister(id)
|
|
||||||
send_resp(conn, 400, "apns unregistered")
|
|
||||||
|
|
||||||
{:forward, {:error, reason}} ->
|
|
||||||
Logger.error("Sending notification: #{inspect(reason)}")
|
|
||||||
send_resp(conn, 500, "failed to send")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp get_salt(conn) do
|
|
||||||
conn
|
|
||||||
|> get_req_header("encryption")
|
|
||||||
|> case do
|
|
||||||
["salt=" <> salt] ->
|
|
||||||
{:salt, salt}
|
|
||||||
|
|
||||||
_ ->
|
|
||||||
{:salt, nil}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp get_key(conn) do
|
|
||||||
conn
|
|
||||||
|> get_req_header("crypto-key")
|
|
||||||
|> case do
|
|
||||||
[value] ->
|
|
||||||
dh =
|
|
||||||
value
|
|
||||||
|> String.split(";")
|
|
||||||
|> Enum.find_value(fn
|
|
||||||
"dh=" <> val -> val
|
|
||||||
_ -> false
|
|
||||||
end)
|
|
||||||
|
|
||||||
{:key, dh}
|
|
||||||
|
|
||||||
_ ->
|
|
||||||
{:key, nil}
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,59 @@
|
||||||
|
defmodule TuskerPushWeb.RegistrationsController do
|
||||||
|
alias TuskerPush.Registration
|
||||||
|
alias Ecto.Changeset
|
||||||
|
use TuskerPushWeb, :controller
|
||||||
|
|
||||||
|
require Logger
|
||||||
|
|
||||||
|
def create(conn, %{
|
||||||
|
"transaction_id" => transaction_id,
|
||||||
|
"environment" => env,
|
||||||
|
"device_token" => token
|
||||||
|
}) do
|
||||||
|
with {:ok, %Registration{id: id}} <-
|
||||||
|
TuskerPush.register(%{
|
||||||
|
storekit_original_transaction_id: transaction_id,
|
||||||
|
apns_environment: env,
|
||||||
|
apns_device_token: token
|
||||||
|
}) do
|
||||||
|
conn
|
||||||
|
|> json(%{
|
||||||
|
id: id,
|
||||||
|
endpoint: url(~p"/mastodon/push/#{id}")
|
||||||
|
})
|
||||||
|
else
|
||||||
|
{:env, nil} ->
|
||||||
|
conn
|
||||||
|
|> put_status(400)
|
||||||
|
|> json(%{error: "invalid apns environment"})
|
||||||
|
|
||||||
|
{:error, %Changeset{valid?: false} = changeset} ->
|
||||||
|
errors =
|
||||||
|
changeset.errors
|
||||||
|
|> Enum.map(fn {k, {reason, _}} -> %{key: k, reason: reason} end)
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> put_status(400)
|
||||||
|
|> json(%{errors: errors})
|
||||||
|
|
||||||
|
{:error, reason} ->
|
||||||
|
Logger.error("Failed creating registration: #{inspect(reason)}")
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> put_status(500)
|
||||||
|
|> json(%{error: "unknown error"})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def create(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
|
|
@ -8,16 +8,12 @@ defmodule TuskerPushWeb.Router do
|
||||||
scope "/app", TuskerPushWeb do
|
scope "/app", TuskerPushWeb do
|
||||||
pipe_through :api
|
pipe_through :api
|
||||||
|
|
||||||
scope "/v1" do
|
resources "/registrations", RegistrationsController, only: [:create, :delete]
|
||||||
resources "/registrations", AppRegistrationsController, only: [:create, :update, :delete]
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
scope "/mastodon", TuskerPushWeb do
|
scope "/mastodon", TuskerPushWeb do
|
||||||
pipe_through :api
|
pipe_through :api
|
||||||
|
|
||||||
scope "/v1" do
|
post "/push/:id", PushController, :push
|
||||||
post "/push/:id", PushController, :push
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
4
mix.exs
4
mix.exs
|
@ -41,9 +41,7 @@ defmodule TuskerPush.MixProject do
|
||||||
{:jason, "~> 1.2"},
|
{:jason, "~> 1.2"},
|
||||||
{:dns_cluster, "~> 0.1.1"},
|
{:dns_cluster, "~> 0.1.1"},
|
||||||
{: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"},
|
|
||||||
{:jose, "~> 1.11"}
|
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
6
mix.lock
6
mix.lock
|
@ -8,15 +8,9 @@
|
||||||
"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": {: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"},
|
"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"},
|
|
||||||
"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"},
|
|
||||||
"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"},
|
|
||||||
"nimble_options": {:hex, :nimble_options, "1.1.0", "3b31a57ede9cb1502071fade751ab0c7b8dbe75a9a4c2b5bbb0943a690b63172", [:mix], [], "hexpm", "8bbbb3941af3ca9acc7835f5655ea062111c9c27bcac53e004460dfd19008a99"},
|
|
||||||
"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_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"},
|
||||||
|
|
|
@ -10,8 +10,6 @@ defmodule TuskerPush.Repo.Migrations.CreateRegistrations do
|
||||||
add :apns_environment, :string, null: false
|
add :apns_environment, :string, null: false
|
||||||
add :apns_device_token, :string, null: false
|
add :apns_device_token, :string, null: false
|
||||||
|
|
||||||
add :push_version, :integer, null: false
|
|
||||||
|
|
||||||
timestamps()
|
timestamps()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue