Merge branch 'feature/compat/push-subscriptions' into 'develop'
Web Push Subscriptions See merge request pleroma/pleroma!506
This commit is contained in:
commit
776179c2ed
|
@ -27,6 +27,12 @@
|
||||||
config :pleroma, :ostatus, Pleroma.Web.OStatusMock
|
config :pleroma, :ostatus, Pleroma.Web.OStatusMock
|
||||||
config :tesla, adapter: Tesla.Mock
|
config :tesla, adapter: Tesla.Mock
|
||||||
|
|
||||||
|
config :web_push_encryption, :vapid_details,
|
||||||
|
subject: "mailto:administrator@example.com",
|
||||||
|
public_key:
|
||||||
|
"BLH1qVhJItRGCfxgTtONfsOKDc9VRAraXw-3NsmjMngWSh7NxOizN6bkuRA7iLTMPS82PjwJAr3UoK9EC1IFrz4",
|
||||||
|
private_key: "_-XZ0iebPrRfZ_o0-IatTdszYa8VCH1yLN-JauK7HHA"
|
||||||
|
|
||||||
try do
|
try do
|
||||||
import_config "test.secret.exs"
|
import_config "test.secret.exs"
|
||||||
rescue
|
rescue
|
||||||
|
|
|
@ -22,6 +22,8 @@ def run(_) do
|
||||||
|
|
||||||
resultSql = EEx.eval_file("lib/mix/tasks/sample_psql.eex", dbpass: dbpass)
|
resultSql = EEx.eval_file("lib/mix/tasks/sample_psql.eex", dbpass: dbpass)
|
||||||
|
|
||||||
|
{web_push_public_key, web_push_private_key} = :crypto.generate_key(:ecdh, :prime256v1)
|
||||||
|
|
||||||
result =
|
result =
|
||||||
EEx.eval_file(
|
EEx.eval_file(
|
||||||
"lib/mix/tasks/sample_config.eex",
|
"lib/mix/tasks/sample_config.eex",
|
||||||
|
@ -29,7 +31,9 @@ def run(_) do
|
||||||
email: email,
|
email: email,
|
||||||
name: name,
|
name: name,
|
||||||
secret: secret,
|
secret: secret,
|
||||||
dbpass: dbpass
|
dbpass: dbpass,
|
||||||
|
web_push_public_key: Base.url_encode64(web_push_public_key, padding: false),
|
||||||
|
web_push_private_key: Base.url_encode64(web_push_private_key, padding: false)
|
||||||
)
|
)
|
||||||
|
|
||||||
IO.puts(
|
IO.puts(
|
||||||
|
|
|
@ -25,6 +25,12 @@ config :pleroma, Pleroma.Repo,
|
||||||
hostname: "localhost",
|
hostname: "localhost",
|
||||||
pool_size: 10
|
pool_size: 10
|
||||||
|
|
||||||
|
# Configure web push notifications
|
||||||
|
config :web_push_encryption, :vapid_details,
|
||||||
|
subject: "mailto:<%= email %>",
|
||||||
|
public_key: "<%= web_push_public_key %>",
|
||||||
|
private_key: "<%= web_push_private_key %>"
|
||||||
|
|
||||||
# Enable Strict-Transport-Security once SSL is working:
|
# Enable Strict-Transport-Security once SSL is working:
|
||||||
# config :pleroma, :http_security,
|
# config :pleroma, :http_security,
|
||||||
# sts: true
|
# sts: true
|
||||||
|
@ -50,9 +56,9 @@ config :pleroma, Pleroma.Repo,
|
||||||
|
|
||||||
|
|
||||||
# Configure Openstack Swift support if desired.
|
# Configure Openstack Swift support if desired.
|
||||||
#
|
#
|
||||||
# Many openstack deployments are different, so config is left very open with
|
# Many openstack deployments are different, so config is left very open with
|
||||||
# no assumptions made on which provider you're using. This should allow very
|
# no assumptions made on which provider you're using. This should allow very
|
||||||
# wide support without needing separate handlers for OVH, Rackspace, etc.
|
# wide support without needing separate handlers for OVH, Rackspace, etc.
|
||||||
#
|
#
|
||||||
# config :pleroma, Pleroma.Uploaders.Swift,
|
# config :pleroma, Pleroma.Uploaders.Swift,
|
||||||
|
|
|
@ -66,7 +66,8 @@ def start(_type, _args) do
|
||||||
),
|
),
|
||||||
worker(Pleroma.Web.Federator.RetryQueue, []),
|
worker(Pleroma.Web.Federator.RetryQueue, []),
|
||||||
worker(Pleroma.Web.Federator, []),
|
worker(Pleroma.Web.Federator, []),
|
||||||
worker(Pleroma.Stats, [])
|
worker(Pleroma.Stats, []),
|
||||||
|
worker(Pleroma.Web.Push, [])
|
||||||
] ++
|
] ++
|
||||||
streamer_child() ++
|
streamer_child() ++
|
||||||
chat_child() ++
|
chat_child() ++
|
||||||
|
|
|
@ -110,6 +110,7 @@ def create_notification(%Activity{} = activity, %User{} = user) do
|
||||||
notification = %Notification{user_id: user.id, activity: activity}
|
notification = %Notification{user_id: user.id, activity: activity}
|
||||||
{:ok, notification} = Repo.insert(notification)
|
{:ok, notification} = Repo.insert(notification)
|
||||||
Pleroma.Web.Streamer.stream("user", notification)
|
Pleroma.Web.Streamer.stream("user", notification)
|
||||||
|
Pleroma.Web.Push.send(notification)
|
||||||
notification
|
notification
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -17,7 +17,9 @@ def call(%{assigns: %{user: %User{}}} = conn, _), do: conn
|
||||||
def call(conn, _) do
|
def call(conn, _) do
|
||||||
with {:ok, token} <- fetch_token(conn),
|
with {:ok, token} <- fetch_token(conn),
|
||||||
{:ok, user} <- fetch_user(token) do
|
{:ok, user} <- fetch_user(token) do
|
||||||
assign(conn, :user, user)
|
conn
|
||||||
|
|> assign(:token, token)
|
||||||
|
|> assign(:user, user)
|
||||||
else
|
else
|
||||||
_ -> conn
|
_ -> conn
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,13 +2,22 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
||||||
use Pleroma.Web, :controller
|
use Pleroma.Web, :controller
|
||||||
alias Pleroma.{Repo, Object, Activity, User, Notification, Stats}
|
alias Pleroma.{Repo, Object, Activity, User, Notification, Stats}
|
||||||
alias Pleroma.Web
|
alias Pleroma.Web
|
||||||
alias Pleroma.Web.MastodonAPI.{StatusView, AccountView, MastodonView, ListView, FilterView}
|
|
||||||
|
alias Pleroma.Web.MastodonAPI.{
|
||||||
|
StatusView,
|
||||||
|
AccountView,
|
||||||
|
MastodonView,
|
||||||
|
ListView,
|
||||||
|
FilterView,
|
||||||
|
PushSubscriptionView
|
||||||
|
}
|
||||||
|
|
||||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
alias Pleroma.Web.ActivityPub.Utils
|
alias Pleroma.Web.ActivityPub.Utils
|
||||||
alias Pleroma.Web.CommonAPI
|
alias Pleroma.Web.CommonAPI
|
||||||
alias Pleroma.Web.OAuth.{Authorization, Token, App}
|
alias Pleroma.Web.OAuth.{Authorization, Token, App}
|
||||||
alias Pleroma.Web.MediaProxy
|
alias Pleroma.Web.MediaProxy
|
||||||
alias Comeonin.Pbkdf2
|
|
||||||
import Ecto.Query
|
import Ecto.Query
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
|
@ -1160,6 +1169,33 @@ def delete_filter(%{assigns: %{user: user}} = conn, %{"id" => filter_id}) do
|
||||||
json(conn, %{})
|
json(conn, %{})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def create_push_subscription(%{assigns: %{user: user, token: token}} = conn, params) do
|
||||||
|
Pleroma.Web.Push.Subscription.delete_if_exists(user, token)
|
||||||
|
{:ok, subscription} = Pleroma.Web.Push.Subscription.create(user, token, params)
|
||||||
|
view = PushSubscriptionView.render("push_subscription.json", subscription: subscription)
|
||||||
|
json(conn, view)
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_push_subscription(%{assigns: %{user: user, token: token}} = conn, _params) do
|
||||||
|
subscription = Pleroma.Web.Push.Subscription.get(user, token)
|
||||||
|
view = PushSubscriptionView.render("push_subscription.json", subscription: subscription)
|
||||||
|
json(conn, view)
|
||||||
|
end
|
||||||
|
|
||||||
|
def update_push_subscription(
|
||||||
|
%{assigns: %{user: user, token: token}} = conn,
|
||||||
|
params
|
||||||
|
) do
|
||||||
|
{:ok, subscription} = Pleroma.Web.Push.Subscription.update(user, token, params)
|
||||||
|
view = PushSubscriptionView.render("push_subscription.json", subscription: subscription)
|
||||||
|
json(conn, view)
|
||||||
|
end
|
||||||
|
|
||||||
|
def delete_push_subscription(%{assigns: %{user: user, token: token}} = conn, _params) do
|
||||||
|
{:ok, _response} = Pleroma.Web.Push.Subscription.delete(user, token)
|
||||||
|
json(conn, %{})
|
||||||
|
end
|
||||||
|
|
||||||
def errors(conn, _) do
|
def errors(conn, _) do
|
||||||
conn
|
conn
|
||||||
|> put_status(500)
|
|> put_status(500)
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
defmodule Pleroma.Web.MastodonAPI.PushSubscriptionView do
|
||||||
|
use Pleroma.Web, :view
|
||||||
|
alias Pleroma.Web.MastodonAPI.PushSubscriptionView
|
||||||
|
|
||||||
|
def render("push_subscription.json", %{subscription: subscription}) do
|
||||||
|
%{
|
||||||
|
id: to_string(subscription.id),
|
||||||
|
endpoint: subscription.endpoint,
|
||||||
|
alerts: Map.get(subscription.data, "alerts")
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,116 @@
|
||||||
|
defmodule Pleroma.Web.Push do
|
||||||
|
use GenServer
|
||||||
|
|
||||||
|
alias Pleroma.{Repo, User}
|
||||||
|
alias Pleroma.Web.Push.Subscription
|
||||||
|
|
||||||
|
require Logger
|
||||||
|
import Ecto.Query
|
||||||
|
|
||||||
|
@types ["Create", "Follow", "Announce", "Like"]
|
||||||
|
|
||||||
|
@gcm_api_key nil
|
||||||
|
|
||||||
|
def start_link() do
|
||||||
|
GenServer.start_link(__MODULE__, :ok, name: __MODULE__)
|
||||||
|
end
|
||||||
|
|
||||||
|
def init(:ok) do
|
||||||
|
case Application.get_env(:web_push_encryption, :vapid_details) do
|
||||||
|
nil ->
|
||||||
|
Logger.warn(
|
||||||
|
"VAPID key pair is not found. Please, add VAPID configuration to config. Run `mix web_push.gen.keypair` mix task to create a key pair"
|
||||||
|
)
|
||||||
|
|
||||||
|
:ignore
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
{:ok, %{}}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def send(notification) do
|
||||||
|
if Application.get_env(:web_push_encryption, :vapid_details) do
|
||||||
|
GenServer.cast(Pleroma.Web.Push, {:send, notification})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def handle_cast(
|
||||||
|
{:send, %{activity: %{data: %{"type" => type}}, user_id: user_id} = notification},
|
||||||
|
state
|
||||||
|
)
|
||||||
|
when type in @types do
|
||||||
|
actor = User.get_cached_by_ap_id(notification.activity.data["actor"])
|
||||||
|
body = notification |> format(actor) |> Jason.encode!()
|
||||||
|
|
||||||
|
Subscription
|
||||||
|
|> where(user_id: ^user_id)
|
||||||
|
|> Repo.all()
|
||||||
|
|> Enum.each(fn record ->
|
||||||
|
subscription = %{
|
||||||
|
keys: %{
|
||||||
|
p256dh: record.key_p256dh,
|
||||||
|
auth: record.key_auth
|
||||||
|
},
|
||||||
|
endpoint: record.endpoint
|
||||||
|
}
|
||||||
|
|
||||||
|
case WebPushEncryption.send_web_push(body, subscription, @gcm_api_key) do
|
||||||
|
{:ok, %{status_code: code}} when 400 <= code and code < 500 ->
|
||||||
|
Logger.debug("Removing subscription record")
|
||||||
|
Repo.delete!(record)
|
||||||
|
:ok
|
||||||
|
|
||||||
|
{:ok, %{status_code: code}} when 200 <= code and code < 300 ->
|
||||||
|
:ok
|
||||||
|
|
||||||
|
{:ok, %{status_code: code}} ->
|
||||||
|
Logger.error("Web Push Nonification failed with code: #{code}")
|
||||||
|
:error
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
Logger.error("Web Push Nonification failed with unknown error")
|
||||||
|
:error
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
{:noreply, state}
|
||||||
|
end
|
||||||
|
|
||||||
|
def handle_cast({:send, _}, state) do
|
||||||
|
Logger.warn("Unknown notification type")
|
||||||
|
{:noreply, state}
|
||||||
|
end
|
||||||
|
|
||||||
|
def format(%{activity: %{data: %{"type" => "Create"}}}, actor) do
|
||||||
|
%{
|
||||||
|
title: "New Mention",
|
||||||
|
body: "@#{actor.nickname} has mentiond you",
|
||||||
|
icon: User.avatar_url(actor)
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def format(%{activity: %{data: %{"type" => "Follow"}}}, actor) do
|
||||||
|
%{
|
||||||
|
title: "New Follower",
|
||||||
|
body: "@#{actor.nickname} has followed you",
|
||||||
|
icon: User.avatar_url(actor)
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def format(%{activity: %{data: %{"type" => "Announce"}}}, actor) do
|
||||||
|
%{
|
||||||
|
title: "New Announce",
|
||||||
|
body: "@#{actor.nickname} has announced your post",
|
||||||
|
icon: User.avatar_url(actor)
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def format(%{activity: %{data: %{"type" => "Like"}}}, actor) do
|
||||||
|
%{
|
||||||
|
title: "New Like",
|
||||||
|
body: "@#{actor.nickname} has liked your post",
|
||||||
|
icon: User.avatar_url(actor)
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,66 @@
|
||||||
|
defmodule Pleroma.Web.Push.Subscription do
|
||||||
|
use Ecto.Schema
|
||||||
|
import Ecto.Changeset
|
||||||
|
alias Pleroma.{Repo, User}
|
||||||
|
alias Pleroma.Web.OAuth.Token
|
||||||
|
alias Pleroma.Web.Push.Subscription
|
||||||
|
|
||||||
|
schema "push_subscriptions" do
|
||||||
|
belongs_to(:user, User)
|
||||||
|
belongs_to(:token, Token)
|
||||||
|
field(:endpoint, :string)
|
||||||
|
field(:key_p256dh, :string)
|
||||||
|
field(:key_auth, :string)
|
||||||
|
field(:data, :map, default: %{})
|
||||||
|
|
||||||
|
timestamps()
|
||||||
|
end
|
||||||
|
|
||||||
|
@supported_alert_types ~w[follow favourite mention reblog]
|
||||||
|
|
||||||
|
defp alerts(%{"data" => %{"alerts" => alerts}}) do
|
||||||
|
alerts = Map.take(alerts, @supported_alert_types)
|
||||||
|
%{"alerts" => alerts}
|
||||||
|
end
|
||||||
|
|
||||||
|
def create(
|
||||||
|
%User{} = user,
|
||||||
|
%Token{} = token,
|
||||||
|
%{
|
||||||
|
"subscription" => %{
|
||||||
|
"endpoint" => endpoint,
|
||||||
|
"keys" => %{"auth" => key_auth, "p256dh" => key_p256dh}
|
||||||
|
}
|
||||||
|
} = params
|
||||||
|
) do
|
||||||
|
Repo.insert(%Subscription{
|
||||||
|
user_id: user.id,
|
||||||
|
token_id: token.id,
|
||||||
|
endpoint: endpoint,
|
||||||
|
key_auth: key_auth,
|
||||||
|
key_p256dh: key_p256dh,
|
||||||
|
data: alerts(params)
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
def get(%User{id: user_id}, %Token{id: token_id}) do
|
||||||
|
Repo.get_by(Subscription, user_id: user_id, token_id: token_id)
|
||||||
|
end
|
||||||
|
|
||||||
|
def update(user, token, params) do
|
||||||
|
get(user, token)
|
||||||
|
|> change(data: alerts(params))
|
||||||
|
|> Repo.update()
|
||||||
|
end
|
||||||
|
|
||||||
|
def delete(user, token) do
|
||||||
|
Repo.delete(get(user, token))
|
||||||
|
end
|
||||||
|
|
||||||
|
def delete_if_exists(user, token) do
|
||||||
|
case get(user, token) do
|
||||||
|
nil -> {:ok, nil}
|
||||||
|
sub -> Repo.delete(sub)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -198,6 +198,11 @@ defmodule Pleroma.Web.Router do
|
||||||
put("/filters/:id", MastodonAPIController, :update_filter)
|
put("/filters/:id", MastodonAPIController, :update_filter)
|
||||||
delete("/filters/:id", MastodonAPIController, :delete_filter)
|
delete("/filters/:id", MastodonAPIController, :delete_filter)
|
||||||
|
|
||||||
|
post("/push/subscription", MastodonAPIController, :create_push_subscription)
|
||||||
|
get("/push/subscription", MastodonAPIController, :get_push_subscription)
|
||||||
|
put("/push/subscription", MastodonAPIController, :update_push_subscription)
|
||||||
|
delete("/push/subscription", MastodonAPIController, :delete_push_subscription)
|
||||||
|
|
||||||
get("/suggestions", MastodonAPIController, :suggestions)
|
get("/suggestions", MastodonAPIController, :suggestions)
|
||||||
|
|
||||||
get("/endorsements", MastodonAPIController, :empty_array)
|
get("/endorsements", MastodonAPIController, :empty_array)
|
||||||
|
|
|
@ -157,13 +157,17 @@ def config(conn, _params) do
|
||||||
|> send_resp(200, response)
|
|> send_resp(200, response)
|
||||||
|
|
||||||
_ ->
|
_ ->
|
||||||
|
vapid_public_key =
|
||||||
|
Keyword.get(Application.get_env(:web_push_encryption, :vapid_details), :public_key)
|
||||||
|
|
||||||
data = %{
|
data = %{
|
||||||
name: Keyword.get(instance, :name),
|
name: Keyword.get(instance, :name),
|
||||||
description: Keyword.get(instance, :description),
|
description: Keyword.get(instance, :description),
|
||||||
server: Web.base_url(),
|
server: Web.base_url(),
|
||||||
textlimit: to_string(Keyword.get(instance, :limit)),
|
textlimit: to_string(Keyword.get(instance, :limit)),
|
||||||
closed: if(Keyword.get(instance, :registrations_open), do: "0", else: "1"),
|
closed: if(Keyword.get(instance, :registrations_open), do: "0", else: "1"),
|
||||||
private: if(Keyword.get(instance, :public, true), do: "0", else: "1")
|
private: if(Keyword.get(instance, :public, true), do: "0", else: "1"),
|
||||||
|
vapidPublicKey: vapid_public_key
|
||||||
}
|
}
|
||||||
|
|
||||||
pleroma_fe = %{
|
pleroma_fe = %{
|
||||||
|
|
3
mix.exs
3
mix.exs
|
@ -68,7 +68,8 @@ defp deps do
|
||||||
{:crypt,
|
{:crypt,
|
||||||
git: "https://github.com/msantos/crypt", ref: "1f2b58927ab57e72910191a7ebaeff984382a1d3"},
|
git: "https://github.com/msantos/crypt", ref: "1f2b58927ab57e72910191a7ebaeff984382a1d3"},
|
||||||
{:cors_plug, "~> 1.5"},
|
{:cors_plug, "~> 1.5"},
|
||||||
{:ex_doc, "> 0.18.3 and < 0.20.0", only: :dev, runtime: false}
|
{:ex_doc, "> 0.18.3 and < 0.20.0", only: :dev, runtime: false},
|
||||||
|
{:web_push_encryption, "~> 0.2.1"}
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
3
mix.lock
3
mix.lock
|
@ -1,4 +1,5 @@
|
||||||
%{
|
%{
|
||||||
|
"base64url": {:hex, :base64url, "0.0.1", "36a90125f5948e3afd7be97662a1504b934dd5dac78451ca6e9abf85a10286be", [:rebar], [], "hexpm"},
|
||||||
"bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], [], "hexpm"},
|
"bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], [], "hexpm"},
|
||||||
"cachex": {:hex, :cachex, "3.0.2", "1351caa4e26e29f7d7ec1d29b53d6013f0447630bbf382b4fb5d5bad0209f203", [:mix], [{:eternal, "~> 1.2", [hex: :eternal, repo: "hexpm", optional: false]}, {:unsafe, "~> 1.0", [hex: :unsafe, repo: "hexpm", optional: false]}], "hexpm"},
|
"cachex": {:hex, :cachex, "3.0.2", "1351caa4e26e29f7d7ec1d29b53d6013f0447630bbf382b4fb5d5bad0209f203", [:mix], [{:eternal, "~> 1.2", [hex: :eternal, repo: "hexpm", optional: false]}, {:unsafe, "~> 1.0", [hex: :unsafe, repo: "hexpm", optional: false]}], "hexpm"},
|
||||||
"calendar": {:hex, :calendar, "0.17.4", "22c5e8d98a4db9494396e5727108dffb820ee0d18fed4b0aa8ab76e4f5bc32f1", [:mix], [{:tzdata, "~> 0.5.8 or ~> 0.1.201603", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm"},
|
"calendar": {:hex, :calendar, "0.17.4", "22c5e8d98a4db9494396e5727108dffb820ee0d18fed4b0aa8ab76e4f5bc32f1", [:mix], [{:tzdata, "~> 0.5.8 or ~> 0.1.201603", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm"},
|
||||||
|
@ -25,6 +26,7 @@
|
||||||
"httpoison": {:hex, :httpoison, "1.2.0", "2702ed3da5fd7a8130fc34b11965c8cfa21ade2f232c00b42d96d4967c39a3a3", [:mix], [{:hackney, "~> 1.8", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm"},
|
"httpoison": {:hex, :httpoison, "1.2.0", "2702ed3da5fd7a8130fc34b11965c8cfa21ade2f232c00b42d96d4967c39a3a3", [:mix], [{:hackney, "~> 1.8", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm"},
|
||||||
"idna": {:hex, :idna, "5.1.2", "e21cb58a09f0228a9e0b95eaa1217f1bcfc31a1aaa6e1fdf2f53a33f7dbd9494", [:rebar3], [{:unicode_util_compat, "0.3.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm"},
|
"idna": {:hex, :idna, "5.1.2", "e21cb58a09f0228a9e0b95eaa1217f1bcfc31a1aaa6e1fdf2f53a33f7dbd9494", [:rebar3], [{:unicode_util_compat, "0.3.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm"},
|
||||||
"jason": {:hex, :jason, "1.0.0", "0f7cfa9bdb23fed721ec05419bcee2b2c21a77e926bce0deda029b5adc716fe2", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm"},
|
"jason": {:hex, :jason, "1.0.0", "0f7cfa9bdb23fed721ec05419bcee2b2c21a77e926bce0deda029b5adc716fe2", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm"},
|
||||||
|
"jose": {:hex, :jose, "1.8.4", "7946d1e5c03a76ac9ef42a6e6a20001d35987afd68c2107bcd8f01a84e75aa73", [:mix, :rebar3], [{:base64url, "~> 0.0.1", [hex: :base64url, repo: "hexpm", optional: false]}], "hexpm"},
|
||||||
"makeup": {:hex, :makeup, "0.5.5", "9e08dfc45280c5684d771ad58159f718a7b5788596099bdfb0284597d368a882", [:mix], [{:nimble_parsec, "~> 0.4", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm"},
|
"makeup": {:hex, :makeup, "0.5.5", "9e08dfc45280c5684d771ad58159f718a7b5788596099bdfb0284597d368a882", [:mix], [{:nimble_parsec, "~> 0.4", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm"},
|
||||||
"makeup_elixir": {:hex, :makeup_elixir, "0.10.0", "0f09c2ddf352887a956d84f8f7e702111122ca32fbbc84c2f0569b8b65cbf7fa", [:mix], [{:makeup, "~> 0.5.5", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm"},
|
"makeup_elixir": {:hex, :makeup_elixir, "0.10.0", "0f09c2ddf352887a956d84f8f7e702111122ca32fbbc84c2f0569b8b65cbf7fa", [:mix], [{:makeup, "~> 0.5.5", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm"},
|
||||||
"meck": {:hex, :meck, "0.8.9", "64c5c0bd8bcca3a180b44196265c8ed7594e16bcc845d0698ec6b4e577f48188", [:rebar3], [], "hexpm"},
|
"meck": {:hex, :meck, "0.8.9", "64c5c0bd8bcca3a180b44196265c8ed7594e16bcc845d0698ec6b4e577f48188", [:rebar3], [], "hexpm"},
|
||||||
|
@ -52,4 +54,5 @@
|
||||||
"tzdata": {:hex, :tzdata, "0.5.17", "50793e3d85af49736701da1a040c415c97dc1caf6464112fd9bd18f425d3053b", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm"},
|
"tzdata": {:hex, :tzdata, "0.5.17", "50793e3d85af49736701da1a040c415c97dc1caf6464112fd9bd18f425d3053b", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm"},
|
||||||
"unicode_util_compat": {:hex, :unicode_util_compat, "0.3.1", "a1f612a7b512638634a603c8f401892afbf99b8ce93a45041f8aaca99cadb85e", [:rebar3], [], "hexpm"},
|
"unicode_util_compat": {:hex, :unicode_util_compat, "0.3.1", "a1f612a7b512638634a603c8f401892afbf99b8ce93a45041f8aaca99cadb85e", [:rebar3], [], "hexpm"},
|
||||||
"unsafe": {:hex, :unsafe, "1.0.0", "7c21742cd05380c7875546b023481d3a26f52df8e5dfedcb9f958f322baae305", [:mix], [], "hexpm"},
|
"unsafe": {:hex, :unsafe, "1.0.0", "7c21742cd05380c7875546b023481d3a26f52df8e5dfedcb9f958f322baae305", [:mix], [], "hexpm"},
|
||||||
|
"web_push_encryption": {:hex, :web_push_encryption, "0.2.1", "d42cecf73420d9dc0053ba3299cc8c8d6ff2be2487d67ca2a57265868e4d9a98", [:mix], [{:httpoison, "~> 1.0", [hex: :httpoison, repo: "hexpm", optional: false]}, {:jose, "~> 1.8", [hex: :jose, repo: "hexpm", optional: false]}, {:poison, "~> 3.0", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm"},
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
defmodule Pleroma.Repo.Migrations.CreatePushSubscriptions do
|
||||||
|
use Ecto.Migration
|
||||||
|
|
||||||
|
def change do
|
||||||
|
create table("push_subscriptions") do
|
||||||
|
add :user_id, references("users", on_delete: :delete_all)
|
||||||
|
add :token_id, references("oauth_tokens", on_delete: :delete_all)
|
||||||
|
add :endpoint, :string
|
||||||
|
add :key_p256dh, :string
|
||||||
|
add :key_auth, :string
|
||||||
|
add :data, :map
|
||||||
|
|
||||||
|
timestamps()
|
||||||
|
end
|
||||||
|
|
||||||
|
create index("push_subscriptions", [:user_id, :token_id], unique: true)
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in New Issue