wip
Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
This commit is contained in:
commit
9032d065e6
103 changed files with 2502 additions and 190 deletions
|
@ -79,7 +79,6 @@ unit-testing:
|
||||||
- "**/*.ex"
|
- "**/*.ex"
|
||||||
- "**/*.exs"
|
- "**/*.exs"
|
||||||
- "mix.lock"
|
- "mix.lock"
|
||||||
retry: 2
|
|
||||||
cache: &testing_cache_policy
|
cache: &testing_cache_policy
|
||||||
<<: *global_cache_policy
|
<<: *global_cache_policy
|
||||||
policy: pull
|
policy: pull
|
||||||
|
@ -94,6 +93,27 @@ unit-testing:
|
||||||
- mix ecto.migrate
|
- mix ecto.migrate
|
||||||
- mix coveralls --preload-modules
|
- mix coveralls --preload-modules
|
||||||
|
|
||||||
|
unit-testing-erratic:
|
||||||
|
stage: test
|
||||||
|
retry: 2
|
||||||
|
only:
|
||||||
|
changes:
|
||||||
|
- "**/*.ex"
|
||||||
|
- "**/*.exs"
|
||||||
|
- "mix.lock"
|
||||||
|
cache: &testing_cache_policy
|
||||||
|
<<: *global_cache_policy
|
||||||
|
policy: pull
|
||||||
|
|
||||||
|
services:
|
||||||
|
- name: postgres:13
|
||||||
|
alias: postgres
|
||||||
|
command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"]
|
||||||
|
script:
|
||||||
|
- mix ecto.create
|
||||||
|
- mix ecto.migrate
|
||||||
|
- mix test --only=erratic
|
||||||
|
|
||||||
# Removed to fix CI issue. In this early state it wasn't adding much value anyway.
|
# Removed to fix CI issue. In this early state it wasn't adding much value anyway.
|
||||||
# TODO Fix and reinstate federated testing
|
# TODO Fix and reinstate federated testing
|
||||||
# federated-testing:
|
# federated-testing:
|
||||||
|
@ -117,7 +137,6 @@ unit-testing-rum:
|
||||||
- "**/*.ex"
|
- "**/*.ex"
|
||||||
- "**/*.exs"
|
- "**/*.exs"
|
||||||
- "mix.lock"
|
- "mix.lock"
|
||||||
retry: 2
|
|
||||||
cache: *testing_cache_policy
|
cache: *testing_cache_policy
|
||||||
services:
|
services:
|
||||||
- name: minibikini/postgres-with-rum:12
|
- name: minibikini/postgres-with-rum:12
|
||||||
|
|
|
@ -14,9 +14,15 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
- Allow users to remove their emails if instance does not need email to register
|
- Allow users to remove their emails if instance does not need email to register
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
- `activeMonth` and `activeHalfyear` fields in NodeInfo usage.users object
|
||||||
|
- Experimental support for Finch. Put `config :tesla, :adapter, {Tesla.Adapter.Finch, name: MyFinch}` in your secrets file to use it. Reverse Proxy will still use Hackney.
|
||||||
|
- AdminAPI: allow moderators to manage reports, users, invites, and custom emojis
|
||||||
|
- AdminAPI: restrict moderators to access sensitive data: change user credentials, get password reset token, read private statuses and chats, etc
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
- Subscription(Bell) Notifications: Don't create from Pipeline Ingested replies
|
- Subscription(Bell) Notifications: Don't create from Pipeline Ingested replies
|
||||||
|
- Handle Reject for already-accepted Follows properly
|
||||||
|
- Display OpenGraph data on alternative notice routes.
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ RUN apk add git gcc g++ musl-dev make cmake file-dev &&\
|
||||||
mkdir release &&\
|
mkdir release &&\
|
||||||
mix release --path release
|
mix release --path release
|
||||||
|
|
||||||
FROM alpine:3.11
|
FROM alpine:3.14
|
||||||
|
|
||||||
ARG BUILD_DATE
|
ARG BUILD_DATE
|
||||||
ARG VCS_REF
|
ARG VCS_REF
|
||||||
|
@ -31,8 +31,7 @@ LABEL maintainer="ops@pleroma.social" \
|
||||||
ARG HOME=/opt/pleroma
|
ARG HOME=/opt/pleroma
|
||||||
ARG DATA=/var/lib/pleroma
|
ARG DATA=/var/lib/pleroma
|
||||||
|
|
||||||
RUN echo "http://nl.alpinelinux.org/alpine/latest-stable/community" >> /etc/apk/repositories &&\
|
RUN apk update &&\
|
||||||
apk update &&\
|
|
||||||
apk add exiftool ffmpeg imagemagick libmagic ncurses postgresql-client &&\
|
apk add exiftool ffmpeg imagemagick libmagic ncurses postgresql-client &&\
|
||||||
adduser --system --shell /bin/false --home ${HOME} pleroma &&\
|
adduser --system --shell /bin/false --home ${HOME} pleroma &&\
|
||||||
mkdir -p ${DATA}/uploads &&\
|
mkdir -p ${DATA}/uploads &&\
|
||||||
|
|
|
@ -394,7 +394,7 @@ defp get_actor(group, users), do: Enum.random(users[group])
|
||||||
|
|
||||||
defp other_data(actor, content) do
|
defp other_data(actor, content) do
|
||||||
%{host: host} = URI.parse(actor.ap_id)
|
%{host: host} = URI.parse(actor.ap_id)
|
||||||
datetime = DateTime.utc_now()
|
datetime = DateTime.utc_now() |> to_string()
|
||||||
context_id = "https://#{host}/contexts/#{UUID.generate()}"
|
context_id = "https://#{host}/contexts/#{UUID.generate()}"
|
||||||
activity_id = "https://#{host}/activities/#{UUID.generate()}"
|
activity_id = "https://#{host}/activities/#{UUID.generate()}"
|
||||||
object_id = "https://#{host}/objects/#{UUID.generate()}"
|
object_id = "https://#{host}/objects/#{UUID.generate()}"
|
||||||
|
|
|
@ -99,15 +99,16 @@ defp hashtag_fetching(params, user, local_only) do
|
||||||
|> Enum.map(&String.downcase(&1))
|
|> Enum.map(&String.downcase(&1))
|
||||||
|
|
||||||
_activities =
|
_activities =
|
||||||
params
|
%{
|
||||||
|> Map.put(:type, "Create")
|
type: "Create",
|
||||||
|> Map.put(:local_only, local_only)
|
local_only: local_only,
|
||||||
|> Map.put(:blocking_user, user)
|
blocking_user: user,
|
||||||
|> Map.put(:muting_user, user)
|
muting_user: user,
|
||||||
|> Map.put(:user, user)
|
user: user,
|
||||||
|> Map.put(:tag, tags)
|
tag: tags,
|
||||||
|> Map.put(:tag_all, tag_all)
|
tag_all: tag_all,
|
||||||
|> Map.put(:tag_reject, tag_reject)
|
tag_reject: tag_reject,
|
||||||
|
}
|
||||||
|> Pleroma.Web.ActivityPub.ActivityPub.fetch_public_activities()
|
|> Pleroma.Web.ActivityPub.ActivityPub.fetch_public_activities()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -17,14 +17,14 @@ def run(_args) do
|
||||||
# Let the user make 100 posts
|
# Let the user make 100 posts
|
||||||
|
|
||||||
1..100
|
1..100
|
||||||
|> Enum.each(fn i -> CommonAPI.post(user, %{"status" => to_string(i)}) end)
|
|> Enum.each(fn i -> CommonAPI.post(user, %{status: to_string(i)}) end)
|
||||||
|
|
||||||
# Let 10 random users post
|
# Let 10 random users post
|
||||||
posts =
|
posts =
|
||||||
users
|
users
|
||||||
|> Enum.take_random(10)
|
|> Enum.take_random(10)
|
||||||
|> Enum.map(fn {:ok, random_user} ->
|
|> Enum.map(fn {:ok, random_user} ->
|
||||||
{:ok, activity} = CommonAPI.post(random_user, %{"status" => "."})
|
{:ok, activity} = CommonAPI.post(random_user, %{status: "."})
|
||||||
activity
|
activity
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
@ -42,7 +42,7 @@ def run(_args) do
|
||||||
|> Conn.assign(:user, reading_user)
|
|> Conn.assign(:user, reading_user)
|
||||||
|> Conn.assign(:skip_link_headers, true)
|
|> Conn.assign(:skip_link_headers, true)
|
||||||
|
|
||||||
Pleroma.Web.MastodonAPI.AccountController.statuses(conn, %{"id" => user.id})
|
Pleroma.Web.MastodonAPI.AccountController.statuses(conn, %{id: user.id})
|
||||||
end
|
end
|
||||||
},
|
},
|
||||||
inputs: %{"user" => user, "no user" => nil},
|
inputs: %{"user" => user, "no user" => nil},
|
||||||
|
@ -50,7 +50,7 @@ def run(_args) do
|
||||||
)
|
)
|
||||||
|
|
||||||
users
|
users
|
||||||
|> Enum.each(fn {:ok, follower, user} -> Pleroma.User.follow(follower, user) end)
|
|> Enum.each(fn {:ok, follower} -> Pleroma.User.follow(follower, user) end)
|
||||||
|
|
||||||
Benchee.run(
|
Benchee.run(
|
||||||
%{
|
%{
|
||||||
|
@ -60,7 +60,7 @@ def run(_args) do
|
||||||
|> Conn.assign(:user, reading_user)
|
|> Conn.assign(:user, reading_user)
|
||||||
|> Conn.assign(:skip_link_headers, true)
|
|> Conn.assign(:skip_link_headers, true)
|
||||||
|
|
||||||
Pleroma.Web.MastodonAPI.AccountController.statuses(conn, %{"id" => user.id})
|
Pleroma.Web.MastodonAPI.AccountController.statuses(conn, %{id: user.id})
|
||||||
end
|
end
|
||||||
},
|
},
|
||||||
inputs: %{"user" => user, "no user" => nil},
|
inputs: %{"user" => user, "no user" => nil},
|
||||||
|
|
|
@ -4,8 +4,7 @@
|
||||||
# you can enable the server option below.
|
# you can enable the server option below.
|
||||||
config :pleroma, Pleroma.Web.Endpoint,
|
config :pleroma, Pleroma.Web.Endpoint,
|
||||||
http: [port: 4001],
|
http: [port: 4001],
|
||||||
url: [port: 4001],
|
url: [port: 4001]
|
||||||
server: true
|
|
||||||
|
|
||||||
# Disable captha for tests
|
# Disable captha for tests
|
||||||
config :pleroma, Pleroma.Captcha,
|
config :pleroma, Pleroma.Captcha,
|
||||||
|
@ -44,7 +43,7 @@
|
||||||
pool_size: 10
|
pool_size: 10
|
||||||
|
|
||||||
# Reduce hash rounds for testing
|
# Reduce hash rounds for testing
|
||||||
config :pbkdf2_elixir, rounds: 1
|
config :pleroma, :password, iterations: 1
|
||||||
|
|
||||||
config :tesla, adapter: Tesla.Mock
|
config :tesla, adapter: Tesla.Mock
|
||||||
|
|
||||||
|
|
|
@ -139,6 +139,7 @@
|
||||||
],
|
],
|
||||||
protocol: "https",
|
protocol: "https",
|
||||||
secret_key_base: "aK4Abxf29xU9TTDKre9coZPUgevcVCFQJe/5xP/7Lt4BEif6idBIbjupVbOrbKxl",
|
secret_key_base: "aK4Abxf29xU9TTDKre9coZPUgevcVCFQJe/5xP/7Lt4BEif6idBIbjupVbOrbKxl",
|
||||||
|
live_view: [signing_salt: "U5ELgdEwTD3n1+D5s0rY0AMg8/y1STxZ3Zvsl3bWh+oBcGrYdil0rXqPMRd3Glcq"],
|
||||||
signing_salt: "CqaoopA2",
|
signing_salt: "CqaoopA2",
|
||||||
render_errors: [view: Pleroma.Web.ErrorView, accepts: ~w(json)],
|
render_errors: [view: Pleroma.Web.ErrorView, accepts: ~w(json)],
|
||||||
pubsub_server: Pleroma.PubSub,
|
pubsub_server: Pleroma.PubSub,
|
||||||
|
@ -148,6 +149,8 @@
|
||||||
]
|
]
|
||||||
|
|
||||||
# Configures Elixir's Logger
|
# Configures Elixir's Logger
|
||||||
|
config :logger, truncate: 65536
|
||||||
|
|
||||||
config :logger, :console,
|
config :logger, :console,
|
||||||
level: :debug,
|
level: :debug,
|
||||||
format: "\n$time $metadata[$level] $message\n",
|
format: "\n$time $metadata[$level] $message\n",
|
||||||
|
@ -253,7 +256,9 @@
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
show_reactions: true,
|
show_reactions: true,
|
||||||
password_reset_token_validity: 60 * 60 * 24
|
password_reset_token_validity: 60 * 60 * 24,
|
||||||
|
profile_directory: true,
|
||||||
|
privileged_staff: false
|
||||||
|
|
||||||
config :pleroma, :welcome,
|
config :pleroma, :welcome,
|
||||||
direct_message: [
|
direct_message: [
|
||||||
|
@ -851,6 +856,13 @@
|
||||||
{Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy, [max_running: 5, max_waiting: 5]}
|
{Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy, [max_running: 5, max_waiting: 5]}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
config :pleroma, :telemetry,
|
||||||
|
slow_queries_logging: [
|
||||||
|
enabled: false,
|
||||||
|
min_duration: 500_000,
|
||||||
|
exclude_sources: [nil, "oban_jobs"]
|
||||||
|
]
|
||||||
|
|
||||||
# Import environment specific config. This must remain at the bottom
|
# Import environment specific config. This must remain at the bottom
|
||||||
# of this file so it overrides the configuration defined above.
|
# of this file so it overrides the configuration defined above.
|
||||||
import_config "#{Mix.env()}.exs"
|
import_config "#{Mix.env()}.exs"
|
||||||
|
|
|
@ -936,6 +936,17 @@
|
||||||
key: :show_reactions,
|
key: :show_reactions,
|
||||||
type: :boolean,
|
type: :boolean,
|
||||||
description: "Let favourites and emoji reactions be viewed through the API."
|
description: "Let favourites and emoji reactions be viewed through the API."
|
||||||
|
},
|
||||||
|
%{
|
||||||
|
key: :profile_directory,
|
||||||
|
type: :boolean,
|
||||||
|
description: "Enable profile directory."
|
||||||
|
},
|
||||||
|
%{
|
||||||
|
key: :privileged_staff,
|
||||||
|
type: :boolean,
|
||||||
|
description:
|
||||||
|
"Let moderators access sensitive data (e.g. updating user credentials, get password reset token, delete users, index and read private statuses and chats)"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
|
@ -261,6 +261,46 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## `PATCH /api/v1/pleroma/admin/users/suggest`
|
||||||
|
|
||||||
|
### Suggest a user
|
||||||
|
|
||||||
|
Adds the user(s) to follower recommendations.
|
||||||
|
|
||||||
|
- Params:
|
||||||
|
- `nicknames`: nicknames array
|
||||||
|
- Response:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
users: [
|
||||||
|
{
|
||||||
|
// user object
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## `PATCH /api/v1/pleroma/admin/users/unsuggest`
|
||||||
|
|
||||||
|
### Unsuggest a user
|
||||||
|
|
||||||
|
Removes the user(s) from follower recommendations.
|
||||||
|
|
||||||
|
- Params:
|
||||||
|
- `nicknames`: nicknames array
|
||||||
|
- Response:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
users: [
|
||||||
|
{
|
||||||
|
// user object
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## `GET /api/v1/pleroma/admin/users/:nickname_or_id`
|
## `GET /api/v1/pleroma/admin/users/:nickname_or_id`
|
||||||
|
|
||||||
### Retrive the details of a user
|
### Retrive the details of a user
|
||||||
|
|
|
@ -383,12 +383,6 @@ Pleroma is generally compatible with the Mastodon 2.7.2 API, but some newer feat
|
||||||
|
|
||||||
- `GET /api/v1/endorsements`: Returns an empty array, `[]`
|
- `GET /api/v1/endorsements`: Returns an empty array, `[]`
|
||||||
|
|
||||||
### Profile directory
|
|
||||||
|
|
||||||
*Added in Mastodon 3.0.0*
|
|
||||||
|
|
||||||
- `GET /api/v1/directory`: Returns HTTP 404
|
|
||||||
|
|
||||||
### Featured tags
|
### Featured tags
|
||||||
|
|
||||||
*Added in Mastodon 3.0.0*
|
*Added in Mastodon 3.0.0*
|
||||||
|
|
347
docs/development/API/nodeinfo.md
Normal file
347
docs/development/API/nodeinfo.md
Normal file
|
@ -0,0 +1,347 @@
|
||||||
|
# Nodeinfo
|
||||||
|
|
||||||
|
See also [the Nodeinfo standard](https://nodeinfo.diaspora.software/).
|
||||||
|
|
||||||
|
## `/.well-known/nodeinfo`
|
||||||
|
### The well-known path
|
||||||
|
* Method: `GET`
|
||||||
|
* Authentication: not required
|
||||||
|
* Params: none
|
||||||
|
* Response: JSON
|
||||||
|
* Example response:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"links":[
|
||||||
|
{
|
||||||
|
"href":"https://example.com/nodeinfo/2.0.json",
|
||||||
|
"rel":"http://nodeinfo.diaspora.software/ns/schema/2.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"href":"https://example.com/nodeinfo/2.1.json",
|
||||||
|
"rel":"http://nodeinfo.diaspora.software/ns/schema/2.1"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## `/nodeinfo/2.0.json`
|
||||||
|
### Nodeinfo 2.0
|
||||||
|
* Method: `GET`
|
||||||
|
* Authentication: not required
|
||||||
|
* Params: none
|
||||||
|
* Response: JSON
|
||||||
|
* Example response:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"metadata":{
|
||||||
|
"accountActivationRequired":false,
|
||||||
|
"features":[
|
||||||
|
"pleroma_api",
|
||||||
|
"mastodon_api",
|
||||||
|
"mastodon_api_streaming",
|
||||||
|
"polls",
|
||||||
|
"pleroma_explicit_addressing",
|
||||||
|
"shareable_emoji_packs",
|
||||||
|
"multifetch",
|
||||||
|
"pleroma:api/v1/notifications:include_types_filter",
|
||||||
|
"chat",
|
||||||
|
"shout",
|
||||||
|
"relay",
|
||||||
|
"pleroma_emoji_reactions",
|
||||||
|
"pleroma_chat_messages"
|
||||||
|
],
|
||||||
|
"federation":{
|
||||||
|
"enabled":true,
|
||||||
|
"exclusions":false,
|
||||||
|
"mrf_hashtag":{
|
||||||
|
"federated_timeline_removal":[
|
||||||
|
|
||||||
|
],
|
||||||
|
"reject":[
|
||||||
|
|
||||||
|
],
|
||||||
|
"sensitive":[
|
||||||
|
"nsfw"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"mrf_object_age":{
|
||||||
|
"actions":[
|
||||||
|
"delist",
|
||||||
|
"strip_followers"
|
||||||
|
],
|
||||||
|
"threshold":604800
|
||||||
|
},
|
||||||
|
"mrf_policies":[
|
||||||
|
"ObjectAgePolicy",
|
||||||
|
"TagPolicy",
|
||||||
|
"HashtagPolicy"
|
||||||
|
],
|
||||||
|
"quarantined_instances":[
|
||||||
|
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"fieldsLimits":{
|
||||||
|
"maxFields":10,
|
||||||
|
"maxRemoteFields":20,
|
||||||
|
"nameLength":512,
|
||||||
|
"valueLength":2048
|
||||||
|
},
|
||||||
|
"invitesEnabled":false,
|
||||||
|
"mailerEnabled":false,
|
||||||
|
"nodeDescription":"Pleroma: An efficient and flexible fediverse server",
|
||||||
|
"nodeName":"Example",
|
||||||
|
"pollLimits":{
|
||||||
|
"max_expiration":31536000,
|
||||||
|
"max_option_chars":200,
|
||||||
|
"max_options":20,
|
||||||
|
"min_expiration":0
|
||||||
|
},
|
||||||
|
"postFormats":[
|
||||||
|
"text/plain",
|
||||||
|
"text/html",
|
||||||
|
"text/markdown",
|
||||||
|
"text/bbcode"
|
||||||
|
],
|
||||||
|
"private":false,
|
||||||
|
"restrictedNicknames":[
|
||||||
|
".well-known",
|
||||||
|
"~",
|
||||||
|
"about",
|
||||||
|
"activities",
|
||||||
|
"api",
|
||||||
|
"auth",
|
||||||
|
"check_password",
|
||||||
|
"dev",
|
||||||
|
"friend-requests",
|
||||||
|
"inbox",
|
||||||
|
"internal",
|
||||||
|
"main",
|
||||||
|
"media",
|
||||||
|
"nodeinfo",
|
||||||
|
"notice",
|
||||||
|
"oauth",
|
||||||
|
"objects",
|
||||||
|
"ostatus_subscribe",
|
||||||
|
"pleroma",
|
||||||
|
"proxy",
|
||||||
|
"push",
|
||||||
|
"registration",
|
||||||
|
"relay",
|
||||||
|
"settings",
|
||||||
|
"status",
|
||||||
|
"tag",
|
||||||
|
"user-search",
|
||||||
|
"user_exists",
|
||||||
|
"users",
|
||||||
|
"web",
|
||||||
|
"verify_credentials",
|
||||||
|
"update_credentials",
|
||||||
|
"relationships",
|
||||||
|
"search",
|
||||||
|
"confirmation_resend",
|
||||||
|
"mfa"
|
||||||
|
],
|
||||||
|
"skipThreadContainment":true,
|
||||||
|
"staffAccounts":[
|
||||||
|
"https://example.com/users/admin",
|
||||||
|
"https://example.com/users/staff"
|
||||||
|
],
|
||||||
|
"suggestions":{
|
||||||
|
"enabled":false
|
||||||
|
},
|
||||||
|
"uploadLimits":{
|
||||||
|
"avatar":2000000,
|
||||||
|
"background":4000000,
|
||||||
|
"banner":4000000,
|
||||||
|
"general":16000000
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"openRegistrations":true,
|
||||||
|
"protocols":[
|
||||||
|
"activitypub"
|
||||||
|
],
|
||||||
|
"services":{
|
||||||
|
"inbound":[
|
||||||
|
|
||||||
|
],
|
||||||
|
"outbound":[
|
||||||
|
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"software":{
|
||||||
|
"name":"pleroma",
|
||||||
|
"version":"2.4.1"
|
||||||
|
},
|
||||||
|
"usage":{
|
||||||
|
"localPosts":27,
|
||||||
|
"users":{
|
||||||
|
"activeHalfyear":129,
|
||||||
|
"activeMonth":70,
|
||||||
|
"total":235
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"version":"2.0"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## `/nodeinfo/2.1.json`
|
||||||
|
### Nodeinfo 2.1
|
||||||
|
* Method: `GET`
|
||||||
|
* Authentication: not required
|
||||||
|
* Params: none
|
||||||
|
* Response: JSON
|
||||||
|
* Example response:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"metadata":{
|
||||||
|
"accountActivationRequired":false,
|
||||||
|
"features":[
|
||||||
|
"pleroma_api",
|
||||||
|
"mastodon_api",
|
||||||
|
"mastodon_api_streaming",
|
||||||
|
"polls",
|
||||||
|
"pleroma_explicit_addressing",
|
||||||
|
"shareable_emoji_packs",
|
||||||
|
"multifetch",
|
||||||
|
"pleroma:api/v1/notifications:include_types_filter",
|
||||||
|
"chat",
|
||||||
|
"shout",
|
||||||
|
"relay",
|
||||||
|
"pleroma_emoji_reactions",
|
||||||
|
"pleroma_chat_messages"
|
||||||
|
],
|
||||||
|
"federation":{
|
||||||
|
"enabled":true,
|
||||||
|
"exclusions":false,
|
||||||
|
"mrf_hashtag":{
|
||||||
|
"federated_timeline_removal":[
|
||||||
|
|
||||||
|
],
|
||||||
|
"reject":[
|
||||||
|
|
||||||
|
],
|
||||||
|
"sensitive":[
|
||||||
|
"nsfw"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"mrf_object_age":{
|
||||||
|
"actions":[
|
||||||
|
"delist",
|
||||||
|
"strip_followers"
|
||||||
|
],
|
||||||
|
"threshold":604800
|
||||||
|
},
|
||||||
|
"mrf_policies":[
|
||||||
|
"ObjectAgePolicy",
|
||||||
|
"TagPolicy",
|
||||||
|
"HashtagPolicy"
|
||||||
|
],
|
||||||
|
"quarantined_instances":[
|
||||||
|
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"fieldsLimits":{
|
||||||
|
"maxFields":10,
|
||||||
|
"maxRemoteFields":20,
|
||||||
|
"nameLength":512,
|
||||||
|
"valueLength":2048
|
||||||
|
},
|
||||||
|
"invitesEnabled":false,
|
||||||
|
"mailerEnabled":false,
|
||||||
|
"nodeDescription":"Pleroma: An efficient and flexible fediverse server",
|
||||||
|
"nodeName":"Example",
|
||||||
|
"pollLimits":{
|
||||||
|
"max_expiration":31536000,
|
||||||
|
"max_option_chars":200,
|
||||||
|
"max_options":20,
|
||||||
|
"min_expiration":0
|
||||||
|
},
|
||||||
|
"postFormats":[
|
||||||
|
"text/plain",
|
||||||
|
"text/html",
|
||||||
|
"text/markdown",
|
||||||
|
"text/bbcode"
|
||||||
|
],
|
||||||
|
"private":false,
|
||||||
|
"restrictedNicknames":[
|
||||||
|
".well-known",
|
||||||
|
"~",
|
||||||
|
"about",
|
||||||
|
"activities",
|
||||||
|
"api",
|
||||||
|
"auth",
|
||||||
|
"check_password",
|
||||||
|
"dev",
|
||||||
|
"friend-requests",
|
||||||
|
"inbox",
|
||||||
|
"internal",
|
||||||
|
"main",
|
||||||
|
"media",
|
||||||
|
"nodeinfo",
|
||||||
|
"notice",
|
||||||
|
"oauth",
|
||||||
|
"objects",
|
||||||
|
"ostatus_subscribe",
|
||||||
|
"pleroma",
|
||||||
|
"proxy",
|
||||||
|
"push",
|
||||||
|
"registration",
|
||||||
|
"relay",
|
||||||
|
"settings",
|
||||||
|
"status",
|
||||||
|
"tag",
|
||||||
|
"user-search",
|
||||||
|
"user_exists",
|
||||||
|
"users",
|
||||||
|
"web",
|
||||||
|
"verify_credentials",
|
||||||
|
"update_credentials",
|
||||||
|
"relationships",
|
||||||
|
"search",
|
||||||
|
"confirmation_resend",
|
||||||
|
"mfa"
|
||||||
|
],
|
||||||
|
"skipThreadContainment":true,
|
||||||
|
"staffAccounts":[
|
||||||
|
"https://example.com/users/admin",
|
||||||
|
"https://example.com/users/staff"
|
||||||
|
],
|
||||||
|
"suggestions":{
|
||||||
|
"enabled":false
|
||||||
|
},
|
||||||
|
"uploadLimits":{
|
||||||
|
"avatar":2000000,
|
||||||
|
"background":4000000,
|
||||||
|
"banner":4000000,
|
||||||
|
"general":16000000
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"openRegistrations":true,
|
||||||
|
"protocols":[
|
||||||
|
"activitypub"
|
||||||
|
],
|
||||||
|
"services":{
|
||||||
|
"inbound":[
|
||||||
|
|
||||||
|
],
|
||||||
|
"outbound":[
|
||||||
|
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"software":{
|
||||||
|
"name":"pleroma",
|
||||||
|
"repository":"https://git.pleroma.social/pleroma/pleroma",
|
||||||
|
"version":"2.4.1"
|
||||||
|
},
|
||||||
|
"usage":{
|
||||||
|
"localPosts":27,
|
||||||
|
"users":{
|
||||||
|
"activeHalfyear":129,
|
||||||
|
"activeMonth":70,
|
||||||
|
"total":235
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"version":"2.1"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
|
@ -159,10 +159,12 @@ See [Admin-API](admin_api.md)
|
||||||
"muting": false,
|
"muting": false,
|
||||||
"muting_notifications": false,
|
"muting_notifications": false,
|
||||||
"subscribing": true,
|
"subscribing": true,
|
||||||
|
"notifying": true,
|
||||||
"requested": false,
|
"requested": false,
|
||||||
"domain_blocking": false,
|
"domain_blocking": false,
|
||||||
"showing_reblogs": true,
|
"showing_reblogs": true,
|
||||||
"endorsed": false
|
"endorsed": false,
|
||||||
|
"note": ""
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -183,10 +185,12 @@ See [Admin-API](admin_api.md)
|
||||||
"muting": false,
|
"muting": false,
|
||||||
"muting_notifications": false,
|
"muting_notifications": false,
|
||||||
"subscribing": false,
|
"subscribing": false,
|
||||||
|
"notifying": false,
|
||||||
"requested": false,
|
"requested": false,
|
||||||
"domain_blocking": false,
|
"domain_blocking": false,
|
||||||
"showing_reblogs": true,
|
"showing_reblogs": true,
|
||||||
"endorsed": false
|
"endorsed": false,
|
||||||
|
"note": ""
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -199,6 +199,7 @@ def run(["gen" | rest]) do
|
||||||
secret = :crypto.strong_rand_bytes(64) |> Base.encode64() |> binary_part(0, 64)
|
secret = :crypto.strong_rand_bytes(64) |> Base.encode64() |> binary_part(0, 64)
|
||||||
jwt_secret = :crypto.strong_rand_bytes(64) |> Base.encode64() |> binary_part(0, 64)
|
jwt_secret = :crypto.strong_rand_bytes(64) |> Base.encode64() |> binary_part(0, 64)
|
||||||
signing_salt = :crypto.strong_rand_bytes(8) |> Base.encode64() |> binary_part(0, 8)
|
signing_salt = :crypto.strong_rand_bytes(8) |> Base.encode64() |> binary_part(0, 8)
|
||||||
|
lv_signing_salt = :crypto.strong_rand_bytes(8) |> Base.encode64() |> binary_part(0, 8)
|
||||||
{web_push_public_key, web_push_private_key} = :crypto.generate_key(:ecdh, :prime256v1)
|
{web_push_public_key, web_push_private_key} = :crypto.generate_key(:ecdh, :prime256v1)
|
||||||
template_dir = Application.app_dir(:pleroma, "priv") <> "/templates"
|
template_dir = Application.app_dir(:pleroma, "priv") <> "/templates"
|
||||||
|
|
||||||
|
@ -217,6 +218,7 @@ def run(["gen" | rest]) do
|
||||||
secret: secret,
|
secret: secret,
|
||||||
jwt_secret: jwt_secret,
|
jwt_secret: jwt_secret,
|
||||||
signing_salt: signing_salt,
|
signing_salt: signing_salt,
|
||||||
|
lv_signing_salt: lv_signing_salt,
|
||||||
web_push_public_key: Base.url_encode64(web_push_public_key, padding: false),
|
web_push_public_key: Base.url_encode64(web_push_public_key, padding: false),
|
||||||
web_push_private_key: Base.url_encode64(web_push_private_key, padding: false),
|
web_push_private_key: Base.url_encode64(web_push_private_key, padding: false),
|
||||||
db_configurable?: db_configurable?,
|
db_configurable?: db_configurable?,
|
||||||
|
|
|
@ -362,11 +362,9 @@ def following_requests_for_actor(%User{ap_id: ap_id}) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def restrict_deactivated_users(query) do
|
def restrict_deactivated_users(query) do
|
||||||
deactivated_users =
|
deactivated_users_query = from(u in User.Query.build(%{deactivated: true}), select: u.ap_id)
|
||||||
from(u in User.Query.build(%{deactivated: true}), select: u.ap_id)
|
|
||||||
|> Repo.all()
|
|
||||||
|
|
||||||
Activity.Queries.exclude_authors(query, deactivated_users)
|
from(activity in query, where: activity.actor not in subquery(deactivated_users_query))
|
||||||
end
|
end
|
||||||
|
|
||||||
defdelegate search(user, query, options \\ []), to: Pleroma.Activity.Search
|
defdelegate search(user, query, options \\ []), to: Pleroma.Activity.Search
|
||||||
|
|
|
@ -61,6 +61,11 @@ def start(_type, _args) do
|
||||||
|
|
||||||
adapter = Application.get_env(:tesla, :adapter)
|
adapter = Application.get_env(:tesla, :adapter)
|
||||||
|
|
||||||
|
if match?({Tesla.Adapter.Finch, _}, adapter) do
|
||||||
|
Logger.info("Starting Finch")
|
||||||
|
Finch.start_link(name: MyFinch)
|
||||||
|
end
|
||||||
|
|
||||||
if adapter == Tesla.Adapter.Gun do
|
if adapter == Tesla.Adapter.Gun do
|
||||||
if version = Pleroma.OTPVersion.version() do
|
if version = Pleroma.OTPVersion.version() do
|
||||||
[major, minor] =
|
[major, minor] =
|
||||||
|
|
|
@ -10,7 +10,8 @@
|
||||||
reblog_mute: 3,
|
reblog_mute: 3,
|
||||||
notification_mute: 4,
|
notification_mute: 4,
|
||||||
inverse_subscription: 5,
|
inverse_subscription: 5,
|
||||||
endorsement: 6
|
suggestion_dismiss: 6,
|
||||||
|
endorsement: 7
|
||||||
)
|
)
|
||||||
|
|
||||||
defenum(Pleroma.FollowingRelationship.State,
|
defenum(Pleroma.FollowingRelationship.State,
|
||||||
|
|
|
@ -103,6 +103,7 @@ defp load_pack(pack_dir, emoji_groups) do
|
||||||
pack_file = Path.join(pack_dir, "pack.json")
|
pack_file = Path.join(pack_dir, "pack.json")
|
||||||
|
|
||||||
if File.exists?(pack_file) do
|
if File.exists?(pack_file) do
|
||||||
|
Logger.info("Loading emoji pack from JSON: #{pack_file}")
|
||||||
contents = Jason.decode!(File.read!(pack_file))
|
contents = Jason.decode!(File.read!(pack_file))
|
||||||
|
|
||||||
contents["files"]
|
contents["files"]
|
||||||
|
@ -115,6 +116,7 @@ defp load_pack(pack_dir, emoji_groups) do
|
||||||
emoji_txt = Path.join(pack_dir, "emoji.txt")
|
emoji_txt = Path.join(pack_dir, "emoji.txt")
|
||||||
|
|
||||||
if File.exists?(emoji_txt) do
|
if File.exists?(emoji_txt) do
|
||||||
|
Logger.info("Loading emoji pack from emoji.txt: #{emoji_txt}")
|
||||||
load_from_file(emoji_txt, emoji_groups)
|
load_from_file(emoji_txt, emoji_groups)
|
||||||
else
|
else
|
||||||
extensions = Config.get([:emoji, :pack_extensions])
|
extensions = Config.get([:emoji, :pack_extensions])
|
||||||
|
|
|
@ -338,6 +338,26 @@ def get_log_entry_message(%ModerationLog{
|
||||||
"@#{actor_nickname} approved users: #{users_to_nicknames_string(users)}"
|
"@#{actor_nickname} approved users: #{users_to_nicknames_string(users)}"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def get_log_entry_message(%ModerationLog{
|
||||||
|
data: %{
|
||||||
|
"actor" => %{"nickname" => actor_nickname},
|
||||||
|
"action" => "add_suggestion",
|
||||||
|
"subject" => users
|
||||||
|
}
|
||||||
|
}) do
|
||||||
|
"@#{actor_nickname} added suggested users: #{users_to_nicknames_string(users)}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_log_entry_message(%ModerationLog{
|
||||||
|
data: %{
|
||||||
|
"actor" => %{"nickname" => actor_nickname},
|
||||||
|
"action" => "remove_suggestion",
|
||||||
|
"subject" => users
|
||||||
|
}
|
||||||
|
}) do
|
||||||
|
"@#{actor_nickname} removed suggested users: #{users_to_nicknames_string(users)}"
|
||||||
|
end
|
||||||
|
|
||||||
def get_log_entry_message(%ModerationLog{
|
def get_log_entry_message(%ModerationLog{
|
||||||
data: %{
|
data: %{
|
||||||
"actor" => %{"nickname" => actor_nickname},
|
"actor" => %{"nickname" => actor_nickname},
|
||||||
|
|
|
@ -94,7 +94,8 @@ defp cast_params(params) do
|
||||||
offset: :integer,
|
offset: :integer,
|
||||||
limit: :integer,
|
limit: :integer,
|
||||||
skip_extra_order: :boolean,
|
skip_extra_order: :boolean,
|
||||||
skip_order: :boolean
|
skip_order: :boolean,
|
||||||
|
shuffle: :boolean,
|
||||||
}
|
}
|
||||||
|
|
||||||
changeset = cast({%{}, param_types}, params, Map.keys(param_types))
|
changeset = cast({%{}, param_types}, params, Map.keys(param_types))
|
||||||
|
@ -113,6 +114,10 @@ defp restrict(query, :max_id, %{max_id: max_id}, table_binding) do
|
||||||
where(query, [{q, table_position(query, table_binding)}], q.id < ^max_id)
|
where(query, [{q, table_position(query, table_binding)}], q.id < ^max_id)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp restrict(query, :order, %{shuffle: true}, _) do
|
||||||
|
order_by(query, [u], fragment("RANDOM()"))
|
||||||
|
end
|
||||||
|
|
||||||
defp restrict(query, :order, %{skip_order: true}, _), do: query
|
defp restrict(query, :order, %{skip_order: true}, _), do: query
|
||||||
|
|
||||||
defp restrict(%{order_bys: [_ | _]} = query, :order, %{skip_extra_order: true}, _), do: query
|
defp restrict(%{order_bys: [_ | _]} = query, :order, %{skip_extra_order: true}, _), do: query
|
||||||
|
|
|
@ -25,5 +25,6 @@ defp client do
|
||||||
|
|
||||||
defp client(Tesla.Adapter.Hackney), do: Pleroma.ReverseProxy.Client.Hackney
|
defp client(Tesla.Adapter.Hackney), do: Pleroma.ReverseProxy.Client.Hackney
|
||||||
defp client(Tesla.Adapter.Gun), do: Pleroma.ReverseProxy.Client.Tesla
|
defp client(Tesla.Adapter.Gun), do: Pleroma.ReverseProxy.Client.Tesla
|
||||||
|
defp client({Tesla.Adapter.Finch, _}), do: Pleroma.ReverseProxy.Client.Hackney
|
||||||
defp client(_), do: Pleroma.Config.get!(Pleroma.ReverseProxy.Client)
|
defp client(_), do: Pleroma.Config.get!(Pleroma.ReverseProxy.Client)
|
||||||
end
|
end
|
||||||
|
|
|
@ -12,10 +12,16 @@ defmodule Pleroma.Telemetry.Logger do
|
||||||
[:pleroma, :connection_pool, :reclaim, :stop],
|
[:pleroma, :connection_pool, :reclaim, :stop],
|
||||||
[:pleroma, :connection_pool, :provision_failure],
|
[:pleroma, :connection_pool, :provision_failure],
|
||||||
[:pleroma, :connection_pool, :client, :dead],
|
[:pleroma, :connection_pool, :client, :dead],
|
||||||
[:pleroma, :connection_pool, :client, :add]
|
[:pleroma, :connection_pool, :client, :add],
|
||||||
|
[:pleroma, :repo, :query]
|
||||||
]
|
]
|
||||||
def attach do
|
def attach do
|
||||||
:telemetry.attach_many("pleroma-logger", @events, &handle_event/4, [])
|
:telemetry.attach_many(
|
||||||
|
"pleroma-logger",
|
||||||
|
@events,
|
||||||
|
&Pleroma.Telemetry.Logger.handle_event/4,
|
||||||
|
[]
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Passing anonymous functions instead of strings to logger is intentional,
|
# Passing anonymous functions instead of strings to logger is intentional,
|
||||||
|
@ -87,4 +93,64 @@ def handle_event(
|
||||||
end
|
end
|
||||||
|
|
||||||
def handle_event([:pleroma, :connection_pool, :client, :add], _, _, _), do: :ok
|
def handle_event([:pleroma, :connection_pool, :client, :add], _, _, _), do: :ok
|
||||||
|
|
||||||
|
def handle_event(
|
||||||
|
[:pleroma, :repo, :query] = _name,
|
||||||
|
%{query_time: query_time} = measurements,
|
||||||
|
%{source: source} = metadata,
|
||||||
|
config
|
||||||
|
) do
|
||||||
|
logging_config = Pleroma.Config.get([:telemetry, :slow_queries_logging], [])
|
||||||
|
|
||||||
|
if logging_config[:enabled] &&
|
||||||
|
logging_config[:min_duration] &&
|
||||||
|
query_time > logging_config[:min_duration] and
|
||||||
|
(is_nil(logging_config[:exclude_sources]) or
|
||||||
|
source not in logging_config[:exclude_sources]) do
|
||||||
|
log_slow_query(measurements, metadata, config)
|
||||||
|
else
|
||||||
|
:ok
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp log_slow_query(
|
||||||
|
%{query_time: query_time} = _measurements,
|
||||||
|
%{source: _source, query: query, params: query_params, repo: repo} = _metadata,
|
||||||
|
_config
|
||||||
|
) do
|
||||||
|
sql_explain =
|
||||||
|
with {:ok, %{rows: explain_result_rows}} <-
|
||||||
|
repo.query("EXPLAIN " <> query, query_params, log: false) do
|
||||||
|
Enum.map_join(explain_result_rows, "\n", & &1)
|
||||||
|
end
|
||||||
|
|
||||||
|
{:current_stacktrace, stacktrace} = Process.info(self(), :current_stacktrace)
|
||||||
|
|
||||||
|
pleroma_stacktrace =
|
||||||
|
Enum.filter(stacktrace, fn
|
||||||
|
{__MODULE__, _, _, _} ->
|
||||||
|
false
|
||||||
|
|
||||||
|
{mod, _, _, _} ->
|
||||||
|
mod
|
||||||
|
|> to_string()
|
||||||
|
|> String.starts_with?("Elixir.Pleroma.")
|
||||||
|
end)
|
||||||
|
|
||||||
|
Logger.warn(fn ->
|
||||||
|
"""
|
||||||
|
Slow query!
|
||||||
|
|
||||||
|
Total time: #{round(query_time / 1_000)} ms
|
||||||
|
|
||||||
|
#{query}
|
||||||
|
|
||||||
|
#{inspect(query_params, limit: :infinity)}
|
||||||
|
|
||||||
|
#{sql_explain}
|
||||||
|
|
||||||
|
#{Exception.format_stacktrace(pleroma_stacktrace)}
|
||||||
|
"""
|
||||||
|
end)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -152,6 +152,8 @@ defmodule Pleroma.User do
|
||||||
field(:last_active_at, :naive_datetime)
|
field(:last_active_at, :naive_datetime)
|
||||||
field(:disclose_client, :boolean, default: true)
|
field(:disclose_client, :boolean, default: true)
|
||||||
field(:pinned_objects, :map, default: %{})
|
field(:pinned_objects, :map, default: %{})
|
||||||
|
field(:is_suggested, :boolean, default: false)
|
||||||
|
field(:last_status_at, :naive_datetime)
|
||||||
|
|
||||||
embeds_one(
|
embeds_one(
|
||||||
:notification_settings,
|
:notification_settings,
|
||||||
|
@ -1709,6 +1711,22 @@ def confirm(%User{is_confirmed: false} = user) do
|
||||||
|
|
||||||
def confirm(%User{} = user), do: {:ok, user}
|
def confirm(%User{} = user), do: {:ok, user}
|
||||||
|
|
||||||
|
def set_suggestion(users, is_suggested) when is_list(users) do
|
||||||
|
Repo.transaction(fn ->
|
||||||
|
Enum.map(users, fn user ->
|
||||||
|
with {:ok, user} <- set_suggestion(user, is_suggested), do: user
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_suggestion(%User{is_suggested: is_suggested} = user, is_suggested), do: {:ok, user}
|
||||||
|
|
||||||
|
def set_suggestion(%User{} = user, is_suggested) when is_boolean(is_suggested) do
|
||||||
|
user
|
||||||
|
|> change(is_suggested: is_suggested)
|
||||||
|
|> update_and_set_cache()
|
||||||
|
end
|
||||||
|
|
||||||
def update_notification_settings(%User{} = user, settings) do
|
def update_notification_settings(%User{} = user, settings) do
|
||||||
user
|
user
|
||||||
|> cast(%{notification_settings: settings}, [])
|
|> cast(%{notification_settings: settings}, [])
|
||||||
|
@ -2507,12 +2525,24 @@ def update_last_active_at(%__MODULE__{local: true} = user) do
|
||||||
|> update_and_set_cache()
|
|> update_and_set_cache()
|
||||||
end
|
end
|
||||||
|
|
||||||
def active_user_count(weeks \\ 4) do
|
def active_user_count(days \\ 30) do
|
||||||
active_after = Timex.shift(NaiveDateTime.utc_now(), weeks: -weeks)
|
active_after = Timex.shift(NaiveDateTime.utc_now(), days: -days)
|
||||||
|
|
||||||
__MODULE__
|
__MODULE__
|
||||||
|> where([u], u.last_active_at >= ^active_after)
|
|> where([u], u.last_active_at >= ^active_after)
|
||||||
|> where([u], u.local == true)
|
|> where([u], u.local == true)
|
||||||
|> Repo.aggregate(:count)
|
|> Repo.aggregate(:count)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def update_last_status_at(user) do
|
||||||
|
User
|
||||||
|
|> where(id: ^user.id)
|
||||||
|
|> update([u], set: [last_status_at: fragment("NOW()")])
|
||||||
|
|> select([u], u)
|
||||||
|
|> Repo.update_all([])
|
||||||
|
|> case do
|
||||||
|
{1, [user]} -> set_cache(user)
|
||||||
|
_ -> {:error, user}
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -46,6 +46,8 @@ defmodule Pleroma.User.Query do
|
||||||
unconfirmed: boolean(),
|
unconfirmed: boolean(),
|
||||||
is_admin: boolean(),
|
is_admin: boolean(),
|
||||||
is_moderator: boolean(),
|
is_moderator: boolean(),
|
||||||
|
is_suggested: boolean(),
|
||||||
|
is_discoverable: boolean(),
|
||||||
super_users: boolean(),
|
super_users: boolean(),
|
||||||
invisible: boolean(),
|
invisible: boolean(),
|
||||||
internal: boolean(),
|
internal: boolean(),
|
||||||
|
@ -167,6 +169,14 @@ defp compose_query({:unconfirmed, _}, query) do
|
||||||
where(query, [u], u.is_confirmed == false)
|
where(query, [u], u.is_confirmed == false)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp compose_query({:is_suggested, bool}, query) do
|
||||||
|
where(query, [u], u.is_suggested == ^bool)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp compose_query({:is_discoverable, bool}, query) do
|
||||||
|
where(query, [u], u.is_discoverable == ^bool)
|
||||||
|
end
|
||||||
|
|
||||||
defp compose_query({:followers, %User{id: id}}, query) do
|
defp compose_query({:followers, %User{id: id}}, query) do
|
||||||
query
|
query
|
||||||
|> where([u], u.id != ^id)
|
|> where([u], u.id != ^id)
|
||||||
|
|
52
lib/pleroma/user_note.ex
Normal file
52
lib/pleroma/user_note.ex
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.UserNote do
|
||||||
|
use Ecto.Schema
|
||||||
|
|
||||||
|
import Ecto.Changeset
|
||||||
|
import Ecto.Query
|
||||||
|
|
||||||
|
alias Pleroma.Repo
|
||||||
|
alias Pleroma.User
|
||||||
|
alias Pleroma.UserNote
|
||||||
|
|
||||||
|
schema "user_notes" do
|
||||||
|
belongs_to(:source, User, type: FlakeId.Ecto.CompatType)
|
||||||
|
belongs_to(:target, User, type: FlakeId.Ecto.CompatType)
|
||||||
|
field(:comment, :string)
|
||||||
|
|
||||||
|
timestamps()
|
||||||
|
end
|
||||||
|
|
||||||
|
def changeset(%UserNote{} = user_note, params \\ %{}) do
|
||||||
|
user_note
|
||||||
|
|> cast(params, [:source_id, :target_id, :comment])
|
||||||
|
|> validate_required([:source_id, :target_id])
|
||||||
|
end
|
||||||
|
|
||||||
|
def show(%User{} = source, %User{} = target) do
|
||||||
|
with %UserNote{} = note <-
|
||||||
|
UserNote
|
||||||
|
|> where(source_id: ^source.id, target_id: ^target.id)
|
||||||
|
|> Repo.one() do
|
||||||
|
note.comment
|
||||||
|
else
|
||||||
|
_ -> ""
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def create(%User{} = source, %User{} = target, comment) do
|
||||||
|
%UserNote{}
|
||||||
|
|> changeset(%{
|
||||||
|
source_id: source.id,
|
||||||
|
target_id: target.id,
|
||||||
|
comment: comment
|
||||||
|
})
|
||||||
|
|> Repo.insert(
|
||||||
|
on_conflict: {:replace, [:comment]},
|
||||||
|
conflict_target: [:source_id, :target_id]
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
|
@ -81,6 +81,10 @@ def decrease_note_count_if_public(actor, object) do
|
||||||
if is_public?(object), do: User.decrease_note_count(actor), else: {:ok, actor}
|
if is_public?(object), do: User.decrease_note_count(actor), else: {:ok, actor}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def update_last_status_at_if_public(actor, object) do
|
||||||
|
if is_public?(object), do: User.update_last_status_at(actor), else: {:ok, actor}
|
||||||
|
end
|
||||||
|
|
||||||
defp increase_replies_count_if_reply(%{
|
defp increase_replies_count_if_reply(%{
|
||||||
"object" => %{"inReplyTo" => reply_ap_id} = object,
|
"object" => %{"inReplyTo" => reply_ap_id} = object,
|
||||||
"type" => "Create"
|
"type" => "Create"
|
||||||
|
@ -288,6 +292,7 @@ defp do_create(%{to: to, actor: actor, context: context, object: object} = param
|
||||||
_ <- increase_replies_count_if_reply(create_data),
|
_ <- increase_replies_count_if_reply(create_data),
|
||||||
{:quick_insert, false, activity} <- {:quick_insert, quick_insert?, activity},
|
{:quick_insert, false, activity} <- {:quick_insert, quick_insert?, activity},
|
||||||
{:ok, _actor} <- increase_note_count_if_public(actor, activity),
|
{:ok, _actor} <- increase_note_count_if_public(actor, activity),
|
||||||
|
{:ok, _actor} <- update_last_status_at_if_public(actor, activity),
|
||||||
_ <- notify_and_stream(activity),
|
_ <- notify_and_stream(activity),
|
||||||
:ok <- maybe_schedule_poll_notifications(activity),
|
:ok <- maybe_schedule_poll_notifications(activity),
|
||||||
:ok <- maybe_federate(activity) do
|
:ok <- maybe_federate(activity) do
|
||||||
|
|
|
@ -68,12 +68,14 @@ def fix_media_type(data) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp handle_href(href, mediaType) do
|
defp handle_href(href, mediaType, data) do
|
||||||
[
|
[
|
||||||
%{
|
%{
|
||||||
"href" => href,
|
"href" => href,
|
||||||
"type" => "Link",
|
"type" => "Link",
|
||||||
"mediaType" => mediaType
|
"mediaType" => mediaType,
|
||||||
|
"width" => data["width"],
|
||||||
|
"height" => data["height"]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
@ -81,10 +83,10 @@ defp handle_href(href, mediaType) do
|
||||||
defp fix_url(data) do
|
defp fix_url(data) do
|
||||||
cond do
|
cond do
|
||||||
is_binary(data["url"]) ->
|
is_binary(data["url"]) ->
|
||||||
Map.put(data, "url", handle_href(data["url"], data["mediaType"]))
|
Map.put(data, "url", handle_href(data["url"], data["mediaType"], data))
|
||||||
|
|
||||||
is_binary(data["href"]) and data["url"] == nil ->
|
is_binary(data["href"]) and data["url"] == nil ->
|
||||||
Map.put(data, "url", handle_href(data["href"], data["mediaType"]))
|
Map.put(data, "url", handle_href(data["href"], data["mediaType"], data))
|
||||||
|
|
||||||
true ->
|
true ->
|
||||||
data
|
data
|
||||||
|
|
|
@ -63,18 +63,17 @@ def publish_one(%{inbox: inbox, json: json, actor: %User{} = actor, id: id} = pa
|
||||||
date: date
|
date: date
|
||||||
})
|
})
|
||||||
|
|
||||||
with {:ok, %{status: code}} when code in 200..299 <-
|
with {:ok, %{status: code}} = result when code in 200..299 <-
|
||||||
result =
|
HTTP.post(
|
||||||
HTTP.post(
|
inbox,
|
||||||
inbox,
|
json,
|
||||||
json,
|
[
|
||||||
[
|
{"Content-Type", "application/activity+json"},
|
||||||
{"Content-Type", "application/activity+json"},
|
{"Date", date},
|
||||||
{"Date", date},
|
{"signature", signature},
|
||||||
{"signature", signature},
|
{"digest", digest}
|
||||||
{"digest", digest}
|
]
|
||||||
]
|
) do
|
||||||
) do
|
|
||||||
if not Map.has_key?(params, :unreachable_since) || params[:unreachable_since] do
|
if not Map.has_key?(params, :unreachable_since) || params[:unreachable_since] do
|
||||||
Instances.set_reachable(inbox)
|
Instances.set_reachable(inbox)
|
||||||
end
|
end
|
||||||
|
|
|
@ -199,8 +199,9 @@ def handle(%{data: %{"type" => "Create"}} = activity, meta) do
|
||||||
%User{} = user <- User.get_cached_by_ap_id(activity.data["actor"]) do
|
%User{} = user <- User.get_cached_by_ap_id(activity.data["actor"]) do
|
||||||
{:ok, notifications} = Notification.create_notifications(activity, do_send: false)
|
{:ok, notifications} = Notification.create_notifications(activity, do_send: false)
|
||||||
{:ok, _user} = ActivityPub.increase_note_count_if_public(user, object)
|
{:ok, _user} = ActivityPub.increase_note_count_if_public(user, object)
|
||||||
|
{:ok, _user} = ActivityPub.update_last_status_at_if_public(user, object)
|
||||||
|
|
||||||
if in_reply_to = object.data["inReplyTo"] && object.data["type"] != "Answer" do
|
if in_reply_to = object.data["type"] != "Answer" && object.data["inReplyTo"] do
|
||||||
Object.increase_replies_count(in_reply_to)
|
Object.increase_replies_count(in_reply_to)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -446,7 +446,7 @@ def update_follow_state_for_all(
|
||||||
|> Activity.Queries.by_type()
|
|> Activity.Queries.by_type()
|
||||||
|> Activity.Queries.by_actor(actor)
|
|> Activity.Queries.by_actor(actor)
|
||||||
|> Activity.Queries.by_object_id(object)
|
|> Activity.Queries.by_object_id(object)
|
||||||
|> where(fragment("data->>'state' = 'pending'"))
|
|> where(fragment("data->>'state' = 'pending'") or fragment("data->>'state' = 'accept'"))
|
||||||
|> update(set: [data: fragment("jsonb_set(data, '{state}', ?)", ^state)])
|
|> update(set: [data: fragment("jsonb_set(data, '{state}', ?)", ^state)])
|
||||||
|> Repo.update_all([])
|
|> Repo.update_all([])
|
||||||
|
|
||||||
|
|
|
@ -35,7 +35,9 @@ defmodule Pleroma.Web.AdminAPI.UserController do
|
||||||
:toggle_activation,
|
:toggle_activation,
|
||||||
:activate,
|
:activate,
|
||||||
:deactivate,
|
:deactivate,
|
||||||
:approve
|
:approve,
|
||||||
|
:suggest,
|
||||||
|
:unsuggest
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -239,6 +241,32 @@ def approve(%{assigns: %{user: admin}, body_params: %{nicknames: nicknames}} = c
|
||||||
render(conn, "index.json", users: updated_users)
|
render(conn, "index.json", users: updated_users)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def suggest(%{assigns: %{user: admin}, body_params: %{nicknames: nicknames}} = conn, _) do
|
||||||
|
users = Enum.map(nicknames, &User.get_cached_by_nickname/1)
|
||||||
|
{:ok, updated_users} = User.set_suggestion(users, true)
|
||||||
|
|
||||||
|
ModerationLog.insert_log(%{
|
||||||
|
actor: admin,
|
||||||
|
subject: users,
|
||||||
|
action: "add_suggestion"
|
||||||
|
})
|
||||||
|
|
||||||
|
render(conn, "index.json", users: updated_users)
|
||||||
|
end
|
||||||
|
|
||||||
|
def unsuggest(%{assigns: %{user: admin}, body_params: %{nicknames: nicknames}} = conn, _) do
|
||||||
|
users = Enum.map(nicknames, &User.get_cached_by_nickname/1)
|
||||||
|
{:ok, updated_users} = User.set_suggestion(users, false)
|
||||||
|
|
||||||
|
ModerationLog.insert_log(%{
|
||||||
|
actor: admin,
|
||||||
|
subject: users,
|
||||||
|
action: "remove_suggestion"
|
||||||
|
})
|
||||||
|
|
||||||
|
render(conn, "index.json", users: updated_users)
|
||||||
|
end
|
||||||
|
|
||||||
def index(conn, params) do
|
def index(conn, params) do
|
||||||
{page, page_size} = page_params(params)
|
{page, page_size} = page_params(params)
|
||||||
filters = maybe_parse_filters(params[:filters])
|
filters = maybe_parse_filters(params[:filters])
|
||||||
|
|
|
@ -80,6 +80,7 @@ def render("show.json", %{user: user}) do
|
||||||
"tags" => user.tags || [],
|
"tags" => user.tags || [],
|
||||||
"is_confirmed" => user.is_confirmed,
|
"is_confirmed" => user.is_confirmed,
|
||||||
"is_approved" => user.is_approved,
|
"is_approved" => user.is_approved,
|
||||||
|
"is_suggested" => user.is_suggested,
|
||||||
"url" => user.uri || user.ap_id,
|
"url" => user.uri || user.ap_id,
|
||||||
"registration_reason" => user.registration_reason,
|
"registration_reason" => user.registration_reason,
|
||||||
"actor_type" => user.actor_type,
|
"actor_type" => user.actor_type,
|
||||||
|
|
|
@ -226,6 +226,12 @@ def follow_operation do
|
||||||
type: :boolean,
|
type: :boolean,
|
||||||
description: "Receive this account's reblogs in home timeline? Defaults to true.",
|
description: "Receive this account's reblogs in home timeline? Defaults to true.",
|
||||||
default: true
|
default: true
|
||||||
|
},
|
||||||
|
notify: %Schema{
|
||||||
|
type: :boolean,
|
||||||
|
description:
|
||||||
|
"Receive notifications for all statuses posted by the account? Defaults to false.",
|
||||||
|
default: false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -334,8 +340,7 @@ def endorse_operation do
|
||||||
summary: "Endorse",
|
summary: "Endorse",
|
||||||
operationId: "AccountController.endorse",
|
operationId: "AccountController.endorse",
|
||||||
security: [%{"oAuth" => ["follow", "write:accounts"]}],
|
security: [%{"oAuth" => ["follow", "write:accounts"]}],
|
||||||
description:
|
description: "Addds the given account to endorsed accounts list.",
|
||||||
"Addds the given account to endorsed accounts list.",
|
|
||||||
parameters: [%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}],
|
parameters: [%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}],
|
||||||
responses: %{
|
responses: %{
|
||||||
200 => Operation.response("Relationship", "application/json", AccountRelationship)
|
200 => Operation.response("Relationship", "application/json", AccountRelationship)
|
||||||
|
@ -357,6 +362,29 @@ def unendorse_operation do
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def note_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Account actions"],
|
||||||
|
summary: "Set a private note about a user.",
|
||||||
|
operationId: "AccountController.note",
|
||||||
|
security: [%{"oAuth" => ["follow", "write:accounts"]}],
|
||||||
|
requestBody: request_body("Parameters", note_request()),
|
||||||
|
description: "Create a note for the given account.",
|
||||||
|
parameters: [
|
||||||
|
%Reference{"$ref": "#/components/parameters/accountIdOrNickname"},
|
||||||
|
Operation.parameter(
|
||||||
|
:comment,
|
||||||
|
:query,
|
||||||
|
%Schema{type: :string},
|
||||||
|
"Account note body"
|
||||||
|
)
|
||||||
|
],
|
||||||
|
responses: %{
|
||||||
|
200 => Operation.response("Relationship", "application/json", AccountRelationship)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
def follow_by_uri_operation do
|
def follow_by_uri_operation do
|
||||||
%Operation{
|
%Operation{
|
||||||
tags: ["Account actions"],
|
tags: ["Account actions"],
|
||||||
|
@ -714,9 +742,11 @@ defp array_of_relationships do
|
||||||
"blocked_by" => true,
|
"blocked_by" => true,
|
||||||
"muting" => false,
|
"muting" => false,
|
||||||
"muting_notifications" => false,
|
"muting_notifications" => false,
|
||||||
|
"note" => "",
|
||||||
"requested" => false,
|
"requested" => false,
|
||||||
"domain_blocking" => false,
|
"domain_blocking" => false,
|
||||||
"subscribing" => false,
|
"subscribing" => false,
|
||||||
|
"notifying" => false,
|
||||||
"endorsed" => true
|
"endorsed" => true
|
||||||
},
|
},
|
||||||
%{
|
%{
|
||||||
|
@ -728,9 +758,11 @@ defp array_of_relationships do
|
||||||
"blocked_by" => true,
|
"blocked_by" => true,
|
||||||
"muting" => true,
|
"muting" => true,
|
||||||
"muting_notifications" => false,
|
"muting_notifications" => false,
|
||||||
|
"note" => "",
|
||||||
"requested" => true,
|
"requested" => true,
|
||||||
"domain_blocking" => false,
|
"domain_blocking" => false,
|
||||||
"subscribing" => false,
|
"subscribing" => false,
|
||||||
|
"notifying" => false,
|
||||||
"endorsed" => false
|
"endorsed" => false
|
||||||
},
|
},
|
||||||
%{
|
%{
|
||||||
|
@ -742,9 +774,11 @@ defp array_of_relationships do
|
||||||
"blocked_by" => false,
|
"blocked_by" => false,
|
||||||
"muting" => true,
|
"muting" => true,
|
||||||
"muting_notifications" => false,
|
"muting_notifications" => false,
|
||||||
|
"note" => "",
|
||||||
"requested" => false,
|
"requested" => false,
|
||||||
"domain_blocking" => true,
|
"domain_blocking" => true,
|
||||||
"subscribing" => true,
|
"subscribing" => true,
|
||||||
|
"notifying" => true,
|
||||||
"endorsed" => false
|
"endorsed" => false
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -789,6 +823,23 @@ defp mute_request do
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp note_request do
|
||||||
|
%Schema{
|
||||||
|
title: "AccountNoteRequest",
|
||||||
|
description: "POST body for adding a note for an account",
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
comment: %Schema{
|
||||||
|
type: :string,
|
||||||
|
description: "Account note body"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
example: %{
|
||||||
|
"comment" => "Example note"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
defp array_of_lists do
|
defp array_of_lists do
|
||||||
%Schema{
|
%Schema{
|
||||||
title: "ArrayOfLists",
|
title: "ArrayOfLists",
|
||||||
|
|
|
@ -216,7 +216,71 @@ def approve_operation do
|
||||||
request_body(
|
request_body(
|
||||||
"Parameters",
|
"Parameters",
|
||||||
%Schema{
|
%Schema{
|
||||||
description: "POST body for deleting multiple users",
|
description: "POST body for approving multiple users",
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
nicknames: %Schema{
|
||||||
|
type: :array,
|
||||||
|
items: %Schema{type: :string}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
),
|
||||||
|
responses: %{
|
||||||
|
200 =>
|
||||||
|
Operation.response("Response", "application/json", %Schema{
|
||||||
|
type: :object,
|
||||||
|
properties: %{user: %Schema{type: :array, items: user()}}
|
||||||
|
}),
|
||||||
|
403 => Operation.response("Forbidden", "application/json", ApiError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def suggest_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["User administration"],
|
||||||
|
summary: "Suggest multiple users",
|
||||||
|
operationId: "AdminAPI.UserController.suggest",
|
||||||
|
security: [%{"oAuth" => ["admin:write:accounts"]}],
|
||||||
|
parameters: admin_api_params(),
|
||||||
|
requestBody:
|
||||||
|
request_body(
|
||||||
|
"Parameters",
|
||||||
|
%Schema{
|
||||||
|
description: "POST body for adding multiple suggested users",
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
nicknames: %Schema{
|
||||||
|
type: :array,
|
||||||
|
items: %Schema{type: :string}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
),
|
||||||
|
responses: %{
|
||||||
|
200 =>
|
||||||
|
Operation.response("Response", "application/json", %Schema{
|
||||||
|
type: :object,
|
||||||
|
properties: %{user: %Schema{type: :array, items: user()}}
|
||||||
|
}),
|
||||||
|
403 => Operation.response("Forbidden", "application/json", ApiError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def unsuggest_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["User administration"],
|
||||||
|
summary: "Unsuggest multiple users",
|
||||||
|
operationId: "AdminAPI.UserController.unsuggest",
|
||||||
|
security: [%{"oAuth" => ["admin:write:accounts"]}],
|
||||||
|
parameters: admin_api_params(),
|
||||||
|
requestBody:
|
||||||
|
request_body(
|
||||||
|
"Parameters",
|
||||||
|
%Schema{
|
||||||
|
description: "POST body for removing multiple suggested users",
|
||||||
type: :object,
|
type: :object,
|
||||||
properties: %{
|
properties: %{
|
||||||
nicknames: %Schema{
|
nicknames: %Schema{
|
||||||
|
|
|
@ -6,6 +6,7 @@ defmodule Pleroma.Web.ApiSpec.AppOperation do
|
||||||
alias OpenApiSpex.Operation
|
alias OpenApiSpex.Operation
|
||||||
alias OpenApiSpex.Schema
|
alias OpenApiSpex.Schema
|
||||||
alias Pleroma.Web.ApiSpec.Helpers
|
alias Pleroma.Web.ApiSpec.Helpers
|
||||||
|
alias Pleroma.Web.ApiSpec.Schemas.App
|
||||||
|
|
||||||
@spec open_api_operation(atom) :: Operation.t()
|
@spec open_api_operation(atom) :: Operation.t()
|
||||||
def open_api_operation(action) do
|
def open_api_operation(action) do
|
||||||
|
@ -22,7 +23,7 @@ def create_operation do
|
||||||
operationId: "AppController.create",
|
operationId: "AppController.create",
|
||||||
requestBody: Helpers.request_body("Parameters", create_request(), required: true),
|
requestBody: Helpers.request_body("Parameters", create_request(), required: true),
|
||||||
responses: %{
|
responses: %{
|
||||||
200 => Operation.response("App", "application/json", create_response()),
|
200 => Operation.response("App", "application/json", App),
|
||||||
422 =>
|
422 =>
|
||||||
Operation.response(
|
Operation.response(
|
||||||
"Unprocessable Entity",
|
"Unprocessable Entity",
|
||||||
|
@ -119,30 +120,4 @@ defp create_request do
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
defp create_response do
|
|
||||||
%Schema{
|
|
||||||
title: "AppCreateResponse",
|
|
||||||
description: "Response schema for an app",
|
|
||||||
type: :object,
|
|
||||||
properties: %{
|
|
||||||
id: %Schema{type: :string},
|
|
||||||
name: %Schema{type: :string},
|
|
||||||
client_id: %Schema{type: :string},
|
|
||||||
client_secret: %Schema{type: :string},
|
|
||||||
redirect_uri: %Schema{type: :string},
|
|
||||||
vapid_key: %Schema{type: :string},
|
|
||||||
website: %Schema{type: :string, nullable: true}
|
|
||||||
},
|
|
||||||
example: %{
|
|
||||||
"id" => "123",
|
|
||||||
"name" => "My App",
|
|
||||||
"client_id" => "TWhM-tNSuncnqN7DBJmoyeLnk6K3iJJ71KKXxgL1hPM",
|
|
||||||
"client_secret" => "ZEaFUFmF0umgBX1qKJDjaU99Q31lDkOU8NutzTOoliw",
|
|
||||||
"vapid_key" =>
|
|
||||||
"BCk-QqERU0q-CfYZjcuB6lnyyOYfJ2AifKqfeGIm7Z-HiTU5T9eTG5GxVA0_OH5mMlI4UkkDTpaZwozy0TzdZ2M=",
|
|
||||||
"website" => "https://myapp.com/"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
41
lib/pleroma/web/api_spec/operations/directory_operation.ex
Normal file
41
lib/pleroma/web/api_spec/operations/directory_operation.ex
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.ApiSpec.DirectoryOperation do
|
||||||
|
alias OpenApiSpex.Operation
|
||||||
|
alias Pleroma.Web.ApiSpec.AccountOperation
|
||||||
|
alias Pleroma.Web.ApiSpec.Schemas.ApiError
|
||||||
|
alias Pleroma.Web.ApiSpec.Schemas.BooleanLike
|
||||||
|
|
||||||
|
import Pleroma.Web.ApiSpec.Helpers
|
||||||
|
|
||||||
|
def open_api_operation(action) do
|
||||||
|
operation = String.to_existing_atom("#{action}_operation")
|
||||||
|
apply(__MODULE__, operation, [])
|
||||||
|
end
|
||||||
|
|
||||||
|
def index_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Directory"],
|
||||||
|
summary: "Profile directory",
|
||||||
|
operationId: "DirectoryController.index",
|
||||||
|
parameters:
|
||||||
|
[
|
||||||
|
Operation.parameter(
|
||||||
|
:order,
|
||||||
|
:query,
|
||||||
|
:string,
|
||||||
|
"Order by recent activity or account creation",
|
||||||
|
required: nil
|
||||||
|
),
|
||||||
|
Operation.parameter(:local, :query, BooleanLike, "Include local users only")
|
||||||
|
] ++ pagination_params(),
|
||||||
|
responses: %{
|
||||||
|
200 =>
|
||||||
|
Operation.response("Accounts", "application/json", AccountOperation.array_of_accounts()),
|
||||||
|
404 => Operation.response("Not Found", "application/json", ApiError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
|
@ -69,7 +69,16 @@ def endorsements_operation do
|
||||||
summary: "Endorsements",
|
summary: "Endorsements",
|
||||||
description: "Returns endorsed accounts",
|
description: "Returns endorsed accounts",
|
||||||
operationId: "PleromaAPI.AccountController.endorsements",
|
operationId: "PleromaAPI.AccountController.endorsements",
|
||||||
parameters: [id_param() | pagination_params()],
|
parameters:
|
||||||
|
[
|
||||||
|
Operation.parameter(
|
||||||
|
:shuffle,
|
||||||
|
:query,
|
||||||
|
:boolean,
|
||||||
|
"Show endorsed accounts in random order"
|
||||||
|
),
|
||||||
|
id_param()
|
||||||
|
] ++ pagination_params(),
|
||||||
security: [%{"oAuth" => ["read:account"]}],
|
security: [%{"oAuth" => ["read:account"]}],
|
||||||
responses: %{
|
responses: %{
|
||||||
200 =>
|
200 =>
|
||||||
|
|
31
lib/pleroma/web/api_spec/operations/pleroma_app_operation.ex
Normal file
31
lib/pleroma/web/api_spec/operations/pleroma_app_operation.ex
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.ApiSpec.PleromaAppOperation do
|
||||||
|
alias OpenApiSpex.Operation
|
||||||
|
alias OpenApiSpex.Schema
|
||||||
|
alias Pleroma.Web.ApiSpec.Schemas.App
|
||||||
|
|
||||||
|
def open_api_operation(action) do
|
||||||
|
operation = String.to_existing_atom("#{action}_operation")
|
||||||
|
apply(__MODULE__, operation, [])
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec index_operation() :: Operation.t()
|
||||||
|
def index_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Applications"],
|
||||||
|
summary: "List applications",
|
||||||
|
description: "List the OAuth applications for the current user",
|
||||||
|
operationId: "AppController.index",
|
||||||
|
responses: %{
|
||||||
|
200 => Operation.response("Array of App", "application/json", array_of_apps())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp array_of_apps do
|
||||||
|
%Schema{type: :array, items: App, example: [App.schema().example]}
|
||||||
|
end
|
||||||
|
end
|
|
@ -191,6 +191,7 @@ def delete_account_operation do
|
||||||
parameters: [
|
parameters: [
|
||||||
Operation.parameter(:password, :query, :string, "Password")
|
Operation.parameter(:password, :query, :string, "Password")
|
||||||
],
|
],
|
||||||
|
requestBody: request_body("Parameters", delete_account_request(), required: false),
|
||||||
responses: %{
|
responses: %{
|
||||||
200 =>
|
200 =>
|
||||||
Operation.response("Success", "application/json", %Schema{
|
Operation.response("Success", "application/json", %Schema{
|
||||||
|
@ -237,4 +238,48 @@ def remote_subscribe_operation do
|
||||||
responses: %{200 => Operation.response("Web Page", "test/html", %Schema{type: :string})}
|
responses: %{200 => Operation.response("Web Page", "test/html", %Schema{type: :string})}
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def remote_interaction_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Accounts"],
|
||||||
|
summary: "Remote interaction",
|
||||||
|
operationId: "UtilController.remote_interaction",
|
||||||
|
requestBody: request_body("Parameters", remote_interaction_request(), required: true),
|
||||||
|
responses: %{
|
||||||
|
200 =>
|
||||||
|
Operation.response("Remote interaction URL", "application/json", %Schema{type: :object})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp remote_interaction_request do
|
||||||
|
%Schema{
|
||||||
|
title: "RemoteInteractionRequest",
|
||||||
|
description: "POST body for remote interaction",
|
||||||
|
type: :object,
|
||||||
|
required: [:ap_id, :profile],
|
||||||
|
properties: %{
|
||||||
|
ap_id: %Schema{type: :string, description: "Profile or status ActivityPub ID"},
|
||||||
|
profile: %Schema{type: :string, description: "Remote profile webfinger"}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp delete_account_request do
|
||||||
|
%Schema{
|
||||||
|
title: "AccountDeleteRequest",
|
||||||
|
description: "POST body for deleting one's own account",
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
password: %Schema{
|
||||||
|
type: :string,
|
||||||
|
description: "The user's own password for confirmation.",
|
||||||
|
format: :password
|
||||||
|
}
|
||||||
|
},
|
||||||
|
example: %{
|
||||||
|
"password" => "prettyp0ony1313"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -194,9 +194,11 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Account do
|
||||||
"id" => "9tKi3esbG7OQgZ2920",
|
"id" => "9tKi3esbG7OQgZ2920",
|
||||||
"muting" => false,
|
"muting" => false,
|
||||||
"muting_notifications" => false,
|
"muting_notifications" => false,
|
||||||
|
"note" => "",
|
||||||
"requested" => false,
|
"requested" => false,
|
||||||
"showing_reblogs" => true,
|
"showing_reblogs" => true,
|
||||||
"subscribing" => false
|
"subscribing" => false,
|
||||||
|
"notifying" => false
|
||||||
},
|
},
|
||||||
"settings_store" => %{
|
"settings_store" => %{
|
||||||
"pleroma-fe" => %{}
|
"pleroma-fe" => %{}
|
||||||
|
|
|
@ -22,9 +22,11 @@ defmodule Pleroma.Web.ApiSpec.Schemas.AccountRelationship do
|
||||||
id: FlakeID,
|
id: FlakeID,
|
||||||
muting: %Schema{type: :boolean},
|
muting: %Schema{type: :boolean},
|
||||||
muting_notifications: %Schema{type: :boolean},
|
muting_notifications: %Schema{type: :boolean},
|
||||||
|
note: %Schema{type: :string},
|
||||||
requested: %Schema{type: :boolean},
|
requested: %Schema{type: :boolean},
|
||||||
showing_reblogs: %Schema{type: :boolean},
|
showing_reblogs: %Schema{type: :boolean},
|
||||||
subscribing: %Schema{type: :boolean}
|
subscribing: %Schema{type: :boolean},
|
||||||
|
notifying: %Schema{type: :boolean}
|
||||||
},
|
},
|
||||||
example: %{
|
example: %{
|
||||||
"blocked_by" => false,
|
"blocked_by" => false,
|
||||||
|
@ -36,9 +38,11 @@ defmodule Pleroma.Web.ApiSpec.Schemas.AccountRelationship do
|
||||||
"id" => "9tKi3esbG7OQgZ2920",
|
"id" => "9tKi3esbG7OQgZ2920",
|
||||||
"muting" => false,
|
"muting" => false,
|
||||||
"muting_notifications" => false,
|
"muting_notifications" => false,
|
||||||
|
"note" => "",
|
||||||
"requested" => false,
|
"requested" => false,
|
||||||
"showing_reblogs" => true,
|
"showing_reblogs" => true,
|
||||||
"subscribing" => false
|
"subscribing" => false,
|
||||||
|
"notifying" => false
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
33
lib/pleroma/web/api_spec/schemas/app.ex
Normal file
33
lib/pleroma/web/api_spec/schemas/app.ex
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.ApiSpec.Schemas.App do
|
||||||
|
alias OpenApiSpex.Schema
|
||||||
|
|
||||||
|
require OpenApiSpex
|
||||||
|
|
||||||
|
OpenApiSpex.schema(%{
|
||||||
|
title: "App",
|
||||||
|
description: "Response schema for an app",
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
id: %Schema{type: :string},
|
||||||
|
name: %Schema{type: :string},
|
||||||
|
client_id: %Schema{type: :string},
|
||||||
|
client_secret: %Schema{type: :string},
|
||||||
|
redirect_uri: %Schema{type: :string},
|
||||||
|
vapid_key: %Schema{type: :string},
|
||||||
|
website: %Schema{type: :string, nullable: true}
|
||||||
|
},
|
||||||
|
example: %{
|
||||||
|
"id" => "123",
|
||||||
|
"name" => "My App",
|
||||||
|
"client_id" => "TWhM-tNSuncnqN7DBJmoyeLnk6K3iJJ71KKXxgL1hPM",
|
||||||
|
"client_secret" => "ZEaFUFmF0umgBX1qKJDjaU99Q31lDkOU8NutzTOoliw",
|
||||||
|
"vapid_key" =>
|
||||||
|
"BCk-QqERU0q-CfYZjcuB6lnyyOYfJ2AifKqfeGIm7Z-HiTU5T9eTG5GxVA0_OH5mMlI4UkkDTpaZwozy0TzdZ2M=",
|
||||||
|
"website" => "https://myapp.com/"
|
||||||
|
}
|
||||||
|
})
|
||||||
|
end
|
|
@ -282,9 +282,11 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Status do
|
||||||
"id" => "9toJCsKN7SmSf3aj5c",
|
"id" => "9toJCsKN7SmSf3aj5c",
|
||||||
"muting" => false,
|
"muting" => false,
|
||||||
"muting_notifications" => false,
|
"muting_notifications" => false,
|
||||||
|
"note" => "",
|
||||||
"requested" => false,
|
"requested" => false,
|
||||||
"showing_reblogs" => true,
|
"showing_reblogs" => true,
|
||||||
"subscribing" => false
|
"subscribing" => false,
|
||||||
|
"notifying" => false
|
||||||
},
|
},
|
||||||
"skip_thread_containment" => false,
|
"skip_thread_containment" => false,
|
||||||
"tags" => []
|
"tags" => []
|
||||||
|
|
|
@ -10,6 +10,7 @@ defmodule Pleroma.Web.Endpoint do
|
||||||
alias Pleroma.Config
|
alias Pleroma.Config
|
||||||
|
|
||||||
socket("/socket", Pleroma.Web.UserSocket)
|
socket("/socket", Pleroma.Web.UserSocket)
|
||||||
|
socket("/live", Phoenix.LiveView.Socket)
|
||||||
|
|
||||||
plug(Plug.Telemetry, event_prefix: [:phoenix, :endpoint])
|
plug(Plug.Telemetry, event_prefix: [:phoenix, :endpoint])
|
||||||
|
|
||||||
|
|
14
lib/pleroma/web/manifest_controller.ex
Normal file
14
lib/pleroma/web/manifest_controller.ex
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.ManifestController do
|
||||||
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
|
plug(:skip_auth when action == :show)
|
||||||
|
|
||||||
|
@doc "GET /manifest.json"
|
||||||
|
def show(conn, _params) do
|
||||||
|
render(conn, "manifest.json")
|
||||||
|
end
|
||||||
|
end
|
|
@ -15,6 +15,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
||||||
|
|
||||||
alias Pleroma.Maps
|
alias Pleroma.Maps
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
|
alias Pleroma.UserNote
|
||||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
alias Pleroma.Web.ActivityPub.Builder
|
alias Pleroma.Web.ActivityPub.Builder
|
||||||
alias Pleroma.Web.ActivityPub.Pipeline
|
alias Pleroma.Web.ActivityPub.Pipeline
|
||||||
|
@ -55,7 +56,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
||||||
|
|
||||||
plug(
|
plug(
|
||||||
OAuthScopesPlug,
|
OAuthScopesPlug,
|
||||||
%{scopes: ["write:accounts"]} when action in [:update_credentials, :endorse, :unendorse]
|
%{scopes: ["write:accounts"]}
|
||||||
|
when action in [:update_credentials, :note, :endorse, :unendorse]
|
||||||
)
|
)
|
||||||
|
|
||||||
plug(OAuthScopesPlug, %{scopes: ["read:lists"]} when action == :lists)
|
plug(OAuthScopesPlug, %{scopes: ["read:lists"]} when action == :lists)
|
||||||
|
@ -82,7 +84,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
||||||
plug(OAuthScopesPlug, %{scopes: ["follow", "write:mutes"]} when action in [:mute, :unmute])
|
plug(OAuthScopesPlug, %{scopes: ["follow", "write:mutes"]} when action in [:mute, :unmute])
|
||||||
|
|
||||||
@relationship_actions [:follow, :unfollow]
|
@relationship_actions [:follow, :unfollow]
|
||||||
@needs_account ~W(followers following lists follow unfollow mute unmute block unblock endorse unendorse)a
|
@needs_account ~W(followers following lists follow unfollow mute unmute block unblock endorse unendorse endorse unendorse)a
|
||||||
|
|
||||||
plug(
|
plug(
|
||||||
RateLimiter,
|
RateLimiter,
|
||||||
|
@ -438,6 +440,16 @@ def unblock(%{assigns: %{user: blocker, account: blocked}} = conn, _params) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@doc "POST /api/v1/accounts/:id/note"
|
||||||
|
def note(
|
||||||
|
%{assigns: %{user: noter, account: target}, body_params: %{comment: comment}} = conn,
|
||||||
|
_params
|
||||||
|
) do
|
||||||
|
with {:ok, _user_note} <- UserNote.create(noter, target, comment) do
|
||||||
|
render(conn, "relationship.json", user: noter, target: target)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
@doc "POST /api/v1/accounts/:id/mute"
|
@doc "POST /api/v1/accounts/:id/mute"
|
||||||
def endorse(%{assigns: %{user: endorser, account: endorsed}} = conn, _params) do
|
def endorse(%{assigns: %{user: endorser, account: endorsed}} = conn, _params) do
|
||||||
with {:ok, _user_relationships} <- User.endorse(endorser, endorsed) do
|
with {:ok, _user_relationships} <- User.endorse(endorser, endorsed) do
|
||||||
|
|
|
@ -10,7 +10,9 @@ defmodule Pleroma.Web.MastodonAPI.AppController do
|
||||||
|
|
||||||
use Pleroma.Web, :controller
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
|
alias Pleroma.Maps
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.OAuth.App
|
alias Pleroma.Web.OAuth.App
|
||||||
alias Pleroma.Web.OAuth.Scopes
|
alias Pleroma.Web.OAuth.Scopes
|
||||||
alias Pleroma.Web.OAuth.Token
|
alias Pleroma.Web.OAuth.Token
|
||||||
|
@ -26,11 +28,13 @@ defmodule Pleroma.Web.MastodonAPI.AppController do
|
||||||
@doc "POST /api/v1/apps"
|
@doc "POST /api/v1/apps"
|
||||||
def create(%{body_params: params} = conn, _params) do
|
def create(%{body_params: params} = conn, _params) do
|
||||||
scopes = Scopes.fetch_scopes(params, ["read"])
|
scopes = Scopes.fetch_scopes(params, ["read"])
|
||||||
|
user_id = get_user_id(conn)
|
||||||
|
|
||||||
app_attrs =
|
app_attrs =
|
||||||
params
|
params
|
||||||
|> Map.take([:client_name, :redirect_uris, :website])
|
|> Map.take([:client_name, :redirect_uris, :website])
|
||||||
|> Map.put(:scopes, scopes)
|
|> Map.put(:scopes, scopes)
|
||||||
|
|> Maps.put_if_present(:user_id, user_id)
|
||||||
|
|
||||||
with cs <- App.register_changeset(%App{}, app_attrs),
|
with cs <- App.register_changeset(%App{}, app_attrs),
|
||||||
{:ok, app} <- Repo.insert(cs) do
|
{:ok, app} <- Repo.insert(cs) do
|
||||||
|
@ -38,6 +42,9 @@ def create(%{body_params: params} = conn, _params) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp get_user_id(%{assigns: %{user: %User{id: user_id}}}), do: user_id
|
||||||
|
defp get_user_id(_conn), do: nil
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
GET /api/v1/apps/verify_credentials
|
GET /api/v1/apps/verify_credentials
|
||||||
Gets compact non-secret representation of the app. Supports app tokens and user tokens.
|
Gets compact non-secret representation of the app. Supports app tokens and user tokens.
|
||||||
|
|
|
@ -0,0 +1,82 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.MastodonAPI.DirectoryController do
|
||||||
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
|
import Ecto.Query
|
||||||
|
alias Pleroma.Pagination
|
||||||
|
alias Pleroma.User
|
||||||
|
alias Pleroma.UserRelationship
|
||||||
|
alias Pleroma.Web.MastodonAPI.AccountView
|
||||||
|
|
||||||
|
require Logger
|
||||||
|
|
||||||
|
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
||||||
|
|
||||||
|
plug(:skip_auth when action == "index")
|
||||||
|
|
||||||
|
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.DirectoryOperation
|
||||||
|
|
||||||
|
@doc "GET /api/v1/directory"
|
||||||
|
def index(%{assigns: %{user: user}} = conn, params) do
|
||||||
|
with true <- Pleroma.Config.get([:instance, :profile_directory]) do
|
||||||
|
limit = Map.get(params, :limit, 20) |> min(80)
|
||||||
|
|
||||||
|
users =
|
||||||
|
User.Query.build(%{is_discoverable: true, invisible: false, limit: limit})
|
||||||
|
|> order_by_creation_date(params)
|
||||||
|
|> exclude_remote(params)
|
||||||
|
|> exclude_user(user)
|
||||||
|
|> exclude_relationships(user, [:block, :mute])
|
||||||
|
|> Pagination.fetch_paginated(params, :offset)
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> put_view(AccountView)
|
||||||
|
|> render("index.json", for: user, users: users, as: :user)
|
||||||
|
else
|
||||||
|
_ -> json(conn, [])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp order_by_creation_date(query, %{order: "new"}) do
|
||||||
|
query
|
||||||
|
end
|
||||||
|
|
||||||
|
defp order_by_creation_date(query, _params) do
|
||||||
|
query
|
||||||
|
|> order_by([u], desc_nulls_last: u.last_status_at)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp exclude_remote(query, %{local: true}) do
|
||||||
|
where(query, [u], u.local == true)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp exclude_remote(query, _params) do
|
||||||
|
query
|
||||||
|
end
|
||||||
|
|
||||||
|
defp exclude_user(query, %User{id: user_id}) do
|
||||||
|
where(query, [u], u.id != ^user_id)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp exclude_user(query, _user) do
|
||||||
|
query
|
||||||
|
end
|
||||||
|
|
||||||
|
defp exclude_relationships(query, %User{id: user_id}, relationship_types) do
|
||||||
|
query
|
||||||
|
|> join(:left, [u], r in UserRelationship,
|
||||||
|
as: :user_relationships,
|
||||||
|
on:
|
||||||
|
r.target_id == u.id and r.source_id == ^user_id and
|
||||||
|
r.relationship_type in ^relationship_types
|
||||||
|
)
|
||||||
|
|> where([user_relationships: r], is_nil(r.target_id))
|
||||||
|
end
|
||||||
|
|
||||||
|
defp exclude_relationships(query, _user, _relationship_types) do
|
||||||
|
query
|
||||||
|
end
|
||||||
|
end
|
|
@ -17,6 +17,8 @@ defmodule Pleroma.Web.MastodonAPI.SearchController do
|
||||||
|
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
|
@search_limit 40
|
||||||
|
|
||||||
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
||||||
|
|
||||||
# Note: Mastodon doesn't allow unauthenticated access (requires read:accounts / read:search)
|
# Note: Mastodon doesn't allow unauthenticated access (requires read:accounts / read:search)
|
||||||
|
@ -77,7 +79,7 @@ defp search_options(params, user) do
|
||||||
[
|
[
|
||||||
resolve: params[:resolve],
|
resolve: params[:resolve],
|
||||||
following: params[:following],
|
following: params[:following],
|
||||||
limit: params[:limit],
|
limit: min(params[:limit], @search_limit),
|
||||||
offset: params[:offset],
|
offset: params[:offset],
|
||||||
type: params[:type],
|
type: params[:type],
|
||||||
author: get_author(params),
|
author: get_author(params),
|
||||||
|
|
|
@ -4,11 +4,16 @@
|
||||||
|
|
||||||
defmodule Pleroma.Web.MastodonAPI.SuggestionController do
|
defmodule Pleroma.Web.MastodonAPI.SuggestionController do
|
||||||
use Pleroma.Web, :controller
|
use Pleroma.Web, :controller
|
||||||
|
import Ecto.Query
|
||||||
|
alias Pleroma.FollowingRelationship
|
||||||
|
alias Pleroma.User
|
||||||
|
alias Pleroma.UserRelationship
|
||||||
|
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
||||||
plug(Pleroma.Web.Plugs.OAuthScopesPlug, %{scopes: ["read"]} when action == :index)
|
plug(Pleroma.Web.Plugs.OAuthScopesPlug, %{scopes: ["read"]} when action in [:index, :index2])
|
||||||
|
plug(Pleroma.Web.Plugs.OAuthScopesPlug, %{scopes: ["write"]} when action in [:dismiss])
|
||||||
|
|
||||||
def open_api_operation(action) do
|
def open_api_operation(action) do
|
||||||
operation = String.to_existing_atom("#{action}_operation")
|
operation = String.to_existing_atom("#{action}_operation")
|
||||||
|
@ -26,7 +31,90 @@ def index_operation do
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def index2_operation do
|
||||||
|
%OpenApiSpex.Operation{
|
||||||
|
tags: ["Suggestions"],
|
||||||
|
summary: "Follow suggestions",
|
||||||
|
operationId: "SuggestionController.index2",
|
||||||
|
responses: %{
|
||||||
|
200 => Pleroma.Web.ApiSpec.Helpers.empty_array_response()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def dismiss_operation do
|
||||||
|
%OpenApiSpex.Operation{
|
||||||
|
tags: ["Suggestions"],
|
||||||
|
summary: "Remove a suggestion",
|
||||||
|
operationId: "SuggestionController.dismiss",
|
||||||
|
parameters: [
|
||||||
|
OpenApiSpex.Operation.parameter(
|
||||||
|
:account_id,
|
||||||
|
:path,
|
||||||
|
%OpenApiSpex.Schema{type: :string},
|
||||||
|
"Account to dismiss",
|
||||||
|
required: true
|
||||||
|
)
|
||||||
|
],
|
||||||
|
responses: %{
|
||||||
|
200 => Pleroma.Web.ApiSpec.Helpers.empty_object_response()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
@doc "GET /api/v1/suggestions"
|
@doc "GET /api/v1/suggestions"
|
||||||
def index(conn, params),
|
def index(conn, params),
|
||||||
do: Pleroma.Web.MastodonAPI.MastodonAPIController.empty_array(conn, params)
|
do: Pleroma.Web.MastodonAPI.MastodonAPIController.empty_array(conn, params)
|
||||||
|
|
||||||
|
@doc "GET /api/v2/suggestions"
|
||||||
|
def index2(%{assigns: %{user: user}} = conn, params) do
|
||||||
|
limit = Map.get(params, :limit, 40) |> min(80)
|
||||||
|
|
||||||
|
users =
|
||||||
|
%{is_suggested: true, invisible: false, limit: limit}
|
||||||
|
|> User.Query.build()
|
||||||
|
|> exclude_user(user)
|
||||||
|
|> exclude_relationships(user, [:block, :mute, :suggestion_dismiss])
|
||||||
|
|> exclude_following(user)
|
||||||
|
|> Pleroma.Repo.all()
|
||||||
|
|
||||||
|
render(conn, "index.json", %{
|
||||||
|
users: users,
|
||||||
|
source: :staff,
|
||||||
|
for: user,
|
||||||
|
skip_visibility_check: true
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
defp exclude_user(query, %User{id: user_id}) do
|
||||||
|
where(query, [u], u.id != ^user_id)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp exclude_relationships(query, %User{id: user_id}, relationship_types) do
|
||||||
|
query
|
||||||
|
|> join(:left, [u], r in UserRelationship,
|
||||||
|
as: :user_relationships,
|
||||||
|
on:
|
||||||
|
r.target_id == u.id and r.source_id == ^user_id and
|
||||||
|
r.relationship_type in ^relationship_types
|
||||||
|
)
|
||||||
|
|> where([user_relationships: r], is_nil(r.target_id))
|
||||||
|
end
|
||||||
|
|
||||||
|
defp exclude_following(query, %User{id: user_id}) do
|
||||||
|
query
|
||||||
|
|> join(:left, [u], r in FollowingRelationship,
|
||||||
|
as: :following_relationships,
|
||||||
|
on: r.following_id == u.id and r.follower_id == ^user_id and r.state == :follow_accept
|
||||||
|
)
|
||||||
|
|> where([following_relationships: r], is_nil(r.following_id))
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc "DELETE /api/v1/suggestions/:account_id"
|
||||||
|
def dismiss(%{assigns: %{user: source}} = conn, %{account_id: user_id}) do
|
||||||
|
with %User{} = target <- User.get_cached_by_id(user_id),
|
||||||
|
{:ok, _} <- UserRelationship.create(:suggestion_dismiss, source, target) do
|
||||||
|
json(conn, %{})
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -24,6 +24,7 @@ def follow(follower, followed, params \\ %{}) do
|
||||||
with {:ok, follower, _followed, _} <- result do
|
with {:ok, follower, _followed, _} <- result do
|
||||||
options = cast_params(params)
|
options = cast_params(params)
|
||||||
set_reblogs_visibility(options[:reblogs], result)
|
set_reblogs_visibility(options[:reblogs], result)
|
||||||
|
set_subscription(options[:notify], result)
|
||||||
{:ok, follower}
|
{:ok, follower}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -36,6 +37,16 @@ defp set_reblogs_visibility(_, {:ok, follower, followed, _}) do
|
||||||
CommonAPI.show_reblogs(follower, followed)
|
CommonAPI.show_reblogs(follower, followed)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp set_subscription(true, {:ok, follower, followed, _}) do
|
||||||
|
User.subscribe(follower, followed)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp set_subscription(false, {:ok, follower, followed, _}) do
|
||||||
|
User.unsubscribe(follower, followed)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp set_subscription(_, _), do: {:ok, nil}
|
||||||
|
|
||||||
@spec get_followers(User.t(), map()) :: list(User.t())
|
@spec get_followers(User.t(), map()) :: list(User.t())
|
||||||
def get_followers(user, params \\ %{}) do
|
def get_followers(user, params \\ %{}) do
|
||||||
user
|
user
|
||||||
|
@ -73,7 +84,8 @@ defp cast_params(params) do
|
||||||
exclude_visibilities: {:array, :string},
|
exclude_visibilities: {:array, :string},
|
||||||
reblogs: :boolean,
|
reblogs: :boolean,
|
||||||
with_muted: :boolean,
|
with_muted: :boolean,
|
||||||
account_ap_id: :string
|
account_ap_id: :string,
|
||||||
|
notify: :boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
changeset = cast({%{}, param_types}, params, Map.keys(param_types))
|
changeset = cast({%{}, param_types}, params, Map.keys(param_types))
|
||||||
|
|
|
@ -7,6 +7,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
|
||||||
|
|
||||||
alias Pleroma.FollowingRelationship
|
alias Pleroma.FollowingRelationship
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
|
alias Pleroma.UserNote
|
||||||
alias Pleroma.UserRelationship
|
alias Pleroma.UserRelationship
|
||||||
alias Pleroma.Web.CommonAPI.Utils
|
alias Pleroma.Web.CommonAPI.Utils
|
||||||
alias Pleroma.Web.MastodonAPI.AccountView
|
alias Pleroma.Web.MastodonAPI.AccountView
|
||||||
|
@ -101,6 +102,15 @@ def render(
|
||||||
User.following?(target, reading_user)
|
User.following?(target, reading_user)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
subscribing =
|
||||||
|
UserRelationship.exists?(
|
||||||
|
user_relationships,
|
||||||
|
:inverse_subscription,
|
||||||
|
target,
|
||||||
|
reading_user,
|
||||||
|
&User.subscribed_to?(&2, &1)
|
||||||
|
)
|
||||||
|
|
||||||
# NOTE: adjust UserRelationship.view_relationships_option/2 on new relation-related flags
|
# NOTE: adjust UserRelationship.view_relationships_option/2 on new relation-related flags
|
||||||
%{
|
%{
|
||||||
id: to_string(target.id),
|
id: to_string(target.id),
|
||||||
|
@ -138,14 +148,8 @@ def render(
|
||||||
target,
|
target,
|
||||||
&User.muted_notifications?(&1, &2)
|
&User.muted_notifications?(&1, &2)
|
||||||
),
|
),
|
||||||
subscribing:
|
subscribing: subscribing,
|
||||||
UserRelationship.exists?(
|
notifying: subscribing,
|
||||||
user_relationships,
|
|
||||||
:inverse_subscription,
|
|
||||||
target,
|
|
||||||
reading_user,
|
|
||||||
&User.subscribed_to?(&2, &1)
|
|
||||||
),
|
|
||||||
requested: follow_state == :follow_pending,
|
requested: follow_state == :follow_pending,
|
||||||
domain_blocking: User.blocks_domain?(reading_user, target),
|
domain_blocking: User.blocks_domain?(reading_user, target),
|
||||||
showing_reblogs:
|
showing_reblogs:
|
||||||
|
@ -156,6 +160,11 @@ def render(
|
||||||
target,
|
target,
|
||||||
&User.muting_reblogs?(&1, &2)
|
&User.muting_reblogs?(&1, &2)
|
||||||
),
|
),
|
||||||
|
note:
|
||||||
|
UserNote.show(
|
||||||
|
reading_user,
|
||||||
|
target
|
||||||
|
),
|
||||||
endorsed:
|
endorsed:
|
||||||
UserRelationship.exists?(
|
UserRelationship.exists?(
|
||||||
user_relationships,
|
user_relationships,
|
||||||
|
@ -163,7 +172,7 @@ def render(
|
||||||
target,
|
target,
|
||||||
reading_user,
|
reading_user,
|
||||||
&User.endorses?(&2, &1)
|
&User.endorses?(&2, &1)
|
||||||
),
|
)
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -268,6 +277,7 @@ defp do_render("show.json", %{user: user} = opts) do
|
||||||
actor_type: user.actor_type
|
actor_type: user.actor_type
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
last_status_at: user.last_status_at,
|
||||||
|
|
||||||
# Pleroma extensions
|
# Pleroma extensions
|
||||||
# Note: it's insecure to output :email but fully-qualified nickname may serve as safe stub
|
# Note: it's insecure to output :email but fully-qualified nickname may serve as safe stub
|
||||||
|
@ -276,6 +286,7 @@ defp do_render("show.json", %{user: user} = opts) do
|
||||||
ap_id: user.ap_id,
|
ap_id: user.ap_id,
|
||||||
also_known_as: user.also_known_as,
|
also_known_as: user.also_known_as,
|
||||||
is_confirmed: user.is_confirmed,
|
is_confirmed: user.is_confirmed,
|
||||||
|
is_suggested: user.is_suggested,
|
||||||
tags: user.tags,
|
tags: user.tags,
|
||||||
hide_followers_count: user.hide_followers_count,
|
hide_followers_count: user.hide_followers_count,
|
||||||
hide_follows_count: user.hide_follows_count,
|
hide_follows_count: user.hide_follows_count,
|
||||||
|
|
|
@ -45,7 +45,8 @@ def render("show.json", _) do
|
||||||
features: features(),
|
features: features(),
|
||||||
federation: federation(),
|
federation: federation(),
|
||||||
fields_limits: fields_limits(),
|
fields_limits: fields_limits(),
|
||||||
post_formats: Config.get([:instance, :allowed_post_formats])
|
post_formats: Config.get([:instance, :allowed_post_formats]),
|
||||||
|
privileged_staff: Config.get([:instance, :privileged_staff])
|
||||||
},
|
},
|
||||||
stats: %{mau: Pleroma.User.active_user_count()},
|
stats: %{mau: Pleroma.User.active_user_count()},
|
||||||
vapid_public_key: Keyword.get(Pleroma.Web.Push.vapid_config(), :public_key)
|
vapid_public_key: Keyword.get(Pleroma.Web.Push.vapid_config(), :public_key)
|
||||||
|
@ -59,6 +60,7 @@ def features do
|
||||||
"mastodon_api",
|
"mastodon_api",
|
||||||
"mastodon_api_streaming",
|
"mastodon_api_streaming",
|
||||||
"polls",
|
"polls",
|
||||||
|
"v2_suggestions",
|
||||||
"pleroma_explicit_addressing",
|
"pleroma_explicit_addressing",
|
||||||
"shareable_emoji_packs",
|
"shareable_emoji_packs",
|
||||||
"multifetch",
|
"multifetch",
|
||||||
|
@ -83,7 +85,13 @@ def features do
|
||||||
"safe_dm_mentions"
|
"safe_dm_mentions"
|
||||||
end,
|
end,
|
||||||
"pleroma_emoji_reactions",
|
"pleroma_emoji_reactions",
|
||||||
"pleroma_chat_messages"
|
"pleroma_chat_messages",
|
||||||
|
if Config.get([:instance, :show_reactions]) do
|
||||||
|
"exposable_reactions"
|
||||||
|
end,
|
||||||
|
if Config.get([:instance, :profile_directory]) do
|
||||||
|
"profile_directory"
|
||||||
|
end
|
||||||
]
|
]
|
||||||
|> Enum.filter(& &1)
|
|> Enum.filter(& &1)
|
||||||
end
|
end
|
||||||
|
|
28
lib/pleroma/web/mastodon_api/views/suggestion_view.ex
Normal file
28
lib/pleroma/web/mastodon_api/views/suggestion_view.ex
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.MastodonAPI.SuggestionView do
|
||||||
|
use Pleroma.Web, :view
|
||||||
|
alias Pleroma.Web.MastodonAPI.AccountView
|
||||||
|
|
||||||
|
@source_types [:staff, :global, :past_interactions]
|
||||||
|
|
||||||
|
def render("index.json", %{users: users} = opts) do
|
||||||
|
Enum.map(users, fn user ->
|
||||||
|
opts =
|
||||||
|
opts
|
||||||
|
|> Map.put(:user, user)
|
||||||
|
|> Map.delete(:users)
|
||||||
|
|
||||||
|
render("show.json", opts)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
def render("show.json", %{source: source, user: _user} = opts) when source in @source_types do
|
||||||
|
%{
|
||||||
|
source: source,
|
||||||
|
account: AccountView.render("show.json", opts)
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
|
@ -35,7 +35,9 @@ def get_nodeinfo("2.0") do
|
||||||
openRegistrations: Config.get([:instance, :registrations_open]),
|
openRegistrations: Config.get([:instance, :registrations_open]),
|
||||||
usage: %{
|
usage: %{
|
||||||
users: %{
|
users: %{
|
||||||
total: Map.get(stats, :user_count, 0)
|
total: Map.get(stats, :user_count, 0),
|
||||||
|
activeMonth: Pleroma.User.active_user_count(30),
|
||||||
|
activeHalfyear: Pleroma.User.active_user_count(180)
|
||||||
},
|
},
|
||||||
localPosts: Map.get(stats, :status_count, 0)
|
localPosts: Map.get(stats, :status_count, 0)
|
||||||
},
|
},
|
||||||
|
@ -67,7 +69,8 @@ def get_nodeinfo("2.0") do
|
||||||
mailerEnabled: Config.get([Pleroma.Emails.Mailer, :enabled], false),
|
mailerEnabled: Config.get([Pleroma.Emails.Mailer, :enabled], false),
|
||||||
features: features,
|
features: features,
|
||||||
restrictedNicknames: Config.get([Pleroma.User, :restricted_nicknames]),
|
restrictedNicknames: Config.get([Pleroma.User, :restricted_nicknames]),
|
||||||
skipThreadContainment: Config.get([:instance, :skip_thread_containment], false)
|
skipThreadContainment: Config.get([:instance, :skip_thread_containment], false),
|
||||||
|
privilegedStaff: Config.get([:instance, :privileged_staff])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
|
@ -7,6 +7,7 @@ defmodule Pleroma.Web.OAuth.App do
|
||||||
import Ecto.Changeset
|
import Ecto.Changeset
|
||||||
import Ecto.Query
|
import Ecto.Query
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
|
alias Pleroma.User
|
||||||
|
|
||||||
@type t :: %__MODULE__{}
|
@type t :: %__MODULE__{}
|
||||||
|
|
||||||
|
@ -19,6 +20,8 @@ defmodule Pleroma.Web.OAuth.App do
|
||||||
field(:client_secret, :string)
|
field(:client_secret, :string)
|
||||||
field(:trusted, :boolean, default: false)
|
field(:trusted, :boolean, default: false)
|
||||||
|
|
||||||
|
belongs_to(:user, User, type: FlakeId.Ecto.CompatType)
|
||||||
|
|
||||||
has_many(:oauth_authorizations, Pleroma.Web.OAuth.Authorization, on_delete: :delete_all)
|
has_many(:oauth_authorizations, Pleroma.Web.OAuth.Authorization, on_delete: :delete_all)
|
||||||
has_many(:oauth_tokens, Pleroma.Web.OAuth.Token, on_delete: :delete_all)
|
has_many(:oauth_tokens, Pleroma.Web.OAuth.Token, on_delete: :delete_all)
|
||||||
|
|
||||||
|
@ -27,7 +30,7 @@ defmodule Pleroma.Web.OAuth.App do
|
||||||
|
|
||||||
@spec changeset(t(), map()) :: Ecto.Changeset.t()
|
@spec changeset(t(), map()) :: Ecto.Changeset.t()
|
||||||
def changeset(struct, params) do
|
def changeset(struct, params) do
|
||||||
cast(struct, params, [:client_name, :redirect_uris, :scopes, :website, :trusted])
|
cast(struct, params, [:client_name, :redirect_uris, :scopes, :website, :trusted, :user_id])
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec register_changeset(t(), map()) :: Ecto.Changeset.t()
|
@spec register_changeset(t(), map()) :: Ecto.Changeset.t()
|
||||||
|
@ -129,6 +132,12 @@ def search(params) do
|
||||||
{:ok, Repo.all(query), count}
|
{:ok, Repo.all(query), count}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@spec get_user_apps(User.t()) :: {:ok, [t()], non_neg_integer()}
|
||||||
|
def get_user_apps(%User{id: user_id}) do
|
||||||
|
from(a in __MODULE__, where: a.user_id == ^user_id)
|
||||||
|
|> Repo.all()
|
||||||
|
end
|
||||||
|
|
||||||
@spec destroy(pos_integer()) :: {:ok, t()} | {:error, Ecto.Changeset.t()}
|
@spec destroy(pos_integer()) :: {:ok, t()} | {:error, Ecto.Changeset.t()}
|
||||||
def destroy(id) do
|
def destroy(id) do
|
||||||
with %__MODULE__{} = app <- Repo.get(__MODULE__, id) do
|
with %__MODULE__{} = app <- Repo.get(__MODULE__, id) do
|
||||||
|
|
|
@ -106,7 +106,7 @@ def endorsements(%{assigns: %{user: for_user, account: user}} = conn, params) do
|
||||||
users =
|
users =
|
||||||
user
|
user
|
||||||
|> User.endorsed_users_relation(_restrict_deactivated = true)
|
|> User.endorsed_users_relation(_restrict_deactivated = true)
|
||||||
|> Pleroma.Pagination.fetch_paginated(Map.put(params, :skip_order, true))
|
|> fetch_paginated_endorsements(params)
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> add_link_headers(users)
|
|> add_link_headers(users)
|
||||||
|
@ -118,6 +118,16 @@ def endorsements(%{assigns: %{user: for_user, account: user}} = conn, params) do
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp fetch_paginated_endorsements(user, %{shuffle: true} = params) do
|
||||||
|
user
|
||||||
|
|> Pleroma.Pagination.fetch_paginated(Map.put(params, :shuffle, true))
|
||||||
|
end
|
||||||
|
|
||||||
|
defp fetch_paginated_endorsements(user, params) do
|
||||||
|
user
|
||||||
|
|> Pleroma.Pagination.fetch_paginated(Map.put(params, :skip_order, true))
|
||||||
|
end
|
||||||
|
|
||||||
@doc "POST /api/v1/pleroma/accounts/:id/subscribe"
|
@doc "POST /api/v1/pleroma/accounts/:id/subscribe"
|
||||||
def subscribe(%{assigns: %{user: user, account: subscription_target}} = conn, _params) do
|
def subscribe(%{assigns: %{user: user, account: subscription_target}} = conn, _params) do
|
||||||
with {:ok, _subscription} <- User.subscribe(user, subscription_target) do
|
with {:ok, _subscription} <- User.subscribe(user, subscription_target) do
|
||||||
|
|
23
lib/pleroma/web/pleroma_api/controllers/app_controller.ex
Normal file
23
lib/pleroma/web/pleroma_api/controllers/app_controller.ex
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.PleromaAPI.AppController do
|
||||||
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
|
alias Pleroma.Web.OAuth.App
|
||||||
|
alias Pleroma.Web.Plugs.OAuthScopesPlug
|
||||||
|
|
||||||
|
plug(OAuthScopesPlug, %{scopes: ["follow", "read"]} when action in [:index])
|
||||||
|
|
||||||
|
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
||||||
|
|
||||||
|
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.PleromaAppOperation
|
||||||
|
|
||||||
|
@doc "GET /api/v1/pleroma/apps"
|
||||||
|
def index(%{assigns: %{user: user}} = conn, _params) do
|
||||||
|
with apps <- App.get_user_apps(user) do
|
||||||
|
render(conn, "index.json", %{apps: apps})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -151,7 +151,9 @@ def index2(%{assigns: %{user: user}} = conn, params) do
|
||||||
index_query(user, params)
|
index_query(user, params)
|
||||||
|> Pagination.fetch_paginated(params)
|
|> Pagination.fetch_paginated(params)
|
||||||
|
|
||||||
render(conn, "index.json", chats: chats)
|
conn
|
||||||
|
|> add_link_headers(chats)
|
||||||
|
|> render("index.json", chats: chats)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp index_query(%{id: user_id} = user, params) do
|
defp index_query(%{id: user_id} = user, params) do
|
||||||
|
|
11
lib/pleroma/web/pleroma_api/views/app_view.ex
Normal file
11
lib/pleroma/web/pleroma_api/views/app_view.ex
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.PleromaAPI.AppView do
|
||||||
|
use Pleroma.Web, :view
|
||||||
|
|
||||||
|
def render("index.json", %{apps: apps}) do
|
||||||
|
render_many(apps, Pleroma.Web.MastodonAPI.AppView, "show.json")
|
||||||
|
end
|
||||||
|
end
|
36
lib/pleroma/web/plugs/ensure_staff_privileged_plug.ex
Normal file
36
lib/pleroma/web/plugs/ensure_staff_privileged_plug.ex
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.Plugs.EnsureStaffPrivilegedPlug do
|
||||||
|
@moduledoc """
|
||||||
|
Ensures staff are privileged enough to do certain tasks.
|
||||||
|
"""
|
||||||
|
import Pleroma.Web.TranslationHelpers
|
||||||
|
import Plug.Conn
|
||||||
|
|
||||||
|
alias Pleroma.Config
|
||||||
|
alias Pleroma.User
|
||||||
|
|
||||||
|
def init(options) do
|
||||||
|
options
|
||||||
|
end
|
||||||
|
|
||||||
|
def call(%{assigns: %{user: %User{is_admin: true}}} = conn, _), do: conn
|
||||||
|
|
||||||
|
def call(%{assigns: %{user: %User{is_moderator: true}}} = conn, _) do
|
||||||
|
if Config.get!([:instance, :privileged_staff]) do
|
||||||
|
conn
|
||||||
|
else
|
||||||
|
conn
|
||||||
|
|> render_error(:forbidden, "User is not an admin.")
|
||||||
|
|> halt()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def call(conn, _) do
|
||||||
|
conn
|
||||||
|
|> render_error(:forbidden, "User is not a staff member.")
|
||||||
|
|> halt()
|
||||||
|
end
|
||||||
|
end
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
defmodule Pleroma.Web.Router do
|
defmodule Pleroma.Web.Router do
|
||||||
use Pleroma.Web, :router
|
use Pleroma.Web, :router
|
||||||
|
import Phoenix.LiveDashboard.Router
|
||||||
|
|
||||||
pipeline :accepts_html do
|
pipeline :accepts_html do
|
||||||
plug(:accepts, ["html"])
|
plug(:accepts, ["html"])
|
||||||
|
@ -100,6 +101,10 @@ defmodule Pleroma.Web.Router do
|
||||||
plug(Pleroma.Web.Plugs.IdempotencyPlug)
|
plug(Pleroma.Web.Plugs.IdempotencyPlug)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
pipeline :require_privileged_staff do
|
||||||
|
plug(Pleroma.Web.Plugs.EnsureStaffPrivilegedPlug)
|
||||||
|
end
|
||||||
|
|
||||||
pipeline :require_admin do
|
pipeline :require_admin do
|
||||||
plug(Pleroma.Web.Plugs.UserIsAdminPlug)
|
plug(Pleroma.Web.Plugs.UserIsAdminPlug)
|
||||||
end
|
end
|
||||||
|
@ -150,6 +155,7 @@ defmodule Pleroma.Web.Router do
|
||||||
get("/emoji", UtilController, :emoji)
|
get("/emoji", UtilController, :emoji)
|
||||||
get("/captcha", UtilController, :captcha)
|
get("/captcha", UtilController, :captcha)
|
||||||
get("/healthcheck", UtilController, :healthcheck)
|
get("/healthcheck", UtilController, :healthcheck)
|
||||||
|
post("/remote_interaction", UtilController, :remote_interaction)
|
||||||
end
|
end
|
||||||
|
|
||||||
scope "/api/v1/pleroma", Pleroma.Web do
|
scope "/api/v1/pleroma", Pleroma.Web do
|
||||||
|
@ -157,12 +163,11 @@ defmodule Pleroma.Web.Router do
|
||||||
post("/uploader_callback/:upload_path", UploaderController, :callback)
|
post("/uploader_callback/:upload_path", UploaderController, :callback)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# AdminAPI: only admins can perform these actions
|
||||||
scope "/api/v1/pleroma/admin", Pleroma.Web.AdminAPI do
|
scope "/api/v1/pleroma/admin", Pleroma.Web.AdminAPI do
|
||||||
pipe_through([:admin_api, :require_admin])
|
pipe_through([:admin_api, :require_admin])
|
||||||
|
|
||||||
put("/users/disable_mfa", AdminAPIController, :disable_mfa)
|
put("/users/disable_mfa", AdminAPIController, :disable_mfa)
|
||||||
put("/users/tag", AdminAPIController, :tag_users)
|
|
||||||
delete("/users/tag", AdminAPIController, :untag_users)
|
|
||||||
|
|
||||||
get("/users/:nickname/permission_group", AdminAPIController, :right_get)
|
get("/users/:nickname/permission_group", AdminAPIController, :right_get)
|
||||||
get("/users/:nickname/permission_group/:permission_group", AdminAPIController, :right_get)
|
get("/users/:nickname/permission_group/:permission_group", AdminAPIController, :right_get)
|
||||||
|
@ -185,35 +190,19 @@ defmodule Pleroma.Web.Router do
|
||||||
|
|
||||||
post("/users/follow", UserController, :follow)
|
post("/users/follow", UserController, :follow)
|
||||||
post("/users/unfollow", UserController, :unfollow)
|
post("/users/unfollow", UserController, :unfollow)
|
||||||
delete("/users", UserController, :delete)
|
|
||||||
post("/users", UserController, :create)
|
post("/users", UserController, :create)
|
||||||
patch("/users/:nickname/toggle_activation", UserController, :toggle_activation)
|
|
||||||
patch("/users/activate", UserController, :activate)
|
patch("/users/suggest", UserController, :suggest)
|
||||||
patch("/users/deactivate", UserController, :deactivate)
|
patch("/users/unsuggest", UserController, :unsuggest)
|
||||||
patch("/users/approve", UserController, :approve)
|
|
||||||
|
|
||||||
get("/relay", RelayController, :index)
|
get("/relay", RelayController, :index)
|
||||||
post("/relay", RelayController, :follow)
|
post("/relay", RelayController, :follow)
|
||||||
delete("/relay", RelayController, :unfollow)
|
delete("/relay", RelayController, :unfollow)
|
||||||
|
|
||||||
post("/users/invite_token", InviteController, :create)
|
|
||||||
get("/users/invites", InviteController, :index)
|
|
||||||
post("/users/revoke_invite", InviteController, :revoke)
|
|
||||||
post("/users/email_invite", InviteController, :email)
|
|
||||||
|
|
||||||
get("/users/:nickname/password_reset", AdminAPIController, :get_password_reset)
|
|
||||||
patch("/users/force_password_reset", AdminAPIController, :force_password_reset)
|
patch("/users/force_password_reset", AdminAPIController, :force_password_reset)
|
||||||
get("/users/:nickname/credentials", AdminAPIController, :show_user_credentials)
|
get("/users/:nickname/credentials", AdminAPIController, :show_user_credentials)
|
||||||
patch("/users/:nickname/credentials", AdminAPIController, :update_user_credentials)
|
patch("/users/:nickname/credentials", AdminAPIController, :update_user_credentials)
|
||||||
|
|
||||||
get("/users", UserController, :index)
|
|
||||||
get("/users/:nickname", UserController, :show)
|
|
||||||
get("/users/:nickname/statuses", AdminAPIController, :list_user_statuses)
|
|
||||||
get("/users/:nickname/chats", AdminAPIController, :list_user_chats)
|
|
||||||
|
|
||||||
get("/instances/:instance/statuses", InstanceController, :list_statuses)
|
|
||||||
delete("/instances/:instance", InstanceController, :delete)
|
|
||||||
|
|
||||||
get("/instance_document/:name", InstanceDocumentController, :show)
|
get("/instance_document/:name", InstanceDocumentController, :show)
|
||||||
patch("/instance_document/:name", InstanceDocumentController, :update)
|
patch("/instance_document/:name", InstanceDocumentController, :update)
|
||||||
delete("/instance_document/:name", InstanceDocumentController, :delete)
|
delete("/instance_document/:name", InstanceDocumentController, :delete)
|
||||||
|
@ -221,28 +210,12 @@ defmodule Pleroma.Web.Router do
|
||||||
patch("/users/confirm_email", AdminAPIController, :confirm_email)
|
patch("/users/confirm_email", AdminAPIController, :confirm_email)
|
||||||
patch("/users/resend_confirmation_email", AdminAPIController, :resend_confirmation_email)
|
patch("/users/resend_confirmation_email", AdminAPIController, :resend_confirmation_email)
|
||||||
|
|
||||||
get("/reports", ReportController, :index)
|
|
||||||
get("/reports/:id", ReportController, :show)
|
|
||||||
patch("/reports", ReportController, :update)
|
|
||||||
post("/reports/:id/notes", ReportController, :notes_create)
|
|
||||||
delete("/reports/:report_id/notes/:id", ReportController, :notes_delete)
|
|
||||||
|
|
||||||
get("/statuses/:id", StatusController, :show)
|
|
||||||
put("/statuses/:id", StatusController, :update)
|
|
||||||
delete("/statuses/:id", StatusController, :delete)
|
|
||||||
get("/statuses", StatusController, :index)
|
|
||||||
|
|
||||||
get("/config", ConfigController, :show)
|
get("/config", ConfigController, :show)
|
||||||
post("/config", ConfigController, :update)
|
post("/config", ConfigController, :update)
|
||||||
get("/config/descriptions", ConfigController, :descriptions)
|
get("/config/descriptions", ConfigController, :descriptions)
|
||||||
get("/need_reboot", AdminAPIController, :need_reboot)
|
get("/need_reboot", AdminAPIController, :need_reboot)
|
||||||
get("/restart", AdminAPIController, :restart)
|
get("/restart", AdminAPIController, :restart)
|
||||||
|
|
||||||
get("/moderation_log", AdminAPIController, :list_log)
|
|
||||||
|
|
||||||
post("/reload_emoji", AdminAPIController, :reload_emoji)
|
|
||||||
get("/stats", AdminAPIController, :stats)
|
|
||||||
|
|
||||||
get("/oauth_app", OAuthAppController, :index)
|
get("/oauth_app", OAuthAppController, :index)
|
||||||
post("/oauth_app", OAuthAppController, :create)
|
post("/oauth_app", OAuthAppController, :create)
|
||||||
patch("/oauth_app/:id", OAuthAppController, :update)
|
patch("/oauth_app/:id", OAuthAppController, :update)
|
||||||
|
@ -252,19 +225,74 @@ defmodule Pleroma.Web.Router do
|
||||||
post("/media_proxy_caches/delete", MediaProxyCacheController, :delete)
|
post("/media_proxy_caches/delete", MediaProxyCacheController, :delete)
|
||||||
post("/media_proxy_caches/purge", MediaProxyCacheController, :purge)
|
post("/media_proxy_caches/purge", MediaProxyCacheController, :purge)
|
||||||
|
|
||||||
get("/chats/:id", ChatController, :show)
|
|
||||||
get("/chats/:id/messages", ChatController, :messages)
|
|
||||||
delete("/chats/:id/messages/:message_id", ChatController, :delete_message)
|
|
||||||
|
|
||||||
get("/frontends", FrontendController, :index)
|
get("/frontends", FrontendController, :index)
|
||||||
post("/frontends/install", FrontendController, :install)
|
post("/frontends/install", FrontendController, :install)
|
||||||
|
|
||||||
post("/backups", AdminAPIController, :create_backup)
|
post("/backups", AdminAPIController, :create_backup)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# AdminAPI: admins and mods (staff) can perform these actions (if enabled by config)
|
||||||
|
scope "/api/v1/pleroma/admin", Pleroma.Web.AdminAPI do
|
||||||
|
pipe_through([:admin_api, :require_privileged_staff])
|
||||||
|
|
||||||
|
delete("/users", UserController, :delete)
|
||||||
|
|
||||||
|
get("/users/:nickname/password_reset", AdminAPIController, :get_password_reset)
|
||||||
|
patch("/users/:nickname/credentials", AdminAPIController, :update_user_credentials)
|
||||||
|
|
||||||
|
get("/users/:nickname/statuses", AdminAPIController, :list_user_statuses)
|
||||||
|
get("/users/:nickname/chats", AdminAPIController, :list_user_chats)
|
||||||
|
|
||||||
|
get("/statuses", StatusController, :index)
|
||||||
|
|
||||||
|
get("/chats/:id", ChatController, :show)
|
||||||
|
get("/chats/:id/messages", ChatController, :messages)
|
||||||
|
end
|
||||||
|
|
||||||
|
# AdminAPI: admins and mods (staff) can perform these actions
|
||||||
|
scope "/api/v1/pleroma/admin", Pleroma.Web.AdminAPI do
|
||||||
|
pipe_through(:admin_api)
|
||||||
|
|
||||||
|
put("/users/tag", AdminAPIController, :tag_users)
|
||||||
|
delete("/users/tag", AdminAPIController, :untag_users)
|
||||||
|
|
||||||
|
patch("/users/:nickname/toggle_activation", UserController, :toggle_activation)
|
||||||
|
patch("/users/activate", UserController, :activate)
|
||||||
|
patch("/users/deactivate", UserController, :deactivate)
|
||||||
|
patch("/users/approve", UserController, :approve)
|
||||||
|
|
||||||
|
post("/users/invite_token", InviteController, :create)
|
||||||
|
get("/users/invites", InviteController, :index)
|
||||||
|
post("/users/revoke_invite", InviteController, :revoke)
|
||||||
|
post("/users/email_invite", InviteController, :email)
|
||||||
|
|
||||||
|
get("/users", UserController, :index)
|
||||||
|
get("/users/:nickname", UserController, :show)
|
||||||
|
|
||||||
|
get("/instances/:instance/statuses", InstanceController, :list_statuses)
|
||||||
|
delete("/instances/:instance", InstanceController, :delete)
|
||||||
|
|
||||||
|
get("/reports", ReportController, :index)
|
||||||
|
get("/reports/:id", ReportController, :show)
|
||||||
|
patch("/reports", ReportController, :update)
|
||||||
|
post("/reports/:id/notes", ReportController, :notes_create)
|
||||||
|
delete("/reports/:report_id/notes/:id", ReportController, :notes_delete)
|
||||||
|
|
||||||
|
get("/statuses/:id", StatusController, :show)
|
||||||
|
put("/statuses/:id", StatusController, :update)
|
||||||
|
delete("/statuses/:id", StatusController, :delete)
|
||||||
|
|
||||||
|
get("/moderation_log", AdminAPIController, :list_log)
|
||||||
|
|
||||||
|
post("/reload_emoji", AdminAPIController, :reload_emoji)
|
||||||
|
get("/stats", AdminAPIController, :stats)
|
||||||
|
|
||||||
|
delete("/chats/:id/messages/:message_id", ChatController, :delete_message)
|
||||||
|
end
|
||||||
|
|
||||||
scope "/api/v1/pleroma/emoji", Pleroma.Web.PleromaAPI do
|
scope "/api/v1/pleroma/emoji", Pleroma.Web.PleromaAPI do
|
||||||
scope "/pack" do
|
scope "/pack" do
|
||||||
pipe_through([:admin_api, :require_admin])
|
pipe_through(:admin_api)
|
||||||
|
|
||||||
post("/", EmojiPackController, :create)
|
post("/", EmojiPackController, :create)
|
||||||
patch("/", EmojiPackController, :update)
|
patch("/", EmojiPackController, :update)
|
||||||
|
@ -279,7 +307,7 @@ defmodule Pleroma.Web.Router do
|
||||||
|
|
||||||
# Modifying packs
|
# Modifying packs
|
||||||
scope "/packs" do
|
scope "/packs" do
|
||||||
pipe_through([:admin_api, :require_admin])
|
pipe_through(:admin_api)
|
||||||
|
|
||||||
get("/import", EmojiPackController, :import_from_filesystem)
|
get("/import", EmojiPackController, :import_from_filesystem)
|
||||||
get("/remote", EmojiPackController, :remote)
|
get("/remote", EmojiPackController, :remote)
|
||||||
|
@ -367,6 +395,7 @@ defmodule Pleroma.Web.Router do
|
||||||
scope "/api/v1/pleroma", Pleroma.Web.PleromaAPI do
|
scope "/api/v1/pleroma", Pleroma.Web.PleromaAPI do
|
||||||
pipe_through(:api)
|
pipe_through(:api)
|
||||||
|
|
||||||
|
get("/apps", AppController, :index)
|
||||||
get("/statuses/:id/reactions/:emoji", EmojiReactionController, :index)
|
get("/statuses/:id/reactions/:emoji", EmojiReactionController, :index)
|
||||||
get("/statuses/:id/reactions", EmojiReactionController, :index)
|
get("/statuses/:id/reactions", EmojiReactionController, :index)
|
||||||
end
|
end
|
||||||
|
@ -457,6 +486,7 @@ defmodule Pleroma.Web.Router do
|
||||||
post("/accounts/:id/unblock", AccountController, :unblock)
|
post("/accounts/:id/unblock", AccountController, :unblock)
|
||||||
post("/accounts/:id/mute", AccountController, :mute)
|
post("/accounts/:id/mute", AccountController, :mute)
|
||||||
post("/accounts/:id/unmute", AccountController, :unmute)
|
post("/accounts/:id/unmute", AccountController, :unmute)
|
||||||
|
post("/accounts/:id/note", AccountController, :note)
|
||||||
post("/accounts/:id/pin", AccountController, :endorse)
|
post("/accounts/:id/pin", AccountController, :endorse)
|
||||||
post("/accounts/:id/unpin", AccountController, :unendorse)
|
post("/accounts/:id/unpin", AccountController, :unendorse)
|
||||||
|
|
||||||
|
@ -538,6 +568,7 @@ defmodule Pleroma.Web.Router do
|
||||||
delete("/push/subscription", SubscriptionController, :delete)
|
delete("/push/subscription", SubscriptionController, :delete)
|
||||||
|
|
||||||
get("/suggestions", SuggestionController, :index)
|
get("/suggestions", SuggestionController, :index)
|
||||||
|
delete("/suggestions/:account_id", SuggestionController, :dismiss)
|
||||||
|
|
||||||
get("/timelines/home", TimelineController, :home)
|
get("/timelines/home", TimelineController, :home)
|
||||||
get("/timelines/direct", TimelineController, :direct)
|
get("/timelines/direct", TimelineController, :direct)
|
||||||
|
@ -582,6 +613,8 @@ defmodule Pleroma.Web.Router do
|
||||||
get("/timelines/tag/:tag", TimelineController, :hashtag)
|
get("/timelines/tag/:tag", TimelineController, :hashtag)
|
||||||
|
|
||||||
get("/polls/:id", PollController, :show)
|
get("/polls/:id", PollController, :show)
|
||||||
|
|
||||||
|
get("/directory", DirectoryController, :index)
|
||||||
end
|
end
|
||||||
|
|
||||||
scope "/api/v2", Pleroma.Web.MastodonAPI do
|
scope "/api/v2", Pleroma.Web.MastodonAPI do
|
||||||
|
@ -589,6 +622,8 @@ defmodule Pleroma.Web.Router do
|
||||||
get("/search", SearchController, :search2)
|
get("/search", SearchController, :search2)
|
||||||
|
|
||||||
post("/media", MediaController, :create2)
|
post("/media", MediaController, :create2)
|
||||||
|
|
||||||
|
get("/suggestions", SuggestionController, :index2)
|
||||||
end
|
end
|
||||||
|
|
||||||
scope "/api", Pleroma.Web do
|
scope "/api", Pleroma.Web do
|
||||||
|
@ -630,6 +665,11 @@ defmodule Pleroma.Web.Router do
|
||||||
get("/activities/:uuid", OStatus.OStatusController, :activity)
|
get("/activities/:uuid", OStatus.OStatusController, :activity)
|
||||||
get("/notice/:id", OStatus.OStatusController, :notice)
|
get("/notice/:id", OStatus.OStatusController, :notice)
|
||||||
|
|
||||||
|
# Notice compatibility routes for other frontends
|
||||||
|
get("/@:nickname/:id", OStatus.OStatusController, :notice)
|
||||||
|
get("/@:nickname/posts/:id", OStatus.OStatusController, :notice)
|
||||||
|
get("/:nickname/status/:id", OStatus.OStatusController, :notice)
|
||||||
|
|
||||||
# Mastodon compatibility routes
|
# Mastodon compatibility routes
|
||||||
get("/users/:nickname/statuses/:id", OStatus.OStatusController, :object)
|
get("/users/:nickname/statuses/:id", OStatus.OStatusController, :object)
|
||||||
get("/users/:nickname/statuses/:id/activity", OStatus.OStatusController, :activity)
|
get("/users/:nickname/statuses/:id/activity", OStatus.OStatusController, :activity)
|
||||||
|
@ -739,6 +779,18 @@ defmodule Pleroma.Web.Router do
|
||||||
get("/:version", Nodeinfo.NodeinfoController, :nodeinfo)
|
get("/:version", Nodeinfo.NodeinfoController, :nodeinfo)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
scope "/", Pleroma.Web do
|
||||||
|
pipe_through(:api)
|
||||||
|
|
||||||
|
get("/manifest.json", ManifestController, :show)
|
||||||
|
end
|
||||||
|
|
||||||
|
scope "/", Pleroma.Web do
|
||||||
|
pipe_through(:pleroma_html)
|
||||||
|
|
||||||
|
post("/auth/password", TwitterAPI.PasswordController, :request)
|
||||||
|
end
|
||||||
|
|
||||||
scope "/proxy/", Pleroma.Web do
|
scope "/proxy/", Pleroma.Web do
|
||||||
get("/preview/:sig/:url", MediaProxy.MediaProxyController, :preview)
|
get("/preview/:sig/:url", MediaProxy.MediaProxyController, :preview)
|
||||||
get("/preview/:sig/:url/:filename", MediaProxy.MediaProxyController, :preview)
|
get("/preview/:sig/:url/:filename", MediaProxy.MediaProxyController, :preview)
|
||||||
|
@ -754,6 +806,11 @@ defmodule Pleroma.Web.Router do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
scope "/" do
|
||||||
|
pipe_through([:pleroma_html, :authenticate, :require_admin])
|
||||||
|
live_dashboard("/phoenix/live_dashboard")
|
||||||
|
end
|
||||||
|
|
||||||
# Test-only routes needed to test action dispatching and plug chain execution
|
# Test-only routes needed to test action dispatching and plug chain execution
|
||||||
if Pleroma.Config.get(:env) == :test do
|
if Pleroma.Config.get(:env) == :test do
|
||||||
@test_actions [
|
@test_actions [
|
||||||
|
|
|
@ -167,6 +167,15 @@ defp represent(%Activity{object: %Object{data: data}} = activity, selected) do
|
||||||
defp assign_id(%{path_info: ["notice", notice_id]} = conn, _opts),
|
defp assign_id(%{path_info: ["notice", notice_id]} = conn, _opts),
|
||||||
do: assign(conn, :notice_id, notice_id)
|
do: assign(conn, :notice_id, notice_id)
|
||||||
|
|
||||||
|
defp assign_id(%{path_info: ["@" <> _nickname, notice_id]} = conn, _opts),
|
||||||
|
do: assign(conn, :notice_id, notice_id)
|
||||||
|
|
||||||
|
defp assign_id(%{path_info: ["@" <> _nickname, "posts", notice_id]} = conn, _opts),
|
||||||
|
do: assign(conn, :notice_id, notice_id)
|
||||||
|
|
||||||
|
defp assign_id(%{path_info: [_nickname, "status", notice_id]} = conn, _opts),
|
||||||
|
do: assign(conn, :notice_id, notice_id)
|
||||||
|
|
||||||
defp assign_id(%{path_info: ["users", user_id]} = conn, _opts),
|
defp assign_id(%{path_info: ["users", user_id]} = conn, _opts),
|
||||||
do: assign(conn, :username_or_id, user_id)
|
do: assign(conn, :username_or_id, user_id)
|
||||||
|
|
||||||
|
|
|
@ -11,9 +11,23 @@ defmodule Pleroma.Web.TwitterAPI.PasswordController do
|
||||||
|
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
|
import Pleroma.Web.ControllerHelper, only: [json_response: 3]
|
||||||
|
|
||||||
alias Pleroma.PasswordResetToken
|
alias Pleroma.PasswordResetToken
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
|
alias Pleroma.Web.TwitterAPI.TwitterAPI
|
||||||
|
|
||||||
|
plug(Pleroma.Web.Plugs.RateLimiter, [name: :request] when action == :request)
|
||||||
|
|
||||||
|
@doc "POST /auth/password"
|
||||||
|
def request(conn, params) do
|
||||||
|
nickname_or_email = params["email"] || params["nickname"]
|
||||||
|
|
||||||
|
TwitterAPI.password_reset(nickname_or_email)
|
||||||
|
|
||||||
|
json_response(conn, :no_content, "")
|
||||||
|
end
|
||||||
|
|
||||||
def reset(conn, %{"token" => token}) do
|
def 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}),
|
||||||
|
|
|
@ -62,6 +62,15 @@ def remote_subscribe(conn, %{"user" => %{"nickname" => nick, "profile" => profil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def remote_interaction(%{body_params: %{ap_id: ap_id, profile: profile}} = conn, _params) do
|
||||||
|
with {:ok, %{"subscribe_address" => template}} <- WebFinger.finger(profile) do
|
||||||
|
conn
|
||||||
|
|> json(%{url: String.replace(template, "{uri}", ap_id)})
|
||||||
|
else
|
||||||
|
_e -> json(conn, %{error: "Couldn't find user"})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def frontend_configurations(conn, _params) do
|
def frontend_configurations(conn, _params) do
|
||||||
render(conn, "frontend_configurations.json")
|
render(conn, "frontend_configurations.json")
|
||||||
end
|
end
|
||||||
|
@ -123,8 +132,10 @@ def change_email(%{assigns: %{user: user}, body_params: body_params} = conn, %{}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def delete_account(%{assigns: %{user: user}} = conn, params) do
|
def delete_account(%{assigns: %{user: user}, body_params: body_params} = conn, params) do
|
||||||
password = params[:password] || ""
|
# This endpoint can accept a query param or JSON body for backwards-compatibility.
|
||||||
|
# Submitting a JSON body is recommended, so passwords don't end up in server logs.
|
||||||
|
password = body_params[:password] || params[:password] || ""
|
||||||
|
|
||||||
case CommonAPI.Utils.confirm_current_password(user, password) do
|
case CommonAPI.Utils.confirm_current_password(user, password) do
|
||||||
{:ok, user} ->
|
{:ok, user} ->
|
||||||
|
|
28
lib/pleroma/web/views/manifest_view.ex
Normal file
28
lib/pleroma/web/views/manifest_view.ex
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.ManifestView do
|
||||||
|
use Pleroma.Web, :view
|
||||||
|
alias Pleroma.Config
|
||||||
|
alias Pleroma.Web.Endpoint
|
||||||
|
|
||||||
|
def render("manifest.json", _params) do
|
||||||
|
%{
|
||||||
|
name: Config.get([:instance, :name]),
|
||||||
|
description: Config.get([:instance, :description]),
|
||||||
|
icons: Config.get([:manifest, :icons]),
|
||||||
|
theme_color: Config.get([:manifest, :theme_color]),
|
||||||
|
background_color: Config.get([:manifest, :background_color]),
|
||||||
|
display: "standalone",
|
||||||
|
scope: Endpoint.url(),
|
||||||
|
start_url: "/",
|
||||||
|
categories: [
|
||||||
|
"social"
|
||||||
|
],
|
||||||
|
serviceworker: %{
|
||||||
|
src: "/sw.js"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
19
mix.exs
19
mix.exs
|
@ -8,7 +8,7 @@ def project do
|
||||||
elixir: "~> 1.9",
|
elixir: "~> 1.9",
|
||||||
elixirc_paths: elixirc_paths(Mix.env()),
|
elixirc_paths: elixirc_paths(Mix.env()),
|
||||||
compilers: [:phoenix, :gettext] ++ Mix.compilers(),
|
compilers: [:phoenix, :gettext] ++ Mix.compilers(),
|
||||||
elixirc_options: [warnings_as_errors: warnings_as_errors(Mix.env())],
|
elixirc_options: [warnings_as_errors: warnings_as_errors()],
|
||||||
xref: [exclude: [:eldap]],
|
xref: [exclude: [:eldap]],
|
||||||
start_permanent: Mix.env() == :prod,
|
start_permanent: Mix.env() == :prod,
|
||||||
aliases: aliases(),
|
aliases: aliases(),
|
||||||
|
@ -79,6 +79,7 @@ def application do
|
||||||
:comeonin,
|
:comeonin,
|
||||||
:quack,
|
:quack,
|
||||||
:fast_sanitize,
|
:fast_sanitize,
|
||||||
|
:os_mon,
|
||||||
:ssl
|
:ssl
|
||||||
],
|
],
|
||||||
included_applications: [:ex_syslogger]
|
included_applications: [:ex_syslogger]
|
||||||
|
@ -86,12 +87,11 @@ def application do
|
||||||
end
|
end
|
||||||
|
|
||||||
# Specifies which paths to compile per environment.
|
# Specifies which paths to compile per environment.
|
||||||
defp elixirc_paths(:benchmark), do: ["lib", "benchmarks"]
|
defp elixirc_paths(:benchmark), do: ["lib", "benchmarks", "priv/scrubbers"]
|
||||||
defp elixirc_paths(:test), do: ["lib", "test/support"]
|
defp elixirc_paths(:test), do: ["lib", "test/support"]
|
||||||
defp elixirc_paths(_), do: ["lib"]
|
defp elixirc_paths(_), do: ["lib"]
|
||||||
|
|
||||||
defp warnings_as_errors(:prod), do: false
|
defp warnings_as_errors, do: System.get_env("CI") == "true"
|
||||||
defp warnings_as_errors(_), do: true
|
|
||||||
|
|
||||||
# Specifies OAuth dependencies.
|
# Specifies OAuth dependencies.
|
||||||
defp oauth_deps do
|
defp oauth_deps do
|
||||||
|
@ -129,7 +129,7 @@ defp deps do
|
||||||
{:trailing_format_plug, "~> 0.0.7"},
|
{:trailing_format_plug, "~> 0.0.7"},
|
||||||
{:fast_sanitize, "~> 0.2.0"},
|
{:fast_sanitize, "~> 0.2.0"},
|
||||||
{:html_entities, "~> 0.5", override: true},
|
{:html_entities, "~> 0.5", override: true},
|
||||||
{:phoenix_html, "~> 2.14"},
|
{:phoenix_html, "~> 3.1", override: true},
|
||||||
{:calendar, "~> 1.0"},
|
{:calendar, "~> 1.0"},
|
||||||
{:cachex, "~> 3.2"},
|
{:cachex, "~> 3.2"},
|
||||||
{:poison, "~> 3.0", override: true},
|
{:poison, "~> 3.0", override: true},
|
||||||
|
@ -137,6 +137,7 @@ defp deps do
|
||||||
{:castore, "~> 0.1"},
|
{:castore, "~> 0.1"},
|
||||||
{:cowlib, "~> 2.9", override: true},
|
{:cowlib, "~> 2.9", override: true},
|
||||||
{:gun, "~> 2.0.0-rc.1", override: true},
|
{:gun, "~> 2.0.0-rc.1", override: true},
|
||||||
|
{:finch, "~> 0.10.0"},
|
||||||
{:jason, "~> 1.2"},
|
{:jason, "~> 1.2"},
|
||||||
{:mogrify, "~> 0.9.1"},
|
{:mogrify, "~> 0.9.1"},
|
||||||
{:ex_aws, "~> 2.1.6"},
|
{:ex_aws, "~> 2.1.6"},
|
||||||
|
@ -192,11 +193,11 @@ defp deps do
|
||||||
git: "https://git.pleroma.social/pleroma/elixir-libraries/elixir-captcha.git",
|
git: "https://git.pleroma.social/pleroma/elixir-libraries/elixir-captcha.git",
|
||||||
ref: "e0f16822d578866e186a0974d65ad58cddc1e2ab"},
|
ref: "e0f16822d578866e186a0974d65ad58cddc1e2ab"},
|
||||||
{:restarter, path: "./restarter"},
|
{:restarter, path: "./restarter"},
|
||||||
{:majic,
|
{:majic, "~> 1.0"},
|
||||||
git: "https://git.pleroma.social/pleroma/elixir-libraries/majic.git",
|
|
||||||
ref: "289cda1b6d0d70ccb2ba508a2b0bd24638db2880"},
|
|
||||||
{:eblurhash, "~> 1.1.0"},
|
{:eblurhash, "~> 1.1.0"},
|
||||||
{:open_api_spex, "~> 3.10"},
|
{:open_api_spex, "~> 3.10"},
|
||||||
|
{:phoenix_live_dashboard, "~> 0.6.2"},
|
||||||
|
{:ecto_psql_extras, "~> 0.6"},
|
||||||
|
|
||||||
# indirect dependency version override
|
# indirect dependency version override
|
||||||
{:plug, "~> 1.10.4", override: true},
|
{:plug, "~> 1.10.4", override: true},
|
||||||
|
@ -208,7 +209,7 @@ defp deps do
|
||||||
{:mock, "~> 0.3.5", only: :test},
|
{:mock, "~> 0.3.5", only: :test},
|
||||||
# temporary downgrade for excoveralls, hackney until hackney max_connections bug will be fixed
|
# temporary downgrade for excoveralls, hackney until hackney max_connections bug will be fixed
|
||||||
{:excoveralls, "0.12.3", only: :test},
|
{:excoveralls, "0.12.3", only: :test},
|
||||||
{:hackney, "~> 1.17.0", override: true},
|
{:hackney, "~> 1.18.0", override: true},
|
||||||
{:mox, "~> 1.0", only: :test},
|
{:mox, "~> 1.0", only: :test},
|
||||||
{:websocket_client, git: "https://github.com/jeremyong/websocket_client.git", only: :test}
|
{:websocket_client, git: "https://github.com/jeremyong/websocket_client.git", only: :test}
|
||||||
] ++ oauth_deps()
|
] ++ oauth_deps()
|
||||||
|
|
20
mix.lock
20
mix.lock
|
@ -11,7 +11,7 @@
|
||||||
"calendar": {:hex, :calendar, "1.0.0", "f52073a708528482ec33d0a171954ca610fe2bd28f1e871f247dc7f1565fa807", [:mix], [{:tzdata, "~> 0.5.20 or ~> 0.1.201603 or ~> 1.0", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm", "990e9581920c82912a5ee50e62ff5ef96da6b15949a2ee4734f935fdef0f0a6f"},
|
"calendar": {:hex, :calendar, "1.0.0", "f52073a708528482ec33d0a171954ca610fe2bd28f1e871f247dc7f1565fa807", [:mix], [{:tzdata, "~> 0.5.20 or ~> 0.1.201603 or ~> 1.0", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm", "990e9581920c82912a5ee50e62ff5ef96da6b15949a2ee4734f935fdef0f0a6f"},
|
||||||
"captcha": {:git, "https://git.pleroma.social/pleroma/elixir-libraries/elixir-captcha.git", "e0f16822d578866e186a0974d65ad58cddc1e2ab", [ref: "e0f16822d578866e186a0974d65ad58cddc1e2ab"]},
|
"captcha": {:git, "https://git.pleroma.social/pleroma/elixir-libraries/elixir-captcha.git", "e0f16822d578866e186a0974d65ad58cddc1e2ab", [ref: "e0f16822d578866e186a0974d65ad58cddc1e2ab"]},
|
||||||
"castore": {:hex, :castore, "0.1.10", "b01a007416a0ae4188e70b3b306236021b16c11474038ead7aff79dd75538c23", [:mix], [], "hexpm", "a48314e0cb45682db2ea27b8ebfa11bd6fa0a6e21a65e5772ad83ca136ff2665"},
|
"castore": {:hex, :castore, "0.1.10", "b01a007416a0ae4188e70b3b306236021b16c11474038ead7aff79dd75538c23", [:mix], [], "hexpm", "a48314e0cb45682db2ea27b8ebfa11bd6fa0a6e21a65e5772ad83ca136ff2665"},
|
||||||
"certifi": {:hex, :certifi, "2.6.1", "dbab8e5e155a0763eea978c913ca280a6b544bfa115633fa20249c3d396d9493", [:rebar3], [], "hexpm", "524c97b4991b3849dd5c17a631223896272c6b0af446778ba4675a1dff53bb7e"},
|
"certifi": {:hex, :certifi, "2.8.0", "d4fb0a6bb20b7c9c3643e22507e42f356ac090a1dcea9ab99e27e0376d695eba", [:rebar3], [], "hexpm", "6ac7efc1c6f8600b08d625292d4bbf584e14847ce1b6b5c44d983d273e1097ea"},
|
||||||
"combine": {:hex, :combine, "0.10.0", "eff8224eeb56498a2af13011d142c5e7997a80c8f5b97c499f84c841032e429f", [:mix], [], "hexpm", "1b1dbc1790073076580d0d1d64e42eae2366583e7aecd455d1215b0d16f2451b"},
|
"combine": {:hex, :combine, "0.10.0", "eff8224eeb56498a2af13011d142c5e7997a80c8f5b97c499f84c841032e429f", [:mix], [], "hexpm", "1b1dbc1790073076580d0d1d64e42eae2366583e7aecd455d1215b0d16f2451b"},
|
||||||
"comeonin": {:hex, :comeonin, "5.3.2", "5c2f893d05c56ae3f5e24c1b983c2d5dfb88c6d979c9287a76a7feb1e1d8d646", [:mix], [], "hexpm", "d0993402844c49539aeadb3fe46a3c9bd190f1ecf86b6f9ebd71957534c95f04"},
|
"comeonin": {:hex, :comeonin, "5.3.2", "5c2f893d05c56ae3f5e24c1b983c2d5dfb88c6d979c9287a76a7feb1e1d8d646", [:mix], [], "hexpm", "d0993402844c49539aeadb3fe46a3c9bd190f1ecf86b6f9ebd71957534c95f04"},
|
||||||
"concurrent_limiter": {:git, "https://git.pleroma.social/pleroma/elixir-libraries/concurrent_limiter.git", "d81be41024569330f296fc472e24198d7499ba78", [ref: "d81be41024569330f296fc472e24198d7499ba78"]},
|
"concurrent_limiter": {:git, "https://git.pleroma.social/pleroma/elixir-libraries/concurrent_limiter.git", "d81be41024569330f296fc472e24198d7499ba78", [ref: "d81be41024569330f296fc472e24198d7499ba78"]},
|
||||||
|
@ -32,6 +32,7 @@
|
||||||
"eblurhash": {:hex, :eblurhash, "1.1.0", "e10ccae762598507ebfacf0b645ed49520f2afa3e7e9943e73a91117dffce415", [:rebar3], [], "hexpm", "2e6b889d09fddd374e3c5ac57c486138768763264e99ac1074ae5fa7fc9ab51d"},
|
"eblurhash": {:hex, :eblurhash, "1.1.0", "e10ccae762598507ebfacf0b645ed49520f2afa3e7e9943e73a91117dffce415", [:rebar3], [], "hexpm", "2e6b889d09fddd374e3c5ac57c486138768763264e99ac1074ae5fa7fc9ab51d"},
|
||||||
"ecto": {:hex, :ecto, "3.6.2", "efdf52acfc4ce29249bab5417415bd50abd62db7b0603b8bab0d7b996548c2bc", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "efad6dfb04e6f986b8a3047822b0f826d9affe8e4ebdd2aeedbfcb14fd48884e"},
|
"ecto": {:hex, :ecto, "3.6.2", "efdf52acfc4ce29249bab5417415bd50abd62db7b0603b8bab0d7b996548c2bc", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "efad6dfb04e6f986b8a3047822b0f826d9affe8e4ebdd2aeedbfcb14fd48884e"},
|
||||||
"ecto_enum": {:hex, :ecto_enum, "1.4.0", "d14b00e04b974afc69c251632d1e49594d899067ee2b376277efd8233027aec8", [:mix], [{:ecto, ">= 3.0.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:ecto_sql, "> 3.0.0", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:mariaex, ">= 0.0.0", [hex: :mariaex, repo: "hexpm", optional: true]}, {:postgrex, ">= 0.0.0", [hex: :postgrex, repo: "hexpm", optional: true]}], "hexpm", "8fb55c087181c2b15eee406519dc22578fa60dd82c088be376d0010172764ee4"},
|
"ecto_enum": {:hex, :ecto_enum, "1.4.0", "d14b00e04b974afc69c251632d1e49594d899067ee2b376277efd8233027aec8", [:mix], [{:ecto, ">= 3.0.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:ecto_sql, "> 3.0.0", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:mariaex, ">= 0.0.0", [hex: :mariaex, repo: "hexpm", optional: true]}, {:postgrex, ">= 0.0.0", [hex: :postgrex, repo: "hexpm", optional: true]}], "hexpm", "8fb55c087181c2b15eee406519dc22578fa60dd82c088be376d0010172764ee4"},
|
||||||
|
"ecto_psql_extras": {:hex, :ecto_psql_extras, "0.7.4", "5d43fd088d39a158c860b17e8d210669587f63ec89ea122a4654861c8c6e2db4", [:mix], [{:ecto_sql, "~> 3.4", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:postgrex, ">= 0.15.7", [hex: :postgrex, repo: "hexpm", optional: false]}, {:table_rex, "~> 3.1.1", [hex: :table_rex, repo: "hexpm", optional: false]}], "hexpm", "311db02f1b772e3d0dc7f56a05044b5e1499d78ed6abf38885e1ca70059449e5"},
|
||||||
"ecto_sql": {:hex, :ecto_sql, "3.6.2", "9526b5f691701a5181427634c30655ac33d11e17e4069eff3ae1176c764e0ba3", [:mix], [{:db_connection, "~> 2.2", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.6.2", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.4.0 or ~> 0.5.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.15.0 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "5ec9d7e6f742ea39b63aceaea9ac1d1773d574ea40df5a53ef8afbd9242fdb6b"},
|
"ecto_sql": {:hex, :ecto_sql, "3.6.2", "9526b5f691701a5181427634c30655ac33d11e17e4069eff3ae1176c764e0ba3", [:mix], [{:db_connection, "~> 2.2", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.6.2", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.4.0 or ~> 0.5.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.15.0 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "5ec9d7e6f742ea39b63aceaea9ac1d1773d574ea40df5a53ef8afbd9242fdb6b"},
|
||||||
"eimp": {:hex, :eimp, "1.0.14", "fc297f0c7e2700457a95a60c7010a5f1dcb768a083b6d53f49cd94ab95a28f22", [:rebar3], [{:p1_utils, "1.0.18", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "501133f3112079b92d9e22da8b88bf4f0e13d4d67ae9c15c42c30bd25ceb83b6"},
|
"eimp": {:hex, :eimp, "1.0.14", "fc297f0c7e2700457a95a60c7010a5f1dcb768a083b6d53f49cd94ab95a28f22", [:rebar3], [{:p1_utils, "1.0.18", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "501133f3112079b92d9e22da8b88bf4f0e13d4d67ae9c15c42c30bd25ceb83b6"},
|
||||||
"elixir_make": {:hex, :elixir_make, "0.6.2", "7dffacd77dec4c37b39af867cedaabb0b59f6a871f89722c25b28fcd4bd70530", [:mix], [], "hexpm", "03e49eadda22526a7e5279d53321d1cced6552f344ba4e03e619063de75348d9"},
|
"elixir_make": {:hex, :elixir_make, "0.6.2", "7dffacd77dec4c37b39af867cedaabb0b59f6a871f89722c25b28fcd4bd70530", [:mix], [], "hexpm", "03e49eadda22526a7e5279d53321d1cced6552f344ba4e03e619063de75348d9"},
|
||||||
|
@ -45,9 +46,10 @@
|
||||||
"ex_machina": {:hex, :ex_machina, "2.7.0", "b792cc3127fd0680fecdb6299235b4727a4944a09ff0fa904cc639272cd92dc7", [:mix], [{:ecto, "~> 2.2 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: true]}, {:ecto_sql, "~> 3.0", [hex: :ecto_sql, repo: "hexpm", optional: true]}], "hexpm", "419aa7a39bde11894c87a615c4ecaa52d8f107bbdd81d810465186f783245bf8"},
|
"ex_machina": {:hex, :ex_machina, "2.7.0", "b792cc3127fd0680fecdb6299235b4727a4944a09ff0fa904cc639272cd92dc7", [:mix], [{:ecto, "~> 2.2 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: true]}, {:ecto_sql, "~> 3.0", [hex: :ecto_sql, repo: "hexpm", optional: true]}], "hexpm", "419aa7a39bde11894c87a615c4ecaa52d8f107bbdd81d810465186f783245bf8"},
|
||||||
"ex_syslogger": {:hex, :ex_syslogger, "1.5.2", "72b6aa2d47a236e999171f2e1ec18698740f40af0bd02c8c650bf5f1fd1bac79", [:mix], [{:poison, ">= 1.5.0", [hex: :poison, repo: "hexpm", optional: true]}, {:syslog, "~> 1.1.0", [hex: :syslog, repo: "hexpm", optional: false]}], "hexpm", "ab9fab4136dbc62651ec6f16fa4842f10cf02ab4433fa3d0976c01be99398399"},
|
"ex_syslogger": {:hex, :ex_syslogger, "1.5.2", "72b6aa2d47a236e999171f2e1ec18698740f40af0bd02c8c650bf5f1fd1bac79", [:mix], [{:poison, ">= 1.5.0", [hex: :poison, repo: "hexpm", optional: true]}, {:syslog, "~> 1.1.0", [hex: :syslog, repo: "hexpm", optional: false]}], "hexpm", "ab9fab4136dbc62651ec6f16fa4842f10cf02ab4433fa3d0976c01be99398399"},
|
||||||
"excoveralls": {:hex, :excoveralls, "0.12.3", "2142be7cb978a3ae78385487edda6d1aff0e482ffc6123877bb7270a8ffbcfe0", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "568a3e616c264283f5dea5b020783ae40eef3f7ee2163f7a67cbd7b35bcadada"},
|
"excoveralls": {:hex, :excoveralls, "0.12.3", "2142be7cb978a3ae78385487edda6d1aff0e482ffc6123877bb7270a8ffbcfe0", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "568a3e616c264283f5dea5b020783ae40eef3f7ee2163f7a67cbd7b35bcadada"},
|
||||||
"fast_html": {:hex, :fast_html, "2.0.4", "4910ee49f2f6b19692e3bf30bf97f1b6b7dac489cd6b0f34cd0fe3042c56ba30", [:make, :mix], [{:elixir_make, "~> 0.4", [hex: :elixir_make, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 0.1.0", [hex: :nimble_pool, repo: "hexpm", optional: false]}], "hexpm", "3bb49d541dfc02ad5e425904f53376d758c09f89e521afc7d2b174b3227761ea"},
|
"fast_html": {:hex, :fast_html, "2.0.5", "c61760340606c1077ff1f196f17834056cb1dd3d5cb92a9f2cabf28bc6221c3c", [:make, :mix], [{:elixir_make, "~> 0.4", [hex: :elixir_make, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 0.2.0", [hex: :nimble_pool, repo: "hexpm", optional: false]}], "hexpm", "605f4f4829443c14127694ebabb681778712ceecb4470ec32aa31012330e6506"},
|
||||||
"fast_sanitize": {:hex, :fast_sanitize, "0.2.2", "3cbbaebaea6043865dfb5b4ecb0f1af066ad410a51470e353714b10c42007b81", [:mix], [{:fast_html, "~> 2.0", [hex: :fast_html, repo: "hexpm", optional: false]}, {:plug, "~> 1.8", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "69f204db9250afa94a0d559d9110139850f57de2b081719fbafa1e9a89e94466"},
|
"fast_sanitize": {:hex, :fast_sanitize, "0.2.2", "3cbbaebaea6043865dfb5b4ecb0f1af066ad410a51470e353714b10c42007b81", [:mix], [{:fast_html, "~> 2.0", [hex: :fast_html, repo: "hexpm", optional: false]}, {:plug, "~> 1.8", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "69f204db9250afa94a0d559d9110139850f57de2b081719fbafa1e9a89e94466"},
|
||||||
"file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"},
|
"file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"},
|
||||||
|
"finch": {:hex, :finch, "0.10.0", "8e5e6101ae98e7f1ef830594f774411a2f9cbce4f92d8179502da69fbbff52bc", [:mix], [{:castore, "~> 0.1", [hex: :castore, repo: "hexpm", optional: false]}, {:mint, "~> 1.3", [hex: :mint, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.4.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 0.2", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "80324ba22edbdebca6fac05c8517e7457b79dfe101e3bf6b2f7c5c65c93a9077"},
|
||||||
"flake_id": {:hex, :flake_id, "0.1.0", "7716b086d2e405d09b647121a166498a0d93d1a623bead243e1f74216079ccb3", [:mix], [{:base62, "~> 1.2", [hex: :base62, repo: "hexpm", optional: false]}, {:ecto, ">= 2.0.0", [hex: :ecto, repo: "hexpm", optional: true]}], "hexpm", "31fc8090fde1acd267c07c36ea7365b8604055f897d3a53dd967658c691bd827"},
|
"flake_id": {:hex, :flake_id, "0.1.0", "7716b086d2e405d09b647121a166498a0d93d1a623bead243e1f74216079ccb3", [:mix], [{:base62, "~> 1.2", [hex: :base62, repo: "hexpm", optional: false]}, {:ecto, ">= 2.0.0", [hex: :ecto, repo: "hexpm", optional: true]}], "hexpm", "31fc8090fde1acd267c07c36ea7365b8604055f897d3a53dd967658c691bd827"},
|
||||||
"floki": {:hex, :floki, "0.30.1", "75d35526d3a1459920b6e87fdbc2e0b8a3670f965dd0903708d2b267e0904c55", [:mix], [{:html_entities, "~> 0.5.0", [hex: :html_entities, repo: "hexpm", optional: false]}], "hexpm", "e9c03524447d1c4cbfccd672d739b8c18453eee377846b119d4fd71b1a176bb8"},
|
"floki": {:hex, :floki, "0.30.1", "75d35526d3a1459920b6e87fdbc2e0b8a3670f965dd0903708d2b267e0904c55", [:mix], [{:html_entities, "~> 0.5.0", [hex: :html_entities, repo: "hexpm", optional: false]}], "hexpm", "e9c03524447d1c4cbfccd672d739b8c18453eee377846b119d4fd71b1a176bb8"},
|
||||||
"gen_smtp": {:hex, :gen_smtp, "0.15.0", "9f51960c17769b26833b50df0b96123605a8024738b62db747fece14eb2fbfcc", [:rebar3], [], "hexpm", "29bd14a88030980849c7ed2447b8db6d6c9278a28b11a44cafe41b791205440f"},
|
"gen_smtp": {:hex, :gen_smtp, "0.15.0", "9f51960c17769b26833b50df0b96123605a8024738b62db747fece14eb2fbfcc", [:rebar3], [], "hexpm", "29bd14a88030980849c7ed2447b8db6d6c9278a28b11a44cafe41b791205440f"},
|
||||||
|
@ -55,7 +57,7 @@
|
||||||
"gen_state_machine": {:hex, :gen_state_machine, "2.0.5", "9ac15ec6e66acac994cc442dcc2c6f9796cf380ec4b08267223014be1c728a95", [:mix], [], "hexpm"},
|
"gen_state_machine": {:hex, :gen_state_machine, "2.0.5", "9ac15ec6e66acac994cc442dcc2c6f9796cf380ec4b08267223014be1c728a95", [:mix], [], "hexpm"},
|
||||||
"gettext": {:hex, :gettext, "0.18.2", "7df3ea191bb56c0309c00a783334b288d08a879f53a7014341284635850a6e55", [:mix], [], "hexpm", "f9f537b13d4fdd30f3039d33cb80144c3aa1f8d9698e47d7bcbcc8df93b1f5c5"},
|
"gettext": {:hex, :gettext, "0.18.2", "7df3ea191bb56c0309c00a783334b288d08a879f53a7014341284635850a6e55", [:mix], [], "hexpm", "f9f537b13d4fdd30f3039d33cb80144c3aa1f8d9698e47d7bcbcc8df93b1f5c5"},
|
||||||
"gun": {:hex, :gun, "2.0.0-rc.2", "7c489a32dedccb77b6e82d1f3c5a7dadfbfa004ec14e322cdb5e579c438632d2", [:make, :rebar3], [{:cowlib, "2.11.0", [hex: :cowlib, repo: "hexpm", optional: false]}], "hexpm", "6b9d1eae146410d727140dbf8b404b9631302ecc2066d1d12f22097ad7d254fc"},
|
"gun": {:hex, :gun, "2.0.0-rc.2", "7c489a32dedccb77b6e82d1f3c5a7dadfbfa004ec14e322cdb5e579c438632d2", [:make, :rebar3], [{:cowlib, "2.11.0", [hex: :cowlib, repo: "hexpm", optional: false]}], "hexpm", "6b9d1eae146410d727140dbf8b404b9631302ecc2066d1d12f22097ad7d254fc"},
|
||||||
"hackney": {:hex, :hackney, "1.17.4", "99da4674592504d3fb0cfef0db84c3ba02b4508bae2dff8c0108baa0d6e0977c", [:rebar3], [{:certifi, "~>2.6.1", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~>6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~>1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.3.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~>1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~>0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "de16ff4996556c8548d512f4dbe22dd58a587bf3332e7fd362430a7ef3986b16"},
|
"hackney": {:hex, :hackney, "1.18.0", "c4443d960bb9fba6d01161d01cd81173089686717d9490e5d3606644c48d121f", [:rebar3], [{:certifi, "~>2.8.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~>6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~>1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.3.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~>1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~>0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "9afcda620704d720db8c6a3123e9848d09c87586dc1c10479c42627b905b5c5e"},
|
||||||
"html_entities": {:hex, :html_entities, "0.5.2", "9e47e70598da7de2a9ff6af8758399251db6dbb7eebe2b013f2bbd2515895c3c", [:mix], [], "hexpm", "c53ba390403485615623b9531e97696f076ed415e8d8058b1dbaa28181f4fdcc"},
|
"html_entities": {:hex, :html_entities, "0.5.2", "9e47e70598da7de2a9ff6af8758399251db6dbb7eebe2b013f2bbd2515895c3c", [:mix], [], "hexpm", "c53ba390403485615623b9531e97696f076ed415e8d8058b1dbaa28181f4fdcc"},
|
||||||
"html_sanitize_ex": {:hex, :html_sanitize_ex, "1.3.0", "f005ad692b717691203f940c686208aa3d8ffd9dd4bb3699240096a51fa9564e", [:mix], [{:mochiweb, "~> 2.15", [hex: :mochiweb, repo: "hexpm", optional: false]}], "hexpm"},
|
"html_sanitize_ex": {:hex, :html_sanitize_ex, "1.3.0", "f005ad692b717691203f940c686208aa3d8ffd9dd4bb3699240096a51fa9564e", [:mix], [{:mochiweb, "~> 2.15", [hex: :mochiweb, repo: "hexpm", optional: false]}], "hexpm"},
|
||||||
"http_signatures": {:hex, :http_signatures, "0.1.1", "ca7ebc1b61542b163644c8c3b1f0e0f41037d35f2395940d3c6c7deceab41fd8", [:mix], [], "hexpm", "cc3b8a007322cc7b624c0c15eec49ee58ac977254ff529a3c482f681465942a3"},
|
"http_signatures": {:hex, :http_signatures, "0.1.1", "ca7ebc1b61542b163644c8c3b1f0e0f41037d35f2395940d3c6c7deceab41fd8", [:mix], [], "hexpm", "cc3b8a007322cc7b624c0c15eec49ee58ac977254ff529a3c482f681465942a3"},
|
||||||
|
@ -68,7 +70,7 @@
|
||||||
"jumper": {:hex, :jumper, "1.0.1", "3c00542ef1a83532b72269fab9f0f0c82bf23a35e27d278bfd9ed0865cecabff", [:mix], [], "hexpm", "318c59078ac220e966d27af3646026db9b5a5e6703cb2aa3e26bcfaba65b7433"},
|
"jumper": {:hex, :jumper, "1.0.1", "3c00542ef1a83532b72269fab9f0f0c82bf23a35e27d278bfd9ed0865cecabff", [:mix], [], "hexpm", "318c59078ac220e966d27af3646026db9b5a5e6703cb2aa3e26bcfaba65b7433"},
|
||||||
"libring": {:hex, :libring, "1.4.0", "41246ba2f3fbc76b3971f6bce83119dfec1eee17e977a48d8a9cfaaf58c2a8d6", [:mix], [], "hexpm"},
|
"libring": {:hex, :libring, "1.4.0", "41246ba2f3fbc76b3971f6bce83119dfec1eee17e977a48d8a9cfaaf58c2a8d6", [:mix], [], "hexpm"},
|
||||||
"linkify": {:hex, :linkify, "0.5.1", "6dc415cbc948b2f6ecec7cb226aab7ba9d3a1815bb501ae33e042334d707ecee", [:mix], [], "hexpm", "a3128c7e22fada4aa7214009501d8131e1fa3faf2f0a68b33dba379dc84ff944"},
|
"linkify": {:hex, :linkify, "0.5.1", "6dc415cbc948b2f6ecec7cb226aab7ba9d3a1815bb501ae33e042334d707ecee", [:mix], [], "hexpm", "a3128c7e22fada4aa7214009501d8131e1fa3faf2f0a68b33dba379dc84ff944"},
|
||||||
"majic": {:git, "https://git.pleroma.social/pleroma/elixir-libraries/majic.git", "289cda1b6d0d70ccb2ba508a2b0bd24638db2880", [ref: "289cda1b6d0d70ccb2ba508a2b0bd24638db2880"]},
|
"majic": {:hex, :majic, "1.0.0", "37e50648db5f5c2ff0c9fb46454d034d11596c03683807b9fb3850676ffdaab3", [:make, :mix], [{:elixir_make, "~> 0.6.1", [hex: :elixir_make, repo: "hexpm", optional: false]}, {:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 0.2", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "7905858f76650d49695f14ea55cd9aaaee0c6654fa391671d4cf305c275a0a9e"},
|
||||||
"makeup": {:hex, :makeup, "1.0.5", "d5a830bc42c9800ce07dd97fa94669dfb93d3bf5fcf6ea7a0c67b2e0e4a7f26c", [:mix], [{:nimble_parsec, "~> 0.5 or ~> 1.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "cfa158c02d3f5c0c665d0af11512fed3fba0144cf1aadee0f2ce17747fba2ca9"},
|
"makeup": {:hex, :makeup, "1.0.5", "d5a830bc42c9800ce07dd97fa94669dfb93d3bf5fcf6ea7a0c67b2e0e4a7f26c", [:mix], [{:nimble_parsec, "~> 0.5 or ~> 1.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "cfa158c02d3f5c0c665d0af11512fed3fba0144cf1aadee0f2ce17747fba2ca9"},
|
||||||
"makeup_elixir": {:hex, :makeup_elixir, "0.14.1", "4f0e96847c63c17841d42c08107405a005a2680eb9c7ccadfd757bd31dabccfb", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "f2438b1a80eaec9ede832b5c41cd4f373b38fd7aa33e3b22d9db79e640cbde11"},
|
"makeup_elixir": {:hex, :makeup_elixir, "0.14.1", "4f0e96847c63c17841d42c08107405a005a2680eb9c7ccadfd757bd31dabccfb", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "f2438b1a80eaec9ede832b5c41cd4f373b38fd7aa33e3b22d9db79e640cbde11"},
|
||||||
"makeup_erlang": {:hex, :makeup_erlang, "0.1.1", "3fcb7f09eb9d98dc4d208f49cc955a34218fc41ff6b84df7c75b3e6e533cc65f", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "174d0809e98a4ef0b3309256cbf97101c6ec01c4ab0b23e926a9e17df2077cbb"},
|
"makeup_erlang": {:hex, :makeup_erlang, "0.1.1", "3fcb7f09eb9d98dc4d208f49cc955a34218fc41ff6b84df7c75b3e6e533cc65f", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "174d0809e98a4ef0b3309256cbf97101c6ec01c4ab0b23e926a9e17df2077cbb"},
|
||||||
|
@ -76,13 +78,15 @@
|
||||||
"metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"},
|
"metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"},
|
||||||
"mime": {:hex, :mime, "1.6.0", "dabde576a497cef4bbdd60aceee8160e02a6c89250d6c0b29e56c0dfb00db3d2", [:mix], [], "hexpm", "31a1a8613f8321143dde1dafc36006a17d28d02bdfecb9e95a880fa7aabd19a7"},
|
"mime": {:hex, :mime, "1.6.0", "dabde576a497cef4bbdd60aceee8160e02a6c89250d6c0b29e56c0dfb00db3d2", [:mix], [], "hexpm", "31a1a8613f8321143dde1dafc36006a17d28d02bdfecb9e95a880fa7aabd19a7"},
|
||||||
"mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"},
|
"mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"},
|
||||||
|
"mint": {:hex, :mint, "1.4.0", "cd7d2451b201fc8e4a8fd86257fb3878d9e3752899eb67b0c5b25b180bde1212", [:mix], [{:castore, "~> 0.1.0", [hex: :castore, repo: "hexpm", optional: true]}], "hexpm", "10a99e144b815cbf8522dccbc8199d15802440fc7a64d67b6853adb6fa170217"},
|
||||||
"mochiweb": {:hex, :mochiweb, "2.18.0", "eb55f1db3e6e960fac4e6db4e2db9ec3602cc9f30b86cd1481d56545c3145d2e", [:rebar3], [], "hexpm"},
|
"mochiweb": {:hex, :mochiweb, "2.18.0", "eb55f1db3e6e960fac4e6db4e2db9ec3602cc9f30b86cd1481d56545c3145d2e", [:rebar3], [], "hexpm"},
|
||||||
"mock": {:hex, :mock, "0.3.7", "75b3bbf1466d7e486ea2052a73c6e062c6256fb429d6797999ab02fa32f29e03", [:mix], [{:meck, "~> 0.9.2", [hex: :meck, repo: "hexpm", optional: false]}], "hexpm", "4da49a4609e41fd99b7836945c26f373623ea968cfb6282742bcb94440cf7e5c"},
|
"mock": {:hex, :mock, "0.3.7", "75b3bbf1466d7e486ea2052a73c6e062c6256fb429d6797999ab02fa32f29e03", [:mix], [{:meck, "~> 0.9.2", [hex: :meck, repo: "hexpm", optional: false]}], "hexpm", "4da49a4609e41fd99b7836945c26f373623ea968cfb6282742bcb94440cf7e5c"},
|
||||||
"mogrify": {:hex, :mogrify, "0.9.1", "a26f107c4987477769f272bd0f7e3ac4b7b75b11ba597fd001b877beffa9c068", [:mix], [], "hexpm", "134edf189337d2125c0948bf0c228fdeef975c594317452d536224069a5b7f05"},
|
"mogrify": {:hex, :mogrify, "0.9.1", "a26f107c4987477769f272bd0f7e3ac4b7b75b11ba597fd001b877beffa9c068", [:mix], [], "hexpm", "134edf189337d2125c0948bf0c228fdeef975c594317452d536224069a5b7f05"},
|
||||||
"mox": {:hex, :mox, "1.0.0", "4b3c7005173f47ff30641ba044eb0fe67287743eec9bd9545e37f3002b0a9f8b", [:mix], [], "hexpm", "201b0a20b7abdaaab083e9cf97884950f8a30a1350a1da403b3145e213c6f4df"},
|
"mox": {:hex, :mox, "1.0.0", "4b3c7005173f47ff30641ba044eb0fe67287743eec9bd9545e37f3002b0a9f8b", [:mix], [], "hexpm", "201b0a20b7abdaaab083e9cf97884950f8a30a1350a1da403b3145e213c6f4df"},
|
||||||
"myhtmlex": {:git, "https://git.pleroma.social/pleroma/myhtmlex.git", "ad0097e2f61d4953bfef20fb6abddf23b87111e6", [ref: "ad0097e2f61d4953bfef20fb6abddf23b87111e6", submodules: true]},
|
"myhtmlex": {:git, "https://git.pleroma.social/pleroma/myhtmlex.git", "ad0097e2f61d4953bfef20fb6abddf23b87111e6", [ref: "ad0097e2f61d4953bfef20fb6abddf23b87111e6", submodules: true]},
|
||||||
|
"nimble_options": {:hex, :nimble_options, "0.4.0", "c89babbab52221a24b8d1ff9e7d838be70f0d871be823165c94dd3418eea728f", [:mix], [], "hexpm", "e6701c1af326a11eea9634a3b1c62b475339ace9456c1a23ec3bc9a847bca02d"},
|
||||||
"nimble_parsec": {:hex, :nimble_parsec, "0.5.0", "90e2eca3d0266e5c53f8fbe0079694740b9c91b6747f2b7e3c5d21966bba8300", [:mix], [], "hexpm", "5c040b8469c1ff1b10093d3186e2e10dbe483cd73d79ec017993fb3985b8a9b3"},
|
"nimble_parsec": {:hex, :nimble_parsec, "0.5.0", "90e2eca3d0266e5c53f8fbe0079694740b9c91b6747f2b7e3c5d21966bba8300", [:mix], [], "hexpm", "5c040b8469c1ff1b10093d3186e2e10dbe483cd73d79ec017993fb3985b8a9b3"},
|
||||||
"nimble_pool": {:hex, :nimble_pool, "0.1.0", "ffa9d5be27eee2b00b0c634eb649aa27f97b39186fec3c493716c2a33e784ec6", [:mix], [], "hexpm", "343a1eaa620ddcf3430a83f39f2af499fe2370390d4f785cd475b4df5acaf3f9"},
|
"nimble_pool": {:hex, :nimble_pool, "0.2.4", "1db8e9f8a53d967d595e0b32a17030cdb6c0dc4a451b8ac787bf601d3f7704c3", [:mix], [], "hexpm", "367e8071e137b787764e6a9992ccb57b276dc2282535f767a07d881951ebeac6"},
|
||||||
"nodex": {:git, "https://git.pleroma.social/pleroma/nodex", "cb6730f943cfc6aad674c92161be23a8411f15d1", [ref: "cb6730f943cfc6aad674c92161be23a8411f15d1"]},
|
"nodex": {:git, "https://git.pleroma.social/pleroma/nodex", "cb6730f943cfc6aad674c92161be23a8411f15d1", [ref: "cb6730f943cfc6aad674c92161be23a8411f15d1"]},
|
||||||
"oban": {:hex, :oban, "2.3.4", "ec7509b9af2524d55f529cb7aee93d36131ae0bf0f37706f65d2fe707f4d9fd8", [:mix], [{:ecto_sql, ">= 3.4.3", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.14", [hex: :postgrex, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "c70ca0434758fd1805422ea4446af5e910ddc697c0c861549c8f0eb0cfbd2fdf"},
|
"oban": {:hex, :oban, "2.3.4", "ec7509b9af2524d55f529cb7aee93d36131ae0bf0f37706f65d2fe707f4d9fd8", [:mix], [{:ecto_sql, ">= 3.4.3", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.14", [hex: :postgrex, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "c70ca0434758fd1805422ea4446af5e910ddc697c0c861549c8f0eb0cfbd2fdf"},
|
||||||
"open_api_spex": {:hex, :open_api_spex, "3.10.0", "94e9521ad525b3fcf6dc77da7c45f87fdac24756d4de588cb0816b413e7c1844", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}, {:poison, "~> 3.1", [hex: :poison, repo: "hexpm", optional: true]}], "hexpm", "2dbb2bde3d2b821f06936e8dfaf3284331186556291946d84eeba3750ac28765"},
|
"open_api_spex": {:hex, :open_api_spex, "3.10.0", "94e9521ad525b3fcf6dc77da7c45f87fdac24756d4de588cb0816b413e7c1844", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}, {:poison, "~> 3.1", [hex: :poison, repo: "hexpm", optional: true]}], "hexpm", "2dbb2bde3d2b821f06936e8dfaf3284331186556291946d84eeba3750ac28765"},
|
||||||
|
@ -91,7 +95,9 @@
|
||||||
"pbkdf2_elixir": {:hex, :pbkdf2_elixir, "1.2.1", "9cbe354b58121075bd20eb83076900a3832324b7dd171a6895fab57b6bb2752c", [:mix], [{:comeonin, "~> 5.3", [hex: :comeonin, repo: "hexpm", optional: false]}], "hexpm", "d3b40a4a4630f0b442f19eca891fcfeeee4c40871936fed2f68e1c4faa30481f"},
|
"pbkdf2_elixir": {:hex, :pbkdf2_elixir, "1.2.1", "9cbe354b58121075bd20eb83076900a3832324b7dd171a6895fab57b6bb2752c", [:mix], [{:comeonin, "~> 5.3", [hex: :comeonin, repo: "hexpm", optional: false]}], "hexpm", "d3b40a4a4630f0b442f19eca891fcfeeee4c40871936fed2f68e1c4faa30481f"},
|
||||||
"phoenix": {:hex, :phoenix, "1.5.9", "a6368d36cfd59d917b37c44386e01315bc89f7609a10a45a22f47c007edf2597", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_html, "~> 2.13 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.0", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:plug, "~> 1.10", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 1.0 or ~> 2.2", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.1.2 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7e4bce20a67c012f1fbb0af90e5da49fa7bf0d34e3a067795703b74aef75427d"},
|
"phoenix": {:hex, :phoenix, "1.5.9", "a6368d36cfd59d917b37c44386e01315bc89f7609a10a45a22f47c007edf2597", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_html, "~> 2.13 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.0", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:plug, "~> 1.10", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 1.0 or ~> 2.2", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.1.2 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7e4bce20a67c012f1fbb0af90e5da49fa7bf0d34e3a067795703b74aef75427d"},
|
||||||
"phoenix_ecto": {:hex, :phoenix_ecto, "4.2.1", "13f124cf0a3ce0f1948cf24654c7b9f2347169ff75c1123f44674afee6af3b03", [:mix], [{:ecto, "~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14.2 or ~> 2.15", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "478a1bae899cac0a6e02be1deec7e2944b7754c04e7d4107fc5a517f877743c0"},
|
"phoenix_ecto": {:hex, :phoenix_ecto, "4.2.1", "13f124cf0a3ce0f1948cf24654c7b9f2347169ff75c1123f44674afee6af3b03", [:mix], [{:ecto, "~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14.2 or ~> 2.15", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "478a1bae899cac0a6e02be1deec7e2944b7754c04e7d4107fc5a517f877743c0"},
|
||||||
"phoenix_html": {:hex, :phoenix_html, "2.14.3", "51f720d0d543e4e157ff06b65de38e13303d5778a7919bcc696599e5934271b8", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "efd697a7fff35a13eeeb6b43db884705cba353a1a41d127d118fda5f90c8e80f"},
|
"phoenix_html": {:hex, :phoenix_html, "3.1.0", "0b499df05aad27160d697a9362f0e89fa0e24d3c7a9065c2bd9d38b4d1416c09", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "0c0a98a2cefa63433657983a2a594c7dee5927e4391e0f1bfd3a151d1def33fc"},
|
||||||
|
"phoenix_live_dashboard": {:hex, :phoenix_live_dashboard, "0.6.2", "0769470265eb13af01b5001b29cb935f4710d6adaa1ffc18417a570a337a2f0f", [:mix], [{:ecto, "~> 3.6.2 or ~> 3.7", [hex: :ecto, repo: "hexpm", optional: true]}, {:ecto_mysql_extras, "~> 0.3", [hex: :ecto_mysql_extras, repo: "hexpm", optional: true]}, {:ecto_psql_extras, "~> 0.7", [hex: :ecto_psql_extras, repo: "hexpm", optional: true]}, {:mime, "~> 1.6 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 0.17.1", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:telemetry_metrics, "~> 0.6.0", [hex: :telemetry_metrics, repo: "hexpm", optional: false]}], "hexpm", "5bc6c6b38a2ca8b5020b442322fcee6afd5e641637a0b1fb059d4bd89bc58e7b"},
|
||||||
|
"phoenix_live_view": {:hex, :phoenix_live_view, "0.17.5", "63f52a6f9f6983f04e424586ff897c016ecc5e4f8d1e2c22c2887af1c57215d8", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.5.9 or ~> 1.6.0", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.1", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "c5586e6a3d4df71b8214c769d4f5eb8ece2b4001711a7ca0f97323c36958b0e3"},
|
||||||
"phoenix_pubsub": {:hex, :phoenix_pubsub, "2.0.0", "a1ae76717bb168cdeb10ec9d92d1480fec99e3080f011402c0a2d68d47395ffb", [:mix], [], "hexpm", "c52d948c4f261577b9c6fa804be91884b381a7f8f18450c5045975435350f771"},
|
"phoenix_pubsub": {:hex, :phoenix_pubsub, "2.0.0", "a1ae76717bb168cdeb10ec9d92d1480fec99e3080f011402c0a2d68d47395ffb", [:mix], [], "hexpm", "c52d948c4f261577b9c6fa804be91884b381a7f8f18450c5045975435350f771"},
|
||||||
"phoenix_swoosh": {:hex, :phoenix_swoosh, "0.3.3", "039435dd975f7e55953525b88f1d596f26c6141412584c16f4db109708a8ee68", [:mix], [{:hackney, "~> 1.9", [hex: :hackney, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:swoosh, "~> 1.0", [hex: :swoosh, repo: "hexpm", optional: false]}], "hexpm", "4a540cea32e05356541737033d666ee7fea7700eb2101bf76783adbfe06601cd"},
|
"phoenix_swoosh": {:hex, :phoenix_swoosh, "0.3.3", "039435dd975f7e55953525b88f1d596f26c6141412584c16f4db109708a8ee68", [:mix], [{:hackney, "~> 1.9", [hex: :hackney, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:swoosh, "~> 1.0", [hex: :swoosh, repo: "hexpm", optional: false]}], "hexpm", "4a540cea32e05356541737033d666ee7fea7700eb2101bf76783adbfe06601cd"},
|
||||||
"plug": {:hex, :plug, "1.10.4", "41eba7d1a2d671faaf531fa867645bd5a3dce0957d8e2a3f398ccff7d2ef017f", [:mix], [{:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "ad1e233fe73d2eec56616568d260777b67f53148a999dc2d048f4eb9778fe4a0"},
|
"plug": {:hex, :plug, "1.10.4", "41eba7d1a2d671faaf531fa867645bd5a3dce0957d8e2a3f398ccff7d2ef017f", [:mix], [{:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "ad1e233fe73d2eec56616568d260777b67f53148a999dc2d048f4eb9778fe4a0"},
|
||||||
|
@ -117,7 +123,9 @@
|
||||||
"sweet_xml": {:hex, :sweet_xml, "0.6.6", "fc3e91ec5dd7c787b6195757fbcf0abc670cee1e4172687b45183032221b66b8", [:mix], [], "hexpm", "2e1ec458f892ffa81f9f8386e3f35a1af6db7a7a37748a64478f13163a1f3573"},
|
"sweet_xml": {:hex, :sweet_xml, "0.6.6", "fc3e91ec5dd7c787b6195757fbcf0abc670cee1e4172687b45183032221b66b8", [:mix], [], "hexpm", "2e1ec458f892ffa81f9f8386e3f35a1af6db7a7a37748a64478f13163a1f3573"},
|
||||||
"swoosh": {:hex, :swoosh, "1.3.11", "34f79c57f19892b43bd2168de9ff5de478a721a26328ef59567aad4243e7a77b", [:mix], [{:cowboy, "~> 1.1 or ~> 2.4", [hex: :cowboy, repo: "hexpm", optional: true]}, {:finch, "~> 0.6", [hex: :finch, repo: "hexpm", optional: true]}, {:gen_smtp, "~> 0.13 or ~> 1.0", [hex: :gen_smtp, repo: "hexpm", optional: true]}, {:hackney, "~> 1.9", [hex: :hackney, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mail, "~> 0.2", [hex: :mail, repo: "hexpm", optional: true]}, {:mime, "~> 1.1", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_cowboy, ">= 1.0.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}], "hexpm", "f1e2a048db454f9982b9cf840f75e7399dd48be31ecc2a7dc10012a803b913af"},
|
"swoosh": {:hex, :swoosh, "1.3.11", "34f79c57f19892b43bd2168de9ff5de478a721a26328ef59567aad4243e7a77b", [:mix], [{:cowboy, "~> 1.1 or ~> 2.4", [hex: :cowboy, repo: "hexpm", optional: true]}, {:finch, "~> 0.6", [hex: :finch, repo: "hexpm", optional: true]}, {:gen_smtp, "~> 0.13 or ~> 1.0", [hex: :gen_smtp, repo: "hexpm", optional: true]}, {:hackney, "~> 1.9", [hex: :hackney, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mail, "~> 0.2", [hex: :mail, repo: "hexpm", optional: true]}, {:mime, "~> 1.1", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_cowboy, ">= 1.0.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}], "hexpm", "f1e2a048db454f9982b9cf840f75e7399dd48be31ecc2a7dc10012a803b913af"},
|
||||||
"syslog": {:hex, :syslog, "1.1.0", "6419a232bea84f07b56dc575225007ffe34d9fdc91abe6f1b2f254fd71d8efc2", [:rebar3], [], "hexpm", "4c6a41373c7e20587be33ef841d3de6f3beba08519809329ecc4d27b15b659e1"},
|
"syslog": {:hex, :syslog, "1.1.0", "6419a232bea84f07b56dc575225007ffe34d9fdc91abe6f1b2f254fd71d8efc2", [:rebar3], [], "hexpm", "4c6a41373c7e20587be33ef841d3de6f3beba08519809329ecc4d27b15b659e1"},
|
||||||
|
"table_rex": {:hex, :table_rex, "3.1.1", "0c67164d1714b5e806d5067c1e96ff098ba7ae79413cc075973e17c38a587caa", [:mix], [], "hexpm", "678a23aba4d670419c23c17790f9dcd635a4a89022040df7d5d772cb21012490"},
|
||||||
"telemetry": {:hex, :telemetry, "0.4.3", "a06428a514bdbc63293cd9a6263aad00ddeb66f608163bdec7c8995784080818", [:rebar3], [], "hexpm", "eb72b8365ffda5bed68a620d1da88525e326cb82a75ee61354fc24b844768041"},
|
"telemetry": {:hex, :telemetry, "0.4.3", "a06428a514bdbc63293cd9a6263aad00ddeb66f608163bdec7c8995784080818", [:rebar3], [], "hexpm", "eb72b8365ffda5bed68a620d1da88525e326cb82a75ee61354fc24b844768041"},
|
||||||
|
"telemetry_metrics": {:hex, :telemetry_metrics, "0.6.1", "315d9163a1d4660aedc3fee73f33f1d355dcc76c5c3ab3d59e76e3edf80eef1f", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7be9e0871c41732c233be71e4be11b96e56177bf15dde64a8ac9ce72ac9834c6"},
|
||||||
"tesla": {:hex, :tesla, "1.4.1", "ff855f1cac121e0d16281b49e8f066c4a0d89965f98864515713878cca849ac8", [:mix], [{:castore, "~> 0.1", [hex: :castore, repo: "hexpm", optional: true]}, {:exjsx, ">= 3.0.0", [hex: :exjsx, repo: "hexpm", optional: true]}, {:finch, "~> 0.3", [hex: :finch, repo: "hexpm", optional: true]}, {:fuse, "~> 2.4", [hex: :fuse, repo: "hexpm", optional: true]}, {:gun, "~> 1.3", [hex: :gun, repo: "hexpm", optional: true]}, {:hackney, "~> 1.6", [hex: :hackney, repo: "hexpm", optional: true]}, {:ibrowse, "~> 4.4.0", [hex: :ibrowse, repo: "hexpm", optional: true]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: true]}, {:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.0", [hex: :mint, repo: "hexpm", optional: true]}, {:poison, ">= 1.0.0", [hex: :poison, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "95f5de35922c8c4b3945bee7406f66eb680b0955232f78f5fb7e853aa1ce201a"},
|
"tesla": {:hex, :tesla, "1.4.1", "ff855f1cac121e0d16281b49e8f066c4a0d89965f98864515713878cca849ac8", [:mix], [{:castore, "~> 0.1", [hex: :castore, repo: "hexpm", optional: true]}, {:exjsx, ">= 3.0.0", [hex: :exjsx, repo: "hexpm", optional: true]}, {:finch, "~> 0.3", [hex: :finch, repo: "hexpm", optional: true]}, {:fuse, "~> 2.4", [hex: :fuse, repo: "hexpm", optional: true]}, {:gun, "~> 1.3", [hex: :gun, repo: "hexpm", optional: true]}, {:hackney, "~> 1.6", [hex: :hackney, repo: "hexpm", optional: true]}, {:ibrowse, "~> 4.4.0", [hex: :ibrowse, repo: "hexpm", optional: true]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: true]}, {:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.0", [hex: :mint, repo: "hexpm", optional: true]}, {:poison, ">= 1.0.0", [hex: :poison, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "95f5de35922c8c4b3945bee7406f66eb680b0955232f78f5fb7e853aa1ce201a"},
|
||||||
"timex": {:hex, :timex, "3.7.5", "3eca56e23bfa4e0848f0b0a29a92fa20af251a975116c6d504966e8a90516dfd", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:gettext, "~> 0.10", [hex: :gettext, repo: "hexpm", optional: false]}, {:tzdata, "~> 1.0", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm", "a15608dca680f2ef663d71c95842c67f0af08a0f3b1d00e17bbd22872e2874e4"},
|
"timex": {:hex, :timex, "3.7.5", "3eca56e23bfa4e0848f0b0a29a92fa20af251a975116c6d504966e8a90516dfd", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:gettext, "~> 0.10", [hex: :gettext, repo: "hexpm", optional: false]}, {:tzdata, "~> 1.0", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm", "a15608dca680f2ef663d71c95842c67f0af08a0f3b1d00e17bbd22872e2874e4"},
|
||||||
"trailing_format_plug": {:hex, :trailing_format_plug, "0.0.7", "64b877f912cf7273bed03379936df39894149e35137ac9509117e59866e10e45", [:mix], [{:plug, "> 0.12.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "bd4fde4c15f3e993a999e019d64347489b91b7a9096af68b2bdadd192afa693f"},
|
"trailing_format_plug": {:hex, :trailing_format_plug, "0.0.7", "64b877f912cf7273bed03379936df39894149e35137ac9509117e59866e10e45", [:mix], [{:plug, "> 0.12.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "bd4fde4c15f3e993a999e019d64347489b91b7a9096af68b2bdadd192afa693f"},
|
||||||
|
|
11
priv/repo/migrations/20210818023112_add_user_id_to_apps.exs
Normal file
11
priv/repo/migrations/20210818023112_add_user_id_to_apps.exs
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
defmodule Pleroma.Repo.Migrations.AddUserIdToApps do
|
||||||
|
use Ecto.Migration
|
||||||
|
|
||||||
|
def change do
|
||||||
|
alter table(:apps) do
|
||||||
|
add(:user_id, references(:users, type: :uuid, on_delete: :delete_all))
|
||||||
|
end
|
||||||
|
|
||||||
|
create_if_not_exists(index(:apps, [:user_id]))
|
||||||
|
end
|
||||||
|
end
|
15
priv/repo/migrations/20211121000000_create_user_notes.exs
Normal file
15
priv/repo/migrations/20211121000000_create_user_notes.exs
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
defmodule Pleroma.Repo.Migrations.CreateUserNotes do
|
||||||
|
use Ecto.Migration
|
||||||
|
|
||||||
|
def change do
|
||||||
|
create_if_not_exists table(:user_notes) do
|
||||||
|
add(:source_id, references(:users, type: :uuid, on_delete: :delete_all))
|
||||||
|
add(:target_id, references(:users, type: :uuid, on_delete: :delete_all))
|
||||||
|
add(:comment, :string)
|
||||||
|
|
||||||
|
timestamps()
|
||||||
|
end
|
||||||
|
|
||||||
|
create_if_not_exists(unique_index(:user_notes, [:source_id, :target_id]))
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,11 @@
|
||||||
|
defmodule Pleroma.Repo.Migrations.ForcePinnedObjectsToExist do
|
||||||
|
use Ecto.Migration
|
||||||
|
|
||||||
|
def change do
|
||||||
|
execute("UPDATE users SET pinned_objects = '{}' WHERE pinned_objects IS NULL")
|
||||||
|
|
||||||
|
alter table("users") do
|
||||||
|
modify(:pinned_objects, :map, null: false, default: %{})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
11
priv/repo/migrations/20211126191138_add_suggestions.exs
Normal file
11
priv/repo/migrations/20211126191138_add_suggestions.exs
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
defmodule Pleroma.Repo.Migrations.AddSuggestions do
|
||||||
|
use Ecto.Migration
|
||||||
|
|
||||||
|
def change do
|
||||||
|
alter table(:users) do
|
||||||
|
add(:is_suggested, :boolean, default: false, null: false)
|
||||||
|
end
|
||||||
|
|
||||||
|
create_if_not_exists(index(:users, [:is_suggested]))
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,11 @@
|
||||||
|
defmodule Pleroma.Repo.Migrations.AddLastStatusAtToUsers do
|
||||||
|
use Ecto.Migration
|
||||||
|
|
||||||
|
def change do
|
||||||
|
alter table(:users) do
|
||||||
|
add(:last_status_at, :naive_datetime)
|
||||||
|
end
|
||||||
|
|
||||||
|
create_if_not_exists(index(:users, [:last_status_at]))
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,7 @@
|
||||||
|
defmodule Pleroma.Repo.Migrations.AddIsDiscoverableIndexToUsers do
|
||||||
|
use Ecto.Migration
|
||||||
|
|
||||||
|
def change do
|
||||||
|
create(index(:users, [:is_discoverable]))
|
||||||
|
end
|
||||||
|
end
|
|
@ -13,6 +13,7 @@ config :pleroma, Pleroma.Web.Endpoint,
|
||||||
url: [host: "<%= domain %>", scheme: "https", port: <%= port %>],
|
url: [host: "<%= domain %>", scheme: "https", port: <%= port %>],
|
||||||
http: [ip: {<%= String.replace(listen_ip, ".", ", ") %>}, port: <%= listen_port %>],
|
http: [ip: {<%= String.replace(listen_ip, ".", ", ") %>}, port: <%= listen_port %>],
|
||||||
secret_key_base: "<%= secret %>",
|
secret_key_base: "<%= secret %>",
|
||||||
|
live_view: [signing_salt: "<%= lv_signing_salt %>"],
|
||||||
signing_salt: "<%= signing_salt %>"
|
signing_salt: "<%= signing_salt %>"
|
||||||
|
|
||||||
config :pleroma, :instance,
|
config :pleroma, :instance,
|
||||||
|
|
|
@ -82,6 +82,7 @@ test "transfer config values with full subkey update" do
|
||||||
on_exit(fn -> Restarter.Pleroma.refresh() end)
|
on_exit(fn -> Restarter.Pleroma.refresh() end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@tag :erratic
|
||||||
test "don't restart if no reboot time settings were changed" do
|
test "don't restart if no reboot time settings were changed" do
|
||||||
clear_config(:emoji)
|
clear_config(:emoji)
|
||||||
insert(:config, key: :emoji, value: [groups: [a: 1, b: 2]])
|
insert(:config, key: :emoji, value: [groups: [a: 1, b: 2]])
|
||||||
|
@ -92,18 +93,21 @@ test "don't restart if no reboot time settings were changed" do
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@tag :erratic
|
||||||
test "on reboot time key" do
|
test "on reboot time key" do
|
||||||
clear_config(:shout)
|
clear_config(:shout)
|
||||||
insert(:config, key: :shout, value: [enabled: false])
|
insert(:config, key: :shout, value: [enabled: false])
|
||||||
assert capture_log(fn -> TransferTask.start_link([]) end) =~ "pleroma restarted"
|
assert capture_log(fn -> TransferTask.start_link([]) end) =~ "pleroma restarted"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@tag :erratic
|
||||||
test "on reboot time subkey" do
|
test "on reboot time subkey" do
|
||||||
clear_config(Pleroma.Captcha)
|
clear_config(Pleroma.Captcha)
|
||||||
insert(:config, key: Pleroma.Captcha, value: [seconds_valid: 60])
|
insert(:config, key: Pleroma.Captcha, value: [seconds_valid: 60])
|
||||||
assert capture_log(fn -> TransferTask.start_link([]) end) =~ "pleroma restarted"
|
assert capture_log(fn -> TransferTask.start_link([]) end) =~ "pleroma restarted"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@tag :erratic
|
||||||
test "don't restart pleroma on reboot time key and subkey if there is false flag" do
|
test "don't restart pleroma on reboot time key and subkey if there is false flag" do
|
||||||
clear_config(:shout)
|
clear_config(:shout)
|
||||||
clear_config(Pleroma.Captcha)
|
clear_config(Pleroma.Captcha)
|
||||||
|
|
|
@ -46,6 +46,7 @@ test "gives the same connection to 2 concurrent requests" do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@tag :erratic
|
||||||
test "connection limit is respected with concurrent requests" do
|
test "connection limit is respected with concurrent requests" do
|
||||||
clear_config([:connections_pool, :max_connections]) do
|
clear_config([:connections_pool, :max_connections]) do
|
||||||
clear_config([:connections_pool, :max_connections], 1)
|
clear_config([:connections_pool, :max_connections], 1)
|
||||||
|
|
|
@ -34,4 +34,14 @@ test "it returns internal users when enabled" do
|
||||||
assert %{internal: true} |> Query.build() |> Repo.aggregate(:count) == 2
|
assert %{internal: true} |> Query.build() |> Repo.aggregate(:count) == 2
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "is_suggested param" do
|
||||||
|
_user1 = insert(:user, is_suggested: false)
|
||||||
|
user2 = insert(:user, is_suggested: true)
|
||||||
|
|
||||||
|
assert [^user2] =
|
||||||
|
%{is_suggested: true}
|
||||||
|
|> User.Query.build()
|
||||||
|
|> Repo.all()
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1718,6 +1718,38 @@ test "delete/1 purges a remote user" do
|
||||||
assert user.banner == %{}
|
assert user.banner == %{}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "set_suggestion" do
|
||||||
|
test "suggests a user" do
|
||||||
|
user = insert(:user, is_suggested: false)
|
||||||
|
refute user.is_suggested
|
||||||
|
{:ok, user} = User.set_suggestion(user, true)
|
||||||
|
assert user.is_suggested
|
||||||
|
end
|
||||||
|
|
||||||
|
test "suggests a list of users" do
|
||||||
|
unsuggested_users = [
|
||||||
|
insert(:user, is_suggested: false),
|
||||||
|
insert(:user, is_suggested: false),
|
||||||
|
insert(:user, is_suggested: false)
|
||||||
|
]
|
||||||
|
|
||||||
|
{:ok, users} = User.set_suggestion(unsuggested_users, true)
|
||||||
|
|
||||||
|
assert Enum.count(users) == 3
|
||||||
|
|
||||||
|
Enum.each(users, fn user ->
|
||||||
|
assert user.is_suggested
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "unsuggests a user" do
|
||||||
|
user = insert(:user, is_suggested: true)
|
||||||
|
assert user.is_suggested
|
||||||
|
{:ok, user} = User.set_suggestion(user, false)
|
||||||
|
refute user.is_suggested
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
test "get_public_key_for_ap_id fetches a user that's not in the db" do
|
test "get_public_key_for_ap_id fetches a user that's not in the db" do
|
||||||
assert {:ok, _key} = User.get_public_key_for_ap_id("http://mastodon.example.org/users/admin")
|
assert {:ok, _key} = User.get_public_key_for_ap_id("http://mastodon.example.org/users/admin")
|
||||||
end
|
end
|
||||||
|
@ -2410,13 +2442,16 @@ test "update_last_active_at/1" do
|
||||||
test "active_user_count/1" do
|
test "active_user_count/1" do
|
||||||
insert(:user)
|
insert(:user)
|
||||||
insert(:user, %{local: false})
|
insert(:user, %{local: false})
|
||||||
insert(:user, %{last_active_at: Timex.shift(NaiveDateTime.utc_now(), weeks: -5)})
|
|
||||||
insert(:user, %{last_active_at: Timex.shift(NaiveDateTime.utc_now(), weeks: -3)})
|
|
||||||
insert(:user, %{last_active_at: NaiveDateTime.utc_now()})
|
insert(:user, %{last_active_at: NaiveDateTime.utc_now()})
|
||||||
|
insert(:user, %{last_active_at: Timex.shift(NaiveDateTime.utc_now(), days: -15)})
|
||||||
|
insert(:user, %{last_active_at: Timex.shift(NaiveDateTime.utc_now(), weeks: -6)})
|
||||||
|
insert(:user, %{last_active_at: Timex.shift(NaiveDateTime.utc_now(), months: -7)})
|
||||||
|
insert(:user, %{last_active_at: Timex.shift(NaiveDateTime.utc_now(), years: -2)})
|
||||||
|
|
||||||
assert User.active_user_count() == 2
|
assert User.active_user_count() == 2
|
||||||
assert User.active_user_count(6) == 3
|
assert User.active_user_count(180) == 3
|
||||||
assert User.active_user_count(1) == 1
|
assert User.active_user_count(365) == 4
|
||||||
|
assert User.active_user_count(1000) == 5
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "pins" do
|
describe "pins" do
|
||||||
|
|
|
@ -776,6 +776,20 @@ test "doesn't return blocked activities" do
|
||||||
assert Enum.member?(activities, activity_one)
|
assert Enum.member?(activities, activity_one)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "doesn't return activities from deactivated users" do
|
||||||
|
_user = insert(:user)
|
||||||
|
deactivated = insert(:user)
|
||||||
|
active = insert(:user)
|
||||||
|
{:ok, activity_one} = CommonAPI.post(deactivated, %{status: "hey!"})
|
||||||
|
{:ok, activity_two} = CommonAPI.post(active, %{status: "yay!"})
|
||||||
|
{:ok, _updated_user} = User.set_activation(deactivated, false)
|
||||||
|
|
||||||
|
activities = ActivityPub.fetch_activities([], %{})
|
||||||
|
|
||||||
|
refute Enum.member?(activities, activity_one)
|
||||||
|
assert Enum.member?(activities, activity_two)
|
||||||
|
end
|
||||||
|
|
||||||
test "always see your own posts even when they address people you block" do
|
test "always see your own posts even when they address people you block" do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
blockee = insert(:user)
|
blockee = insert(:user)
|
||||||
|
|
|
@ -105,5 +105,37 @@ test "it handles image dimensions" do
|
||||||
|
|
||||||
assert attachment.mediaType == "image/jpeg"
|
assert attachment.mediaType == "image/jpeg"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "it transforms image dimentions to our internal format" do
|
||||||
|
attachment = %{
|
||||||
|
"type" => "Document",
|
||||||
|
"name" => "Hello world",
|
||||||
|
"url" => "https://media.example.tld/1.jpg",
|
||||||
|
"width" => 880,
|
||||||
|
"height" => 960,
|
||||||
|
"mediaType" => "image/jpeg",
|
||||||
|
"blurhash" => "eTKL26+HDjcEIBVl;ds+K6t301W.t7nit7y1E,R:v}ai4nXSt7V@of"
|
||||||
|
}
|
||||||
|
|
||||||
|
expected = %AttachmentValidator{
|
||||||
|
type: "Document",
|
||||||
|
name: "Hello world",
|
||||||
|
mediaType: "image/jpeg",
|
||||||
|
blurhash: "eTKL26+HDjcEIBVl;ds+K6t301W.t7nit7y1E,R:v}ai4nXSt7V@of",
|
||||||
|
url: [
|
||||||
|
%AttachmentValidator.UrlObjectValidator{
|
||||||
|
type: "Link",
|
||||||
|
mediaType: "image/jpeg",
|
||||||
|
href: "https://media.example.tld/1.jpg",
|
||||||
|
width: 880,
|
||||||
|
height: 960
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
{:ok, ^expected} =
|
||||||
|
AttachmentValidator.cast_and_validate(attachment)
|
||||||
|
|> Ecto.Changeset.apply_action(:insert)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -88,6 +88,16 @@ test "it unfollows and blocks", %{user: user, blocked: blocked, block: block} do
|
||||||
assert User.blocks?(user, blocked)
|
assert User.blocks?(user, blocked)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "it updates following relationship", %{user: user, blocked: blocked, block: block} do
|
||||||
|
{:ok, _, _} = SideEffects.handle(block)
|
||||||
|
|
||||||
|
refute Pleroma.FollowingRelationship.get(user, blocked)
|
||||||
|
assert User.get_follow_state(user, blocked) == nil
|
||||||
|
assert User.get_follow_state(blocked, user) == nil
|
||||||
|
assert User.get_follow_state(user, blocked, nil) == nil
|
||||||
|
assert User.get_follow_state(blocked, user, nil) == nil
|
||||||
|
end
|
||||||
|
|
||||||
test "it blocks but does not unfollow if the relevant setting is set", %{
|
test "it blocks but does not unfollow if the relevant setting is set", %{
|
||||||
user: user,
|
user: user,
|
||||||
blocked: blocked,
|
blocked: blocked,
|
||||||
|
@ -542,4 +552,74 @@ test "it streams out the announce", %{announce: announce} do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "removing a follower" do
|
||||||
|
setup do
|
||||||
|
user = insert(:user)
|
||||||
|
followed = insert(:user)
|
||||||
|
|
||||||
|
{:ok, _, _, follow_activity} = CommonAPI.follow(user, followed)
|
||||||
|
|
||||||
|
{:ok, reject_data, []} = Builder.reject(followed, follow_activity)
|
||||||
|
{:ok, reject, _meta} = ActivityPub.persist(reject_data, local: true)
|
||||||
|
|
||||||
|
%{user: user, followed: followed, reject: reject}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "", %{user: user, followed: followed, reject: reject} do
|
||||||
|
assert User.following?(user, followed)
|
||||||
|
assert Pleroma.FollowingRelationship.get(user, followed)
|
||||||
|
|
||||||
|
{:ok, _, _} = SideEffects.handle(reject)
|
||||||
|
|
||||||
|
refute User.following?(user, followed)
|
||||||
|
refute Pleroma.FollowingRelationship.get(user, followed)
|
||||||
|
assert User.get_follow_state(user, followed) == nil
|
||||||
|
assert User.get_follow_state(user, followed, nil) == nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "removing a follower from remote" do
|
||||||
|
setup do
|
||||||
|
user = insert(:user)
|
||||||
|
followed = insert(:user, local: false)
|
||||||
|
|
||||||
|
# Mock a local-to-remote follow
|
||||||
|
{:ok, follow_data, []} = Builder.follow(user, followed)
|
||||||
|
|
||||||
|
follow_data =
|
||||||
|
follow_data
|
||||||
|
|> Map.put("state", "accept")
|
||||||
|
|
||||||
|
{:ok, follow, _meta} = ActivityPub.persist(follow_data, local: true)
|
||||||
|
{:ok, _, _} = SideEffects.handle(follow)
|
||||||
|
|
||||||
|
# Mock a remote-to-local accept
|
||||||
|
{:ok, accept_data, _} = Builder.accept(followed, follow)
|
||||||
|
{:ok, accept, _} = ActivityPub.persist(accept_data, local: false)
|
||||||
|
{:ok, _, _} = SideEffects.handle(accept)
|
||||||
|
|
||||||
|
# Mock a remote-to-local reject
|
||||||
|
{:ok, reject_data, []} = Builder.reject(followed, follow)
|
||||||
|
{:ok, reject, _meta} = ActivityPub.persist(reject_data, local: false)
|
||||||
|
|
||||||
|
%{user: user, followed: followed, reject: reject}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "", %{user: user, followed: followed, reject: reject} do
|
||||||
|
assert User.following?(user, followed)
|
||||||
|
assert Pleroma.FollowingRelationship.get(user, followed)
|
||||||
|
|
||||||
|
{:ok, _, _} = SideEffects.handle(reject)
|
||||||
|
|
||||||
|
refute User.following?(user, followed)
|
||||||
|
refute Pleroma.FollowingRelationship.get(user, followed)
|
||||||
|
|
||||||
|
assert Pleroma.Web.ActivityPub.Utils.fetch_latest_follow(user, followed).data["state"] ==
|
||||||
|
"reject"
|
||||||
|
|
||||||
|
assert User.get_follow_state(user, followed) == nil
|
||||||
|
assert User.get_follow_state(user, followed, nil) == nil
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -58,7 +58,8 @@ test "it remaps video URLs as attachments if necessary" do
|
||||||
"href" =>
|
"href" =>
|
||||||
"https://peertube.moe/static/webseed/df5f464b-be8d-46fb-ad81-2d4c2d1630e3-480.mp4",
|
"https://peertube.moe/static/webseed/df5f464b-be8d-46fb-ad81-2d4c2d1630e3-480.mp4",
|
||||||
"mediaType" => "video/mp4",
|
"mediaType" => "video/mp4",
|
||||||
"type" => "Link"
|
"type" => "Link",
|
||||||
|
"width" => 480
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -79,7 +80,8 @@ test "it remaps video URLs as attachments if necessary" do
|
||||||
"href" =>
|
"href" =>
|
||||||
"https://framatube.org/static/webseed/6050732a-8a7a-43d4-a6cd-809525a1d206-1080.mp4",
|
"https://framatube.org/static/webseed/6050732a-8a7a-43d4-a6cd-809525a1d206-1080.mp4",
|
||||||
"mediaType" => "video/mp4",
|
"mediaType" => "video/mp4",
|
||||||
"type" => "Link"
|
"type" => "Link",
|
||||||
|
"height" => 1080
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -107,7 +109,8 @@ test "it works for peertube videos with only their mpegURL map" do
|
||||||
"href" =>
|
"href" =>
|
||||||
"https://peertube.stream/static/streaming-playlists/hls/abece3c3-b9c6-47f4-8040-f3eed8c602e6/abece3c3-b9c6-47f4-8040-f3eed8c602e6-1080-fragmented.mp4",
|
"https://peertube.stream/static/streaming-playlists/hls/abece3c3-b9c6-47f4-8040-f3eed8c602e6/abece3c3-b9c6-47f4-8040-f3eed8c602e6-1080-fragmented.mp4",
|
||||||
"mediaType" => "video/mp4",
|
"mediaType" => "video/mp4",
|
||||||
"type" => "Link"
|
"type" => "Link",
|
||||||
|
"height" => 1080
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -524,4 +524,44 @@ test "returns {:ok, %Object{}} for success case" do
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "fix_attachments/1" do
|
||||||
|
test "puts dimensions into attachment url field" do
|
||||||
|
object = %{
|
||||||
|
"attachment" => [
|
||||||
|
%{
|
||||||
|
"type" => "Document",
|
||||||
|
"name" => "Hello world",
|
||||||
|
"url" => "https://media.example.tld/1.jpg",
|
||||||
|
"width" => 880,
|
||||||
|
"height" => 960,
|
||||||
|
"mediaType" => "image/jpeg",
|
||||||
|
"blurhash" => "eTKL26+HDjcEIBVl;ds+K6t301W.t7nit7y1E,R:v}ai4nXSt7V@of"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
expected = %{
|
||||||
|
"attachment" => [
|
||||||
|
%{
|
||||||
|
"type" => "Document",
|
||||||
|
"name" => "Hello world",
|
||||||
|
"url" => [
|
||||||
|
%{
|
||||||
|
"type" => "Link",
|
||||||
|
"mediaType" => "image/jpeg",
|
||||||
|
"href" => "https://media.example.tld/1.jpg",
|
||||||
|
"width" => 880,
|
||||||
|
"height" => 960
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"mediaType" => "image/jpeg",
|
||||||
|
"blurhash" => "eTKL26+HDjcEIBVl;ds+K6t301W.t7nit7y1E,R:v}ai4nXSt7V@of"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
assert Transmogrifier.fix_attachments(object) == expected
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -213,6 +213,20 @@ test "updates the state of all Follow activities with the same actor and object"
|
||||||
assert refresh_record(follow_activity).data["state"] == "accept"
|
assert refresh_record(follow_activity).data["state"] == "accept"
|
||||||
assert refresh_record(follow_activity_two).data["state"] == "accept"
|
assert refresh_record(follow_activity_two).data["state"] == "accept"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "also updates the state of accepted follows" do
|
||||||
|
user = insert(:user)
|
||||||
|
follower = insert(:user)
|
||||||
|
|
||||||
|
{:ok, _, _, follow_activity} = CommonAPI.follow(follower, user)
|
||||||
|
{:ok, _, _, follow_activity_two} = CommonAPI.follow(follower, user)
|
||||||
|
|
||||||
|
{:ok, follow_activity_two} =
|
||||||
|
Utils.update_follow_state_for_all(follow_activity_two, "reject")
|
||||||
|
|
||||||
|
assert refresh_record(follow_activity).data["state"] == "reject"
|
||||||
|
assert refresh_record(follow_activity_two).data["state"] == "reject"
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "update_follow_state/2" do
|
describe "update_follow_state/2" do
|
||||||
|
|
|
@ -873,6 +873,56 @@ test "PATCH /api/pleroma/admin/users/approve", %{admin: admin, conn: conn} do
|
||||||
"@#{admin.nickname} approved users: @#{user_one.nickname}, @#{user_two.nickname}"
|
"@#{admin.nickname} approved users: @#{user_one.nickname}, @#{user_two.nickname}"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "PATCH /api/pleroma/admin/users/suggest", %{admin: admin, conn: conn} do
|
||||||
|
user1 = insert(:user, is_suggested: false)
|
||||||
|
user2 = insert(:user, is_suggested: false)
|
||||||
|
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> patch(
|
||||||
|
"/api/pleroma/admin/users/suggest",
|
||||||
|
%{nicknames: [user1.nickname, user2.nickname]}
|
||||||
|
)
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert Enum.map(response["users"], & &1["is_suggested"]) == [true, true]
|
||||||
|
[user1, user2] = Repo.reload!([user1, user2])
|
||||||
|
|
||||||
|
assert user1.is_suggested
|
||||||
|
assert user2.is_suggested
|
||||||
|
|
||||||
|
log_entry = Repo.one(ModerationLog)
|
||||||
|
|
||||||
|
assert ModerationLog.get_log_entry_message(log_entry) ==
|
||||||
|
"@#{admin.nickname} added suggested users: @#{user1.nickname}, @#{user2.nickname}"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "PATCH /api/pleroma/admin/users/unsuggest", %{admin: admin, conn: conn} do
|
||||||
|
user1 = insert(:user, is_suggested: true)
|
||||||
|
user2 = insert(:user, is_suggested: true)
|
||||||
|
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> patch(
|
||||||
|
"/api/pleroma/admin/users/unsuggest",
|
||||||
|
%{nicknames: [user1.nickname, user2.nickname]}
|
||||||
|
)
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert Enum.map(response["users"], & &1["is_suggested"]) == [false, false]
|
||||||
|
[user1, user2] = Repo.reload!([user1, user2])
|
||||||
|
|
||||||
|
refute user1.is_suggested
|
||||||
|
refute user2.is_suggested
|
||||||
|
|
||||||
|
log_entry = Repo.one(ModerationLog)
|
||||||
|
|
||||||
|
assert ModerationLog.get_log_entry_message(log_entry) ==
|
||||||
|
"@#{admin.nickname} removed suggested users: @#{user1.nickname}, @#{user2.nickname}"
|
||||||
|
end
|
||||||
|
|
||||||
test "PATCH /api/pleroma/admin/users/:nickname/toggle_activation", %{admin: admin, conn: conn} do
|
test "PATCH /api/pleroma/admin/users/:nickname/toggle_activation", %{admin: admin, conn: conn} do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
|
|
||||||
|
@ -906,6 +956,7 @@ defp user_response(user, attrs \\ %{}) do
|
||||||
"display_name" => HTML.strip_tags(user.name || user.nickname),
|
"display_name" => HTML.strip_tags(user.name || user.nickname),
|
||||||
"is_confirmed" => true,
|
"is_confirmed" => true,
|
||||||
"is_approved" => true,
|
"is_approved" => true,
|
||||||
|
"is_suggested" => false,
|
||||||
"url" => user.ap_id,
|
"url" => user.ap_id,
|
||||||
"registration_reason" => nil,
|
"registration_reason" => nil,
|
||||||
"actor_type" => "Person",
|
"actor_type" => "Person",
|
||||||
|
|
17
test/pleroma/web/manifest_controller_test.exs
Normal file
17
test/pleroma/web/manifest_controller_test.exs
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.ManifestControllerTest do
|
||||||
|
use Pleroma.Web.ConnCase
|
||||||
|
|
||||||
|
setup do
|
||||||
|
clear_config([:instance, :name], "Manifest Test")
|
||||||
|
clear_config([:manifest, :theme_color], "#ff0000")
|
||||||
|
end
|
||||||
|
|
||||||
|
test "manifest.json", %{conn: conn} do
|
||||||
|
conn = get(conn, "/manifest.json")
|
||||||
|
assert %{"name" => "Manifest Test", "theme_color" => "#ff0000"} = json_response(conn, 200)
|
||||||
|
end
|
||||||
|
end
|
|
@ -922,6 +922,27 @@ test "following with reblogs" do
|
||||||
|> json_response_and_validate_schema(200)
|
|> json_response_and_validate_schema(200)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "following with subscription and unsubscribing" do
|
||||||
|
%{conn: conn} = oauth_access(["follow"])
|
||||||
|
followed = insert(:user)
|
||||||
|
|
||||||
|
ret_conn =
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> post("/api/v1/accounts/#{followed.id}/follow", %{notify: true})
|
||||||
|
|
||||||
|
assert %{"id" => _id, "subscribing" => true} =
|
||||||
|
json_response_and_validate_schema(ret_conn, 200)
|
||||||
|
|
||||||
|
ret_conn =
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> post("/api/v1/accounts/#{followed.id}/follow", %{notify: false})
|
||||||
|
|
||||||
|
assert %{"id" => _id, "subscribing" => false} =
|
||||||
|
json_response_and_validate_schema(ret_conn, 200)
|
||||||
|
end
|
||||||
|
|
||||||
test "following / unfollowing errors", %{user: user, conn: conn} do
|
test "following / unfollowing errors", %{user: user, conn: conn} do
|
||||||
# self follow
|
# self follow
|
||||||
conn_res = post(conn, "/api/v1/accounts/#{user.id}/follow")
|
conn_res = post(conn, "/api/v1/accounts/#{user.id}/follow")
|
||||||
|
@ -1776,4 +1797,21 @@ test "getting a list of blocks" do
|
||||||
|
|
||||||
assert [%{"id" => ^id2}] = result
|
assert [%{"id" => ^id2}] = result
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "create a note on a user" do
|
||||||
|
%{conn: conn} = oauth_access(["write:accounts", "read:follows"])
|
||||||
|
other_user = insert(:user)
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> post("/api/v1/accounts/#{other_user.id}/note", %{
|
||||||
|
"comment" => "Example note"
|
||||||
|
})
|
||||||
|
|
||||||
|
assert [%{"note" => "Example note"}] =
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> get("/api/v1/accounts/relationships?id=#{other_user.id}")
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -35,6 +35,33 @@ test "apps/verify_credentials", %{conn: conn} do
|
||||||
end
|
end
|
||||||
|
|
||||||
test "creates an oauth app", %{conn: conn} do
|
test "creates an oauth app", %{conn: conn} do
|
||||||
|
app_attrs = build(:oauth_app)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> post("/api/v1/apps", %{
|
||||||
|
client_name: app_attrs.client_name,
|
||||||
|
redirect_uris: app_attrs.redirect_uris
|
||||||
|
})
|
||||||
|
|
||||||
|
[app] = Repo.all(App)
|
||||||
|
|
||||||
|
expected = %{
|
||||||
|
"name" => app.client_name,
|
||||||
|
"website" => app.website,
|
||||||
|
"client_id" => app.client_id,
|
||||||
|
"client_secret" => app.client_secret,
|
||||||
|
"id" => app.id |> to_string(),
|
||||||
|
"redirect_uri" => app.redirect_uris,
|
||||||
|
"vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
|
||||||
|
}
|
||||||
|
|
||||||
|
assert expected == json_response_and_validate_schema(conn, 200)
|
||||||
|
assert app.user_id == nil
|
||||||
|
end
|
||||||
|
|
||||||
|
test "creates an oauth app with a user", %{conn: conn} do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
app_attrs = build(:oauth_app)
|
app_attrs = build(:oauth_app)
|
||||||
|
|
||||||
|
@ -60,5 +87,6 @@ test "creates an oauth app", %{conn: conn} do
|
||||||
}
|
}
|
||||||
|
|
||||||
assert expected == json_response_and_validate_schema(conn, 200)
|
assert expected == json_response_and_validate_schema(conn, 200)
|
||||||
|
assert app.user_id == user.id
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
defmodule Pleroma.Web.MastodonAPI.DirectoryControllerTest do
|
||||||
|
use Pleroma.Web.ConnCase, async: true
|
||||||
|
alias Pleroma.Web.CommonAPI
|
||||||
|
import Pleroma.Factory
|
||||||
|
|
||||||
|
test "GET /api/v1/directory with :profile_directory disabled returns empty array", %{conn: conn} do
|
||||||
|
clear_config([:instance, :profile_directory], false)
|
||||||
|
|
||||||
|
insert(:user, is_discoverable: true)
|
||||||
|
insert(:user, is_discoverable: true)
|
||||||
|
|
||||||
|
result =
|
||||||
|
conn
|
||||||
|
|> get("/api/v1/directory")
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert result == []
|
||||||
|
end
|
||||||
|
|
||||||
|
test "GET /api/v1/directory returns discoverable users only", %{conn: conn} do
|
||||||
|
%{id: user_id} = insert(:user, is_discoverable: true)
|
||||||
|
insert(:user, is_discoverable: false)
|
||||||
|
|
||||||
|
result =
|
||||||
|
conn
|
||||||
|
|> get("/api/v1/directory")
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert [%{"id" => ^user_id}] = result
|
||||||
|
end
|
||||||
|
|
||||||
|
test "GET /api/v1/directory returns users sorted by most recent statuses", %{conn: conn} do
|
||||||
|
insert(:user, is_discoverable: true)
|
||||||
|
%{id: user_id} = user = insert(:user, is_discoverable: true)
|
||||||
|
insert(:user, is_discoverable: true)
|
||||||
|
|
||||||
|
{:ok, _activity} = CommonAPI.post(user, %{status: "yay i'm discoverable"})
|
||||||
|
|
||||||
|
result =
|
||||||
|
conn
|
||||||
|
|> get("/api/v1/directory?order=active")
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert [%{"id" => ^user_id} | _tail] = result
|
||||||
|
end
|
||||||
|
end
|
|
@ -4,8 +4,11 @@
|
||||||
|
|
||||||
defmodule Pleroma.Web.MastodonAPI.SuggestionControllerTest do
|
defmodule Pleroma.Web.MastodonAPI.SuggestionControllerTest do
|
||||||
use Pleroma.Web.ConnCase, async: true
|
use Pleroma.Web.ConnCase, async: true
|
||||||
|
alias Pleroma.UserRelationship
|
||||||
|
alias Pleroma.Web.CommonAPI
|
||||||
|
import Pleroma.Factory
|
||||||
|
|
||||||
setup do: oauth_access(["read"])
|
setup do: oauth_access(["read", "write"])
|
||||||
|
|
||||||
test "returns empty result", %{conn: conn} do
|
test "returns empty result", %{conn: conn} do
|
||||||
res =
|
res =
|
||||||
|
@ -15,4 +18,66 @@ test "returns empty result", %{conn: conn} do
|
||||||
|
|
||||||
assert res == []
|
assert res == []
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "returns v2 suggestions", %{conn: conn} do
|
||||||
|
%{id: user_id} = insert(:user, is_suggested: true)
|
||||||
|
|
||||||
|
res =
|
||||||
|
conn
|
||||||
|
|> get("/api/v2/suggestions")
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert [%{"source" => "staff", "account" => %{"id" => ^user_id}}] = res
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns v2 suggestions excluding dismissed accounts", %{conn: conn} do
|
||||||
|
%{id: user_id} = insert(:user, is_suggested: true)
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> delete("/api/v1/suggestions/#{user_id}")
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
res =
|
||||||
|
conn
|
||||||
|
|> get("/api/v2/suggestions")
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert [] = res
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns v2 suggestions excluding blocked accounts", %{conn: conn, user: blocker} do
|
||||||
|
blocked = insert(:user, is_suggested: true)
|
||||||
|
{:ok, _} = CommonAPI.block(blocker, blocked)
|
||||||
|
|
||||||
|
res =
|
||||||
|
conn
|
||||||
|
|> get("/api/v2/suggestions")
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert [] = res
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns v2 suggestions excluding followed accounts", %{conn: conn, user: follower} do
|
||||||
|
followed = insert(:user, is_suggested: true)
|
||||||
|
{:ok, _, _, _} = CommonAPI.follow(follower, followed)
|
||||||
|
|
||||||
|
res =
|
||||||
|
conn
|
||||||
|
|> get("/api/v2/suggestions")
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert [] = res
|
||||||
|
end
|
||||||
|
|
||||||
|
test "dismiss suggestion", %{conn: conn, user: source} do
|
||||||
|
target = insert(:user, is_suggested: true)
|
||||||
|
|
||||||
|
res =
|
||||||
|
conn
|
||||||
|
|> delete("/api/v1/suggestions/#{target.id}")
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert res == %{}
|
||||||
|
assert UserRelationship.exists?(:suggestion_dismiss, source, target)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -74,6 +74,7 @@ test "Represent a user account" do
|
||||||
fields: []
|
fields: []
|
||||||
},
|
},
|
||||||
fqn: "shp@shitposter.club",
|
fqn: "shp@shitposter.club",
|
||||||
|
last_status_at: nil,
|
||||||
pleroma: %{
|
pleroma: %{
|
||||||
ap_id: user.ap_id,
|
ap_id: user.ap_id,
|
||||||
also_known_as: ["https://shitposter.zone/users/shp"],
|
also_known_as: ["https://shitposter.zone/users/shp"],
|
||||||
|
@ -83,6 +84,7 @@ test "Represent a user account" do
|
||||||
tags: [],
|
tags: [],
|
||||||
is_admin: false,
|
is_admin: false,
|
||||||
is_moderator: false,
|
is_moderator: false,
|
||||||
|
is_suggested: false,
|
||||||
hide_favorites: true,
|
hide_favorites: true,
|
||||||
hide_followers: false,
|
hide_followers: false,
|
||||||
hide_follows: false,
|
hide_follows: false,
|
||||||
|
@ -174,6 +176,7 @@ test "Represent a Service(bot) account" do
|
||||||
fields: []
|
fields: []
|
||||||
},
|
},
|
||||||
fqn: "shp@shitposter.club",
|
fqn: "shp@shitposter.club",
|
||||||
|
last_status_at: nil,
|
||||||
pleroma: %{
|
pleroma: %{
|
||||||
ap_id: user.ap_id,
|
ap_id: user.ap_id,
|
||||||
also_known_as: [],
|
also_known_as: [],
|
||||||
|
@ -183,6 +186,7 @@ test "Represent a Service(bot) account" do
|
||||||
tags: [],
|
tags: [],
|
||||||
is_admin: false,
|
is_admin: false,
|
||||||
is_moderator: false,
|
is_moderator: false,
|
||||||
|
is_suggested: false,
|
||||||
hide_favorites: true,
|
hide_favorites: true,
|
||||||
hide_followers: false,
|
hide_followers: false,
|
||||||
hide_follows: false,
|
hide_follows: false,
|
||||||
|
@ -268,10 +272,12 @@ defp test_relationship_rendering(user, other_user, expected_result) do
|
||||||
muting: false,
|
muting: false,
|
||||||
muting_notifications: false,
|
muting_notifications: false,
|
||||||
subscribing: false,
|
subscribing: false,
|
||||||
|
notifying: false,
|
||||||
requested: false,
|
requested: false,
|
||||||
domain_blocking: false,
|
domain_blocking: false,
|
||||||
showing_reblogs: true,
|
showing_reblogs: true,
|
||||||
endorsed: false
|
endorsed: false,
|
||||||
|
note: ""
|
||||||
}
|
}
|
||||||
|
|
||||||
test "represent a relationship for the following and followed user" do
|
test "represent a relationship for the following and followed user" do
|
||||||
|
@ -293,6 +299,7 @@ test "represent a relationship for the following and followed user" do
|
||||||
muting: true,
|
muting: true,
|
||||||
muting_notifications: true,
|
muting_notifications: true,
|
||||||
subscribing: true,
|
subscribing: true,
|
||||||
|
notifying: true,
|
||||||
showing_reblogs: false,
|
showing_reblogs: false,
|
||||||
id: to_string(other_user.id)
|
id: to_string(other_user.id)
|
||||||
}
|
}
|
||||||
|
|
34
test/pleroma/web/mastodon_api/views/suggestion_view_test.exs
Normal file
34
test/pleroma/web/mastodon_api/views/suggestion_view_test.exs
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.MastodonAPI.SuggestionViewTest do
|
||||||
|
use Pleroma.DataCase, async: true
|
||||||
|
import Pleroma.Factory
|
||||||
|
alias Pleroma.Web.MastodonAPI.SuggestionView, as: View
|
||||||
|
|
||||||
|
test "show.json" do
|
||||||
|
user = insert(:user, is_suggested: true)
|
||||||
|
json = View.render("show.json", %{user: user, source: :staff, skip_visibility_check: true})
|
||||||
|
|
||||||
|
assert json.source == :staff
|
||||||
|
assert json.account.id == user.id
|
||||||
|
end
|
||||||
|
|
||||||
|
test "index.json" do
|
||||||
|
user1 = insert(:user, is_suggested: true)
|
||||||
|
user2 = insert(:user, is_suggested: true)
|
||||||
|
user3 = insert(:user, is_suggested: true)
|
||||||
|
|
||||||
|
[suggestion1, suggestion2, suggestion3] =
|
||||||
|
View.render("index.json", %{
|
||||||
|
users: [user1, user2, user3],
|
||||||
|
source: :staff,
|
||||||
|
skip_visibility_check: true
|
||||||
|
})
|
||||||
|
|
||||||
|
assert suggestion1.source == :staff
|
||||||
|
assert suggestion2.account.id == user2.id
|
||||||
|
assert suggestion3.account.url == user3.ap_id
|
||||||
|
end
|
||||||
|
end
|
|
@ -41,4 +41,16 @@ test "has unique client_id" do
|
||||||
assert error.type == :unique
|
assert error.type == :unique
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "get_user_apps/1" do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
apps = [
|
||||||
|
insert(:oauth_app, user_id: user.id),
|
||||||
|
insert(:oauth_app, user_id: user.id),
|
||||||
|
insert(:oauth_app, user_id: user.id)
|
||||||
|
]
|
||||||
|
|
||||||
|
assert App.get_user_apps(user) == apps
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -343,4 +343,54 @@ test "does not require authentication on non-federating instances", %{
|
||||||
|> response(200)
|
|> response(200)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "notice compatibility routes" do
|
||||||
|
test "Soapbox FE", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
note_activity = insert(:note_activity, user: user)
|
||||||
|
|
||||||
|
resp =
|
||||||
|
conn
|
||||||
|
|> put_req_header("accept", "text/html")
|
||||||
|
|> get("/@#{user.nickname}/posts/#{note_activity.id}")
|
||||||
|
|> response(200)
|
||||||
|
|
||||||
|
expected =
|
||||||
|
"<meta content=\"#{Endpoint.url()}/notice/#{note_activity.id}\" property=\"og:url\">"
|
||||||
|
|
||||||
|
assert resp =~ expected
|
||||||
|
end
|
||||||
|
|
||||||
|
test "Mastodon", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
note_activity = insert(:note_activity, user: user)
|
||||||
|
|
||||||
|
resp =
|
||||||
|
conn
|
||||||
|
|> put_req_header("accept", "text/html")
|
||||||
|
|> get("/@#{user.nickname}/#{note_activity.id}")
|
||||||
|
|> response(200)
|
||||||
|
|
||||||
|
expected =
|
||||||
|
"<meta content=\"#{Endpoint.url()}/notice/#{note_activity.id}\" property=\"og:url\">"
|
||||||
|
|
||||||
|
assert resp =~ expected
|
||||||
|
end
|
||||||
|
|
||||||
|
test "Twitter", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
note_activity = insert(:note_activity, user: user)
|
||||||
|
|
||||||
|
resp =
|
||||||
|
conn
|
||||||
|
|> put_req_header("accept", "text/html")
|
||||||
|
|> get("/#{user.nickname}/status/#{note_activity.id}")
|
||||||
|
|> response(200)
|
||||||
|
|
||||||
|
expected =
|
||||||
|
"<meta content=\"#{Endpoint.url()}/notice/#{note_activity.id}\" property=\"og:url\">"
|
||||||
|
|
||||||
|
assert resp =~ expected
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.PleromaAPI.AppControllerTest do
|
||||||
|
use Pleroma.Web.ConnCase, async: true
|
||||||
|
|
||||||
|
alias Pleroma.Web.OAuth.App
|
||||||
|
alias Pleroma.Web.Push
|
||||||
|
|
||||||
|
import Pleroma.Factory
|
||||||
|
|
||||||
|
test "apps", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
app_attrs = build(:oauth_app)
|
||||||
|
|
||||||
|
creation =
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> post("/api/v1/apps", %{
|
||||||
|
client_name: app_attrs.client_name,
|
||||||
|
redirect_uris: app_attrs.redirect_uris
|
||||||
|
})
|
||||||
|
|
||||||
|
[app] = App.get_user_apps(user)
|
||||||
|
|
||||||
|
expected = %{
|
||||||
|
"name" => app.client_name,
|
||||||
|
"website" => app.website,
|
||||||
|
"client_id" => app.client_id,
|
||||||
|
"client_secret" => app.client_secret,
|
||||||
|
"id" => app.id |> to_string(),
|
||||||
|
"redirect_uri" => app.redirect_uris,
|
||||||
|
"vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
|
||||||
|
}
|
||||||
|
|
||||||
|
assert expected == json_response_and_validate_schema(creation, 200)
|
||||||
|
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> assign(:token, insert(:oauth_token, user: user, scopes: ["read", "follow"]))
|
||||||
|
|> get("/api/v1/pleroma/apps")
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
[apps] = response
|
||||||
|
|
||||||
|
assert length(response) == 1
|
||||||
|
assert apps["client_id"] == app.client_id
|
||||||
|
end
|
||||||
|
end
|
21
test/pleroma/web/pleroma_api/views/app_view_test.exs
Normal file
21
test/pleroma/web/pleroma_api/views/app_view_test.exs
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.PleromaAPI.AppViewTest do
|
||||||
|
use Pleroma.DataCase, async: true
|
||||||
|
alias Pleroma.Web.PleromaAPI.AppView
|
||||||
|
import Pleroma.Factory
|
||||||
|
|
||||||
|
test "index.json" do
|
||||||
|
apps = [
|
||||||
|
insert(:oauth_app),
|
||||||
|
insert(:oauth_app),
|
||||||
|
insert(:oauth_app)
|
||||||
|
]
|
||||||
|
|
||||||
|
results = AppView.render("index.json", %{apps: apps})
|
||||||
|
|
||||||
|
assert [%{client_id: _, client_secret: _}, _, _] = results
|
||||||
|
end
|
||||||
|
end
|
60
test/pleroma/web/plugs/ensure_staff_privileged_plug_test.exs
Normal file
60
test/pleroma/web/plugs/ensure_staff_privileged_plug_test.exs
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.Plugs.EnsureStaffPrivilegedPlugTest do
|
||||||
|
use Pleroma.Web.ConnCase, async: true
|
||||||
|
|
||||||
|
alias Pleroma.Web.Plugs.EnsureStaffPrivilegedPlug
|
||||||
|
import Pleroma.Factory
|
||||||
|
|
||||||
|
test "accepts a user that is an admin" do
|
||||||
|
user = insert(:user, is_admin: true)
|
||||||
|
|
||||||
|
conn = assign(build_conn(), :user, user)
|
||||||
|
|
||||||
|
ret_conn = EnsureStaffPrivilegedPlug.call(conn, %{})
|
||||||
|
|
||||||
|
assert conn == ret_conn
|
||||||
|
end
|
||||||
|
|
||||||
|
test "accepts a user that is a moderator when :privileged_staff is enabled" do
|
||||||
|
clear_config([:instance, :privileged_staff], true)
|
||||||
|
user = insert(:user, is_moderator: true)
|
||||||
|
|
||||||
|
conn = assign(build_conn(), :user, user)
|
||||||
|
|
||||||
|
ret_conn = EnsureStaffPrivilegedPlug.call(conn, %{})
|
||||||
|
|
||||||
|
assert conn == ret_conn
|
||||||
|
end
|
||||||
|
|
||||||
|
test "denies a user that is a moderator when :privileged_staff is disabled" do
|
||||||
|
clear_config([:instance, :privileged_staff], false)
|
||||||
|
user = insert(:user, is_moderator: true)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
build_conn()
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> EnsureStaffPrivilegedPlug.call(%{})
|
||||||
|
|
||||||
|
assert conn.status == 403
|
||||||
|
end
|
||||||
|
|
||||||
|
test "denies a user that isn't a staff member" do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
build_conn()
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> EnsureStaffPrivilegedPlug.call(%{})
|
||||||
|
|
||||||
|
assert conn.status == 403
|
||||||
|
end
|
||||||
|
|
||||||
|
test "denies when a user isn't set" do
|
||||||
|
conn = EnsureStaffPrivilegedPlug.call(build_conn(), %{})
|
||||||
|
|
||||||
|
assert conn.status == 403
|
||||||
|
end
|
||||||
|
end
|
|
@ -86,6 +86,8 @@ test "api routes are detected correctly" do
|
||||||
"objects",
|
"objects",
|
||||||
"activities",
|
"activities",
|
||||||
"notice",
|
"notice",
|
||||||
|
"@:nickname",
|
||||||
|
":nickname",
|
||||||
"users",
|
"users",
|
||||||
"tags",
|
"tags",
|
||||||
"mailer",
|
"mailer",
|
||||||
|
@ -94,7 +96,10 @@ test "api routes are detected correctly" do
|
||||||
"internal",
|
"internal",
|
||||||
".well-known",
|
".well-known",
|
||||||
"nodeinfo",
|
"nodeinfo",
|
||||||
|
"manifest.json",
|
||||||
|
"auth",
|
||||||
"proxy",
|
"proxy",
|
||||||
|
"phoenix",
|
||||||
"test",
|
"test",
|
||||||
"user_exists",
|
"user_exists",
|
||||||
"check_password"
|
"check_password"
|
||||||
|
|
|
@ -48,6 +48,7 @@ test "it is enabled if remote_ip_found flag doesn't exist" do
|
||||||
refute RateLimiter.disabled?(build_conn())
|
refute RateLimiter.disabled?(build_conn())
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@tag :erratic
|
||||||
test "it restricts based on config values" do
|
test "it restricts based on config values" do
|
||||||
limiter_name = :test_plug_opts
|
limiter_name = :test_plug_opts
|
||||||
scale = 80
|
scale = 80
|
||||||
|
@ -137,6 +138,7 @@ test "it supports combination of options modifying bucket name" do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "unauthenticated users" do
|
describe "unauthenticated users" do
|
||||||
|
@tag :erratic
|
||||||
test "are restricted based on remote IP" do
|
test "are restricted based on remote IP" do
|
||||||
limiter_name = :test_unauthenticated
|
limiter_name = :test_unauthenticated
|
||||||
clear_config([:rate_limit, limiter_name], [{1000, 5}, {1, 10}])
|
clear_config([:rate_limit, limiter_name], [{1000, 5}, {1, 10}])
|
||||||
|
@ -174,6 +176,7 @@ test "are restricted based on remote IP" do
|
||||||
:ok
|
:ok
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@tag :erratic
|
||||||
test "can have limits separate from unauthenticated connections" do
|
test "can have limits separate from unauthenticated connections" do
|
||||||
limiter_name = :test_authenticated1
|
limiter_name = :test_authenticated1
|
||||||
|
|
||||||
|
@ -199,6 +202,7 @@ test "can have limits separate from unauthenticated connections" do
|
||||||
assert conn.halted
|
assert conn.halted
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@tag :erratic
|
||||||
test "different users are counted independently" do
|
test "different users are counted independently" do
|
||||||
limiter_name = :test_authenticated2
|
limiter_name = :test_authenticated2
|
||||||
clear_config([:rate_limit, limiter_name], [{1, 10}, {1000, 5}])
|
clear_config([:rate_limit, limiter_name], [{1, 10}, {1000, 5}])
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue