Merge remote-tracking branch 'upstream/develop' into feature/openldap-support
This commit is contained in:
commit
54e7087ab4
12
.credo.exs
12
.credo.exs
|
@ -69,8 +69,8 @@
|
||||||
# You can also customize the exit_status of each check.
|
# You can also customize the exit_status of each check.
|
||||||
# If you don't want TODO comments to cause `mix credo` to fail, just
|
# If you don't want TODO comments to cause `mix credo` to fail, just
|
||||||
# set this value to 0 (zero).
|
# set this value to 0 (zero).
|
||||||
{Credo.Check.Design.TagTODO, exit_status: 2},
|
{Credo.Check.Design.TagTODO, exit_status: 0},
|
||||||
{Credo.Check.Design.TagFIXME},
|
{Credo.Check.Design.TagFIXME, exit_status: 0},
|
||||||
|
|
||||||
{Credo.Check.Readability.FunctionNames},
|
{Credo.Check.Readability.FunctionNames},
|
||||||
{Credo.Check.Readability.LargeNumbers},
|
{Credo.Check.Readability.LargeNumbers},
|
||||||
|
@ -81,7 +81,9 @@
|
||||||
{Credo.Check.Readability.ParenthesesOnZeroArityDefs},
|
{Credo.Check.Readability.ParenthesesOnZeroArityDefs},
|
||||||
{Credo.Check.Readability.ParenthesesInCondition},
|
{Credo.Check.Readability.ParenthesesInCondition},
|
||||||
{Credo.Check.Readability.PredicateFunctionNames},
|
{Credo.Check.Readability.PredicateFunctionNames},
|
||||||
{Credo.Check.Readability.PreferImplicitTry},
|
# lanodan: I think PreferImplicitTry should be consistency, and the behaviour seems
|
||||||
|
# inconsistent, see: https://github.com/rrrene/credo/issues/224
|
||||||
|
{Credo.Check.Readability.PreferImplicitTry, false},
|
||||||
{Credo.Check.Readability.RedundantBlankLines},
|
{Credo.Check.Readability.RedundantBlankLines},
|
||||||
{Credo.Check.Readability.StringSigils},
|
{Credo.Check.Readability.StringSigils},
|
||||||
{Credo.Check.Readability.TrailingBlankLine},
|
{Credo.Check.Readability.TrailingBlankLine},
|
||||||
|
@ -126,10 +128,6 @@
|
||||||
|
|
||||||
# Deprecated checks (these will be deleted after a grace period)
|
# Deprecated checks (these will be deleted after a grace period)
|
||||||
{Credo.Check.Readability.Specs, false},
|
{Credo.Check.Readability.Specs, false},
|
||||||
{Credo.Check.Warning.NameRedeclarationByAssignment, false},
|
|
||||||
{Credo.Check.Warning.NameRedeclarationByCase, false},
|
|
||||||
{Credo.Check.Warning.NameRedeclarationByDef, false},
|
|
||||||
{Credo.Check.Warning.NameRedeclarationByFn, false},
|
|
||||||
|
|
||||||
# Custom checks can be created using `mix credo.gen.check`.
|
# Custom checks can be created using `mix credo.gen.check`.
|
||||||
#
|
#
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
image: elixir:1.7.2
|
image: elixir:1.8.1
|
||||||
|
|
||||||
services:
|
services:
|
||||||
- name: postgres:9.6.2
|
- name: postgres:9.6.2
|
||||||
|
@ -19,6 +19,7 @@ cache:
|
||||||
stages:
|
stages:
|
||||||
- lint
|
- lint
|
||||||
- test
|
- test
|
||||||
|
- analysis
|
||||||
|
|
||||||
before_script:
|
before_script:
|
||||||
- mix local.hex --force
|
- mix local.hex --force
|
||||||
|
@ -37,3 +38,8 @@ unit-testing:
|
||||||
stage: test
|
stage: test
|
||||||
script:
|
script:
|
||||||
- mix test --trace --preload-modules
|
- mix test --trace --preload-modules
|
||||||
|
|
||||||
|
analysis:
|
||||||
|
stage: analysis
|
||||||
|
script:
|
||||||
|
- mix credo --strict --only=warnings,todo,fixme,consistency,readability
|
||||||
|
|
12
TODO.txt
12
TODO.txt
|
@ -1,12 +0,0 @@
|
||||||
Unliking:
|
|
||||||
|
|
||||||
- Add a proper undo activity, find out how to ignore those in twitter api.
|
|
||||||
|
|
||||||
WEBSUB:
|
|
||||||
|
|
||||||
- Add unsubscription
|
|
||||||
|
|
||||||
OSTATUS:
|
|
||||||
|
|
||||||
- Save and output 'updated'
|
|
||||||
|
|
|
@ -133,7 +133,14 @@
|
||||||
config :tesla, adapter: Tesla.Adapter.Hackney
|
config :tesla, adapter: Tesla.Adapter.Hackney
|
||||||
|
|
||||||
# Configures http settings, upstream proxy etc.
|
# Configures http settings, upstream proxy etc.
|
||||||
config :pleroma, :http, proxy_url: nil
|
config :pleroma, :http,
|
||||||
|
proxy_url: nil,
|
||||||
|
adapter: [
|
||||||
|
ssl_options: [
|
||||||
|
# We don't support TLS v1.3 yet
|
||||||
|
versions: [:tlsv1, :"tlsv1.1", :"tlsv1.2"]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
config :pleroma, :instance,
|
config :pleroma, :instance,
|
||||||
name: "Pleroma",
|
name: "Pleroma",
|
||||||
|
@ -215,6 +222,9 @@
|
||||||
scopeCopy: true,
|
scopeCopy: true,
|
||||||
subjectLineBehavior: "email",
|
subjectLineBehavior: "email",
|
||||||
alwaysShowSubjectInput: true
|
alwaysShowSubjectInput: true
|
||||||
|
},
|
||||||
|
masto_fe: %{
|
||||||
|
showInstanceSpecificPanel: true
|
||||||
}
|
}
|
||||||
|
|
||||||
config :pleroma, :activitypub,
|
config :pleroma, :activitypub,
|
||||||
|
@ -345,6 +355,10 @@
|
||||||
federator_outgoing: [max_jobs: 50],
|
federator_outgoing: [max_jobs: 50],
|
||||||
mailer: [max_jobs: 10]
|
mailer: [max_jobs: 10]
|
||||||
|
|
||||||
|
config :pleroma, :fetch_initial_posts,
|
||||||
|
enabled: false,
|
||||||
|
pages: 5
|
||||||
|
|
||||||
config :auto_linker,
|
config :auto_linker,
|
||||||
opts: [
|
opts: [
|
||||||
scheme: true,
|
scheme: true,
|
||||||
|
|
|
@ -44,6 +44,8 @@
|
||||||
"BLH1qVhJItRGCfxgTtONfsOKDc9VRAraXw-3NsmjMngWSh7NxOizN6bkuRA7iLTMPS82PjwJAr3UoK9EC1IFrz4",
|
"BLH1qVhJItRGCfxgTtONfsOKDc9VRAraXw-3NsmjMngWSh7NxOizN6bkuRA7iLTMPS82PjwJAr3UoK9EC1IFrz4",
|
||||||
private_key: "_-XZ0iebPrRfZ_o0-IatTdszYa8VCH1yLN-JauK7HHA"
|
private_key: "_-XZ0iebPrRfZ_o0-IatTdszYa8VCH1yLN-JauK7HHA"
|
||||||
|
|
||||||
|
config :web_push_encryption, :http_client, Pleroma.Web.WebPushHttpClientMock
|
||||||
|
|
||||||
config :pleroma, Pleroma.Jobs, testing: [max_jobs: 2]
|
config :pleroma, Pleroma.Jobs, testing: [max_jobs: 2]
|
||||||
|
|
||||||
try do
|
try do
|
||||||
|
|
|
@ -7,36 +7,11 @@ Authentication is required and the user must be an admin.
|
||||||
### List users
|
### List users
|
||||||
|
|
||||||
- Method `GET`
|
- Method `GET`
|
||||||
- Params:
|
- Query Params:
|
||||||
- `page`: **integer** page number
|
- `query`: **string** *optional* search term
|
||||||
- `page_size`: **integer** number of users per page (default is `50`)
|
- `local_only`: **bool** *optional* whether to return only local users
|
||||||
- Response:
|
- `page`: **integer** *optional* page number
|
||||||
|
- `page_size`: **integer** *optional* number of users per page (default is `50`)
|
||||||
```JSON
|
|
||||||
{
|
|
||||||
"page_size": integer,
|
|
||||||
"count": integer,
|
|
||||||
"users": [
|
|
||||||
{
|
|
||||||
"deactivated": bool,
|
|
||||||
"id": integer,
|
|
||||||
"nickname": string
|
|
||||||
},
|
|
||||||
...
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## `/api/pleroma/admin/users/search?query={query}&local={local}&page={page}&page_size={page_size}`
|
|
||||||
|
|
||||||
### Search users by name or nickname
|
|
||||||
|
|
||||||
- Method `GET`
|
|
||||||
- Params:
|
|
||||||
- `query`: **string** search term
|
|
||||||
- `local`: **bool** whether to return only local users
|
|
||||||
- `page`: **integer** page number
|
|
||||||
- `page_size`: **integer** number of users per page (default is `50`)
|
|
||||||
- Response:
|
- Response:
|
||||||
|
|
||||||
```JSON
|
```JSON
|
||||||
|
|
|
@ -4,8 +4,8 @@ Feel free to contact us to be added to this list!
|
||||||
|
|
||||||
## Desktop
|
## Desktop
|
||||||
### Roma for Desktop
|
### Roma for Desktop
|
||||||
- Homepage: <http://www.pleroma.com/desktop-app/>
|
- Homepage: <https://www.pleroma.com/#desktopApp>
|
||||||
- Source Code: ???
|
- Source Code: <https://github.com/roma-apps/roma-desktop>
|
||||||
- Platforms: Windows, Mac, (Linux?)
|
- Platforms: Windows, Mac, (Linux?)
|
||||||
- Features: Streaming Ready
|
- Features: Streaming Ready
|
||||||
|
|
||||||
|
@ -30,6 +30,12 @@ Feel free to contact us to be added to this list!
|
||||||
- Platforms: iOS
|
- Platforms: iOS
|
||||||
- Features: No Streaming
|
- Features: No Streaming
|
||||||
|
|
||||||
|
### Fedilab
|
||||||
|
- Source Code: <https://gitlab.com/tom79/mastalab/>
|
||||||
|
- Contact: [@tom79@mastodon.social](https://mastodon.social/users/tom79)
|
||||||
|
- Platforms: Android
|
||||||
|
- Features: Streaming Ready
|
||||||
|
|
||||||
### Nekonium
|
### Nekonium
|
||||||
- Homepage: [F-Droid Repository](https://repo.gdgd.jp.net/), [Google Play](https://play.google.com/store/apps/details?id=com.apps.nekonium), [Amazon](https://www.amazon.co.jp/dp/B076FXPRBC/)
|
- Homepage: [F-Droid Repository](https://repo.gdgd.jp.net/), [Google Play](https://play.google.com/store/apps/details?id=com.apps.nekonium), [Amazon](https://www.amazon.co.jp/dp/B076FXPRBC/)
|
||||||
- Source: <https://git.gdgd.jp.net/lin/nekonium/>
|
- Source: <https://git.gdgd.jp.net/lin/nekonium/>
|
||||||
|
@ -37,15 +43,9 @@ Feel free to contact us to be added to this list!
|
||||||
- Platforms: Android
|
- Platforms: Android
|
||||||
- Features: Streaming Ready
|
- Features: Streaming Ready
|
||||||
|
|
||||||
### Mastalab
|
|
||||||
- Source Code: <https://gitlab.com/tom79/mastalab/>
|
|
||||||
- Contact: [@tom79@mastodon.social](https://mastodon.social/users/tom79)
|
|
||||||
- Platforms: Android
|
|
||||||
- Features: Streaming Ready
|
|
||||||
|
|
||||||
### Roma
|
### Roma
|
||||||
- Homepage: <http://www.pleroma.com/>
|
- Homepage: <https://www.pleroma.com/#mobileApps>
|
||||||
- Source Code: ???
|
- Source Code: [iOS](https://github.com/roma-apps/roma-ios), [Android](https://github.com/roma-apps/roma-android)
|
||||||
- Platforms: iOS, Android
|
- Platforms: iOS, Android
|
||||||
- Features: No Streaming
|
- Features: No Streaming
|
||||||
|
|
||||||
|
|
|
@ -13,3 +13,13 @@ Some apps operate under the assumption that no more than 4 attachments can be re
|
||||||
## Timelines
|
## Timelines
|
||||||
|
|
||||||
Adding the parameter `with_muted=true` to the timeline queries will also return activities by muted (not by blocked!) users.
|
Adding the parameter `with_muted=true` to the timeline queries will also return activities by muted (not by blocked!) users.
|
||||||
|
|
||||||
|
## Statuses
|
||||||
|
|
||||||
|
Has these additional fields under the `pleroma` object:
|
||||||
|
|
||||||
|
- `local`: true if the post was made on the local instance.
|
||||||
|
|
||||||
|
## Accounts
|
||||||
|
|
||||||
|
- `/api/v1/accounts/:id`: The `id` parameter can also be the `nickname` of the user. This only works in this endpoint, not the deeper nested ones for following etc.
|
||||||
|
|
|
@ -129,7 +129,7 @@ See: [logger’s documentation](https://hexdocs.pm/logger/Logger.html) and [ex_s
|
||||||
|
|
||||||
## :frontend_configurations
|
## :frontend_configurations
|
||||||
|
|
||||||
This can be used to configure a keyword list that keeps the configuration data for any kind of frontend. By default, settings for `pleroma_fe` are configured.
|
This can be used to configure a keyword list that keeps the configuration data for any kind of frontend. By default, settings for `pleroma_fe` and `masto_fe` are configured.
|
||||||
|
|
||||||
Frontends can access these settings at `/api/pleroma/frontend_configurations`
|
Frontends can access these settings at `/api/pleroma/frontend_configurations`
|
||||||
|
|
||||||
|
@ -285,6 +285,10 @@ This config contains two queues: `federator_incoming` and `federator_outgoing`.
|
||||||
## :rich_media
|
## :rich_media
|
||||||
* `enabled`: if enabled the instance will parse metadata from attached links to generate link previews
|
* `enabled`: if enabled the instance will parse metadata from attached links to generate link previews
|
||||||
|
|
||||||
|
## :fetch_initial_posts
|
||||||
|
* `enabled`: if enabled, when a new user is federated with, fetch some of their latest posts
|
||||||
|
* `pages`: the amount of pages to fetch
|
||||||
|
|
||||||
## :hackney_pools
|
## :hackney_pools
|
||||||
|
|
||||||
Advanced. Tweaks Hackney (http client) connections pools.
|
Advanced. Tweaks Hackney (http client) connections pools.
|
||||||
|
|
|
@ -4,8 +4,8 @@
|
||||||
|
|
||||||
defmodule Mix.Tasks.Pleroma.Relay do
|
defmodule Mix.Tasks.Pleroma.Relay do
|
||||||
use Mix.Task
|
use Mix.Task
|
||||||
alias Pleroma.Web.ActivityPub.Relay
|
|
||||||
alias Mix.Tasks.Pleroma.Common
|
alias Mix.Tasks.Pleroma.Common
|
||||||
|
alias Pleroma.Web.ActivityPub.Relay
|
||||||
|
|
||||||
@shortdoc "Manages remote relays"
|
@shortdoc "Manages remote relays"
|
||||||
@moduledoc """
|
@moduledoc """
|
||||||
|
|
|
@ -4,9 +4,9 @@
|
||||||
|
|
||||||
defmodule Mix.Tasks.Pleroma.Uploads do
|
defmodule Mix.Tasks.Pleroma.Uploads do
|
||||||
use Mix.Task
|
use Mix.Task
|
||||||
|
alias Mix.Tasks.Pleroma.Common
|
||||||
alias Pleroma.Upload
|
alias Pleroma.Upload
|
||||||
alias Pleroma.Uploaders.Local
|
alias Pleroma.Uploaders.Local
|
||||||
alias Mix.Tasks.Pleroma.Common
|
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
@log_every 50
|
@log_every 50
|
||||||
|
@ -20,7 +20,6 @@ defmodule Mix.Tasks.Pleroma.Uploads do
|
||||||
Options:
|
Options:
|
||||||
- `--delete` - delete local uploads after migrating them to the target uploader
|
- `--delete` - delete local uploads after migrating them to the target uploader
|
||||||
|
|
||||||
|
|
||||||
A list of available uploaders can be seen in config.exs
|
A list of available uploaders can be seen in config.exs
|
||||||
"""
|
"""
|
||||||
def run(["migrate_local", target_uploader | args]) do
|
def run(["migrate_local", target_uploader | args]) do
|
||||||
|
|
|
@ -5,9 +5,9 @@
|
||||||
defmodule Mix.Tasks.Pleroma.User do
|
defmodule Mix.Tasks.Pleroma.User do
|
||||||
use Mix.Task
|
use Mix.Task
|
||||||
import Ecto.Changeset
|
import Ecto.Changeset
|
||||||
|
alias Mix.Tasks.Pleroma.Common
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Mix.Tasks.Pleroma.Common
|
|
||||||
|
|
||||||
@shortdoc "Manages Pleroma users"
|
@shortdoc "Manages Pleroma users"
|
||||||
@moduledoc """
|
@moduledoc """
|
||||||
|
|
|
@ -7,9 +7,9 @@ defmodule Pleroma.PasswordResetToken do
|
||||||
|
|
||||||
import Ecto.Changeset
|
import Ecto.Changeset
|
||||||
|
|
||||||
alias Pleroma.User
|
|
||||||
alias Pleroma.Repo
|
|
||||||
alias Pleroma.PasswordResetToken
|
alias Pleroma.PasswordResetToken
|
||||||
|
alias Pleroma.Repo
|
||||||
|
alias Pleroma.User
|
||||||
|
|
||||||
schema "password_reset_tokens" do
|
schema "password_reset_tokens" do
|
||||||
belongs_to(:user, User, type: Pleroma.FlakeId)
|
belongs_to(:user, User, type: Pleroma.FlakeId)
|
||||||
|
|
|
@ -5,9 +5,9 @@
|
||||||
defmodule Pleroma.Activity do
|
defmodule Pleroma.Activity do
|
||||||
use Ecto.Schema
|
use Ecto.Schema
|
||||||
|
|
||||||
alias Pleroma.Repo
|
|
||||||
alias Pleroma.Activity
|
alias Pleroma.Activity
|
||||||
alias Pleroma.Notification
|
alias Pleroma.Notification
|
||||||
|
alias Pleroma.Repo
|
||||||
|
|
||||||
import Ecto.Query
|
import Ecto.Query
|
||||||
|
|
||||||
|
@ -107,6 +107,18 @@ def get_in_reply_to_activity(%Activity{data: %{"object" => %{"inReplyTo" => ap_i
|
||||||
|
|
||||||
def get_in_reply_to_activity(_), do: nil
|
def get_in_reply_to_activity(_), do: nil
|
||||||
|
|
||||||
|
def delete_by_ap_id(id) when is_binary(id) do
|
||||||
|
by_object_ap_id(id)
|
||||||
|
|> Repo.delete_all(returning: true)
|
||||||
|
|> elem(1)
|
||||||
|
|> Enum.find(fn
|
||||||
|
%{data: %{"type" => "Create", "object" => %{"id" => ap_id}}} -> ap_id == id
|
||||||
|
_ -> nil
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
def delete_by_ap_id(_), do: nil
|
||||||
|
|
||||||
for {ap_type, type} <- @mastodon_notification_types do
|
for {ap_type, type} <- @mastodon_notification_types do
|
||||||
def mastodon_notification_type(%Activity{data: %{"type" => unquote(ap_type)}}),
|
def mastodon_notification_type(%Activity{data: %{"type" => unquote(ap_type)}}),
|
||||||
do: unquote(type)
|
do: unquote(type)
|
||||||
|
|
|
@ -11,10 +11,10 @@ defmodule Pleroma.Application do
|
||||||
@repository Mix.Project.config()[:source_url]
|
@repository Mix.Project.config()[:source_url]
|
||||||
def name, do: @name
|
def name, do: @name
|
||||||
def version, do: @version
|
def version, do: @version
|
||||||
def named_version(), do: @name <> " " <> @version
|
def named_version, do: @name <> " " <> @version
|
||||||
def repository, do: @repository
|
def repository, do: @repository
|
||||||
|
|
||||||
def user_agent() do
|
def user_agent do
|
||||||
info = "#{Pleroma.Web.base_url()} <#{Pleroma.Config.get([:instance, :email], "")}>"
|
info = "#{Pleroma.Web.base_url()} <#{Pleroma.Config.get([:instance, :email], "")}>"
|
||||||
named_version() <> "; " <> info
|
named_version() <> "; " <> info
|
||||||
end
|
end
|
||||||
|
@ -48,7 +48,7 @@ def start(_type, _args) do
|
||||||
[
|
[
|
||||||
:user_cache,
|
:user_cache,
|
||||||
[
|
[
|
||||||
default_ttl: 25000,
|
default_ttl: 25_000,
|
||||||
ttl_interval: 1000,
|
ttl_interval: 1000,
|
||||||
limit: 2500
|
limit: 2500
|
||||||
]
|
]
|
||||||
|
@ -60,7 +60,7 @@ def start(_type, _args) do
|
||||||
[
|
[
|
||||||
:object_cache,
|
:object_cache,
|
||||||
[
|
[
|
||||||
default_ttl: 25000,
|
default_ttl: 25_000,
|
||||||
ttl_interval: 1000,
|
ttl_interval: 1000,
|
||||||
limit: 2500
|
limit: 2500
|
||||||
]
|
]
|
||||||
|
@ -127,7 +127,7 @@ def start(_type, _args) do
|
||||||
Supervisor.start_link(children, opts)
|
Supervisor.start_link(children, opts)
|
||||||
end
|
end
|
||||||
|
|
||||||
def enabled_hackney_pools() do
|
def enabled_hackney_pools do
|
||||||
[:media] ++
|
[:media] ++
|
||||||
if Application.get_env(:tesla, :adapter) == Tesla.Adapter.Hackney do
|
if Application.get_env(:tesla, :adapter) == Tesla.Adapter.Hackney do
|
||||||
[:federation]
|
[:federation]
|
||||||
|
@ -142,14 +142,14 @@ def enabled_hackney_pools() do
|
||||||
end
|
end
|
||||||
|
|
||||||
if Mix.env() == :test do
|
if Mix.env() == :test do
|
||||||
defp streamer_child(), do: []
|
defp streamer_child, do: []
|
||||||
defp chat_child(), do: []
|
defp chat_child, do: []
|
||||||
else
|
else
|
||||||
defp streamer_child() do
|
defp streamer_child do
|
||||||
[worker(Pleroma.Web.Streamer, [])]
|
[worker(Pleroma.Web.Streamer, [])]
|
||||||
end
|
end
|
||||||
|
|
||||||
defp chat_child() do
|
defp chat_child do
|
||||||
if Pleroma.Config.get([:chat, :enabled]) do
|
if Pleroma.Config.get([:chat, :enabled]) do
|
||||||
[worker(Pleroma.Web.ChatChannel.ChatChannelState, [])]
|
[worker(Pleroma.Web.ChatChannel.ChatChannelState, [])]
|
||||||
else
|
else
|
||||||
|
@ -158,7 +158,7 @@ defp chat_child() do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp hackney_pool_children() do
|
defp hackney_pool_children do
|
||||||
for pool <- enabled_hackney_pools() do
|
for pool <- enabled_hackney_pools() do
|
||||||
options = Pleroma.Config.get([:hackney_pools, pool])
|
options = Pleroma.Config.get([:hackney_pools, pool])
|
||||||
:hackney_pool.child_spec(pool, options)
|
:hackney_pool.child_spec(pool, options)
|
||||||
|
|
|
@ -10,7 +10,7 @@ defmodule Pleroma.Captcha do
|
||||||
use GenServer
|
use GenServer
|
||||||
|
|
||||||
@doc false
|
@doc false
|
||||||
def start_link() do
|
def start_link do
|
||||||
GenServer.start_link(__MODULE__, [], name: __MODULE__)
|
GenServer.start_link(__MODULE__, [], name: __MODULE__)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ def init(_) do
|
||||||
@doc """
|
@doc """
|
||||||
Ask the configured captcha service for a new captcha
|
Ask the configured captcha service for a new captcha
|
||||||
"""
|
"""
|
||||||
def new() do
|
def new do
|
||||||
GenServer.call(__MODULE__, :new)
|
GenServer.call(__MODULE__, :new)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -73,7 +73,7 @@ def handle_call({:validate, token, captcha, answer_data}, _from, state) do
|
||||||
secret = KeyGenerator.generate(secret_key_base, token <> "_encrypt")
|
secret = KeyGenerator.generate(secret_key_base, token <> "_encrypt")
|
||||||
sign_secret = KeyGenerator.generate(secret_key_base, token <> "_sign")
|
sign_secret = KeyGenerator.generate(secret_key_base, token <> "_sign")
|
||||||
|
|
||||||
# If the time found is less than (current_time - seconds_valid), then the time has already passed.
|
# If the time found is less than (current_time-seconds_valid) then the time has already passed
|
||||||
# Later we check that the time found is more than the presumed invalidatation time, that means
|
# Later we check that the time found is more than the presumed invalidatation time, that means
|
||||||
# that the data is still valid and the captcha can be checked
|
# that the data is still valid and the captcha can be checked
|
||||||
seconds_valid = Pleroma.Config.get!([Pleroma.Captcha, :seconds_valid])
|
seconds_valid = Pleroma.Config.get!([Pleroma.Captcha, :seconds_valid])
|
||||||
|
|
|
@ -7,7 +7,7 @@ defmodule Pleroma.Captcha.Kocaptcha do
|
||||||
@behaviour Service
|
@behaviour Service
|
||||||
|
|
||||||
@impl Service
|
@impl Service
|
||||||
def new() do
|
def new do
|
||||||
endpoint = Pleroma.Config.get!([__MODULE__, :endpoint])
|
endpoint = Pleroma.Config.get!([__MODULE__, :endpoint])
|
||||||
|
|
||||||
case Tesla.get(endpoint <> "/new") do
|
case Tesla.get(endpoint <> "/new") do
|
||||||
|
|
|
@ -7,13 +7,13 @@ defmodule Pleroma.Clippy do
|
||||||
# No software is complete until they have a Clippy implementation.
|
# No software is complete until they have a Clippy implementation.
|
||||||
# A ballmer peak _may_ be required to change this module.
|
# A ballmer peak _may_ be required to change this module.
|
||||||
|
|
||||||
def tip() do
|
def tip do
|
||||||
tips()
|
tips()
|
||||||
|> Enum.random()
|
|> Enum.random()
|
||||||
|> puts()
|
|> puts()
|
||||||
end
|
end
|
||||||
|
|
||||||
def tips() do
|
def tips do
|
||||||
host = Pleroma.Config.get([Pleroma.Web.Endpoint, :url, :host])
|
host = Pleroma.Config.get([Pleroma.Web.Endpoint, :url, :host])
|
||||||
|
|
||||||
[
|
[
|
||||||
|
@ -92,8 +92,8 @@ def puts(text_or_lines) do
|
||||||
|
|
||||||
# surrond one/five line clippy with blank lines around to not fuck up the layout
|
# surrond one/five line clippy with blank lines around to not fuck up the layout
|
||||||
#
|
#
|
||||||
# yes this fix sucks but it's good enough, have you ever seen a release of windows wihtout some butched
|
# yes this fix sucks but it's good enough, have you ever seen a release of windows
|
||||||
# features anyway?
|
# without some butched features anyway?
|
||||||
lines =
|
lines =
|
||||||
if length(lines) == 1 or length(lines) == 5 do
|
if length(lines) == 1 or length(lines) == 5 do
|
||||||
[""] ++ lines ++ [""]
|
[""] ++ lines ++ [""]
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
defmodule Pleroma.Config.DeprecationWarnings do
|
defmodule Pleroma.Config.DeprecationWarnings do
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
def check_frontend_config_mechanism() do
|
def check_frontend_config_mechanism do
|
||||||
if Pleroma.Config.get(:fe) do
|
if Pleroma.Config.get(:fe) do
|
||||||
Logger.warn("""
|
Logger.warn("""
|
||||||
!!!DEPRECATION WARNING!!!
|
!!!DEPRECATION WARNING!!!
|
||||||
|
|
|
@ -17,13 +17,13 @@ defmodule Pleroma.Emoji do
|
||||||
@ets_options [:ordered_set, :protected, :named_table, {:read_concurrency, true}]
|
@ets_options [:ordered_set, :protected, :named_table, {:read_concurrency, true}]
|
||||||
|
|
||||||
@doc false
|
@doc false
|
||||||
def start_link() do
|
def start_link do
|
||||||
GenServer.start_link(__MODULE__, [], name: __MODULE__)
|
GenServer.start_link(__MODULE__, [], name: __MODULE__)
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc "Reloads the emojis from disk."
|
@doc "Reloads the emojis from disk."
|
||||||
@spec reload() :: :ok
|
@spec reload() :: :ok
|
||||||
def reload() do
|
def reload do
|
||||||
GenServer.call(__MODULE__, :reload)
|
GenServer.call(__MODULE__, :reload)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@ def get(name) do
|
||||||
|
|
||||||
@doc "Returns all the emojos!!"
|
@doc "Returns all the emojos!!"
|
||||||
@spec get_all() :: [{String.t(), String.t()}, ...]
|
@spec get_all() :: [{String.t(), String.t()}, ...]
|
||||||
def get_all() do
|
def get_all do
|
||||||
:ets.tab2list(@ets)
|
:ets.tab2list(@ets)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -72,7 +72,7 @@ def code_change(_old_vsn, state, _extra) do
|
||||||
{:ok, state}
|
{:ok, state}
|
||||||
end
|
end
|
||||||
|
|
||||||
defp load() do
|
defp load do
|
||||||
emojis =
|
emojis =
|
||||||
(load_finmoji(Keyword.get(Application.get_env(:pleroma, :instance), :finmoji_enabled)) ++
|
(load_finmoji(Keyword.get(Application.get_env(:pleroma, :instance), :finmoji_enabled)) ++
|
||||||
load_from_file("config/emoji.txt") ++
|
load_from_file("config/emoji.txt") ++
|
||||||
|
|
|
@ -8,8 +8,8 @@ defmodule Pleroma.Filter do
|
||||||
import Ecto.Changeset
|
import Ecto.Changeset
|
||||||
import Ecto.Query
|
import Ecto.Query
|
||||||
|
|
||||||
alias Pleroma.User
|
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
|
alias Pleroma.User
|
||||||
|
|
||||||
schema "filters" do
|
schema "filters" do
|
||||||
belongs_to(:user, User, type: Pleroma.FlakeId)
|
belongs_to(:user, User, type: Pleroma.FlakeId)
|
||||||
|
|
|
@ -85,7 +85,7 @@ def dump(value) do
|
||||||
{:ok, FlakeId.from_string(value)}
|
{:ok, FlakeId.from_string(value)}
|
||||||
end
|
end
|
||||||
|
|
||||||
def autogenerate(), do: get()
|
def autogenerate, do: get()
|
||||||
|
|
||||||
# -- GenServer API
|
# -- GenServer API
|
||||||
def start_link do
|
def start_link do
|
||||||
|
@ -165,7 +165,7 @@ defp time do
|
||||||
1_000_000_000 * mega_seconds + seconds * 1000 + :erlang.trunc(micro_seconds / 1000)
|
1_000_000_000 * mega_seconds + seconds * 1000 + :erlang.trunc(micro_seconds / 1000)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp worker_id() do
|
defp worker_id do
|
||||||
<<worker::integer-size(48)>> = :crypto.strong_rand_bytes(6)
|
<<worker::integer-size(48)>> = :crypto.strong_rand_bytes(6)
|
||||||
worker
|
worker
|
||||||
end
|
end
|
||||||
|
|
|
@ -10,6 +10,7 @@ defmodule Pleroma.Formatter do
|
||||||
|
|
||||||
@markdown_characters_regex ~r/(`|\*|_|{|}|[|]|\(|\)|#|\+|-|\.|!)/
|
@markdown_characters_regex ~r/(`|\*|_|{|}|[|]|\(|\)|#|\+|-|\.|!)/
|
||||||
@link_regex ~r{((?:http(s)?:\/\/)?[\w.-]+(?:\.[\w\.-]+)+[\w\-\._~%:/?#[\]@!\$&'\(\)\*\+,;=.]+)|[0-9a-z+\-\.]+:[0-9a-z$-_.+!*'(),]+}ui
|
@link_regex ~r{((?:http(s)?:\/\/)?[\w.-]+(?:\.[\w\.-]+)+[\w\-\._~%:/?#[\]@!\$&'\(\)\*\+,;=.]+)|[0-9a-z+\-\.]+:[0-9a-z$-_.+!*'(),]+}ui
|
||||||
|
# credo:disable-for-previous-line Credo.Check.Readability.MaxLineLength
|
||||||
|
|
||||||
@auto_linker_config hashtag: true,
|
@auto_linker_config hashtag: true,
|
||||||
hashtag_handler: &Pleroma.Formatter.hashtag_handler/4,
|
hashtag_handler: &Pleroma.Formatter.hashtag_handler/4,
|
||||||
|
|
|
@ -6,7 +6,7 @@ defmodule Pleroma.Gopher.Server do
|
||||||
use GenServer
|
use GenServer
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
def start_link() do
|
def start_link do
|
||||||
config = Pleroma.Config.get(:gopher, [])
|
config = Pleroma.Config.get(:gopher, [])
|
||||||
ip = Keyword.get(config, :ip, {0, 0, 0, 0})
|
ip = Keyword.get(config, :ip, {0, 0, 0, 0})
|
||||||
port = Keyword.get(config, :port, 1234)
|
port = Keyword.get(config, :port, 1234)
|
||||||
|
@ -36,12 +36,12 @@ def init([ip, port]) do
|
||||||
end
|
end
|
||||||
|
|
||||||
defmodule Pleroma.Gopher.Server.ProtocolHandler do
|
defmodule Pleroma.Gopher.Server.ProtocolHandler do
|
||||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
|
||||||
alias Pleroma.Web.ActivityPub.Visibility
|
|
||||||
alias Pleroma.Activity
|
alias Pleroma.Activity
|
||||||
alias Pleroma.HTML
|
alias Pleroma.HTML
|
||||||
alias Pleroma.User
|
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
|
alias Pleroma.User
|
||||||
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
|
alias Pleroma.Web.ActivityPub.Visibility
|
||||||
|
|
||||||
def start_link(ref, socket, transport, opts) do
|
def start_link(ref, socket, transport, opts) do
|
||||||
pid = spawn_link(__MODULE__, :init, [ref, socket, transport, opts])
|
pid = spawn_link(__MODULE__, :init, [ref, socket, transport, opts])
|
||||||
|
|
|
@ -9,7 +9,7 @@ defp get_scrubbers(scrubber) when is_atom(scrubber), do: [scrubber]
|
||||||
defp get_scrubbers(scrubbers) when is_list(scrubbers), do: scrubbers
|
defp get_scrubbers(scrubbers) when is_list(scrubbers), do: scrubbers
|
||||||
defp get_scrubbers(_), do: [Pleroma.HTML.Scrubber.Default]
|
defp get_scrubbers(_), do: [Pleroma.HTML.Scrubber.Default]
|
||||||
|
|
||||||
def get_scrubbers() do
|
def get_scrubbers do
|
||||||
Pleroma.Config.get([:markup, :scrub_policy])
|
Pleroma.Config.get([:markup, :scrub_policy])
|
||||||
|> get_scrubbers
|
|> get_scrubbers
|
||||||
end
|
end
|
||||||
|
|
|
@ -8,8 +8,8 @@ defmodule Pleroma.HTTP.Connection do
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@hackney_options [
|
@hackney_options [
|
||||||
timeout: 10000,
|
connect_timeout: 2_000,
|
||||||
recv_timeout: 20000,
|
recv_timeout: 20_000,
|
||||||
follow_redirect: true,
|
follow_redirect: true,
|
||||||
pool: :federation
|
pool: :federation
|
||||||
]
|
]
|
||||||
|
@ -31,6 +31,10 @@ def new(opts \\ []) do
|
||||||
#
|
#
|
||||||
defp hackney_options(opts) do
|
defp hackney_options(opts) do
|
||||||
options = Keyword.get(opts, :adapter, [])
|
options = Keyword.get(opts, :adapter, [])
|
||||||
@hackney_options ++ options
|
adapter_options = Pleroma.Config.get([:http, :adapter], [])
|
||||||
|
|
||||||
|
@hackney_options
|
||||||
|
|> Keyword.merge(adapter_options)
|
||||||
|
|> Keyword.merge(options)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -27,6 +27,7 @@ defmodule Pleroma.HTTP do
|
||||||
|
|
||||||
"""
|
"""
|
||||||
def request(method, url, body \\ "", headers \\ [], options \\ []) do
|
def request(method, url, body \\ "", headers \\ [], options \\ []) do
|
||||||
|
try do
|
||||||
options =
|
options =
|
||||||
process_request_options(options)
|
process_request_options(options)
|
||||||
|> process_sni_options(url)
|
|> process_sni_options(url)
|
||||||
|
@ -41,7 +42,14 @@ def request(method, url, body \\ "", headers \\ [], options \\ []) do
|
||||||
|> Builder.add_param(:body, :body, body)
|
|> Builder.add_param(:body, :body, body)
|
||||||
|> Builder.add_param(:query, :query, params)
|
|> Builder.add_param(:query, :query, params)
|
||||||
|> Enum.into([])
|
|> Enum.into([])
|
||||||
|> (&Tesla.request(Connection.new(), &1)).()
|
|> (&Tesla.request(Connection.new(options), &1)).()
|
||||||
|
rescue
|
||||||
|
e ->
|
||||||
|
{:error, e}
|
||||||
|
catch
|
||||||
|
:exit, e ->
|
||||||
|
{:error, e}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp process_sni_options(options, nil), do: options
|
defp process_sni_options(options, nil), do: options
|
||||||
|
|
|
@ -2,8 +2,8 @@ defmodule Pleroma.Instances.Instance do
|
||||||
@moduledoc "Instance."
|
@moduledoc "Instance."
|
||||||
|
|
||||||
alias Pleroma.Instances
|
alias Pleroma.Instances
|
||||||
alias Pleroma.Repo
|
|
||||||
alias Pleroma.Instances.Instance
|
alias Pleroma.Instances.Instance
|
||||||
|
alias Pleroma.Repo
|
||||||
|
|
||||||
use Ecto.Schema
|
use Ecto.Schema
|
||||||
|
|
||||||
|
|
|
@ -5,12 +5,12 @@
|
||||||
defmodule Pleroma.Notification do
|
defmodule Pleroma.Notification do
|
||||||
use Ecto.Schema
|
use Ecto.Schema
|
||||||
|
|
||||||
alias Pleroma.User
|
|
||||||
alias Pleroma.Activity
|
alias Pleroma.Activity
|
||||||
alias Pleroma.Notification
|
alias Pleroma.Notification
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
alias Pleroma.Web.CommonAPI.Utils
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.CommonAPI
|
alias Pleroma.Web.CommonAPI
|
||||||
|
alias Pleroma.Web.CommonAPI.Utils
|
||||||
|
|
||||||
import Ecto.Query
|
import Ecto.Query
|
||||||
|
|
||||||
|
|
|
@ -5,11 +5,11 @@
|
||||||
defmodule Pleroma.Object do
|
defmodule Pleroma.Object do
|
||||||
use Ecto.Schema
|
use Ecto.Schema
|
||||||
|
|
||||||
alias Pleroma.Repo
|
|
||||||
alias Pleroma.Object
|
|
||||||
alias Pleroma.User
|
|
||||||
alias Pleroma.Activity
|
alias Pleroma.Activity
|
||||||
|
alias Pleroma.Object
|
||||||
alias Pleroma.ObjectTombstone
|
alias Pleroma.ObjectTombstone
|
||||||
|
alias Pleroma.Repo
|
||||||
|
alias Pleroma.User
|
||||||
|
|
||||||
import Ecto.Query
|
import Ecto.Query
|
||||||
import Ecto.Changeset
|
import Ecto.Changeset
|
||||||
|
@ -86,9 +86,9 @@ def swap_object_with_tombstone(object) do
|
||||||
|
|
||||||
def delete(%Object{data: %{"id" => id}} = object) do
|
def delete(%Object{data: %{"id" => id}} = object) do
|
||||||
with {:ok, _obj} = swap_object_with_tombstone(object),
|
with {:ok, _obj} = swap_object_with_tombstone(object),
|
||||||
Repo.delete_all(Activity.by_object_ap_id(id)),
|
deleted_activity = Activity.delete_by_ap_id(id),
|
||||||
{:ok, true} <- Cachex.del(:object_cache, "object:#{id}") do
|
{:ok, true} <- Cachex.del(:object_cache, "object:#{id}") do
|
||||||
{:ok, object}
|
{:ok, object, deleted_activity}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -34,13 +34,16 @@ defp headers do
|
||||||
|
|
||||||
defp csp_string do
|
defp csp_string do
|
||||||
scheme = Config.get([Pleroma.Web.Endpoint, :url])[:scheme]
|
scheme = Config.get([Pleroma.Web.Endpoint, :url])[:scheme]
|
||||||
websocket_url = String.replace(Pleroma.Web.Endpoint.static_url(), "http", "ws")
|
static_url = Pleroma.Web.Endpoint.static_url()
|
||||||
|
websocket_url = String.replace(static_url, "http", "ws")
|
||||||
|
|
||||||
|
connect_src = "connect-src 'self' #{static_url} #{websocket_url}"
|
||||||
|
|
||||||
connect_src =
|
connect_src =
|
||||||
if Mix.env() == :dev do
|
if Mix.env() == :dev do
|
||||||
"connect-src 'self' http://localhost:3035/ " <> websocket_url
|
connect_src <> " http://localhost:3035/"
|
||||||
else
|
else
|
||||||
"connect-src 'self' " <> websocket_url
|
connect_src
|
||||||
end
|
end
|
||||||
|
|
||||||
script_src =
|
script_src =
|
||||||
|
|
|
@ -3,8 +3,8 @@
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.Plugs.HTTPSignaturePlug do
|
defmodule Pleroma.Web.Plugs.HTTPSignaturePlug do
|
||||||
alias Pleroma.Web.HTTPSignatures
|
|
||||||
alias Pleroma.Web.ActivityPub.Utils
|
alias Pleroma.Web.ActivityPub.Utils
|
||||||
|
alias Pleroma.Web.HTTPSignatures
|
||||||
import Plug.Conn
|
import Plug.Conn
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
|
|
|
@ -6,8 +6,8 @@ defmodule Pleroma.Plugs.OAuthPlug do
|
||||||
import Plug.Conn
|
import Plug.Conn
|
||||||
import Ecto.Query
|
import Ecto.Query
|
||||||
|
|
||||||
alias Pleroma.User
|
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.OAuth.Token
|
alias Pleroma.Web.OAuth.Token
|
||||||
|
|
||||||
@realm_reg Regex.compile!("Bearer\:?\s+(.*)$", "i")
|
@realm_reg Regex.compile!("Bearer\:?\s+(.*)$", "i")
|
||||||
|
@ -38,6 +38,7 @@ defp fetch_user_and_token(token) do
|
||||||
preload: [user: user]
|
preload: [user: user]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# credo:disable-for-next-line Credo.Check.Readability.MaxLineLength
|
||||||
with %Token{user: %{info: %{deactivated: false} = _} = user} = token_record <- Repo.one(query) do
|
with %Token{user: %{info: %{deactivated: false} = _} = user} = token_record <- Repo.one(query) do
|
||||||
{:ok, user, token_record}
|
{:ok, user, token_record}
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,8 +3,8 @@
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Plugs.UserFetcherPlug do
|
defmodule Pleroma.Plugs.UserFetcherPlug do
|
||||||
alias Pleroma.User
|
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
|
alias Pleroma.User
|
||||||
|
|
||||||
import Plug.Conn
|
import Plug.Conn
|
||||||
|
|
||||||
|
|
|
@ -3,10 +3,12 @@
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.ReverseProxy do
|
defmodule Pleroma.ReverseProxy do
|
||||||
@keep_req_headers ~w(accept user-agent accept-encoding cache-control if-modified-since if-unmodified-since if-none-match if-range range)
|
@keep_req_headers ~w(accept user-agent accept-encoding cache-control if-modified-since) ++
|
||||||
|
~w(if-unmodified-since if-none-match if-range range)
|
||||||
@resp_cache_headers ~w(etag date last-modified cache-control)
|
@resp_cache_headers ~w(etag date last-modified cache-control)
|
||||||
@keep_resp_headers @resp_cache_headers ++
|
@keep_resp_headers @resp_cache_headers ++
|
||||||
~w(content-type content-disposition content-encoding content-range accept-ranges vary)
|
~w(content-type content-disposition content-encoding content-range) ++
|
||||||
|
~w(accept-ranges vary)
|
||||||
@default_cache_control_header "public, max-age=1209600"
|
@default_cache_control_header "public, max-age=1209600"
|
||||||
@valid_resp_codes [200, 206, 304]
|
@valid_resp_codes [200, 206, 304]
|
||||||
@max_read_duration :timer.seconds(30)
|
@max_read_duration :timer.seconds(30)
|
||||||
|
@ -282,8 +284,8 @@ defp build_resp_cache_headers(headers, _opts) do
|
||||||
headers
|
headers
|
||||||
|
|
||||||
has_cache? ->
|
has_cache? ->
|
||||||
# There's caching header present but no cache-control -- we need to explicitely override it to public
|
# There's caching header present but no cache-control -- we need to explicitely override it
|
||||||
# as Plug defaults to "max-age=0, private, must-revalidate"
|
# to public as Plug defaults to "max-age=0, private, must-revalidate"
|
||||||
List.keystore(headers, "cache-control", 0, {"cache-control", "public"})
|
List.keystore(headers, "cache-control", 0, {"cache-control", "public"})
|
||||||
|
|
||||||
true ->
|
true ->
|
||||||
|
|
|
@ -4,8 +4,8 @@
|
||||||
|
|
||||||
defmodule Pleroma.Stats do
|
defmodule Pleroma.Stats do
|
||||||
import Ecto.Query
|
import Ecto.Query
|
||||||
alias Pleroma.User
|
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
|
alias Pleroma.User
|
||||||
|
|
||||||
def start_link do
|
def start_link do
|
||||||
agent = Agent.start_link(fn -> {[], %{}} end, name: __MODULE__)
|
agent = Agent.start_link(fn -> {[], %{}} end, name: __MODULE__)
|
||||||
|
|
|
@ -4,7 +4,11 @@
|
||||||
|
|
||||||
defmodule Pleroma.ThreadMute do
|
defmodule Pleroma.ThreadMute do
|
||||||
use Ecto.Schema
|
use Ecto.Schema
|
||||||
alias Pleroma.{Repo, User, ThreadMute}
|
|
||||||
|
alias Pleroma.Repo
|
||||||
|
alias Pleroma.ThreadMute
|
||||||
|
alias Pleroma.User
|
||||||
|
|
||||||
require Ecto.Query
|
require Ecto.Query
|
||||||
|
|
||||||
schema "thread_mutes" do
|
schema "thread_mutes" do
|
||||||
|
|
|
@ -85,6 +85,10 @@ def store(upload, opts \\ []) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def char_unescaped?(char) do
|
||||||
|
URI.char_unreserved?(char) or char == ?/
|
||||||
|
end
|
||||||
|
|
||||||
defp get_opts(opts) do
|
defp get_opts(opts) do
|
||||||
{size_limit, activity_type} =
|
{size_limit, activity_type} =
|
||||||
case Keyword.get(opts, :type) do
|
case Keyword.get(opts, :type) do
|
||||||
|
@ -218,9 +222,7 @@ defp tempfile_for_image(data) do
|
||||||
defp url_from_spec(base_url, {:file, path}) do
|
defp url_from_spec(base_url, {:file, path}) do
|
||||||
path =
|
path =
|
||||||
path
|
path
|
||||||
|> URI.encode()
|
|> URI.encode(&char_unescaped?/1)
|
||||||
|> String.replace("?", "%3F")
|
|
||||||
|> String.replace(":", "%3A")
|
|
||||||
|
|
||||||
[base_url, "media", path]
|
[base_url, "media", path]
|
||||||
|> Path.join()
|
|> Path.join()
|
||||||
|
|
|
@ -6,7 +6,8 @@ defmodule Pleroma.Uploaders.S3 do
|
||||||
@behaviour Pleroma.Uploaders.Uploader
|
@behaviour Pleroma.Uploaders.Uploader
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
# The file name is re-encoded with S3's constraints here to comply with previous links with less strict filenames
|
# The file name is re-encoded with S3's constraints here to comply with previous
|
||||||
|
# links with less strict filenames
|
||||||
def get_file(file) do
|
def get_file(file) do
|
||||||
config = Pleroma.Config.get([__MODULE__])
|
config = Pleroma.Config.get([__MODULE__])
|
||||||
bucket = Keyword.fetch!(config, :bucket)
|
bucket = Keyword.fetch!(config, :bucket)
|
||||||
|
|
|
@ -17,7 +17,7 @@ def process_response_body(body) do
|
||||||
|> Poison.decode!()
|
|> Poison.decode!()
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_token() do
|
def get_token do
|
||||||
settings = Pleroma.Config.get(Pleroma.Uploaders.Swift)
|
settings = Pleroma.Config.get(Pleroma.Uploaders.Swift)
|
||||||
username = Keyword.fetch!(settings, :username)
|
username = Keyword.fetch!(settings, :username)
|
||||||
password = Keyword.fetch!(settings, :password)
|
password = Keyword.fetch!(settings, :password)
|
||||||
|
|
|
@ -29,7 +29,6 @@ defmodule Pleroma.Uploaders.Uploader do
|
||||||
* `{:error, String.t}` error information if the file failed to be saved to the backend.
|
* `{:error, String.t}` error information if the file failed to be saved to the backend.
|
||||||
* `:wait_callback` will wait for an http post request at `/api/pleroma/upload_callback/:upload_path` and call the uploader's `http_callback/3` method.
|
* `:wait_callback` will wait for an http post request at `/api/pleroma/upload_callback/:upload_path` and call the uploader's `http_callback/3` method.
|
||||||
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
@type file_spec :: {:file | :url, String.t()}
|
@type file_spec :: {:file | :url, String.t()}
|
||||||
@callback put_file(Pleroma.Upload.t()) ::
|
@callback put_file(Pleroma.Upload.t()) ::
|
||||||
|
|
|
@ -8,21 +8,21 @@ defmodule Pleroma.User do
|
||||||
import Ecto.Changeset
|
import Ecto.Changeset
|
||||||
import Ecto.Query
|
import Ecto.Query
|
||||||
|
|
||||||
|
alias Comeonin.Pbkdf2
|
||||||
|
alias Pleroma.Activity
|
||||||
|
alias Pleroma.Formatter
|
||||||
|
alias Pleroma.Notification
|
||||||
|
alias Pleroma.Object
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.Object
|
|
||||||
alias Pleroma.Web
|
alias Pleroma.Web
|
||||||
alias Pleroma.Activity
|
|
||||||
alias Pleroma.Notification
|
|
||||||
alias Comeonin.Pbkdf2
|
|
||||||
alias Pleroma.Formatter
|
|
||||||
alias Pleroma.Web.CommonAPI.Utils, as: CommonUtils
|
|
||||||
alias Pleroma.Web.OStatus
|
|
||||||
alias Pleroma.Web.Websub
|
|
||||||
alias Pleroma.Web.OAuth
|
|
||||||
alias Pleroma.Web.ActivityPub.Utils
|
|
||||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
|
alias Pleroma.Web.ActivityPub.Utils
|
||||||
|
alias Pleroma.Web.CommonAPI.Utils, as: CommonUtils
|
||||||
|
alias Pleroma.Web.OAuth
|
||||||
|
alias Pleroma.Web.OStatus
|
||||||
alias Pleroma.Web.RelMe
|
alias Pleroma.Web.RelMe
|
||||||
|
alias Pleroma.Web.Websub
|
||||||
|
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
|
@ -30,6 +30,7 @@ defmodule Pleroma.User do
|
||||||
|
|
||||||
@primary_key {:id, Pleroma.FlakeId, autogenerate: true}
|
@primary_key {:id, Pleroma.FlakeId, autogenerate: true}
|
||||||
|
|
||||||
|
# credo:disable-for-next-line Credo.Check.Readability.MaxLineLength
|
||||||
@email_regex ~r/^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/
|
@email_regex ~r/^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/
|
||||||
|
|
||||||
@strict_local_nickname_regex ~r/^[a-zA-Z\d]+$/
|
@strict_local_nickname_regex ~r/^[a-zA-Z\d]+$/
|
||||||
|
@ -285,7 +286,7 @@ def needs_update?(%User{local: true}), do: false
|
||||||
def needs_update?(%User{local: false, last_refreshed_at: nil}), do: true
|
def needs_update?(%User{local: false, last_refreshed_at: nil}), do: true
|
||||||
|
|
||||||
def needs_update?(%User{local: false} = user) do
|
def needs_update?(%User{local: false} = user) do
|
||||||
NaiveDateTime.diff(NaiveDateTime.utc_now(), user.last_refreshed_at) >= 86400
|
NaiveDateTime.diff(NaiveDateTime.utc_now(), user.last_refreshed_at) >= 86_400
|
||||||
end
|
end
|
||||||
|
|
||||||
def needs_update?(_), do: true
|
def needs_update?(_), do: true
|
||||||
|
@ -435,7 +436,8 @@ def get_by_ap_id(ap_id) do
|
||||||
Repo.get_by(User, ap_id: ap_id)
|
Repo.get_by(User, ap_id: ap_id)
|
||||||
end
|
end
|
||||||
|
|
||||||
# This is mostly an SPC migration fix. This guesses the user nickname (by taking the last part of the ap_id and the domain) and tries to get that user
|
# This is mostly an SPC migration fix. This guesses the user nickname by taking the last part
|
||||||
|
# of the ap_id and the domain and tries to get that user
|
||||||
def get_by_guessed_nickname(ap_id) do
|
def get_by_guessed_nickname(ap_id) do
|
||||||
domain = URI.parse(ap_id).host
|
domain = URI.parse(ap_id).host
|
||||||
name = List.last(String.split(ap_id, "/"))
|
name = List.last(String.split(ap_id, "/"))
|
||||||
|
@ -532,6 +534,10 @@ def get_or_fetch_by_nickname(nickname) do
|
||||||
_e ->
|
_e ->
|
||||||
with [_nick, _domain] <- String.split(nickname, "@"),
|
with [_nick, _domain] <- String.split(nickname, "@"),
|
||||||
{:ok, user} <- fetch_by_nickname(nickname) do
|
{:ok, user} <- fetch_by_nickname(nickname) do
|
||||||
|
if Pleroma.Config.get([:fetch_initial_posts, :enabled]) do
|
||||||
|
{:ok, _} = Task.start(__MODULE__, :fetch_initial_posts, [user])
|
||||||
|
end
|
||||||
|
|
||||||
user
|
user
|
||||||
else
|
else
|
||||||
_e -> nil
|
_e -> nil
|
||||||
|
@ -539,6 +545,17 @@ def get_or_fetch_by_nickname(nickname) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@doc "Fetch some posts when the user has just been federated with"
|
||||||
|
def fetch_initial_posts(user) do
|
||||||
|
pages = Pleroma.Config.get!([:fetch_initial_posts, :pages])
|
||||||
|
|
||||||
|
Enum.each(
|
||||||
|
# Insert all the posts in reverse order, so they're in the right order on the timeline
|
||||||
|
Enum.reverse(Utils.fetch_ordered_collection(user.info.source_data["outbox"], pages)),
|
||||||
|
&Pleroma.Web.Federator.incoming_ap_doc/1
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
def get_followers_query(%User{id: id, follower_address: follower_address}, nil) do
|
def get_followers_query(%User{id: id, follower_address: follower_address}, nil) do
|
||||||
from(
|
from(
|
||||||
u in User,
|
u in User,
|
||||||
|
@ -749,13 +766,41 @@ def get_recipients_from_activity(%Activity{recipients: to}) do
|
||||||
Repo.all(query)
|
Repo.all(query)
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec search_for_admin(binary(), %{
|
@spec search_for_admin(%{
|
||||||
|
local: boolean(),
|
||||||
|
page: number(),
|
||||||
|
page_size: number()
|
||||||
|
}) :: {:ok, [Pleroma.User.t()], number()}
|
||||||
|
def search_for_admin(%{query: nil, local: local, page: page, page_size: page_size}) do
|
||||||
|
query =
|
||||||
|
from(u in User, order_by: u.id)
|
||||||
|
|> maybe_local_user_query(local)
|
||||||
|
|
||||||
|
paginated_query =
|
||||||
|
query
|
||||||
|
|> paginate(page, page_size)
|
||||||
|
|
||||||
|
count =
|
||||||
|
query
|
||||||
|
|> Repo.aggregate(:count, :id)
|
||||||
|
|
||||||
|
{:ok, Repo.all(paginated_query), count}
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec search_for_admin(%{
|
||||||
|
query: binary(),
|
||||||
admin: Pleroma.User.t(),
|
admin: Pleroma.User.t(),
|
||||||
local: boolean(),
|
local: boolean(),
|
||||||
page: number(),
|
page: number(),
|
||||||
page_size: number()
|
page_size: number()
|
||||||
}) :: {:ok, [Pleroma.User.t()], number()}
|
}) :: {:ok, [Pleroma.User.t()], number()}
|
||||||
def search_for_admin(term, %{admin: admin, local: local, page: page, page_size: page_size}) do
|
def search_for_admin(%{
|
||||||
|
query: term,
|
||||||
|
admin: admin,
|
||||||
|
local: local,
|
||||||
|
page: page,
|
||||||
|
page_size: page_size
|
||||||
|
}) do
|
||||||
term = String.trim_leading(term, "@")
|
term = String.trim_leading(term, "@")
|
||||||
|
|
||||||
local_paginated_query =
|
local_paginated_query =
|
||||||
|
@ -774,21 +819,6 @@ def search_for_admin(term, %{admin: admin, local: local, page: page, page_size:
|
||||||
{:ok, do_search(search_query, admin), count}
|
{:ok, do_search(search_query, admin), count}
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec all_for_admin(number(), number()) :: {:ok, [Pleroma.User.t()], number()}
|
|
||||||
def all_for_admin(page, page_size) do
|
|
||||||
query = from(u in User, order_by: u.id)
|
|
||||||
|
|
||||||
paginated_query =
|
|
||||||
query
|
|
||||||
|> paginate(page, page_size)
|
|
||||||
|
|
||||||
count =
|
|
||||||
query
|
|
||||||
|> Repo.aggregate(:count, :id)
|
|
||||||
|
|
||||||
{:ok, Repo.all(paginated_query), count}
|
|
||||||
end
|
|
||||||
|
|
||||||
def search(query, resolve \\ false, for_user \\ nil) do
|
def search(query, resolve \\ false, for_user \\ nil) do
|
||||||
# Strip the beginning @ off if there is a query
|
# Strip the beginning @ off if there is a query
|
||||||
query = String.trim_leading(query, "@")
|
query = String.trim_leading(query, "@")
|
||||||
|
@ -1108,12 +1138,7 @@ def html_filter_policy(%User{info: %{no_rich_text: true}}) do
|
||||||
|
|
||||||
def html_filter_policy(_), do: @default_scrubbers
|
def html_filter_policy(_), do: @default_scrubbers
|
||||||
|
|
||||||
def get_or_fetch_by_ap_id(ap_id) do
|
def fetch_by_ap_id(ap_id) do
|
||||||
user = get_by_ap_id(ap_id)
|
|
||||||
|
|
||||||
if !is_nil(user) and !User.needs_update?(user) do
|
|
||||||
user
|
|
||||||
else
|
|
||||||
ap_try = ActivityPub.make_user_from_ap_id(ap_id)
|
ap_try = ActivityPub.make_user_from_ap_id(ap_id)
|
||||||
|
|
||||||
case ap_try do
|
case ap_try do
|
||||||
|
@ -1127,6 +1152,23 @@ def get_or_fetch_by_ap_id(ap_id) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def get_or_fetch_by_ap_id(ap_id) do
|
||||||
|
user = get_by_ap_id(ap_id)
|
||||||
|
|
||||||
|
if !is_nil(user) and !User.needs_update?(user) do
|
||||||
|
user
|
||||||
|
else
|
||||||
|
user = fetch_by_ap_id(ap_id)
|
||||||
|
|
||||||
|
if Pleroma.Config.get([:fetch_initial_posts, :enabled]) do
|
||||||
|
with %User{} = user do
|
||||||
|
{:ok, _} = Task.start(__MODULE__, :fetch_initial_posts, [user])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
user
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_or_create_instance_user do
|
def get_or_create_instance_user do
|
||||||
|
@ -1300,7 +1342,7 @@ defp normalize_tags(tags) do
|
||||||
|> Enum.map(&String.downcase(&1))
|
|> Enum.map(&String.downcase(&1))
|
||||||
end
|
end
|
||||||
|
|
||||||
defp local_nickname_regex() do
|
defp local_nickname_regex do
|
||||||
if Pleroma.Config.get([:instance, :extended_nickname_format]) do
|
if Pleroma.Config.get([:instance, :extended_nickname_format]) do
|
||||||
@extended_local_nickname_regex
|
@extended_local_nickname_regex
|
||||||
else
|
else
|
||||||
|
|
|
@ -6,6 +6,8 @@ defmodule Pleroma.User.Info do
|
||||||
use Ecto.Schema
|
use Ecto.Schema
|
||||||
import Ecto.Changeset
|
import Ecto.Changeset
|
||||||
|
|
||||||
|
alias Pleroma.User.Info
|
||||||
|
|
||||||
embedded_schema do
|
embedded_schema do
|
||||||
field(:banner, :map, default: %{})
|
field(:banner, :map, default: %{})
|
||||||
field(:background, :map, default: %{})
|
field(:background, :map, default: %{})
|
||||||
|
@ -250,4 +252,11 @@ def remove_pinnned_activity(info, %Pleroma.Activity{id: id}) do
|
||||||
|
|
||||||
cast(info, params, [:pinned_activities])
|
cast(info, params, [:pinned_activities])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def roles(%Info{is_moderator: is_moderator, is_admin: is_admin}) do
|
||||||
|
%{
|
||||||
|
admin: is_admin,
|
||||||
|
moderator: is_moderator
|
||||||
|
}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -14,7 +14,7 @@ def post_welcome_message_to_user(user) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp welcome_user() do
|
defp welcome_user do
|
||||||
with nickname when is_binary(nickname) <-
|
with nickname when is_binary(nickname) <-
|
||||||
Pleroma.Config.get([:instance, :welcome_user_nickname]),
|
Pleroma.Config.get([:instance, :welcome_user_nickname]),
|
||||||
%User{local: true} = user <- User.get_cached_by_nickname(nickname) do
|
%User{local: true} = user <- User.get_cached_by_nickname(nickname) do
|
||||||
|
@ -24,7 +24,7 @@ defp welcome_user() do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp welcome_message() do
|
defp welcome_message do
|
||||||
Pleroma.Config.get([:instance, :welcome_message])
|
Pleroma.Config.get([:instance, :welcome_message])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -7,8 +7,8 @@ defmodule Pleroma.UserInviteToken do
|
||||||
|
|
||||||
import Ecto.Changeset
|
import Ecto.Changeset
|
||||||
|
|
||||||
alias Pleroma.UserInviteToken
|
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
|
alias Pleroma.UserInviteToken
|
||||||
|
|
||||||
schema "user_invite_tokens" do
|
schema "user_invite_tokens" do
|
||||||
field(:token, :string)
|
field(:token, :string)
|
||||||
|
|
|
@ -4,17 +4,17 @@
|
||||||
|
|
||||||
defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
||||||
alias Pleroma.Activity
|
alias Pleroma.Activity
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Instances
|
||||||
|
alias Pleroma.Notification
|
||||||
alias Pleroma.Object
|
alias Pleroma.Object
|
||||||
|
alias Pleroma.Repo
|
||||||
alias Pleroma.Upload
|
alias Pleroma.Upload
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.Notification
|
|
||||||
alias Pleroma.Instances
|
|
||||||
alias Pleroma.Web.ActivityPub.Transmogrifier
|
|
||||||
alias Pleroma.Web.ActivityPub.MRF
|
alias Pleroma.Web.ActivityPub.MRF
|
||||||
alias Pleroma.Web.WebFinger
|
alias Pleroma.Web.ActivityPub.Transmogrifier
|
||||||
alias Pleroma.Web.Federator
|
alias Pleroma.Web.Federator
|
||||||
alias Pleroma.Web.OStatus
|
alias Pleroma.Web.OStatus
|
||||||
|
alias Pleroma.Web.WebFinger
|
||||||
|
|
||||||
import Ecto.Query
|
import Ecto.Query
|
||||||
import Pleroma.Web.ActivityPub.Utils
|
import Pleroma.Web.ActivityPub.Utils
|
||||||
|
@ -170,7 +170,8 @@ def create(%{to: to, actor: actor, context: context, object: object} = params) d
|
||||||
additional
|
additional
|
||||||
),
|
),
|
||||||
{:ok, activity} <- insert(create_data, local),
|
{:ok, activity} <- insert(create_data, local),
|
||||||
# Changing note count prior to enqueuing federation task in order to avoid race conditions on updating user.info
|
# Changing note count prior to enqueuing federation task in order to avoid
|
||||||
|
# race conditions on updating user.info
|
||||||
{:ok, _actor} <- increase_note_count_if_public(actor, activity),
|
{:ok, _actor} <- increase_note_count_if_public(actor, activity),
|
||||||
:ok <- maybe_federate(activity) do
|
:ok <- maybe_federate(activity) do
|
||||||
{:ok, activity}
|
{:ok, activity}
|
||||||
|
@ -309,17 +310,19 @@ def unfollow(follower, followed, activity_id \\ nil, local \\ true) do
|
||||||
|
|
||||||
def delete(%Object{data: %{"id" => id, "actor" => actor}} = object, local \\ true) do
|
def delete(%Object{data: %{"id" => id, "actor" => actor}} = object, local \\ true) do
|
||||||
user = User.get_cached_by_ap_id(actor)
|
user = User.get_cached_by_ap_id(actor)
|
||||||
|
to = object.data["to"] || [] ++ object.data["cc"] || []
|
||||||
|
|
||||||
data = %{
|
with {:ok, object, activity} <- Object.delete(object),
|
||||||
|
data <- %{
|
||||||
"type" => "Delete",
|
"type" => "Delete",
|
||||||
"actor" => actor,
|
"actor" => actor,
|
||||||
"object" => id,
|
"object" => id,
|
||||||
"to" => [user.follower_address, "https://www.w3.org/ns/activitystreams#Public"]
|
"to" => to,
|
||||||
}
|
"deleted_activity_id" => activity && activity.id
|
||||||
|
},
|
||||||
with {:ok, _} <- Object.delete(object),
|
|
||||||
{:ok, activity} <- insert(data, local),
|
{:ok, activity} <- insert(data, local),
|
||||||
# Changing note count prior to enqueuing federation task in order to avoid race conditions on updating user.info
|
# Changing note count prior to enqueuing federation task in order to avoid
|
||||||
|
# race conditions on updating user.info
|
||||||
{:ok, _actor} <- decrease_note_count_if_public(user, object),
|
{:ok, _actor} <- decrease_note_count_if_public(user, object),
|
||||||
:ok <- maybe_federate(activity) do
|
:ok <- maybe_federate(activity) do
|
||||||
{:ok, activity}
|
{:ok, activity}
|
||||||
|
@ -501,7 +504,7 @@ defp restrict_tag_reject(query, %{"tag_reject" => tag_reject})
|
||||||
when is_list(tag_reject) and tag_reject != [] do
|
when is_list(tag_reject) and tag_reject != [] do
|
||||||
from(
|
from(
|
||||||
activity in query,
|
activity in query,
|
||||||
where: fragment("(not (? #> '{\"object\",\"tag\"}') \\?| ?)", activity.data, ^tag_reject)
|
where: fragment(~s(\(not \(? #> '{"object","tag"}'\) \\?| ?\)), activity.data, ^tag_reject)
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -511,7 +514,7 @@ defp restrict_tag_all(query, %{"tag_all" => tag_all})
|
||||||
when is_list(tag_all) and tag_all != [] do
|
when is_list(tag_all) and tag_all != [] do
|
||||||
from(
|
from(
|
||||||
activity in query,
|
activity in query,
|
||||||
where: fragment("(? #> '{\"object\",\"tag\"}') \\?& ?", activity.data, ^tag_all)
|
where: fragment(~s(\(? #> '{"object","tag"}'\) \\?& ?), activity.data, ^tag_all)
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -520,14 +523,14 @@ defp restrict_tag_all(query, _), do: query
|
||||||
defp restrict_tag(query, %{"tag" => tag}) when is_list(tag) do
|
defp restrict_tag(query, %{"tag" => tag}) when is_list(tag) do
|
||||||
from(
|
from(
|
||||||
activity in query,
|
activity in query,
|
||||||
where: fragment("(? #> '{\"object\",\"tag\"}') \\?| ?", activity.data, ^tag)
|
where: fragment(~s(\(? #> '{"object","tag"}'\) \\?| ?), activity.data, ^tag)
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp restrict_tag(query, %{"tag" => tag}) when is_binary(tag) do
|
defp restrict_tag(query, %{"tag" => tag}) when is_binary(tag) do
|
||||||
from(
|
from(
|
||||||
activity in query,
|
activity in query,
|
||||||
where: fragment("? <@ (? #> '{\"object\",\"tag\"}')", ^tag, activity.data)
|
where: fragment(~s(? <@ (? #> '{"object","tag"}'\)), ^tag, activity.data)
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -600,7 +603,7 @@ defp restrict_type(query, _), do: query
|
||||||
defp restrict_favorited_by(query, %{"favorited_by" => ap_id}) do
|
defp restrict_favorited_by(query, %{"favorited_by" => ap_id}) do
|
||||||
from(
|
from(
|
||||||
activity in query,
|
activity in query,
|
||||||
where: fragment("? <@ (? #> '{\"object\",\"likes\"}')", ^ap_id, activity.data)
|
where: fragment(~s(? <@ (? #> '{"object","likes"}'\)), ^ap_id, activity.data)
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -609,7 +612,7 @@ defp restrict_favorited_by(query, _), do: query
|
||||||
defp restrict_media(query, %{"only_media" => val}) when val == "true" or val == "1" do
|
defp restrict_media(query, %{"only_media" => val}) when val == "true" or val == "1" do
|
||||||
from(
|
from(
|
||||||
activity in query,
|
activity in query,
|
||||||
where: fragment("not (? #> '{\"object\",\"attachment\"}' = ?)", activity.data, ^[])
|
where: fragment(~s(not (? #> '{"object","attachment"}' = ?\)), activity.data, ^[])
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -6,15 +6,15 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|
||||||
use Pleroma.Web, :controller
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
alias Pleroma.Activity
|
alias Pleroma.Activity
|
||||||
alias Pleroma.User
|
|
||||||
alias Pleroma.Object
|
alias Pleroma.Object
|
||||||
alias Pleroma.Web.ActivityPub.ObjectView
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.ActivityPub.UserView
|
|
||||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
alias Pleroma.Web.ActivityPub.Visibility
|
alias Pleroma.Web.ActivityPub.ObjectView
|
||||||
alias Pleroma.Web.ActivityPub.Relay
|
alias Pleroma.Web.ActivityPub.Relay
|
||||||
alias Pleroma.Web.ActivityPub.Transmogrifier
|
alias Pleroma.Web.ActivityPub.Transmogrifier
|
||||||
|
alias Pleroma.Web.ActivityPub.UserView
|
||||||
alias Pleroma.Web.ActivityPub.Utils
|
alias Pleroma.Web.ActivityPub.Utils
|
||||||
|
alias Pleroma.Web.ActivityPub.Visibility
|
||||||
alias Pleroma.Web.Federator
|
alias Pleroma.Web.Federator
|
||||||
|
|
||||||
require Logger
|
require Logger
|
||||||
|
|
|
@ -16,7 +16,7 @@ def filter(object) do
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_policies() do
|
def get_policies do
|
||||||
Application.get_env(:pleroma, :instance, [])
|
Application.get_env(:pleroma, :instance, [])
|
||||||
|> Keyword.get(:rewrite_policy, [])
|
|> Keyword.get(:rewrite_policy, [])
|
||||||
|> get_policies()
|
|> get_policies()
|
||||||
|
|
|
@ -23,15 +23,21 @@ defp score_displayname("fedibot"), do: 1.0
|
||||||
defp score_displayname(_), do: 0.0
|
defp score_displayname(_), do: 0.0
|
||||||
|
|
||||||
defp determine_if_followbot(%User{nickname: nickname, name: displayname}) do
|
defp determine_if_followbot(%User{nickname: nickname, name: displayname}) do
|
||||||
|
# nickname will always be a binary string because it's generated by Pleroma.
|
||||||
nick_score =
|
nick_score =
|
||||||
nickname
|
nickname
|
||||||
|> String.downcase()
|
|> String.downcase()
|
||||||
|> score_nickname()
|
|> score_nickname()
|
||||||
|
|
||||||
|
# displayname will either be a binary string or nil, if a displayname isn't set.
|
||||||
name_score =
|
name_score =
|
||||||
|
if is_binary(displayname) do
|
||||||
displayname
|
displayname
|
||||||
|> String.downcase()
|
|> String.downcase()
|
||||||
|> score_displayname()
|
|> score_displayname()
|
||||||
|
else
|
||||||
|
0.0
|
||||||
|
end
|
||||||
|
|
||||||
nick_score + name_score
|
nick_score + name_score
|
||||||
end
|
end
|
||||||
|
|
|
@ -45,13 +45,14 @@ defp check_ftl_removal(
|
||||||
|
|
||||||
defp check_replace(%{"object" => %{"content" => content, "summary" => summary}} = message) do
|
defp check_replace(%{"object" => %{"content" => content, "summary" => summary}} = message) do
|
||||||
{content, summary} =
|
{content, summary} =
|
||||||
Enum.reduce(Pleroma.Config.get([:mrf_keyword, :replace]), {content, summary}, fn {pattern,
|
Enum.reduce(
|
||||||
replacement},
|
Pleroma.Config.get([:mrf_keyword, :replace]),
|
||||||
{content_acc,
|
{content, summary},
|
||||||
summary_acc} ->
|
fn {pattern, replacement}, {content_acc, summary_acc} ->
|
||||||
{String.replace(content_acc, pattern, replacement),
|
{String.replace(content_acc, pattern, replacement),
|
||||||
String.replace(summary_acc, pattern, replacement)}
|
String.replace(summary_acc, pattern, replacement)}
|
||||||
end)
|
end
|
||||||
|
)
|
||||||
|
|
||||||
{:ok,
|
{:ok,
|
||||||
message
|
message
|
||||||
|
|
|
@ -3,9 +3,9 @@
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.ActivityPub.Relay do
|
defmodule Pleroma.Web.ActivityPub.Relay do
|
||||||
alias Pleroma.User
|
|
||||||
alias Pleroma.Object
|
|
||||||
alias Pleroma.Activity
|
alias Pleroma.Activity
|
||||||
|
alias Pleroma.Object
|
||||||
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
|
|
|
@ -7,9 +7,9 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
||||||
A module to handle coding from internal to wire ActivityPub and back.
|
A module to handle coding from internal to wire ActivityPub and back.
|
||||||
"""
|
"""
|
||||||
alias Pleroma.Activity
|
alias Pleroma.Activity
|
||||||
alias Pleroma.User
|
|
||||||
alias Pleroma.Object
|
alias Pleroma.Object
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
alias Pleroma.Web.ActivityPub.Utils
|
alias Pleroma.Web.ActivityPub.Utils
|
||||||
alias Pleroma.Web.ActivityPub.Visibility
|
alias Pleroma.Web.ActivityPub.Visibility
|
||||||
|
@ -650,10 +650,10 @@ def get_obj_helper(id) do
|
||||||
if object = Object.normalize(id), do: {:ok, object}, else: nil
|
if object = Object.normalize(id), do: {:ok, object}, else: nil
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_reply_to_uri(%{"inReplyTo" => inReplyTo} = object) when is_binary(inReplyTo) do
|
def set_reply_to_uri(%{"inReplyTo" => in_reply_to} = object) when is_binary(in_reply_to) do
|
||||||
with false <- String.starts_with?(inReplyTo, "http"),
|
with false <- String.starts_with?(in_reply_to, "http"),
|
||||||
{:ok, %{data: replied_to_object}} <- get_obj_helper(inReplyTo) do
|
{:ok, %{data: replied_to_object}} <- get_obj_helper(in_reply_to) do
|
||||||
Map.put(object, "inReplyTo", replied_to_object["external_url"] || inReplyTo)
|
Map.put(object, "inReplyTo", replied_to_object["external_url"] || in_reply_to)
|
||||||
else
|
else
|
||||||
_e -> object
|
_e -> object
|
||||||
end
|
end
|
||||||
|
@ -736,6 +736,7 @@ def prepare_outgoing(%{"type" => "Reject"} = data) do
|
||||||
def prepare_outgoing(%{"type" => _type} = data) do
|
def prepare_outgoing(%{"type" => _type} = data) do
|
||||||
data =
|
data =
|
||||||
data
|
data
|
||||||
|
|> strip_internal_fields
|
||||||
|> maybe_fix_object_url
|
|> maybe_fix_object_url
|
||||||
|> Map.merge(Utils.make_json_ld_header())
|
|> Map.merge(Utils.make_json_ld_header())
|
||||||
|
|
||||||
|
@ -829,10 +830,10 @@ def set_sensitive(object) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def add_attributed_to(object) do
|
def add_attributed_to(object) do
|
||||||
attributedTo = object["attributedTo"] || object["actor"]
|
attributed_to = object["attributedTo"] || object["actor"]
|
||||||
|
|
||||||
object
|
object
|
||||||
|> Map.put("attributedTo", attributedTo)
|
|> Map.put("attributedTo", attributed_to)
|
||||||
end
|
end
|
||||||
|
|
||||||
def add_likes(%{"id" => id, "like_count" => likes} = object) do
|
def add_likes(%{"id" => id, "like_count" => likes} = object) do
|
||||||
|
@ -870,7 +871,8 @@ defp strip_internal_fields(object) do
|
||||||
"announcements",
|
"announcements",
|
||||||
"announcement_count",
|
"announcement_count",
|
||||||
"emoji",
|
"emoji",
|
||||||
"context_id"
|
"context_id",
|
||||||
|
"deleted_activity_id"
|
||||||
])
|
])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -3,16 +3,17 @@
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.ActivityPub.Utils do
|
defmodule Pleroma.Web.ActivityPub.Utils do
|
||||||
alias Pleroma.Repo
|
|
||||||
alias Pleroma.Web
|
|
||||||
alias Pleroma.Object
|
|
||||||
alias Pleroma.Activity
|
|
||||||
alias Pleroma.User
|
|
||||||
alias Pleroma.Notification
|
|
||||||
alias Pleroma.Web.Router.Helpers
|
|
||||||
alias Pleroma.Web.Endpoint
|
|
||||||
alias Ecto.Changeset
|
alias Ecto.Changeset
|
||||||
alias Ecto.UUID
|
alias Ecto.UUID
|
||||||
|
alias Pleroma.Activity
|
||||||
|
alias Pleroma.Notification
|
||||||
|
alias Pleroma.Object
|
||||||
|
alias Pleroma.Repo
|
||||||
|
alias Pleroma.User
|
||||||
|
alias Pleroma.Web
|
||||||
|
alias Pleroma.Web.ActivityPub.Visibility
|
||||||
|
alias Pleroma.Web.Endpoint
|
||||||
|
alias Pleroma.Web.Router.Helpers
|
||||||
|
|
||||||
import Ecto.Query
|
import Ecto.Query
|
||||||
|
|
||||||
|
@ -274,13 +275,31 @@ def get_object_likes(%{data: %{"id" => id}}) do
|
||||||
Repo.all(query)
|
Repo.all(query)
|
||||||
end
|
end
|
||||||
|
|
||||||
def make_like_data(%User{ap_id: ap_id} = actor, %{data: %{"id" => id}} = object, activity_id) do
|
def make_like_data(
|
||||||
|
%User{ap_id: ap_id} = actor,
|
||||||
|
%{data: %{"actor" => object_actor_id, "id" => id}} = object,
|
||||||
|
activity_id
|
||||||
|
) do
|
||||||
|
object_actor = User.get_cached_by_ap_id(object_actor_id)
|
||||||
|
|
||||||
|
to =
|
||||||
|
if Visibility.is_public?(object) do
|
||||||
|
[actor.follower_address, object.data["actor"]]
|
||||||
|
else
|
||||||
|
[object.data["actor"]]
|
||||||
|
end
|
||||||
|
|
||||||
|
cc =
|
||||||
|
(object.data["to"] ++ (object.data["cc"] || []))
|
||||||
|
|> List.delete(actor.ap_id)
|
||||||
|
|> List.delete(object_actor.follower_address)
|
||||||
|
|
||||||
data = %{
|
data = %{
|
||||||
"type" => "Like",
|
"type" => "Like",
|
||||||
"actor" => ap_id,
|
"actor" => ap_id,
|
||||||
"object" => id,
|
"object" => id,
|
||||||
"to" => [actor.follower_address, object.data["actor"]],
|
"to" => to,
|
||||||
"cc" => ["https://www.w3.org/ns/activitystreams#Public"],
|
"cc" => cc,
|
||||||
"context" => object.data["context"]
|
"context" => object.data["context"]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -614,4 +633,43 @@ def make_flag_data(params, additional) do
|
||||||
}
|
}
|
||||||
|> Map.merge(additional)
|
|> Map.merge(additional)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Fetches the OrderedCollection/OrderedCollectionPage from `from`, limiting the amount of pages fetched after
|
||||||
|
the first one to `pages_left` pages.
|
||||||
|
If the amount of pages is higher than the collection has, it returns whatever was there.
|
||||||
|
"""
|
||||||
|
def fetch_ordered_collection(from, pages_left, acc \\ []) do
|
||||||
|
with {:ok, response} <- Tesla.get(from),
|
||||||
|
{:ok, collection} <- Poison.decode(response.body) do
|
||||||
|
case collection["type"] do
|
||||||
|
"OrderedCollection" ->
|
||||||
|
# If we've encountered the OrderedCollection and not the page,
|
||||||
|
# just call the same function on the page address
|
||||||
|
fetch_ordered_collection(collection["first"], pages_left)
|
||||||
|
|
||||||
|
"OrderedCollectionPage" ->
|
||||||
|
if pages_left > 0 do
|
||||||
|
# There are still more pages
|
||||||
|
if Map.has_key?(collection, "next") do
|
||||||
|
# There are still more pages, go deeper saving what we have into the accumulator
|
||||||
|
fetch_ordered_collection(
|
||||||
|
collection["next"],
|
||||||
|
pages_left - 1,
|
||||||
|
acc ++ collection["orderedItems"]
|
||||||
|
)
|
||||||
|
else
|
||||||
|
# No more pages left, just return whatever we already have
|
||||||
|
acc ++ collection["orderedItems"]
|
||||||
|
end
|
||||||
|
else
|
||||||
|
# Got the amount of pages needed, add them all to the accumulator
|
||||||
|
acc ++ collection["orderedItems"]
|
||||||
|
end
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
{:error, "Not an OrderedCollection or OrderedCollectionPage"}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,15 +5,15 @@
|
||||||
defmodule Pleroma.Web.ActivityPub.UserView do
|
defmodule Pleroma.Web.ActivityPub.UserView do
|
||||||
use Pleroma.Web, :view
|
use Pleroma.Web, :view
|
||||||
|
|
||||||
alias Pleroma.Web.WebFinger
|
|
||||||
alias Pleroma.Web.Salmon
|
|
||||||
alias Pleroma.User
|
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
alias Pleroma.Web.ActivityPub.Transmogrifier
|
alias Pleroma.Web.ActivityPub.Transmogrifier
|
||||||
alias Pleroma.Web.ActivityPub.Utils
|
alias Pleroma.Web.ActivityPub.Utils
|
||||||
alias Pleroma.Web.Router.Helpers
|
|
||||||
alias Pleroma.Web.Endpoint
|
alias Pleroma.Web.Endpoint
|
||||||
|
alias Pleroma.Web.Router.Helpers
|
||||||
|
alias Pleroma.Web.Salmon
|
||||||
|
alias Pleroma.Web.WebFinger
|
||||||
|
|
||||||
import Ecto.Query
|
import Ecto.Query
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
||||||
use Pleroma.Web, :controller
|
use Pleroma.Web, :controller
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.ActivityPub.Relay
|
alias Pleroma.Web.ActivityPub.Relay
|
||||||
alias Pleroma.Web.MastodonAPI.Admin.AccountView
|
alias Pleroma.Web.AdminAPI.AccountView
|
||||||
|
|
||||||
import Pleroma.Web.ControllerHelper, only: [json_response: 3]
|
import Pleroma.Web.ControllerHelper, only: [json_response: 3]
|
||||||
|
|
||||||
|
@ -63,28 +63,14 @@ def untag_users(conn, %{"nicknames" => nicknames, "tags" => tags}) do
|
||||||
do: json_response(conn, :no_content, "")
|
do: json_response(conn, :no_content, "")
|
||||||
end
|
end
|
||||||
|
|
||||||
def list_users(conn, params) do
|
def list_users(%{assigns: %{user: admin}} = conn, params) do
|
||||||
{page, page_size} = page_params(params)
|
|
||||||
|
|
||||||
with {:ok, users, count} <- User.all_for_admin(page, page_size),
|
|
||||||
do:
|
|
||||||
conn
|
|
||||||
|> json(
|
|
||||||
AccountView.render("index.json",
|
|
||||||
users: users,
|
|
||||||
count: count,
|
|
||||||
page_size: page_size
|
|
||||||
)
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def search_users(%{assigns: %{user: admin}} = conn, %{"query" => query} = params) do
|
|
||||||
{page, page_size} = page_params(params)
|
{page, page_size} = page_params(params)
|
||||||
|
|
||||||
with {:ok, users, count} <-
|
with {:ok, users, count} <-
|
||||||
User.search_for_admin(query, %{
|
User.search_for_admin(%{
|
||||||
|
query: params["query"],
|
||||||
admin: admin,
|
admin: admin,
|
||||||
local: params["local"] == "true",
|
local: params["local_only"] == "true",
|
||||||
page: page,
|
page: page,
|
||||||
page_size: page_size
|
page_size: page_size
|
||||||
}),
|
}),
|
||||||
|
|
|
@ -2,10 +2,11 @@
|
||||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.MastodonAPI.Admin.AccountView do
|
defmodule Pleroma.Web.AdminAPI.AccountView do
|
||||||
use Pleroma.Web, :view
|
use Pleroma.Web, :view
|
||||||
|
|
||||||
alias Pleroma.Web.MastodonAPI.Admin.AccountView
|
alias Pleroma.User.Info
|
||||||
|
alias Pleroma.Web.AdminAPI.AccountView
|
||||||
|
|
||||||
def render("index.json", %{users: users, count: count, page_size: page_size}) do
|
def render("index.json", %{users: users, count: count, page_size: page_size}) do
|
||||||
%{
|
%{
|
||||||
|
@ -19,7 +20,10 @@ def render("show.json", %{user: user}) do
|
||||||
%{
|
%{
|
||||||
"id" => user.id,
|
"id" => user.id,
|
||||||
"nickname" => user.nickname,
|
"nickname" => user.nickname,
|
||||||
"deactivated" => user.info.deactivated
|
"deactivated" => user.info.deactivated,
|
||||||
|
"local" => user.local,
|
||||||
|
"roles" => Info.roles(user.info),
|
||||||
|
"tags" => user.tags || []
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
end
|
end
|
|
@ -3,8 +3,8 @@
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.Auth.PleromaAuthenticator do
|
defmodule Pleroma.Web.Auth.PleromaAuthenticator do
|
||||||
alias Pleroma.User
|
|
||||||
alias Comeonin.Pbkdf2
|
alias Comeonin.Pbkdf2
|
||||||
|
alias Pleroma.User
|
||||||
|
|
||||||
@behaviour Pleroma.Web.Auth.Authenticator
|
@behaviour Pleroma.Web.Auth.Authenticator
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,7 @@ defmodule Pleroma.Web.UserSocket do
|
||||||
# performing token verification on connect.
|
# performing token verification on connect.
|
||||||
def connect(%{"token" => token}, socket) do
|
def connect(%{"token" => token}, socket) do
|
||||||
with true <- Pleroma.Config.get([:chat, :enabled]),
|
with true <- Pleroma.Config.get([:chat, :enabled]),
|
||||||
{:ok, user_id} <- Phoenix.Token.verify(socket, "user socket", token, max_age: 84600),
|
{:ok, user_id} <- Phoenix.Token.verify(socket, "user socket", token, max_age: 84_600),
|
||||||
%User{} = user <- Pleroma.Repo.get(User, user_id) do
|
%User{} = user <- Pleroma.Repo.get(User, user_id) do
|
||||||
{:ok, assign(socket, :user_name, user.nickname)}
|
{:ok, assign(socket, :user_name, user.nickname)}
|
||||||
else
|
else
|
||||||
|
|
|
@ -4,8 +4,8 @@
|
||||||
|
|
||||||
defmodule Pleroma.Web.ChatChannel do
|
defmodule Pleroma.Web.ChatChannel do
|
||||||
use Phoenix.Channel
|
use Phoenix.Channel
|
||||||
alias Pleroma.Web.ChatChannel.ChatChannelState
|
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
|
alias Pleroma.Web.ChatChannel.ChatChannelState
|
||||||
|
|
||||||
def join("chat:public", _message, socket) do
|
def join("chat:public", _message, socket) do
|
||||||
send(self(), :after_join)
|
send(self(), :after_join)
|
||||||
|
@ -48,7 +48,7 @@ def add_message(message) do
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
def messages() do
|
def messages do
|
||||||
Agent.get(__MODULE__, fn state -> state[:messages] |> Enum.reverse() end)
|
Agent.get(__MODULE__, fn state -> state[:messages] |> Enum.reverse() end)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,21 +3,70 @@
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.CommonAPI do
|
defmodule Pleroma.Web.CommonAPI do
|
||||||
alias Pleroma.User
|
|
||||||
alias Pleroma.Repo
|
|
||||||
alias Pleroma.Activity
|
alias Pleroma.Activity
|
||||||
|
alias Pleroma.Formatter
|
||||||
alias Pleroma.Object
|
alias Pleroma.Object
|
||||||
|
alias Pleroma.Repo
|
||||||
alias Pleroma.ThreadMute
|
alias Pleroma.ThreadMute
|
||||||
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
alias Pleroma.Web.ActivityPub.Utils
|
alias Pleroma.Web.ActivityPub.Utils
|
||||||
alias Pleroma.Formatter
|
|
||||||
|
|
||||||
import Pleroma.Web.CommonAPI.Utils
|
import Pleroma.Web.CommonAPI.Utils
|
||||||
|
|
||||||
|
def follow(follower, followed) do
|
||||||
|
with {:ok, follower} <- User.maybe_direct_follow(follower, followed),
|
||||||
|
{:ok, activity} <- ActivityPub.follow(follower, followed),
|
||||||
|
{:ok, follower, followed} <-
|
||||||
|
User.wait_and_refresh(
|
||||||
|
Pleroma.Config.get([:activitypub, :follow_handshake_timeout]),
|
||||||
|
follower,
|
||||||
|
followed
|
||||||
|
) do
|
||||||
|
{:ok, follower, followed, activity}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def unfollow(follower, unfollowed) do
|
||||||
|
with {:ok, follower, _follow_activity} <- User.unfollow(follower, unfollowed),
|
||||||
|
{:ok, _activity} <- ActivityPub.unfollow(follower, unfollowed) do
|
||||||
|
{:ok, follower}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def accept_follow_request(follower, followed) do
|
||||||
|
with {:ok, follower} <- User.maybe_follow(follower, followed),
|
||||||
|
%Activity{} = follow_activity <- Utils.fetch_latest_follow(follower, followed),
|
||||||
|
{:ok, follow_activity} <- Utils.update_follow_state(follow_activity, "accept"),
|
||||||
|
{:ok, _activity} <-
|
||||||
|
ActivityPub.accept(%{
|
||||||
|
to: [follower.ap_id],
|
||||||
|
actor: followed,
|
||||||
|
object: follow_activity.data["id"],
|
||||||
|
type: "Accept"
|
||||||
|
}) do
|
||||||
|
{:ok, follower}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def reject_follow_request(follower, followed) do
|
||||||
|
with %Activity{} = follow_activity <- Utils.fetch_latest_follow(follower, followed),
|
||||||
|
{:ok, follow_activity} <- Utils.update_follow_state(follow_activity, "reject"),
|
||||||
|
{:ok, _activity} <-
|
||||||
|
ActivityPub.reject(%{
|
||||||
|
to: [follower.ap_id],
|
||||||
|
actor: followed,
|
||||||
|
object: follow_activity.data["id"],
|
||||||
|
type: "Reject"
|
||||||
|
}) do
|
||||||
|
{:ok, follower}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def delete(activity_id, user) do
|
def delete(activity_id, user) do
|
||||||
with %Activity{data: %{"object" => %{"id" => object_id}}} <- Repo.get(Activity, activity_id),
|
with %Activity{data: %{"object" => %{"id" => object_id}}} <- Repo.get(Activity, activity_id),
|
||||||
%Object{} = object <- Object.normalize(object_id),
|
%Object{} = object <- Object.normalize(object_id),
|
||||||
true <- user.info.is_moderator || user.ap_id == object.data["actor"],
|
true <- User.superuser?(user) || user.ap_id == object.data["actor"],
|
||||||
{:ok, _} <- unpin(activity_id, user),
|
{:ok, _} <- unpin(activity_id, user),
|
||||||
{:ok, delete} <- ActivityPub.delete(object) do
|
{:ok, delete} <- ActivityPub.delete(object) do
|
||||||
{:ok, delete}
|
{:ok, delete}
|
||||||
|
@ -75,8 +124,8 @@ def get_visibility(%{"in_reply_to_status_id" => status_id}) when not is_nil(stat
|
||||||
nil ->
|
nil ->
|
||||||
"public"
|
"public"
|
||||||
|
|
||||||
inReplyTo ->
|
in_reply_to ->
|
||||||
Pleroma.Web.MastodonAPI.StatusView.get_visibility(inReplyTo.data["object"])
|
Pleroma.Web.MastodonAPI.StatusView.get_visibility(in_reply_to.data["object"])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -88,15 +137,15 @@ def post(user, %{"status" => status} = data) do
|
||||||
|
|
||||||
with status <- String.trim(status),
|
with status <- String.trim(status),
|
||||||
attachments <- attachments_from_ids(data),
|
attachments <- attachments_from_ids(data),
|
||||||
inReplyTo <- get_replied_to_activity(data["in_reply_to_status_id"]),
|
in_reply_to <- get_replied_to_activity(data["in_reply_to_status_id"]),
|
||||||
{content_html, mentions, tags} <-
|
{content_html, mentions, tags} <-
|
||||||
make_content_html(
|
make_content_html(
|
||||||
status,
|
status,
|
||||||
attachments,
|
attachments,
|
||||||
data
|
data
|
||||||
),
|
),
|
||||||
{to, cc} <- to_for_user_and_mentions(user, mentions, inReplyTo, visibility),
|
{to, cc} <- to_for_user_and_mentions(user, mentions, in_reply_to, visibility),
|
||||||
context <- make_context(inReplyTo),
|
context <- make_context(in_reply_to),
|
||||||
cw <- data["spoiler_text"],
|
cw <- data["spoiler_text"],
|
||||||
full_payload <- String.trim(status <> (data["spoiler_text"] || "")),
|
full_payload <- String.trim(status <> (data["spoiler_text"] || "")),
|
||||||
length when length in 1..limit <- String.length(full_payload),
|
length when length in 1..limit <- String.length(full_payload),
|
||||||
|
@ -107,7 +156,7 @@ def post(user, %{"status" => status} = data) do
|
||||||
context,
|
context,
|
||||||
content_html,
|
content_html,
|
||||||
attachments,
|
attachments,
|
||||||
inReplyTo,
|
in_reply_to,
|
||||||
tags,
|
tags,
|
||||||
cw,
|
cw,
|
||||||
cc
|
cc
|
||||||
|
|
|
@ -6,14 +6,14 @@ defmodule Pleroma.Web.CommonAPI.Utils do
|
||||||
alias Calendar.Strftime
|
alias Calendar.Strftime
|
||||||
alias Comeonin.Pbkdf2
|
alias Comeonin.Pbkdf2
|
||||||
alias Pleroma.Activity
|
alias Pleroma.Activity
|
||||||
|
alias Pleroma.Config
|
||||||
alias Pleroma.Formatter
|
alias Pleroma.Formatter
|
||||||
alias Pleroma.Object
|
alias Pleroma.Object
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.Config
|
alias Pleroma.Web.ActivityPub.Utils
|
||||||
alias Pleroma.Web.Endpoint
|
alias Pleroma.Web.Endpoint
|
||||||
alias Pleroma.Web.MediaProxy
|
alias Pleroma.Web.MediaProxy
|
||||||
alias Pleroma.Web.ActivityPub.Utils
|
|
||||||
|
|
||||||
# This is a hack for twidere.
|
# This is a hack for twidere.
|
||||||
def get_by_id_or_ap_id(id) do
|
def get_by_id_or_ap_id(id) do
|
||||||
|
|
|
@ -6,7 +6,8 @@ defmodule Pleroma.Web.ControllerHelper do
|
||||||
use Pleroma.Web, :controller
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
def oauth_scopes(params, default) do
|
def oauth_scopes(params, default) do
|
||||||
# Note: `scopes` is used by Mastodon — supporting it but sticking to OAuth's standard `scope` wherever we control it
|
# Note: `scopes` is used by Mastodon — supporting it but sticking to
|
||||||
|
# OAuth's standard `scope` wherever we control it
|
||||||
Pleroma.Web.OAuth.parse_scopes(params["scope"] || params["scopes"], default)
|
Pleroma.Web.OAuth.parse_scopes(params["scope"] || params["scopes"], default)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,7 @@ defmodule Pleroma.Web.Endpoint do
|
||||||
from: :pleroma,
|
from: :pleroma,
|
||||||
only:
|
only:
|
||||||
~w(index.html static finmoji emoji packs sounds images instance sw.js sw-pleroma.js favicon.png schemas doc)
|
~w(index.html static finmoji emoji packs sounds images instance sw.js sw-pleroma.js favicon.png schemas doc)
|
||||||
|
# credo:disable-for-previous-line Credo.Check.Readability.MaxLineLength
|
||||||
)
|
)
|
||||||
|
|
||||||
# Code reloading can be explicitly enabled under the
|
# Code reloading can be explicitly enabled under the
|
||||||
|
|
|
@ -4,27 +4,27 @@
|
||||||
|
|
||||||
defmodule Pleroma.Web.Federator do
|
defmodule Pleroma.Web.Federator do
|
||||||
alias Pleroma.Activity
|
alias Pleroma.Activity
|
||||||
|
alias Pleroma.Jobs
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.WebFinger
|
|
||||||
alias Pleroma.Web.Websub
|
|
||||||
alias Pleroma.Web.Salmon
|
|
||||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
alias Pleroma.Web.ActivityPub.Visibility
|
|
||||||
alias Pleroma.Web.ActivityPub.Relay
|
alias Pleroma.Web.ActivityPub.Relay
|
||||||
alias Pleroma.Web.ActivityPub.Transmogrifier
|
alias Pleroma.Web.ActivityPub.Transmogrifier
|
||||||
alias Pleroma.Web.ActivityPub.Utils
|
alias Pleroma.Web.ActivityPub.Utils
|
||||||
|
alias Pleroma.Web.ActivityPub.Visibility
|
||||||
alias Pleroma.Web.Federator.RetryQueue
|
alias Pleroma.Web.Federator.RetryQueue
|
||||||
alias Pleroma.Web.OStatus
|
alias Pleroma.Web.OStatus
|
||||||
alias Pleroma.Jobs
|
alias Pleroma.Web.Salmon
|
||||||
|
alias Pleroma.Web.WebFinger
|
||||||
|
alias Pleroma.Web.Websub
|
||||||
|
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
@websub Application.get_env(:pleroma, :websub)
|
@websub Application.get_env(:pleroma, :websub)
|
||||||
@ostatus Application.get_env(:pleroma, :ostatus)
|
@ostatus Application.get_env(:pleroma, :ostatus)
|
||||||
|
|
||||||
def init() do
|
def init do
|
||||||
# 1 minute
|
# 1 minute
|
||||||
Process.sleep(1000 * 60 * 1)
|
Process.sleep(1000 * 60)
|
||||||
refresh_subscriptions()
|
refresh_subscriptions()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -58,7 +58,7 @@ def request_subscription(sub) do
|
||||||
Jobs.enqueue(:federator_outgoing, __MODULE__, [:request_subscription, sub])
|
Jobs.enqueue(:federator_outgoing, __MODULE__, [:request_subscription, sub])
|
||||||
end
|
end
|
||||||
|
|
||||||
def refresh_subscriptions() do
|
def refresh_subscriptions do
|
||||||
Jobs.enqueue(:federator_outgoing, __MODULE__, [:refresh_subscriptions])
|
Jobs.enqueue(:federator_outgoing, __MODULE__, [:refresh_subscriptions])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@ def init(args) do
|
||||||
{:ok, %{args | queue_table: queue_table, running_jobs: :sets.new()}}
|
{:ok, %{args | queue_table: queue_table, running_jobs: :sets.new()}}
|
||||||
end
|
end
|
||||||
|
|
||||||
def start_link() do
|
def start_link do
|
||||||
enabled =
|
enabled =
|
||||||
if Mix.env() == :test, do: true, else: Pleroma.Config.get([__MODULE__, :enabled], false)
|
if Mix.env() == :test, do: true, else: Pleroma.Config.get([__MODULE__, :enabled], false)
|
||||||
|
|
||||||
|
@ -39,11 +39,11 @@ def enqueue(data, transport, retries \\ 0) do
|
||||||
GenServer.cast(__MODULE__, {:maybe_enqueue, data, transport, retries + 1})
|
GenServer.cast(__MODULE__, {:maybe_enqueue, data, transport, retries + 1})
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_stats() do
|
def get_stats do
|
||||||
GenServer.call(__MODULE__, :get_stats)
|
GenServer.call(__MODULE__, :get_stats)
|
||||||
end
|
end
|
||||||
|
|
||||||
def reset_stats() do
|
def reset_stats do
|
||||||
GenServer.call(__MODULE__, :reset_stats)
|
GenServer.call(__MODULE__, :reset_stats)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -55,7 +55,7 @@ def get_retry_params(retries) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_retry_timer_interval() do
|
def get_retry_timer_interval do
|
||||||
Pleroma.Config.get([:retry_queue, :interval], 1000)
|
Pleroma.Config.get([:retry_queue, :interval], 1000)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -231,7 +231,7 @@ defp growth_function(retries) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp maybe_kickoff_timer() do
|
defp maybe_kickoff_timer do
|
||||||
GenServer.cast(__MODULE__, :kickoff_timer)
|
GenServer.cast(__MODULE__, :kickoff_timer)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1 +1,63 @@
|
||||||
|
defmodule Pleroma.Web.MastodonAPI.MastodonAPI do
|
||||||
|
import Ecto.Query
|
||||||
|
import Ecto.Changeset
|
||||||
|
|
||||||
|
alias Pleroma.Repo
|
||||||
|
alias Pleroma.User
|
||||||
|
|
||||||
|
@default_limit 20
|
||||||
|
|
||||||
|
def get_followers(user, params \\ %{}) do
|
||||||
|
user
|
||||||
|
|> User.get_followers_query()
|
||||||
|
|> paginate(params)
|
||||||
|
|> Repo.all()
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_friends(user, params \\ %{}) do
|
||||||
|
user
|
||||||
|
|> User.get_friends_query()
|
||||||
|
|> paginate(params)
|
||||||
|
|> Repo.all()
|
||||||
|
end
|
||||||
|
|
||||||
|
def paginate(query, params \\ %{}) do
|
||||||
|
options = cast_params(params)
|
||||||
|
|
||||||
|
query
|
||||||
|
|> restrict(:max_id, options)
|
||||||
|
|> restrict(:since_id, options)
|
||||||
|
|> restrict(:limit, options)
|
||||||
|
|> order_by([u], fragment("? desc nulls last", u.id))
|
||||||
|
end
|
||||||
|
|
||||||
|
def cast_params(params) do
|
||||||
|
param_types = %{
|
||||||
|
max_id: :string,
|
||||||
|
since_id: :string,
|
||||||
|
limit: :integer
|
||||||
|
}
|
||||||
|
|
||||||
|
changeset = cast({%{}, param_types}, params, Map.keys(param_types))
|
||||||
|
changeset.changes
|
||||||
|
end
|
||||||
|
|
||||||
|
defp restrict(query, :max_id, %{max_id: max_id}) do
|
||||||
|
query
|
||||||
|
|> where([q], q.id < ^max_id)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp restrict(query, :since_id, %{since_id: since_id}) do
|
||||||
|
query
|
||||||
|
|> where([q], q.id > ^since_id)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp restrict(query, :limit, options) do
|
||||||
|
limit = Map.get(options, :limit, @default_limit)
|
||||||
|
|
||||||
|
query
|
||||||
|
|> limit(^limit)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp restrict(query, _, _), do: query
|
||||||
|
end
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
||||||
use Pleroma.Web, :controller
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
alias Pleroma.Activity
|
alias Pleroma.Activity
|
||||||
alias Pleroma.Config
|
alias Pleroma.Config
|
||||||
alias Pleroma.Filter
|
alias Pleroma.Filter
|
||||||
|
@ -13,21 +14,17 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
||||||
alias Pleroma.Stats
|
alias Pleroma.Stats
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.Web
|
alias Pleroma.Web
|
||||||
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
|
alias Pleroma.Web.ActivityPub.Visibility
|
||||||
alias Pleroma.Web.CommonAPI
|
alias Pleroma.Web.CommonAPI
|
||||||
alias Pleroma.Web.MediaProxy
|
|
||||||
alias Pleroma.Web.Push
|
|
||||||
alias Push.Subscription
|
|
||||||
|
|
||||||
alias Pleroma.Web.MastodonAPI.AccountView
|
alias Pleroma.Web.MastodonAPI.AccountView
|
||||||
alias Pleroma.Web.MastodonAPI.FilterView
|
alias Pleroma.Web.MastodonAPI.FilterView
|
||||||
alias Pleroma.Web.MastodonAPI.ListView
|
alias Pleroma.Web.MastodonAPI.ListView
|
||||||
|
alias Pleroma.Web.MastodonAPI.MastodonAPI
|
||||||
alias Pleroma.Web.MastodonAPI.MastodonView
|
alias Pleroma.Web.MastodonAPI.MastodonView
|
||||||
alias Pleroma.Web.MastodonAPI.PushSubscriptionView
|
|
||||||
alias Pleroma.Web.MastodonAPI.StatusView
|
|
||||||
alias Pleroma.Web.MastodonAPI.ReportView
|
alias Pleroma.Web.MastodonAPI.ReportView
|
||||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
alias Pleroma.Web.MastodonAPI.StatusView
|
||||||
alias Pleroma.Web.ActivityPub.Utils
|
alias Pleroma.Web.MediaProxy
|
||||||
alias Pleroma.Web.ActivityPub.Visibility
|
|
||||||
alias Pleroma.Web.OAuth.App
|
alias Pleroma.Web.OAuth.App
|
||||||
alias Pleroma.Web.OAuth.Authorization
|
alias Pleroma.Web.OAuth.Authorization
|
||||||
alias Pleroma.Web.OAuth.Token
|
alias Pleroma.Web.OAuth.Token
|
||||||
|
@ -134,8 +131,8 @@ def verify_credentials(%{assigns: %{user: user}} = conn, _) do
|
||||||
json(conn, account)
|
json(conn, account)
|
||||||
end
|
end
|
||||||
|
|
||||||
def user(%{assigns: %{user: for_user}} = conn, %{"id" => id}) do
|
def user(%{assigns: %{user: for_user}} = conn, %{"id" => nickname_or_id}) do
|
||||||
with %User{} = user <- Repo.get(User, id),
|
with %User{} = user <- User.get_cached_by_nickname_or_id(nickname_or_id),
|
||||||
true <- User.auth_active?(user) || user.id == for_user.id || User.superuser?(for_user) do
|
true <- User.auth_active?(user) || user.id == for_user.id || User.superuser?(for_user) do
|
||||||
account = AccountView.render("account.json", %{user: user, for: for_user})
|
account = AccountView.render("account.json", %{user: user, for: for_user})
|
||||||
json(conn, account)
|
json(conn, account)
|
||||||
|
@ -193,6 +190,11 @@ def custom_emojis(conn, _params) do
|
||||||
end
|
end
|
||||||
|
|
||||||
defp add_link_headers(conn, method, activities, param \\ nil, params \\ %{}) do
|
defp add_link_headers(conn, method, activities, param \\ nil, params \\ %{}) do
|
||||||
|
params =
|
||||||
|
conn.params
|
||||||
|
|> Map.drop(["since_id", "max_id"])
|
||||||
|
|> Map.merge(params)
|
||||||
|
|
||||||
last = List.last(activities)
|
last = List.last(activities)
|
||||||
first = List.first(activities)
|
first = List.first(activities)
|
||||||
|
|
||||||
|
@ -292,13 +294,17 @@ def user_statuses(%{assigns: %{user: reading_user}} = conn, params) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def dm_timeline(%{assigns: %{user: user}} = conn, params) do
|
def dm_timeline(%{assigns: %{user: user}} = conn, params) do
|
||||||
query =
|
params =
|
||||||
ActivityPub.fetch_activities_query(
|
params
|
||||||
[user.ap_id],
|
|> Map.put("type", "Create")
|
||||||
Map.merge(params, %{"type" => "Create", visibility: "direct"})
|
|> Map.put("blocking_user", user)
|
||||||
)
|
|> Map.put("user", user)
|
||||||
|
|> Map.put(:visibility, "direct")
|
||||||
|
|
||||||
activities = Repo.all(query)
|
activities =
|
||||||
|
[user.ap_id]
|
||||||
|
|> ActivityPub.fetch_activities_query(params)
|
||||||
|
|> Repo.all()
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> add_link_headers(:dm_timeline, activities)
|
|> add_link_headers(:dm_timeline, activities)
|
||||||
|
@ -646,9 +652,9 @@ def hashtag_timeline(%{assigns: %{user: user}} = conn, params) do
|
||||||
|> render("index.json", %{activities: activities, for: user, as: :activity})
|
|> render("index.json", %{activities: activities, for: user, as: :activity})
|
||||||
end
|
end
|
||||||
|
|
||||||
def followers(%{assigns: %{user: for_user}} = conn, %{"id" => id}) do
|
def followers(%{assigns: %{user: for_user}} = conn, %{"id" => id} = params) do
|
||||||
with %User{} = user <- Repo.get(User, id),
|
with %User{} = user <- Repo.get(User, id),
|
||||||
{:ok, followers} <- User.get_followers(user) do
|
followers <- MastodonAPI.get_followers(user, params) do
|
||||||
followers =
|
followers =
|
||||||
cond do
|
cond do
|
||||||
for_user && user.id == for_user.id -> followers
|
for_user && user.id == for_user.id -> followers
|
||||||
|
@ -657,14 +663,15 @@ def followers(%{assigns: %{user: for_user}} = conn, %{"id" => id}) do
|
||||||
end
|
end
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|
|> add_link_headers(:followers, followers, user)
|
||||||
|> put_view(AccountView)
|
|> put_view(AccountView)
|
||||||
|> render("accounts.json", %{users: followers, as: :user})
|
|> render("accounts.json", %{users: followers, as: :user})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def following(%{assigns: %{user: for_user}} = conn, %{"id" => id}) do
|
def following(%{assigns: %{user: for_user}} = conn, %{"id" => id} = params) do
|
||||||
with %User{} = user <- Repo.get(User, id),
|
with %User{} = user <- Repo.get(User, id),
|
||||||
{:ok, followers} <- User.get_friends(user) do
|
followers <- MastodonAPI.get_friends(user, params) do
|
||||||
followers =
|
followers =
|
||||||
cond do
|
cond do
|
||||||
for_user && user.id == for_user.id -> followers
|
for_user && user.id == for_user.id -> followers
|
||||||
|
@ -673,6 +680,7 @@ def following(%{assigns: %{user: for_user}} = conn, %{"id" => id}) do
|
||||||
end
|
end
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|
|> add_link_headers(:following, followers, user)
|
||||||
|> put_view(AccountView)
|
|> put_view(AccountView)
|
||||||
|> render("accounts.json", %{users: followers, as: :user})
|
|> render("accounts.json", %{users: followers, as: :user})
|
||||||
end
|
end
|
||||||
|
@ -688,16 +696,7 @@ def follow_requests(%{assigns: %{user: followed}} = conn, _params) do
|
||||||
|
|
||||||
def authorize_follow_request(%{assigns: %{user: followed}} = conn, %{"id" => id}) do
|
def authorize_follow_request(%{assigns: %{user: followed}} = conn, %{"id" => id}) do
|
||||||
with %User{} = follower <- Repo.get(User, id),
|
with %User{} = follower <- Repo.get(User, id),
|
||||||
{:ok, follower} <- User.maybe_follow(follower, followed),
|
{:ok, follower} <- CommonAPI.accept_follow_request(follower, followed) do
|
||||||
%Activity{} = follow_activity <- Utils.fetch_latest_follow(follower, followed),
|
|
||||||
{:ok, follow_activity} <- Utils.update_follow_state(follow_activity, "accept"),
|
|
||||||
{:ok, _activity} <-
|
|
||||||
ActivityPub.accept(%{
|
|
||||||
to: [follower.ap_id],
|
|
||||||
actor: followed,
|
|
||||||
object: follow_activity.data["id"],
|
|
||||||
type: "Accept"
|
|
||||||
}) do
|
|
||||||
conn
|
conn
|
||||||
|> put_view(AccountView)
|
|> put_view(AccountView)
|
||||||
|> render("relationship.json", %{user: followed, target: follower})
|
|> render("relationship.json", %{user: followed, target: follower})
|
||||||
|
@ -711,15 +710,7 @@ def authorize_follow_request(%{assigns: %{user: followed}} = conn, %{"id" => id}
|
||||||
|
|
||||||
def reject_follow_request(%{assigns: %{user: followed}} = conn, %{"id" => id}) do
|
def reject_follow_request(%{assigns: %{user: followed}} = conn, %{"id" => id}) do
|
||||||
with %User{} = follower <- Repo.get(User, id),
|
with %User{} = follower <- Repo.get(User, id),
|
||||||
%Activity{} = follow_activity <- Utils.fetch_latest_follow(follower, followed),
|
{:ok, follower} <- CommonAPI.reject_follow_request(follower, followed) do
|
||||||
{:ok, follow_activity} <- Utils.update_follow_state(follow_activity, "reject"),
|
|
||||||
{:ok, _activity} <-
|
|
||||||
ActivityPub.reject(%{
|
|
||||||
to: [follower.ap_id],
|
|
||||||
actor: followed,
|
|
||||||
object: follow_activity.data["id"],
|
|
||||||
type: "Reject"
|
|
||||||
}) do
|
|
||||||
conn
|
conn
|
||||||
|> put_view(AccountView)
|
|> put_view(AccountView)
|
||||||
|> render("relationship.json", %{user: followed, target: follower})
|
|> render("relationship.json", %{user: followed, target: follower})
|
||||||
|
@ -733,14 +724,7 @@ def reject_follow_request(%{assigns: %{user: followed}} = conn, %{"id" => id}) d
|
||||||
|
|
||||||
def follow(%{assigns: %{user: follower}} = conn, %{"id" => id}) do
|
def follow(%{assigns: %{user: follower}} = conn, %{"id" => id}) do
|
||||||
with %User{} = followed <- Repo.get(User, id),
|
with %User{} = followed <- Repo.get(User, id),
|
||||||
{:ok, follower} <- User.maybe_direct_follow(follower, followed),
|
{:ok, follower, followed, _} <- CommonAPI.follow(follower, followed) do
|
||||||
{:ok, _activity} <- ActivityPub.follow(follower, followed),
|
|
||||||
{:ok, follower, followed} <-
|
|
||||||
User.wait_and_refresh(
|
|
||||||
Config.get([:activitypub, :follow_handshake_timeout]),
|
|
||||||
follower,
|
|
||||||
followed
|
|
||||||
) do
|
|
||||||
conn
|
conn
|
||||||
|> put_view(AccountView)
|
|> put_view(AccountView)
|
||||||
|> render("relationship.json", %{user: follower, target: followed})
|
|> render("relationship.json", %{user: follower, target: followed})
|
||||||
|
@ -754,8 +738,7 @@ def follow(%{assigns: %{user: follower}} = conn, %{"id" => id}) do
|
||||||
|
|
||||||
def follow(%{assigns: %{user: follower}} = conn, %{"uri" => uri}) do
|
def follow(%{assigns: %{user: follower}} = conn, %{"uri" => uri}) do
|
||||||
with %User{} = followed <- Repo.get_by(User, nickname: uri),
|
with %User{} = followed <- Repo.get_by(User, nickname: uri),
|
||||||
{:ok, follower} <- User.maybe_direct_follow(follower, followed),
|
{:ok, follower, followed, _} <- CommonAPI.follow(follower, followed) do
|
||||||
{:ok, _activity} <- ActivityPub.follow(follower, followed) do
|
|
||||||
conn
|
conn
|
||||||
|> put_view(AccountView)
|
|> put_view(AccountView)
|
||||||
|> render("account.json", %{user: followed, for: follower})
|
|> render("account.json", %{user: followed, for: follower})
|
||||||
|
@ -769,8 +752,7 @@ def follow(%{assigns: %{user: follower}} = conn, %{"uri" => uri}) do
|
||||||
|
|
||||||
def unfollow(%{assigns: %{user: follower}} = conn, %{"id" => id}) do
|
def unfollow(%{assigns: %{user: follower}} = conn, %{"id" => id}) do
|
||||||
with %User{} = followed <- Repo.get(User, id),
|
with %User{} = followed <- Repo.get(User, id),
|
||||||
{:ok, _activity} <- ActivityPub.unfollow(follower, followed),
|
{:ok, follower} <- CommonAPI.unfollow(follower, followed) do
|
||||||
{:ok, follower, _} <- User.unfollow(follower, followed) do
|
|
||||||
conn
|
conn
|
||||||
|> put_view(AccountView)
|
|> put_view(AccountView)
|
||||||
|> render("relationship.json", %{user: follower, target: followed})
|
|> render("relationship.json", %{user: follower, target: followed})
|
||||||
|
@ -1128,7 +1110,8 @@ def index(%{assigns: %{user: user}} = conn, _params) do
|
||||||
compose: %{
|
compose: %{
|
||||||
me: "#{user.id}",
|
me: "#{user.id}",
|
||||||
default_privacy: user.info.default_scope,
|
default_privacy: user.info.default_scope,
|
||||||
default_sensitive: false
|
default_sensitive: false,
|
||||||
|
allow_content_types: Config.get([:instance, :allowed_post_formats])
|
||||||
},
|
},
|
||||||
media_attachments: %{
|
media_attachments: %{
|
||||||
accept_content_types: [
|
accept_content_types: [
|
||||||
|
@ -1273,7 +1256,7 @@ def login(conn, _) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp get_or_make_app() do
|
defp get_or_make_app do
|
||||||
find_attrs = %{client_name: @local_mastodon_name, redirect_uris: "."}
|
find_attrs = %{client_name: @local_mastodon_name, redirect_uris: "."}
|
||||||
scopes = ["read", "write", "follow", "push"]
|
scopes = ["read", "write", "follow", "push"]
|
||||||
|
|
||||||
|
@ -1424,37 +1407,8 @@ 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
|
# fallback action
|
||||||
true = Push.enabled()
|
#
|
||||||
Subscription.delete_if_exists(user, token)
|
|
||||||
{:ok, subscription} = 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
|
|
||||||
true = Push.enabled()
|
|
||||||
subscription = 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
|
|
||||||
true = Push.enabled()
|
|
||||||
{:ok, subscription} = 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
|
|
||||||
true = Push.enabled()
|
|
||||||
{:ok, _response} = Subscription.delete(user, token)
|
|
||||||
json(conn, %{})
|
|
||||||
end
|
|
||||||
|
|
||||||
def errors(conn, _) do
|
def errors(conn, _) do
|
||||||
conn
|
conn
|
||||||
|> put_status(500)
|
|> put_status(500)
|
||||||
|
@ -1483,7 +1437,6 @@ def suggestions(%{assigns: %{user: user}} = conn, _) do
|
||||||
url,
|
url,
|
||||||
[],
|
[],
|
||||||
adapter: [
|
adapter: [
|
||||||
timeout: timeout,
|
|
||||||
recv_timeout: timeout,
|
recv_timeout: timeout,
|
||||||
pool: :default
|
pool: :default
|
||||||
]
|
]
|
||||||
|
|
|
@ -0,0 +1,71 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.MastodonAPI.SubscriptionController do
|
||||||
|
@moduledoc "The module represents functions to manage user subscriptions."
|
||||||
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
|
alias Pleroma.Web.Push
|
||||||
|
alias Pleroma.Web.Push.Subscription
|
||||||
|
alias Pleroma.Web.MastodonAPI.PushSubscriptionView, as: View
|
||||||
|
|
||||||
|
action_fallback(:errors)
|
||||||
|
|
||||||
|
# Creates PushSubscription
|
||||||
|
# POST /api/v1/push/subscription
|
||||||
|
#
|
||||||
|
def create(%{assigns: %{user: user, token: token}} = conn, params) do
|
||||||
|
with true <- Push.enabled(),
|
||||||
|
{:ok, _} <- Subscription.delete_if_exists(user, token),
|
||||||
|
{:ok, subscription} <- Subscription.create(user, token, params) do
|
||||||
|
view = View.render("push_subscription.json", subscription: subscription)
|
||||||
|
json(conn, view)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Gets PushSubscription
|
||||||
|
# GET /api/v1/push/subscription
|
||||||
|
#
|
||||||
|
def get(%{assigns: %{user: user, token: token}} = conn, _params) do
|
||||||
|
with true <- Push.enabled(),
|
||||||
|
{:ok, subscription} <- Subscription.get(user, token) do
|
||||||
|
view = View.render("push_subscription.json", subscription: subscription)
|
||||||
|
json(conn, view)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Updates PushSubscription
|
||||||
|
# PUT /api/v1/push/subscription
|
||||||
|
#
|
||||||
|
def update(%{assigns: %{user: user, token: token}} = conn, params) do
|
||||||
|
with true <- Push.enabled(),
|
||||||
|
{:ok, subscription} <- Subscription.update(user, token, params) do
|
||||||
|
view = View.render("push_subscription.json", subscription: subscription)
|
||||||
|
json(conn, view)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Deletes PushSubscription
|
||||||
|
# DELETE /api/v1/push/subscription
|
||||||
|
#
|
||||||
|
def delete(%{assigns: %{user: user, token: token}} = conn, _params) do
|
||||||
|
with true <- Push.enabled(),
|
||||||
|
{:ok, _response} <- Subscription.delete(user, token),
|
||||||
|
do: json(conn, %{})
|
||||||
|
end
|
||||||
|
|
||||||
|
# fallback action
|
||||||
|
#
|
||||||
|
def errors(conn, {:error, :not_found}) do
|
||||||
|
conn
|
||||||
|
|> put_status(404)
|
||||||
|
|> json("Not found")
|
||||||
|
end
|
||||||
|
|
||||||
|
def errors(conn, _) do
|
||||||
|
conn
|
||||||
|
|> put_status(500)
|
||||||
|
|> json("Something went wrong")
|
||||||
|
end
|
||||||
|
end
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
defmodule Pleroma.Web.MastodonAPI.PushSubscriptionView do
|
defmodule Pleroma.Web.MastodonAPI.PushSubscriptionView do
|
||||||
use Pleroma.Web, :view
|
use Pleroma.Web, :view
|
||||||
|
alias Pleroma.Web.Push
|
||||||
|
|
||||||
def render("push_subscription.json", %{subscription: subscription}) do
|
def render("push_subscription.json", %{subscription: subscription}) do
|
||||||
%{
|
%{
|
||||||
|
@ -14,7 +15,5 @@ def render("push_subscription.json", %{subscription: subscription}) do
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
defp server_key do
|
defp server_key, do: Keyword.get(Push.vapid_config(), :public_key)
|
||||||
Keyword.get(Application.get_env(:web_push_encryption, :vapid_details), :public_key)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -102,7 +102,10 @@ def render(
|
||||||
website: nil
|
website: nil
|
||||||
},
|
},
|
||||||
language: nil,
|
language: nil,
|
||||||
emojis: []
|
emojis: [],
|
||||||
|
pleroma: %{
|
||||||
|
local: activity.local
|
||||||
|
}
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -181,7 +184,10 @@ def render("status.json", %{activity: %{data: %{"object" => object}} = activity}
|
||||||
website: nil
|
website: nil
|
||||||
},
|
},
|
||||||
language: nil,
|
language: nil,
|
||||||
emojis: build_emojis(activity.data["object"]["emoji"])
|
emojis: build_emojis(activity.data["object"]["emoji"]),
|
||||||
|
pleroma: %{
|
||||||
|
local: activity.local
|
||||||
|
}
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -5,9 +5,9 @@
|
||||||
defmodule Pleroma.Web.MastodonAPI.WebsocketHandler do
|
defmodule Pleroma.Web.MastodonAPI.WebsocketHandler do
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
alias Pleroma.Web.OAuth.Token
|
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
|
alias Pleroma.Web.OAuth.Token
|
||||||
|
|
||||||
@behaviour :cowboy_websocket
|
@behaviour :cowboy_websocket
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,8 @@ def url(url) do
|
||||||
else
|
else
|
||||||
secret = Application.get_env(:pleroma, Pleroma.Web.Endpoint)[:secret_key_base]
|
secret = Application.get_env(:pleroma, Pleroma.Web.Endpoint)[:secret_key_base]
|
||||||
|
|
||||||
# Must preserve `%2F` for compatibility with S3 (https://git.pleroma.social/pleroma/pleroma/issues/580)
|
# Must preserve `%2F` for compatibility with S3
|
||||||
|
# https://git.pleroma.social/pleroma/pleroma/issues/580
|
||||||
replacement = get_replacement(url, ":2F:")
|
replacement = get_replacement(url, ":2F:")
|
||||||
|
|
||||||
# The URL is url-decoded and encoded again to ensure it is correctly encoded and not twice.
|
# The URL is url-decoded and encoded again to ensure it is correctly encoded and not twice.
|
||||||
|
|
|
@ -88,7 +88,7 @@ defp build_attachments(%{data: %{"attachment" => attachments}}) do
|
||||||
|
|
||||||
# TODO: Add additional properties to objects when we have the data available.
|
# TODO: Add additional properties to objects when we have the data available.
|
||||||
# Also, Whatsapp only wants JPEG or PNGs. It seems that if we add a second og:image
|
# Also, Whatsapp only wants JPEG or PNGs. It seems that if we add a second og:image
|
||||||
# object when a Video or GIF is attached it will display that in the Whatsapp Rich Preview.
|
# object when a Video or GIF is attached it will display that in Whatsapp Rich Preview.
|
||||||
case media_type do
|
case media_type do
|
||||||
"audio" ->
|
"audio" ->
|
||||||
[
|
[
|
||||||
|
|
|
@ -97,7 +97,8 @@ defp build_attachments(id, %{data: %{"attachment" => attachments}}) do
|
||||||
| acc
|
| acc
|
||||||
]
|
]
|
||||||
|
|
||||||
# TODO: Need the true width and height values here or Twitter renders an iFrame with a bad aspect ratio
|
# TODO: Need the true width and height values here or Twitter renders an iFrame with
|
||||||
|
# a bad aspect ratio
|
||||||
"video" ->
|
"video" ->
|
||||||
[
|
[
|
||||||
{:meta, [property: "twitter:card", content: "player"], []},
|
{:meta, [property: "twitter:card", content: "player"], []},
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
# Pleroma: A lightweight social networking server
|
# Pleroma: A lightweight social networking server
|
||||||
# Copyright \xc2\xa9 2017-2019 Pleroma Authors <https://pleroma.social/>
|
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.Metadata.Utils do
|
defmodule Pleroma.Web.Metadata.Utils do
|
||||||
alias Pleroma.HTML
|
|
||||||
alias Pleroma.Formatter
|
alias Pleroma.Formatter
|
||||||
|
alias Pleroma.HTML
|
||||||
alias Pleroma.Web.MediaProxy
|
alias Pleroma.Web.MediaProxy
|
||||||
|
|
||||||
def scrub_html_and_truncate(%{data: %{"content" => content}} = object) do
|
def scrub_html_and_truncate(%{data: %{"content" => content}} = object) do
|
||||||
|
@ -17,14 +17,14 @@ def scrub_html_and_truncate(%{data: %{"content" => content}} = object) do
|
||||||
|> Formatter.truncate()
|
|> Formatter.truncate()
|
||||||
end
|
end
|
||||||
|
|
||||||
def scrub_html_and_truncate(content) when is_binary(content) do
|
def scrub_html_and_truncate(content, max_length \\ 200) when is_binary(content) do
|
||||||
content
|
content
|
||||||
# html content comes from DB already encoded, decode first and scrub after
|
# html content comes from DB already encoded, decode first and scrub after
|
||||||
|> HtmlEntities.decode()
|
|> HtmlEntities.decode()
|
||||||
|> String.replace(~r/<br\s?\/?>/, " ")
|
|> String.replace(~r/<br\s?\/?>/, " ")
|
||||||
|> HTML.strip_tags()
|
|> HTML.strip_tags()
|
||||||
|> Formatter.demojify()
|
|> Formatter.demojify()
|
||||||
|> Formatter.truncate()
|
|> Formatter.truncate(max_length)
|
||||||
end
|
end
|
||||||
|
|
||||||
def attachment_url(url) do
|
def attachment_url(url) do
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
|
|
|
@ -6,7 +6,6 @@ defmodule Pleroma.Web.Nodeinfo.NodeinfoController do
|
||||||
use Pleroma.Web, :controller
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
alias Pleroma.Config
|
alias Pleroma.Config
|
||||||
alias Pleroma.Repo
|
|
||||||
alias Pleroma.Stats
|
alias Pleroma.Stats
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.Web
|
alias Pleroma.Web
|
||||||
|
@ -86,8 +85,7 @@ def raw_nodeinfo do
|
||||||
end
|
end
|
||||||
|
|
||||||
staff_accounts =
|
staff_accounts =
|
||||||
User.moderator_user_query()
|
User.all_superusers()
|
||||||
|> Repo.all()
|
|
||||||
|> Enum.map(fn u -> u.ap_id end)
|
|> Enum.map(fn u -> u.ap_id end)
|
||||||
|
|
||||||
mrf_user_allowlist =
|
mrf_user_allowlist =
|
||||||
|
|
|
@ -5,10 +5,10 @@
|
||||||
defmodule Pleroma.Web.OAuth.Authorization do
|
defmodule Pleroma.Web.OAuth.Authorization do
|
||||||
use Ecto.Schema
|
use Ecto.Schema
|
||||||
|
|
||||||
alias Pleroma.User
|
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
alias Pleroma.Web.OAuth.Authorization
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.OAuth.App
|
alias Pleroma.Web.OAuth.App
|
||||||
|
alias Pleroma.Web.OAuth.Authorization
|
||||||
|
|
||||||
import Ecto.Changeset
|
import Ecto.Changeset
|
||||||
import Ecto.Query
|
import Ecto.Query
|
||||||
|
|
|
@ -5,12 +5,12 @@
|
||||||
defmodule Pleroma.Web.OAuth.OAuthController do
|
defmodule Pleroma.Web.OAuth.OAuthController do
|
||||||
use Pleroma.Web, :controller
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
alias Pleroma.Web.Auth.Authenticator
|
|
||||||
alias Pleroma.Web.OAuth.Authorization
|
|
||||||
alias Pleroma.Web.OAuth.Token
|
|
||||||
alias Pleroma.Web.OAuth.App
|
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
|
alias Pleroma.Web.Auth.Authenticator
|
||||||
|
alias Pleroma.Web.OAuth.App
|
||||||
|
alias Pleroma.Web.OAuth.Authorization
|
||||||
|
alias Pleroma.Web.OAuth.Token
|
||||||
|
|
||||||
import Pleroma.Web.ControllerHelper, only: [oauth_scopes: 2]
|
import Pleroma.Web.ControllerHelper, only: [oauth_scopes: 2]
|
||||||
|
|
||||||
|
|
|
@ -7,11 +7,11 @@ defmodule Pleroma.Web.OAuth.Token do
|
||||||
|
|
||||||
import Ecto.Query
|
import Ecto.Query
|
||||||
|
|
||||||
alias Pleroma.User
|
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
alias Pleroma.Web.OAuth.Token
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.OAuth.App
|
alias Pleroma.Web.OAuth.App
|
||||||
alias Pleroma.Web.OAuth.Authorization
|
alias Pleroma.Web.OAuth.Authorization
|
||||||
|
alias Pleroma.Web.OAuth.Token
|
||||||
|
|
||||||
schema "oauth_tokens" do
|
schema "oauth_tokens" do
|
||||||
field(:token, :string)
|
field(:token, :string)
|
||||||
|
|
|
@ -4,8 +4,8 @@
|
||||||
|
|
||||||
defmodule Pleroma.Web.OStatus.ActivityRepresenter do
|
defmodule Pleroma.Web.OStatus.ActivityRepresenter do
|
||||||
alias Pleroma.Activity
|
alias Pleroma.Activity
|
||||||
alias Pleroma.User
|
|
||||||
alias Pleroma.Object
|
alias Pleroma.Object
|
||||||
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.OStatus.UserRepresenter
|
alias Pleroma.Web.OStatus.UserRepresenter
|
||||||
|
|
||||||
require Logger
|
require Logger
|
||||||
|
|
|
@ -4,8 +4,8 @@
|
||||||
|
|
||||||
defmodule Pleroma.Web.OStatus.FeedRepresenter do
|
defmodule Pleroma.Web.OStatus.FeedRepresenter do
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.OStatus
|
|
||||||
alias Pleroma.Web.MediaProxy
|
alias Pleroma.Web.MediaProxy
|
||||||
|
alias Pleroma.Web.OStatus
|
||||||
alias Pleroma.Web.OStatus.ActivityRepresenter
|
alias Pleroma.Web.OStatus.ActivityRepresenter
|
||||||
alias Pleroma.Web.OStatus.UserRepresenter
|
alias Pleroma.Web.OStatus.UserRepresenter
|
||||||
|
|
||||||
|
|
|
@ -4,9 +4,9 @@
|
||||||
|
|
||||||
defmodule Pleroma.Web.OStatus.DeleteHandler do
|
defmodule Pleroma.Web.OStatus.DeleteHandler do
|
||||||
require Logger
|
require Logger
|
||||||
alias Pleroma.Web.XML
|
|
||||||
alias Pleroma.Object
|
alias Pleroma.Object
|
||||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
|
alias Pleroma.Web.XML
|
||||||
|
|
||||||
def handle_delete(entry, _doc \\ nil) do
|
def handle_delete(entry, _doc \\ nil) do
|
||||||
with id <- XML.string_from_xpath("//id", entry),
|
with id <- XML.string_from_xpath("//id", entry),
|
||||||
|
|
|
@ -3,10 +3,10 @@
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.OStatus.FollowHandler do
|
defmodule Pleroma.Web.OStatus.FollowHandler do
|
||||||
alias Pleroma.Web.XML
|
|
||||||
alias Pleroma.Web.OStatus
|
|
||||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
|
alias Pleroma.Web.OStatus
|
||||||
|
alias Pleroma.Web.XML
|
||||||
|
|
||||||
def handle(entry, doc) do
|
def handle(entry, doc) do
|
||||||
with {:ok, actor} <- OStatus.find_make_or_update_user(doc),
|
with {:ok, actor} <- OStatus.find_make_or_update_user(doc),
|
||||||
|
|
|
@ -4,13 +4,14 @@
|
||||||
|
|
||||||
defmodule Pleroma.Web.OStatus.NoteHandler do
|
defmodule Pleroma.Web.OStatus.NoteHandler do
|
||||||
require Logger
|
require Logger
|
||||||
alias Pleroma.Web.OStatus
|
|
||||||
alias Pleroma.Web.XML
|
|
||||||
alias Pleroma.Activity
|
alias Pleroma.Activity
|
||||||
alias Pleroma.Object
|
alias Pleroma.Object
|
||||||
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.OStatus
|
||||||
|
alias Pleroma.Web.XML
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Get the context for this note. Uses this:
|
Get the context for this note. Uses this:
|
||||||
|
@ -18,13 +19,13 @@ defmodule Pleroma.Web.OStatus.NoteHandler do
|
||||||
2. The conversation reference in the ostatus xml
|
2. The conversation reference in the ostatus xml
|
||||||
3. A newly generated context id.
|
3. A newly generated context id.
|
||||||
"""
|
"""
|
||||||
def get_context(entry, inReplyTo) do
|
def get_context(entry, in_reply_to) do
|
||||||
context =
|
context =
|
||||||
(XML.string_from_xpath("//ostatus:conversation[1]", entry) ||
|
(XML.string_from_xpath("//ostatus:conversation[1]", entry) ||
|
||||||
XML.string_from_xpath("//ostatus:conversation[1]/@ref", entry) || "")
|
XML.string_from_xpath("//ostatus:conversation[1]/@ref", entry) || "")
|
||||||
|> String.trim()
|
|> String.trim()
|
||||||
|
|
||||||
with %{data: %{"context" => context}} <- Object.get_cached_by_ap_id(inReplyTo) do
|
with %{data: %{"context" => context}} <- Object.get_cached_by_ap_id(in_reply_to) do
|
||||||
context
|
context
|
||||||
else
|
else
|
||||||
_e ->
|
_e ->
|
||||||
|
@ -87,14 +88,14 @@ def add_external_url(note, entry) do
|
||||||
Map.put(note, "external_url", url)
|
Map.put(note, "external_url", url)
|
||||||
end
|
end
|
||||||
|
|
||||||
def fetch_replied_to_activity(entry, inReplyTo) do
|
def fetch_replied_to_activity(entry, in_reply_to) do
|
||||||
with %Activity{} = activity <- Activity.get_create_by_object_ap_id(inReplyTo) do
|
with %Activity{} = activity <- Activity.get_create_by_object_ap_id(in_reply_to) do
|
||||||
activity
|
activity
|
||||||
else
|
else
|
||||||
_e ->
|
_e ->
|
||||||
with inReplyToHref when not is_nil(inReplyToHref) <-
|
with in_reply_to_href when not is_nil(in_reply_to_href) <-
|
||||||
XML.string_from_xpath("//thr:in-reply-to[1]/@href", entry),
|
XML.string_from_xpath("//thr:in-reply-to[1]/@href", entry),
|
||||||
{:ok, [activity | _]} <- OStatus.fetch_activity_from_url(inReplyToHref) do
|
{:ok, [activity | _]} <- OStatus.fetch_activity_from_url(in_reply_to_href) do
|
||||||
activity
|
activity
|
||||||
else
|
else
|
||||||
_e -> nil
|
_e -> nil
|
||||||
|
@ -110,11 +111,12 @@ def handle_note(entry, doc \\ nil) do
|
||||||
{:ok, actor} <- OStatus.find_make_or_update_user(author),
|
{:ok, actor} <- OStatus.find_make_or_update_user(author),
|
||||||
content_html <- OStatus.get_content(entry),
|
content_html <- OStatus.get_content(entry),
|
||||||
cw <- OStatus.get_cw(entry),
|
cw <- OStatus.get_cw(entry),
|
||||||
inReplyTo <- XML.string_from_xpath("//thr:in-reply-to[1]/@ref", entry),
|
in_reply_to <- XML.string_from_xpath("//thr:in-reply-to[1]/@ref", entry),
|
||||||
inReplyToActivity <- fetch_replied_to_activity(entry, inReplyTo),
|
in_reply_to_activity <- fetch_replied_to_activity(entry, in_reply_to),
|
||||||
inReplyTo <- (inReplyToActivity && inReplyToActivity.data["object"]["id"]) || inReplyTo,
|
in_reply_to <-
|
||||||
|
(in_reply_to_activity && in_reply_to_activity.data["object"]["id"]) || in_reply_to,
|
||||||
attachments <- OStatus.get_attachments(entry),
|
attachments <- OStatus.get_attachments(entry),
|
||||||
context <- get_context(entry, inReplyTo),
|
context <- get_context(entry, in_reply_to),
|
||||||
tags <- OStatus.get_tags(entry),
|
tags <- OStatus.get_tags(entry),
|
||||||
mentions <- get_mentions(entry),
|
mentions <- get_mentions(entry),
|
||||||
to <- make_to_list(actor, mentions),
|
to <- make_to_list(actor, mentions),
|
||||||
|
@ -128,7 +130,7 @@ def handle_note(entry, doc \\ nil) do
|
||||||
context,
|
context,
|
||||||
content_html,
|
content_html,
|
||||||
attachments,
|
attachments,
|
||||||
inReplyToActivity,
|
in_reply_to_activity,
|
||||||
[],
|
[],
|
||||||
cw
|
cw
|
||||||
),
|
),
|
||||||
|
@ -140,8 +142,8 @@ def handle_note(entry, doc \\ nil) do
|
||||||
# TODO: Handle this case in make_note_data
|
# TODO: Handle this case in make_note_data
|
||||||
note <-
|
note <-
|
||||||
if(
|
if(
|
||||||
inReplyTo && !inReplyToActivity,
|
in_reply_to && !in_reply_to_activity,
|
||||||
do: note |> Map.put("inReplyTo", inReplyTo),
|
do: note |> Map.put("inReplyTo", in_reply_to),
|
||||||
else: note
|
else: note
|
||||||
) do
|
) do
|
||||||
ActivityPub.create(%{
|
ActivityPub.create(%{
|
||||||
|
|
|
@ -3,10 +3,10 @@
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.OStatus.UnfollowHandler do
|
defmodule Pleroma.Web.OStatus.UnfollowHandler do
|
||||||
alias Pleroma.Web.XML
|
|
||||||
alias Pleroma.Web.OStatus
|
|
||||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
|
alias Pleroma.Web.OStatus
|
||||||
|
alias Pleroma.Web.XML
|
||||||
|
|
||||||
def handle(entry, doc) do
|
def handle(entry, doc) do
|
||||||
with {:ok, actor} <- OStatus.find_make_or_update_user(doc),
|
with {:ok, actor} <- OStatus.find_make_or_update_user(doc),
|
||||||
|
|
|
@ -9,19 +9,19 @@ defmodule Pleroma.Web.OStatus do
|
||||||
import Pleroma.Web.XML
|
import Pleroma.Web.XML
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
|
alias Pleroma.Activity
|
||||||
|
alias Pleroma.Object
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.Web
|
alias Pleroma.Web
|
||||||
alias Pleroma.Object
|
|
||||||
alias Pleroma.Activity
|
|
||||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
alias Pleroma.Web.ActivityPub.Transmogrifier
|
alias Pleroma.Web.ActivityPub.Transmogrifier
|
||||||
|
alias Pleroma.Web.OStatus.DeleteHandler
|
||||||
|
alias Pleroma.Web.OStatus.FollowHandler
|
||||||
|
alias Pleroma.Web.OStatus.NoteHandler
|
||||||
|
alias Pleroma.Web.OStatus.UnfollowHandler
|
||||||
alias Pleroma.Web.WebFinger
|
alias Pleroma.Web.WebFinger
|
||||||
alias Pleroma.Web.Websub
|
alias Pleroma.Web.Websub
|
||||||
alias Pleroma.Web.OStatus.FollowHandler
|
|
||||||
alias Pleroma.Web.OStatus.UnfollowHandler
|
|
||||||
alias Pleroma.Web.OStatus.NoteHandler
|
|
||||||
alias Pleroma.Web.OStatus.DeleteHandler
|
|
||||||
|
|
||||||
def is_representable?(%Activity{data: data}) do
|
def is_representable?(%Activity{data: data}) do
|
||||||
object = Object.normalize(data["object"])
|
object = Object.normalize(data["object"])
|
||||||
|
|
|
@ -9,13 +9,13 @@ defmodule Pleroma.Web.OStatus.OStatusController do
|
||||||
alias Pleroma.Object
|
alias Pleroma.Object
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
alias Pleroma.Web.ActivityPub.Visibility
|
|
||||||
alias Pleroma.Web.ActivityPub.ActivityPubController
|
alias Pleroma.Web.ActivityPub.ActivityPubController
|
||||||
alias Pleroma.Web.ActivityPub.ObjectView
|
alias Pleroma.Web.ActivityPub.ObjectView
|
||||||
alias Pleroma.Web.OStatus.ActivityRepresenter
|
alias Pleroma.Web.ActivityPub.Visibility
|
||||||
alias Pleroma.Web.OStatus.FeedRepresenter
|
|
||||||
alias Pleroma.Web.Federator
|
alias Pleroma.Web.Federator
|
||||||
alias Pleroma.Web.OStatus
|
alias Pleroma.Web.OStatus
|
||||||
|
alias Pleroma.Web.OStatus.ActivityRepresenter
|
||||||
|
alias Pleroma.Web.OStatus.FeedRepresenter
|
||||||
alias Pleroma.Web.XML
|
alias Pleroma.Web.XML
|
||||||
|
|
||||||
plug(Pleroma.Web.FederatingPlug when action in [:salmon_incoming])
|
plug(Pleroma.Web.FederatingPlug when action in [:salmon_incoming])
|
||||||
|
|
|
@ -0,0 +1,133 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.Push.Impl do
|
||||||
|
@moduledoc "The module represents implementation push web notification"
|
||||||
|
|
||||||
|
alias Pleroma.Activity
|
||||||
|
alias Pleroma.Notification
|
||||||
|
alias Pleroma.Object
|
||||||
|
alias Pleroma.Repo
|
||||||
|
alias Pleroma.User
|
||||||
|
alias Pleroma.Web.Metadata.Utils
|
||||||
|
alias Pleroma.Web.Push.Subscription
|
||||||
|
|
||||||
|
require Logger
|
||||||
|
import Ecto.Query
|
||||||
|
|
||||||
|
@types ["Create", "Follow", "Announce", "Like"]
|
||||||
|
|
||||||
|
@doc "Performs sending notifications for user subscriptions"
|
||||||
|
@spec perform_send(Notification.t()) :: list(any)
|
||||||
|
def perform_send(
|
||||||
|
%{activity: %{data: %{"type" => activity_type}, id: activity_id}, user_id: user_id} =
|
||||||
|
notif
|
||||||
|
)
|
||||||
|
when activity_type in @types do
|
||||||
|
actor = User.get_cached_by_ap_id(notif.activity.data["actor"])
|
||||||
|
|
||||||
|
type = Activity.mastodon_notification_type(notif.activity)
|
||||||
|
gcm_api_key = Application.get_env(:web_push_encryption, :gcm_api_key)
|
||||||
|
avatar_url = User.avatar_url(actor)
|
||||||
|
|
||||||
|
for subscription <- fetch_subsriptions(user_id),
|
||||||
|
get_in(subscription.data, ["alerts", type]) do
|
||||||
|
%{
|
||||||
|
title: format_title(notif),
|
||||||
|
access_token: subscription.token.token,
|
||||||
|
body: format_body(notif, actor),
|
||||||
|
notification_id: notif.id,
|
||||||
|
notification_type: type,
|
||||||
|
icon: avatar_url,
|
||||||
|
preferred_locale: "en",
|
||||||
|
pleroma: %{
|
||||||
|
activity_id: activity_id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|> Jason.encode!()
|
||||||
|
|> push_message(build_sub(subscription), gcm_api_key, subscription)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def perform_send(_) do
|
||||||
|
Logger.warn("Unknown notification type")
|
||||||
|
:error
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc "Push message to web"
|
||||||
|
def push_message(body, sub, api_key, subscription) do
|
||||||
|
case WebPushEncryption.send_web_push(body, sub, api_key) do
|
||||||
|
{:ok, %{status_code: code}} when 400 <= code and code < 500 ->
|
||||||
|
Logger.debug("Removing subscription record")
|
||||||
|
Repo.delete!(subscription)
|
||||||
|
:ok
|
||||||
|
|
||||||
|
{:ok, %{status_code: code}} when 200 <= code and code < 300 ->
|
||||||
|
:ok
|
||||||
|
|
||||||
|
{:ok, %{status_code: code}} ->
|
||||||
|
Logger.error("Web Push Notification failed with code: #{code}")
|
||||||
|
:error
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
Logger.error("Web Push Notification failed with unknown error")
|
||||||
|
:error
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc "Gets user subscriptions"
|
||||||
|
def fetch_subsriptions(user_id) do
|
||||||
|
Subscription
|
||||||
|
|> where(user_id: ^user_id)
|
||||||
|
|> preload(:token)
|
||||||
|
|> Repo.all()
|
||||||
|
end
|
||||||
|
|
||||||
|
def build_sub(subscription) do
|
||||||
|
%{
|
||||||
|
keys: %{
|
||||||
|
p256dh: subscription.key_p256dh,
|
||||||
|
auth: subscription.key_auth
|
||||||
|
},
|
||||||
|
endpoint: subscription.endpoint
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def format_body(
|
||||||
|
%{activity: %{data: %{"type" => "Create", "object" => %{"content" => content}}}},
|
||||||
|
actor
|
||||||
|
) do
|
||||||
|
"@#{actor.nickname}: #{Utils.scrub_html_and_truncate(content, 80)}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def format_body(
|
||||||
|
%{activity: %{data: %{"type" => "Announce", "object" => activity_id}}},
|
||||||
|
actor
|
||||||
|
) do
|
||||||
|
%Activity{data: %{"object" => %{"id" => object_id}}} = Activity.get_by_ap_id(activity_id)
|
||||||
|
%Object{data: %{"content" => content}} = Object.get_by_ap_id(object_id)
|
||||||
|
|
||||||
|
"@#{actor.nickname} repeated: #{Utils.scrub_html_and_truncate(content, 80)}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def format_body(
|
||||||
|
%{activity: %{data: %{"type" => type}}},
|
||||||
|
actor
|
||||||
|
)
|
||||||
|
when type in ["Follow", "Like"] do
|
||||||
|
case type do
|
||||||
|
"Follow" -> "@#{actor.nickname} has followed you"
|
||||||
|
"Like" -> "@#{actor.nickname} has favorited your post"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def format_title(%{activity: %{data: %{"type" => type}}}) do
|
||||||
|
case type do
|
||||||
|
"Create" -> "New Mention"
|
||||||
|
"Follow" -> "New Follower"
|
||||||
|
"Announce" -> "New Repeat"
|
||||||
|
"Like" -> "New Favorite"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -5,24 +5,23 @@
|
||||||
defmodule Pleroma.Web.Push do
|
defmodule Pleroma.Web.Push do
|
||||||
use GenServer
|
use GenServer
|
||||||
|
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Web.Push.Impl
|
||||||
alias Pleroma.User
|
|
||||||
alias Pleroma.Web.Push.Subscription
|
|
||||||
|
|
||||||
require Logger
|
require Logger
|
||||||
import Ecto.Query
|
|
||||||
|
|
||||||
@types ["Create", "Follow", "Announce", "Like"]
|
##############
|
||||||
|
# Client API #
|
||||||
|
##############
|
||||||
|
|
||||||
def start_link() do
|
def start_link do
|
||||||
GenServer.start_link(__MODULE__, :ok, name: __MODULE__)
|
GenServer.start_link(__MODULE__, :ok, name: __MODULE__)
|
||||||
end
|
end
|
||||||
|
|
||||||
def vapid_config() do
|
def vapid_config do
|
||||||
Application.get_env(:web_push_encryption, :vapid_details, [])
|
Application.get_env(:web_push_encryption, :vapid_details, [])
|
||||||
end
|
end
|
||||||
|
|
||||||
def enabled() do
|
def enabled do
|
||||||
case vapid_config() do
|
case vapid_config() do
|
||||||
[] -> false
|
[] -> false
|
||||||
list when is_list(list) -> true
|
list when is_list(list) -> true
|
||||||
|
@ -30,14 +29,18 @@ def enabled() do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def send(notification) do
|
def send(notification),
|
||||||
if enabled() do
|
do: GenServer.cast(__MODULE__, {:send, notification})
|
||||||
GenServer.cast(Pleroma.Web.Push, {:send, notification})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
####################
|
||||||
|
# Server Callbacks #
|
||||||
|
####################
|
||||||
|
|
||||||
|
@impl true
|
||||||
def init(:ok) do
|
def init(:ok) do
|
||||||
if !enabled() do
|
if enabled() do
|
||||||
|
{:ok, nil}
|
||||||
|
else
|
||||||
Logger.warn("""
|
Logger.warn("""
|
||||||
VAPID key pair is not found. If you wish to enabled web push, please run
|
VAPID key pair is not found. If you wish to enabled web push, please run
|
||||||
|
|
||||||
|
@ -47,93 +50,15 @@ def init(:ok) do
|
||||||
""")
|
""")
|
||||||
|
|
||||||
:ignore
|
:ignore
|
||||||
else
|
|
||||||
{:ok, nil}
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def handle_cast(
|
@impl true
|
||||||
{:send, %{activity: %{data: %{"type" => type}}, user_id: user_id} = notification},
|
def handle_cast({:send, notification}, state) do
|
||||||
state
|
if enabled() do
|
||||||
)
|
Impl.perform_send(notification)
|
||||||
when type in @types do
|
|
||||||
actor = User.get_cached_by_ap_id(notification.activity.data["actor"])
|
|
||||||
|
|
||||||
type = Pleroma.Activity.mastodon_notification_type(notification.activity)
|
|
||||||
|
|
||||||
Subscription
|
|
||||||
|> where(user_id: ^user_id)
|
|
||||||
|> preload(:token)
|
|
||||||
|> Repo.all()
|
|
||||||
|> Enum.filter(fn subscription ->
|
|
||||||
get_in(subscription.data, ["alerts", type]) || false
|
|
||||||
end)
|
|
||||||
|> Enum.each(fn subscription ->
|
|
||||||
sub = %{
|
|
||||||
keys: %{
|
|
||||||
p256dh: subscription.key_p256dh,
|
|
||||||
auth: subscription.key_auth
|
|
||||||
},
|
|
||||||
endpoint: subscription.endpoint
|
|
||||||
}
|
|
||||||
|
|
||||||
body =
|
|
||||||
Jason.encode!(%{
|
|
||||||
title: format_title(notification),
|
|
||||||
access_token: subscription.token.token,
|
|
||||||
body: format_body(notification, actor),
|
|
||||||
notification_id: notification.id,
|
|
||||||
notification_type: type,
|
|
||||||
icon: User.avatar_url(actor),
|
|
||||||
preferred_locale: "en"
|
|
||||||
})
|
|
||||||
|
|
||||||
case WebPushEncryption.send_web_push(
|
|
||||||
body,
|
|
||||||
sub,
|
|
||||||
Application.get_env(:web_push_encryption, :gcm_api_key)
|
|
||||||
) do
|
|
||||||
{:ok, %{status_code: code}} when 400 <= code and code < 500 ->
|
|
||||||
Logger.debug("Removing subscription record")
|
|
||||||
Repo.delete!(subscription)
|
|
||||||
:ok
|
|
||||||
|
|
||||||
{:ok, %{status_code: code}} when 200 <= code and code < 300 ->
|
|
||||||
:ok
|
|
||||||
|
|
||||||
{:ok, %{status_code: code}} ->
|
|
||||||
Logger.error("Web Push Notification failed with code: #{code}")
|
|
||||||
:error
|
|
||||||
|
|
||||||
_ ->
|
|
||||||
Logger.error("Web Push Notification failed with unknown error")
|
|
||||||
:error
|
|
||||||
end
|
end
|
||||||
end)
|
|
||||||
|
|
||||||
{:noreply, state}
|
{:noreply, state}
|
||||||
end
|
end
|
||||||
|
|
||||||
def handle_cast({:send, _}, state) do
|
|
||||||
Logger.warn("Unknown notification type")
|
|
||||||
{:noreply, state}
|
|
||||||
end
|
|
||||||
|
|
||||||
defp format_title(%{activity: %{data: %{"type" => type}}}) do
|
|
||||||
case type do
|
|
||||||
"Create" -> "New Mention"
|
|
||||||
"Follow" -> "New Follower"
|
|
||||||
"Announce" -> "New Repeat"
|
|
||||||
"Like" -> "New Favorite"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp format_body(%{activity: %{data: %{"type" => type}}}, actor) do
|
|
||||||
case type do
|
|
||||||
"Create" -> "@#{actor.nickname} has mentioned you"
|
|
||||||
"Follow" -> "@#{actor.nickname} has followed you"
|
|
||||||
"Announce" -> "@#{actor.nickname} has repeated your post"
|
|
||||||
"Like" -> "@#{actor.nickname} has favorited your post"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -12,6 +12,8 @@ defmodule Pleroma.Web.Push.Subscription do
|
||||||
alias Pleroma.Web.OAuth.Token
|
alias Pleroma.Web.OAuth.Token
|
||||||
alias Pleroma.Web.Push.Subscription
|
alias Pleroma.Web.Push.Subscription
|
||||||
|
|
||||||
|
@type t :: %__MODULE__{}
|
||||||
|
|
||||||
schema "push_subscriptions" do
|
schema "push_subscriptions" do
|
||||||
belongs_to(:user, User, type: Pleroma.FlakeId)
|
belongs_to(:user, User, type: Pleroma.FlakeId)
|
||||||
belongs_to(:token, Token)
|
belongs_to(:token, Token)
|
||||||
|
@ -50,30 +52,38 @@ def create(
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@doc "Gets subsciption by user & token"
|
||||||
|
@spec get(User.t(), Token.t()) :: {:ok, t()} | {:error, :not_found}
|
||||||
def get(%User{id: user_id}, %Token{id: token_id}) do
|
def get(%User{id: user_id}, %Token{id: token_id}) do
|
||||||
Repo.get_by(Subscription, user_id: user_id, token_id: token_id)
|
case Repo.get_by(Subscription, user_id: user_id, token_id: token_id) do
|
||||||
|
nil -> {:error, :not_found}
|
||||||
|
subscription -> {:ok, subscription}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def update(user, token, params) do
|
def update(user, token, params) do
|
||||||
get(user, token)
|
with {:ok, subscription} <- get(user, token) do
|
||||||
|
subscription
|
||||||
|> change(data: alerts(params))
|
|> change(data: alerts(params))
|
||||||
|> Repo.update()
|
|> Repo.update()
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def delete(user, token) do
|
def delete(user, token) do
|
||||||
Repo.delete(get(user, token))
|
with {:ok, subscription} <- get(user, token),
|
||||||
|
do: Repo.delete(subscription)
|
||||||
end
|
end
|
||||||
|
|
||||||
def delete_if_exists(user, token) do
|
def delete_if_exists(user, token) do
|
||||||
case get(user, token) do
|
case get(user, token) do
|
||||||
nil -> {:ok, nil}
|
{:error, _} -> {:ok, nil}
|
||||||
sub -> Repo.delete(sub)
|
{:ok, sub} -> Repo.delete(sub)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Some webpush clients (e.g. iOS Toot!) use an non urlsafe base64 as an encoding for the key.
|
# Some webpush clients (e.g. iOS Toot!) use an non urlsafe base64 as an encoding for the key.
|
||||||
# However, the web push rfs specify to use base64 urlsafe, and the `web_push_encryption` library we use
|
# However, the web push rfs specify to use base64 urlsafe, and the `web_push_encryption` library
|
||||||
# requires the key to be properly encoded. So we just convert base64 to urlsafe base64.
|
# we use requires the key to be properly encoded. So we just convert base64 to urlsafe base64.
|
||||||
defp ensure_base64_urlsafe(string) do
|
defp ensure_base64_urlsafe(string) do
|
||||||
string
|
string
|
||||||
|> String.replace("+", "-")
|
|> String.replace("+", "-")
|
||||||
|
|
|
@ -5,7 +5,6 @@
|
||||||
defmodule Pleroma.Web.RelMe do
|
defmodule Pleroma.Web.RelMe do
|
||||||
@hackney_options [
|
@hackney_options [
|
||||||
pool: :media,
|
pool: :media,
|
||||||
timeout: 2_000,
|
|
||||||
recv_timeout: 2_000,
|
recv_timeout: 2_000,
|
||||||
max_body: 2_000_000
|
max_body: 2_000_000
|
||||||
]
|
]
|
||||||
|
@ -28,7 +27,8 @@ defp parse_url(url) do
|
||||||
{:ok, %Tesla.Env{body: html}} = Pleroma.HTTP.get(url, [], adapter: @hackney_options)
|
{:ok, %Tesla.Env{body: html}} = Pleroma.HTTP.get(url, [], adapter: @hackney_options)
|
||||||
|
|
||||||
data =
|
data =
|
||||||
Floki.attribute(html, "link[rel=me]", "href") ++ Floki.attribute(html, "a[rel=me]", "href")
|
Floki.attribute(html, "link[rel~=me]", "href") ++
|
||||||
|
Floki.attribute(html, "a[rel~=me]", "href")
|
||||||
|
|
||||||
{:ok, data}
|
{:ok, data}
|
||||||
rescue
|
rescue
|
||||||
|
|
|
@ -4,14 +4,28 @@
|
||||||
|
|
||||||
defmodule Pleroma.Web.RichMedia.Helpers do
|
defmodule Pleroma.Web.RichMedia.Helpers do
|
||||||
alias Pleroma.Activity
|
alias Pleroma.Activity
|
||||||
alias Pleroma.Object
|
|
||||||
alias Pleroma.HTML
|
alias Pleroma.HTML
|
||||||
|
alias Pleroma.Object
|
||||||
alias Pleroma.Web.RichMedia.Parser
|
alias Pleroma.Web.RichMedia.Parser
|
||||||
|
|
||||||
|
defp validate_page_url(page_url) when is_binary(page_url) do
|
||||||
|
if AutoLinker.Parser.is_url?(page_url, true) do
|
||||||
|
URI.parse(page_url) |> validate_page_url
|
||||||
|
else
|
||||||
|
:error
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp validate_page_url(%URI{authority: nil}), do: :error
|
||||||
|
defp validate_page_url(%URI{scheme: nil}), do: :error
|
||||||
|
defp validate_page_url(%URI{}), do: :ok
|
||||||
|
defp validate_page_url(_), do: :error
|
||||||
|
|
||||||
def fetch_data_for_activity(%Activity{} = activity) do
|
def fetch_data_for_activity(%Activity{} = activity) do
|
||||||
with true <- Pleroma.Config.get([:rich_media, :enabled]),
|
with true <- Pleroma.Config.get([:rich_media, :enabled]),
|
||||||
%Object{} = object <- Object.normalize(activity.data["object"]),
|
%Object{} = object <- Object.normalize(activity.data["object"]),
|
||||||
{:ok, page_url} <- HTML.extract_first_external_url(object, object.data["content"]),
|
{:ok, page_url} <- HTML.extract_first_external_url(object, object.data["content"]),
|
||||||
|
:ok <- validate_page_url(page_url),
|
||||||
{:ok, rich_media} <- Parser.parse(page_url) do
|
{:ok, rich_media} <- Parser.parse(page_url) do
|
||||||
%{page_url: page_url, rich_media: rich_media}
|
%{page_url: page_url, rich_media: rich_media}
|
||||||
else
|
else
|
||||||
|
|
|
@ -11,7 +11,6 @@ defmodule Pleroma.Web.RichMedia.Parser do
|
||||||
|
|
||||||
@hackney_options [
|
@hackney_options [
|
||||||
pool: :media,
|
pool: :media,
|
||||||
timeout: 2_000,
|
|
||||||
recv_timeout: 2_000,
|
recv_timeout: 2_000,
|
||||||
max_body: 2_000_000
|
max_body: 2_000_000
|
||||||
]
|
]
|
||||||
|
|
|
@ -140,7 +140,6 @@ defmodule Pleroma.Web.Router do
|
||||||
pipe_through([:admin_api, :oauth_write])
|
pipe_through([:admin_api, :oauth_write])
|
||||||
|
|
||||||
get("/users", AdminAPIController, :list_users)
|
get("/users", AdminAPIController, :list_users)
|
||||||
get("/users/search", AdminAPIController, :search_users)
|
|
||||||
delete("/user", AdminAPIController, :user_delete)
|
delete("/user", AdminAPIController, :user_delete)
|
||||||
patch("/users/:nickname/toggle_activation", AdminAPIController, :user_toggle_activation)
|
patch("/users/:nickname/toggle_activation", AdminAPIController, :user_toggle_activation)
|
||||||
post("/user", AdminAPIController, :user_create)
|
post("/user", AdminAPIController, :user_create)
|
||||||
|
@ -304,10 +303,10 @@ defmodule Pleroma.Web.Router do
|
||||||
scope [] do
|
scope [] do
|
||||||
pipe_through(:oauth_push)
|
pipe_through(:oauth_push)
|
||||||
|
|
||||||
post("/push/subscription", MastodonAPIController, :create_push_subscription)
|
post("/push/subscription", SubscriptionController, :create)
|
||||||
get("/push/subscription", MastodonAPIController, :get_push_subscription)
|
get("/push/subscription", SubscriptionController, :get)
|
||||||
put("/push/subscription", MastodonAPIController, :update_push_subscription)
|
put("/push/subscription", SubscriptionController, :update)
|
||||||
delete("/push/subscription", MastodonAPIController, :delete_push_subscription)
|
delete("/push/subscription", SubscriptionController, :delete)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -632,8 +631,8 @@ defmodule Pleroma.Web.Router do
|
||||||
|
|
||||||
defmodule Fallback.RedirectController do
|
defmodule Fallback.RedirectController do
|
||||||
use Pleroma.Web, :controller
|
use Pleroma.Web, :controller
|
||||||
alias Pleroma.Web.Metadata
|
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
|
alias Pleroma.Web.Metadata
|
||||||
|
|
||||||
def redirector(conn, _params, code \\ 200) do
|
def redirector(conn, _params, code \\ 200) do
|
||||||
conn
|
conn
|
||||||
|
|
|
@ -9,8 +9,8 @@ defmodule Pleroma.Web.Salmon do
|
||||||
|
|
||||||
alias Pleroma.Instances
|
alias Pleroma.Instances
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.XML
|
|
||||||
alias Pleroma.Web.OStatus.ActivityRepresenter
|
alias Pleroma.Web.OStatus.ActivityRepresenter
|
||||||
|
alias Pleroma.Web.XML
|
||||||
|
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
|
@ -86,10 +86,10 @@ def encode_key({:RSAPublicKey, modulus, exponent}) do
|
||||||
# Native generation of RSA keys is only available since OTP 20+ and in default build conditions
|
# Native generation of RSA keys is only available since OTP 20+ and in default build conditions
|
||||||
# We try at compile time to generate natively an RSA key otherwise we fallback on the old way.
|
# We try at compile time to generate natively an RSA key otherwise we fallback on the old way.
|
||||||
try do
|
try do
|
||||||
_ = :public_key.generate_key({:rsa, 2048, 65537})
|
_ = :public_key.generate_key({:rsa, 2048, 65_537})
|
||||||
|
|
||||||
def generate_rsa_pem do
|
def generate_rsa_pem do
|
||||||
key = :public_key.generate_key({:rsa, 2048, 65537})
|
key = :public_key.generate_key({:rsa, 2048, 65_537})
|
||||||
entry = :public_key.pem_entry_encode(:RSAPrivateKey, key)
|
entry = :public_key.pem_entry_encode(:RSAPrivateKey, key)
|
||||||
pem = :public_key.pem_encode([entry]) |> String.trim_trailing()
|
pem = :public_key.pem_encode([entry]) |> String.trim_trailing()
|
||||||
{:ok, pem}
|
{:ok, pem}
|
||||||
|
|
|
@ -5,11 +5,11 @@
|
||||||
defmodule Pleroma.Web.Streamer do
|
defmodule Pleroma.Web.Streamer do
|
||||||
use GenServer
|
use GenServer
|
||||||
require Logger
|
require Logger
|
||||||
alias Pleroma.User
|
|
||||||
alias Pleroma.Notification
|
|
||||||
alias Pleroma.Activity
|
alias Pleroma.Activity
|
||||||
|
alias Pleroma.Notification
|
||||||
alias Pleroma.Object
|
alias Pleroma.Object
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.ActivityPub.Visibility
|
alias Pleroma.Web.ActivityPub.Visibility
|
||||||
|
|
||||||
@keepalive_interval :timer.seconds(30)
|
@keepalive_interval :timer.seconds(30)
|
||||||
|
@ -197,10 +197,12 @@ def push_to_socket(topics, topic, %Activity{data: %{"type" => "Announce"}} = ite
|
||||||
if socket.assigns[:user] do
|
if socket.assigns[:user] do
|
||||||
user = User.get_cached_by_ap_id(socket.assigns[:user].ap_id)
|
user = User.get_cached_by_ap_id(socket.assigns[:user].ap_id)
|
||||||
blocks = user.info.blocks || []
|
blocks = user.info.blocks || []
|
||||||
|
mutes = user.info.mutes || []
|
||||||
|
|
||||||
parent = Object.normalize(item.data["object"])
|
parent = Object.normalize(item.data["object"])
|
||||||
|
|
||||||
unless is_nil(parent) or item.actor in blocks or parent.data["actor"] in blocks do
|
unless is_nil(parent) or item.actor in blocks or item.actor in mutes or
|
||||||
|
parent.data["actor"] in blocks or parent.data["actor"] in mutes do
|
||||||
send(socket.transport_pid, {:text, represent_update(item, user)})
|
send(socket.transport_pid, {:text, represent_update(item, user)})
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
|
@ -209,23 +211,28 @@ def push_to_socket(topics, topic, %Activity{data: %{"type" => "Announce"}} = ite
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
def push_to_socket(topics, topic, %Activity{id: id, data: %{"type" => "Delete"}}) do
|
def push_to_socket(topics, topic, %Activity{
|
||||||
|
data: %{"type" => "Delete", "deleted_activity_id" => deleted_activity_id}
|
||||||
|
}) do
|
||||||
Enum.each(topics[topic] || [], fn socket ->
|
Enum.each(topics[topic] || [], fn socket ->
|
||||||
send(
|
send(
|
||||||
socket.transport_pid,
|
socket.transport_pid,
|
||||||
{:text, %{event: "delete", payload: to_string(id)} |> Jason.encode!()}
|
{:text, %{event: "delete", payload: to_string(deleted_activity_id)} |> Jason.encode!()}
|
||||||
)
|
)
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def push_to_socket(_topics, _topic, %Activity{data: %{"type" => "Delete"}}), do: :noop
|
||||||
|
|
||||||
def push_to_socket(topics, topic, item) do
|
def push_to_socket(topics, topic, item) do
|
||||||
Enum.each(topics[topic] || [], fn socket ->
|
Enum.each(topics[topic] || [], fn socket ->
|
||||||
# Get the current user so we have up-to-date blocks etc.
|
# Get the current user so we have up-to-date blocks etc.
|
||||||
if socket.assigns[:user] do
|
if socket.assigns[:user] do
|
||||||
user = User.get_cached_by_ap_id(socket.assigns[:user].ap_id)
|
user = User.get_cached_by_ap_id(socket.assigns[:user].ap_id)
|
||||||
blocks = user.info.blocks || []
|
blocks = user.info.blocks || []
|
||||||
|
mutes = user.info.mutes || []
|
||||||
|
|
||||||
unless item.actor in blocks do
|
unless item.actor in blocks or item.actor in mutes do
|
||||||
send(socket.transport_pid, {:text, represent_update(item, user)})
|
send(socket.transport_pid, {:text, represent_update(item, user)})
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
|
|
|
@ -8,75 +8,145 @@
|
||||||
</title>
|
</title>
|
||||||
<style>
|
<style>
|
||||||
body {
|
body {
|
||||||
background-color: #282c37;
|
background-color: #121a24;
|
||||||
font-family: sans-serif;
|
font-family: sans-serif;
|
||||||
color:white;
|
color: #b9b9ba;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.container {
|
.container {
|
||||||
margin: 50px auto;
|
max-width: 420px;
|
||||||
max-width: 320px;
|
padding: 20px;
|
||||||
padding: 0;
|
background-color: #182230;
|
||||||
padding: 40px 40px 40px 40px;
|
|
||||||
background-color: #313543;
|
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
|
margin: auto;
|
||||||
|
margin-top: 10vh;
|
||||||
|
box-shadow: 0 1px 4px 0px rgba(0, 0, 0, 0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
h1 {
|
h1 {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
font-size: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
h2 {
|
h2 {
|
||||||
color: #9baec8;
|
color: #b9b9ba;
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
font-size: 20px;
|
font-size: 18px;
|
||||||
margin-bottom: 40px;
|
margin-bottom: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
form {
|
form {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.input {
|
||||||
|
text-align: left;
|
||||||
|
color: #89898a;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
input {
|
input {
|
||||||
box-sizing: border-box;
|
box-sizing: content-box;
|
||||||
width: 100%;
|
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
margin-top: 20px;
|
margin-top: 5px;
|
||||||
background-color: rgba(0,0,0,.1);
|
margin-bottom: 10px;
|
||||||
color: white;
|
background-color: #121a24;
|
||||||
|
color: #b9b9ba;
|
||||||
border: 0;
|
border: 0;
|
||||||
border-bottom: 2px solid #9baec8;
|
transition-property: border-bottom;
|
||||||
|
transition-duration: 0.35s;
|
||||||
|
border-bottom: 2px solid #2a384a;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
input:focus {
|
.scopes-input {
|
||||||
border-bottom: 2px solid #4b8ed8;
|
display: flex;
|
||||||
|
margin-top: 1em;
|
||||||
|
text-align: left;
|
||||||
|
color: #89898a;
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type="checkbox"] {
|
.scopes-input label:first-child {
|
||||||
width: auto;
|
flex-basis: 40%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scopes {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
text-align: left;
|
||||||
|
color: #b9b9ba;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scope {
|
||||||
|
flex-basis: 100%;
|
||||||
|
display: flex;
|
||||||
|
height: 2em;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
[type="checkbox"] + label {
|
||||||
|
margin: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
[type="checkbox"] {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
[type="checkbox"] + label:before {
|
||||||
|
display: inline-block;
|
||||||
|
color: white;
|
||||||
|
background-color: #121a24;
|
||||||
|
border: 4px solid #121a24;
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: 1.2em;
|
||||||
|
height: 1.2em;
|
||||||
|
margin-right: 1.0em;
|
||||||
|
content: "";
|
||||||
|
transition-property: background-color;
|
||||||
|
transition-duration: 0.35s;
|
||||||
|
color: #121a24;
|
||||||
|
margin-bottom: -0.2em;
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
[type="checkbox"]:checked + label:before {
|
||||||
|
background-color: #d8a070;
|
||||||
|
}
|
||||||
|
|
||||||
|
input:focus {
|
||||||
|
outline: none;
|
||||||
|
border-bottom: 2px solid #d8a070;
|
||||||
}
|
}
|
||||||
|
|
||||||
button {
|
button {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
color: white;
|
background-color: #1c2a3a;
|
||||||
background-color: #419bdd;
|
color: #b9b9ba;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
border: none;
|
border: none;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
margin-top: 30px;
|
margin-top: 30px;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
font-weight: 500;
|
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
|
box-shadow: 0px 0px 2px 0px black,
|
||||||
|
0px 1px 0px 0px rgba(255, 255, 255, 0.2) inset,
|
||||||
|
0px -1px 0px 0px rgba(0, 0, 0, 0.2) inset;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
box-shadow: 0px 0px 0px 1px #d8a070,
|
||||||
|
0px 1px 0px 0px rgba(255, 255, 255, 0.2) inset,
|
||||||
|
0px -1px 0px 0px rgba(0, 0, 0, 0.2) inset;
|
||||||
}
|
}
|
||||||
|
|
||||||
.alert-danger {
|
.alert-danger {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
color: #D8000C;
|
background-color: #931014;
|
||||||
background-color: #FFD2D2;
|
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
border: none;
|
border: none;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
|
@ -88,20 +158,32 @@
|
||||||
.alert-info {
|
.alert-info {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
color: #00529B;
|
|
||||||
background-color: #BDE5F8;
|
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
border: none;
|
border: 1px solid #7d796a;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media all and (max-width: 440px) {
|
||||||
|
.container {
|
||||||
|
margin-top: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
.scopes-input {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scope {
|
||||||
|
flex-basis: 50%;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<h1>Pleroma</h1>
|
<h1><%= Application.get_env(:pleroma, :instance)[:name] %></h1>
|
||||||
<%= render @view_module, @view_template, assigns %>
|
<%= render @view_module, @view_template, assigns %>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
@ -6,23 +6,26 @@
|
||||||
<% end %>
|
<% end %>
|
||||||
<h2>OAuth Authorization</h2>
|
<h2>OAuth Authorization</h2>
|
||||||
<%= form_for @conn, o_auth_path(@conn, :authorize), [as: "authorization"], fn f -> %>
|
<%= form_for @conn, o_auth_path(@conn, :authorize), [as: "authorization"], fn f -> %>
|
||||||
<%= label f, :name, "Name or email" %>
|
<div class="input">
|
||||||
<%= text_input f, :name %>
|
<%= label f, :name, "Name or email" %>
|
||||||
<br>
|
<%= text_input f, :name %>
|
||||||
<br>
|
</div>
|
||||||
<%= label f, :password, "Password" %>
|
<div class="input">
|
||||||
<%= password_input f, :password %>
|
<%= label f, :password, "Password" %>
|
||||||
<br>
|
<%= password_input f, :password %>
|
||||||
<br>
|
</div>
|
||||||
|
<div class="scopes-input">
|
||||||
<%= label f, :scope, "Permissions" %>
|
<%= label f, :scope, "Permissions" %>
|
||||||
<br>
|
<div class="scopes">
|
||||||
<%= for scope <- @available_scopes do %>
|
<%= for scope <- @available_scopes do %>
|
||||||
<%# Note: using hidden input with `unchecked_value` in order to distinguish user's empty selection from `scope` param being omitted %>
|
<%# Note: using hidden input with `unchecked_value` in order to distinguish user's empty selection from `scope` param being omitted %>
|
||||||
|
<div class="scope">
|
||||||
<%= checkbox f, :"scope_#{scope}", value: scope in @scopes && scope, checked_value: scope, unchecked_value: "", name: "authorization[scope][]" %>
|
<%= checkbox f, :"scope_#{scope}", value: scope in @scopes && scope, checked_value: scope, unchecked_value: "", name: "authorization[scope][]" %>
|
||||||
<%= label f, :"scope_#{scope}", String.capitalize(scope) %>
|
<%= label f, :"scope_#{scope}", String.capitalize(scope) %>
|
||||||
<br>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<%= hidden_input f, :client_id, value: @client_id %>
|
<%= hidden_input f, :client_id, value: @client_id %>
|
||||||
<%= hidden_input f, :response_type, value: @response_type %>
|
<%= hidden_input f, :response_type, value: @response_type %>
|
||||||
|
|
|
@ -10,13 +10,13 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
|
||||||
alias Comeonin.Pbkdf2
|
alias Comeonin.Pbkdf2
|
||||||
alias Pleroma.Emoji
|
alias Pleroma.Emoji
|
||||||
alias Pleroma.PasswordResetToken
|
alias Pleroma.PasswordResetToken
|
||||||
alias Pleroma.User
|
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
|
alias Pleroma.User
|
||||||
alias Pleroma.Web
|
alias Pleroma.Web
|
||||||
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
alias Pleroma.Web.CommonAPI
|
alias Pleroma.Web.CommonAPI
|
||||||
alias Pleroma.Web.OStatus
|
alias Pleroma.Web.OStatus
|
||||||
alias Pleroma.Web.WebFinger
|
alias Pleroma.Web.WebFinger
|
||||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
|
||||||
|
|
||||||
def show_password_reset(conn, %{"token" => token}) do
|
def show_password_reset(conn, %{"token" => token}) do
|
||||||
with %{used: false} = token <- Repo.get_by(PasswordResetToken, %{token: token}),
|
with %{used: false} = token <- Repo.get_by(PasswordResetToken, %{token: token}),
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue