Compare commits
39 Commits
8767413197
...
af34602a4b
Author | SHA1 | Date |
---|---|---|
Your New SJW Waifu | af34602a4b | |
Your New SJW Waifu | 6078d63f4a | |
feld | 0b9bc4a0d0 | |
Mark Felder | 7e99d0619d | |
Haelwenn | a145d909b5 | |
SyoBoN | 2c9fed9b73 | |
feld | 0fbec6b532 | |
Mark Felder | 1951d56ed9 | |
Mark Felder | c9cd449bba | |
feld | 03834454d9 | |
Mark Felder | 2a4fa4c408 | |
Mark Felder | 772f8d08cf | |
Mark Felder | 4648997a10 | |
feld | b91317b9bf | |
Mark Felder | 202721e80c | |
Mark Felder | 0c5bec0493 | |
feld | 4dd8a1a1ca | |
lain | d3208d2250 | |
lain | f2f455f283 | |
Haelwenn | f28dcc9cb7 | |
Haelwenn | d19642d7eb | |
feld | 802c618885 | |
Mark Felder | 9138754b0a | |
Mark Felder | 91c83a82a0 | |
Mark Felder | 86e6d395d9 | |
Mark Felder | 6be129ead2 | |
Mark Felder | d0f4b2b02f | |
Mark Felder | 64ad451a7b | |
feld | af9bb77cad | |
feld | 1b2f9d4a6d | |
Mark Felder | 9a4c8e2316 | |
Mark Felder | 60ba6fd244 | |
Haelwenn (lanodan) Monnier | 3b82864bcc | |
marcin mikołajczak | 0fcdcc2300 | |
Haelwenn (lanodan) Monnier | 799891d359 | |
Haelwenn (lanodan) Monnier | 558b421079 | |
Haelwenn (lanodan) Monnier | acef2a4a40 | |
Haelwenn (lanodan) Monnier | 0de1a7629c | |
Mark Felder | 6b74a55274 |
|
@ -0,0 +1 @@
|
|||
Support Bandit as an alternative to Cowboy for the HTTP server.
|
|
@ -0,0 +1 @@
|
|||
Fix federation with Convergence AP Bridge
|
|
@ -0,0 +1 @@
|
|||
- Config: Check the permissions of the linked file instead of the symlink
|
|
@ -0,0 +1 @@
|
|||
MediaProxy was setting the content-length header which is not permitted by RFC9112§6.2 when we are chunking the reply as it conflicts with the existence of the transfer-encoding header.
|
|
@ -0,0 +1 @@
|
|||
Fix a memory leak caused by Websocket connections that would not enter a state where a full garbage collection run could be triggered.
|
|
@ -0,0 +1 @@
|
|||
Use User.full_nickname/1 in oauth html template
|
|
@ -0,0 +1 @@
|
|||
Update Tesla HTTP client middleware to 1.8.0
|
|
@ -0,0 +1 @@
|
|||
Refactor the Mastodon /api/v1/streaming websocket handler to use Phoenix.Socket.Transport
|
|
@ -114,14 +114,7 @@ config :pleroma, :uri_schemes,
|
|||
config :pleroma, Pleroma.Web.Endpoint,
|
||||
url: [host: "localhost"],
|
||||
http: [
|
||||
ip: {127, 0, 0, 1},
|
||||
dispatch: [
|
||||
{:_,
|
||||
[
|
||||
{"/api/v1/streaming", Pleroma.Web.MastodonAPI.WebsocketHandler, []},
|
||||
{:_, Plug.Cowboy.Handler, {Pleroma.Web.Endpoint, []}}
|
||||
]}
|
||||
]
|
||||
ip: {127, 0, 0, 1}
|
||||
],
|
||||
protocol: "https",
|
||||
secret_key_base: "aK4Abxf29xU9TTDKre9coZPUgevcVCFQJe/5xP/7Lt4BEif6idBIbjupVbOrbKxl",
|
||||
|
|
|
@ -8,8 +8,7 @@ import Config
|
|||
# with brunch.io to recompile .js and .css sources.
|
||||
config :pleroma, Pleroma.Web.Endpoint,
|
||||
http: [
|
||||
port: 4000,
|
||||
protocol_options: [max_request_line_length: 8192, max_header_value_length: 8192]
|
||||
port: 4000
|
||||
],
|
||||
protocol: "http",
|
||||
debug_errors: true,
|
||||
|
|
|
@ -1,93 +0,0 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Phoenix.Transports.WebSocket.Raw do
|
||||
import Plug.Conn,
|
||||
only: [
|
||||
fetch_query_params: 1,
|
||||
send_resp: 3
|
||||
]
|
||||
|
||||
alias Phoenix.Socket.Transport
|
||||
|
||||
def default_config do
|
||||
[
|
||||
timeout: 60_000,
|
||||
transport_log: false,
|
||||
cowboy: Phoenix.Endpoint.CowboyWebSocket
|
||||
]
|
||||
end
|
||||
|
||||
def init(%Plug.Conn{method: "GET"} = conn, {endpoint, handler, transport}) do
|
||||
{_, opts} = handler.__transport__(transport)
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> fetch_query_params
|
||||
|> Transport.transport_log(opts[:transport_log])
|
||||
|> Transport.check_origin(handler, endpoint, opts)
|
||||
|
||||
case conn do
|
||||
%{halted: false} = conn ->
|
||||
case handler.connect(%{
|
||||
endpoint: endpoint,
|
||||
transport: transport,
|
||||
options: [serializer: nil],
|
||||
params: conn.params
|
||||
}) do
|
||||
{:ok, socket} ->
|
||||
{:ok, conn, {__MODULE__, {socket, opts}}}
|
||||
|
||||
:error ->
|
||||
send_resp(conn, :forbidden, "")
|
||||
{:error, conn}
|
||||
end
|
||||
|
||||
_ ->
|
||||
{:error, conn}
|
||||
end
|
||||
end
|
||||
|
||||
def init(conn, _) do
|
||||
send_resp(conn, :bad_request, "")
|
||||
{:error, conn}
|
||||
end
|
||||
|
||||
def ws_init({socket, config}) do
|
||||
Process.flag(:trap_exit, true)
|
||||
{:ok, %{socket: socket}, config[:timeout]}
|
||||
end
|
||||
|
||||
def ws_handle(op, data, state) do
|
||||
state.socket.handler
|
||||
|> apply(:handle, [op, data, state])
|
||||
|> case do
|
||||
{op, data} ->
|
||||
{:reply, {op, data}, state}
|
||||
|
||||
{op, data, state} ->
|
||||
{:reply, {op, data}, state}
|
||||
|
||||
%{} = state ->
|
||||
{:ok, state}
|
||||
|
||||
_ ->
|
||||
{:ok, state}
|
||||
end
|
||||
end
|
||||
|
||||
def ws_info({_, _} = tuple, state) do
|
||||
{:reply, tuple, state}
|
||||
end
|
||||
|
||||
def ws_info(_tuple, state), do: {:ok, state}
|
||||
|
||||
def ws_close(state) do
|
||||
ws_handle(:closed, :normal, state)
|
||||
end
|
||||
|
||||
def ws_terminate(reason, state) do
|
||||
ws_handle(:closed, reason, state)
|
||||
end
|
||||
end
|
|
@ -21,7 +21,7 @@ defmodule Pleroma.Config.ReleaseRuntimeProvider do
|
|||
with_runtime_config =
|
||||
if File.exists?(config_path) do
|
||||
# <https://git.pleroma.social/pleroma/pleroma/-/issues/3135>
|
||||
%File.Stat{mode: mode} = File.lstat!(config_path)
|
||||
%File.Stat{mode: mode} = File.stat!(config_path)
|
||||
|
||||
if Bitwise.band(mode, 0o007) > 0 do
|
||||
raise "Configuration at #{config_path} has world-permissions, execute the following: chmod o= #{config_path}"
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||
# Copyright © 2017-2024 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Maps do
|
||||
|
@ -18,4 +18,17 @@ defmodule Pleroma.Maps do
|
|||
rescue
|
||||
_ -> data
|
||||
end
|
||||
|
||||
def filter_empty_values(data) do
|
||||
# TODO: Change to Map.filter in Elixir 1.13+
|
||||
data
|
||||
|> Enum.filter(fn
|
||||
{_k, nil} -> false
|
||||
{_k, ""} -> false
|
||||
{_k, []} -> false
|
||||
{_k, %{} = v} -> Map.keys(v) != []
|
||||
{_k, _v} -> true
|
||||
end)
|
||||
|> Map.new()
|
||||
end
|
||||
end
|
||||
|
|
|
@ -28,7 +28,7 @@ defmodule Pleroma.Password.Pbkdf2 do
|
|||
|
||||
iterations = String.to_integer(iterations)
|
||||
|
||||
digest = String.to_atom(digest)
|
||||
digest = String.to_existing_atom(digest)
|
||||
|
||||
binary_hash =
|
||||
KeyGenerator.generate(password, salt, digest: digest, iterations: iterations, length: 64)
|
||||
|
|
|
@ -8,7 +8,7 @@ defmodule Pleroma.ReverseProxy do
|
|||
~w(if-unmodified-since if-none-match) ++ @range_headers
|
||||
@resp_cache_headers ~w(etag date last-modified)
|
||||
@keep_resp_headers @resp_cache_headers ++
|
||||
~w(content-length content-type content-disposition content-encoding) ++
|
||||
~w(content-type content-disposition content-encoding) ++
|
||||
~w(content-range accept-ranges vary)
|
||||
@default_cache_control_header "public, max-age=1209600"
|
||||
@valid_resp_codes [200, 206, 304]
|
||||
|
|
|
@ -59,7 +59,7 @@ defmodule Pleroma.Telemetry.Logger do
|
|||
_,
|
||||
_
|
||||
) do
|
||||
Logger.error(fn ->
|
||||
Logger.debug(fn ->
|
||||
"Connection pool had to refuse opening a connection to #{key} due to connection limit exhaustion"
|
||||
end)
|
||||
end
|
||||
|
@ -81,7 +81,7 @@ defmodule Pleroma.Telemetry.Logger do
|
|||
%{key: key, protocol: :http},
|
||||
_
|
||||
) do
|
||||
Logger.info(fn ->
|
||||
Logger.debug(fn ->
|
||||
"Pool worker for #{key}: #{length(clients)} clients are using an HTTP1 connection at the same time, head-of-line blocking might occur."
|
||||
end)
|
||||
end
|
||||
|
|
|
@ -0,0 +1,85 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright ©️ 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.ActivityPub.MRF.AntiMentionSpamPolicy do
|
||||
alias Pleroma.User
|
||||
require Pleroma.Constants
|
||||
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF.Policy
|
||||
|
||||
defp user_has_posted?(%User{} = u), do: u.note_count > 0
|
||||
|
||||
defp user_has_age?(%User{} = u) do
|
||||
diff = NaiveDateTime.utc_now() |> NaiveDateTime.diff(u.inserted_at, :second)
|
||||
diff >= :timer.seconds(30)
|
||||
end
|
||||
|
||||
defp good_reputation?(%User{} = u) do
|
||||
user_has_age?(u) and user_has_posted?(u)
|
||||
end
|
||||
|
||||
# copied from HellthreadPolicy
|
||||
defp get_recipient_count(message) do
|
||||
recipients = (message["to"] || []) ++ (message["cc"] || [])
|
||||
|
||||
follower_collection =
|
||||
User.get_cached_by_ap_id(message["actor"] || message["attributedTo"]).follower_address
|
||||
|
||||
if Enum.member?(recipients, Pleroma.Constants.as_public()) do
|
||||
recipients =
|
||||
recipients
|
||||
|> List.delete(Pleroma.Constants.as_public())
|
||||
|> List.delete(follower_collection)
|
||||
|
||||
{:public, length(recipients)}
|
||||
else
|
||||
recipients =
|
||||
recipients
|
||||
|> List.delete(follower_collection)
|
||||
|
||||
{:not_public, length(recipients)}
|
||||
end
|
||||
end
|
||||
|
||||
defp object_has_recipients?(%{"object" => object} = activity) do
|
||||
{_, object_count} = get_recipient_count(object)
|
||||
{_, activity_count} = get_recipient_count(activity)
|
||||
object_count + activity_count > 0
|
||||
end
|
||||
|
||||
defp object_has_recipients?(object) do
|
||||
{_, count} = get_recipient_count(object)
|
||||
count > 0
|
||||
end
|
||||
|
||||
@impl true
|
||||
def filter(%{"type" => "Create", "actor" => actor} = activity) do
|
||||
with {:ok, %User{local: false} = u} <- User.get_or_fetch_by_ap_id(actor),
|
||||
{:has_mentions, true} <- {:has_mentions, object_has_recipients?(activity)},
|
||||
{:good_reputation, true} <- {:good_reputation, good_reputation?(u)} do
|
||||
{:ok, activity}
|
||||
else
|
||||
{:ok, %User{local: true}} ->
|
||||
{:ok, activity}
|
||||
|
||||
{:has_mentions, false} ->
|
||||
{:ok, activity}
|
||||
|
||||
{:good_reputation, false} ->
|
||||
{:reject, "[AntiMentionSpamPolicy] User rejected"}
|
||||
|
||||
{:error, _} ->
|
||||
{:reject, "[AntiMentionSpamPolicy] Failed to get or fetch user by ap_id"}
|
||||
|
||||
e ->
|
||||
{:reject, "[AntiMentionSpamPolicy] Unhandled error #{inspect(e)}"}
|
||||
end
|
||||
end
|
||||
|
||||
# in all other cases, pass through
|
||||
def filter(message), do: {:ok, message}
|
||||
|
||||
@impl true
|
||||
def describe, do: {:ok, %{}}
|
||||
end
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes do
|
||||
alias Pleroma.EctoType.ActivityPub.ObjectValidators
|
||||
alias Pleroma.Maps
|
||||
alias Pleroma.Object
|
||||
alias Pleroma.Object.Containment
|
||||
alias Pleroma.User
|
||||
|
@ -24,6 +25,8 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes do
|
|||
end
|
||||
|
||||
def fix_object_defaults(data) do
|
||||
data = Maps.filter_empty_values(data)
|
||||
|
||||
context =
|
||||
Utils.maybe_create_context(
|
||||
data["context"] || data["conversation"] || data["inReplyTo"] || data["id"]
|
||||
|
|
|
@ -336,10 +336,6 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
|||
|
||||
def fix_tag(object), do: object
|
||||
|
||||
def fix_content_map(%{"contentMap" => nil} = object) do
|
||||
Map.drop(object, ["contentMap"])
|
||||
end
|
||||
|
||||
# content map usually only has one language so this will do for now.
|
||||
def fix_content_map(%{"contentMap" => content_map} = object) do
|
||||
content_groups = Map.to_list(content_map)
|
||||
|
|
|
@ -9,6 +9,16 @@ defmodule Pleroma.Web.Endpoint do
|
|||
|
||||
alias Pleroma.Config
|
||||
|
||||
socket("/api/v1/streaming", Pleroma.Web.MastodonAPI.WebsocketHandler,
|
||||
longpoll: false,
|
||||
websocket: [
|
||||
path: "/",
|
||||
compress: false,
|
||||
error_handler: {Pleroma.Web.MastodonAPI.WebsocketHandler, :handle_error, []},
|
||||
fullsweep_after: 20
|
||||
]
|
||||
)
|
||||
|
||||
socket("/socket", Pleroma.Web.UserSocket,
|
||||
websocket: [
|
||||
path: "/websocket",
|
||||
|
@ -18,7 +28,8 @@ defmodule Pleroma.Web.Endpoint do
|
|||
],
|
||||
timeout: 60_000,
|
||||
transport_log: false,
|
||||
compress: false
|
||||
compress: false,
|
||||
fullsweep_after: 20
|
||||
],
|
||||
longpoll: false
|
||||
)
|
||||
|
@ -32,7 +43,8 @@ defmodule Pleroma.Web.Endpoint do
|
|||
plug(Pleroma.Web.Plugs.HTTPSecurityPlug)
|
||||
plug(Pleroma.Web.Plugs.UploadedMedia)
|
||||
|
||||
@static_cache_control "public, no-cache"
|
||||
@static_cache_control "public, max-age=1209600"
|
||||
@static_cache_disabled "public, no-cache"
|
||||
|
||||
# InstanceStatic needs to be before Plug.Static to be able to override shipped-static files
|
||||
# If you're adding new paths to `only:` you'll need to configure them in InstanceStatic as well
|
||||
|
@ -43,22 +55,32 @@ defmodule Pleroma.Web.Endpoint do
|
|||
from: :pleroma,
|
||||
only: ["emoji", "images"],
|
||||
gzip: true,
|
||||
cache_control_for_etags: "public, max-age=1209600",
|
||||
headers: %{
|
||||
"cache-control" => "public, max-age=1209600"
|
||||
}
|
||||
)
|
||||
|
||||
plug(Pleroma.Web.Plugs.InstanceStatic,
|
||||
at: "/",
|
||||
gzip: true,
|
||||
cache_control_for_etags: @static_cache_control,
|
||||
headers: %{
|
||||
"cache-control" => @static_cache_control
|
||||
}
|
||||
)
|
||||
|
||||
# Careful! No `only` restriction here, as we don't know what frontends contain.
|
||||
plug(Pleroma.Web.Plugs.InstanceStatic,
|
||||
at: "/",
|
||||
gzip: true,
|
||||
cache_control_for_etags: @static_cache_disabled,
|
||||
headers: %{
|
||||
"cache-control" => @static_cache_disabled
|
||||
}
|
||||
)
|
||||
|
||||
plug(Pleroma.Web.Plugs.FrontendStatic,
|
||||
at: "/",
|
||||
frontend_type: :primary,
|
||||
only: ["index.html"],
|
||||
gzip: true,
|
||||
cache_control_for_etags: @static_cache_disabled,
|
||||
headers: %{
|
||||
"cache-control" => @static_cache_disabled
|
||||
}
|
||||
)
|
||||
|
||||
plug(Pleroma.Web.Plugs.FrontendStatic,
|
||||
at: "/",
|
||||
frontend_type: :primary,
|
||||
|
@ -75,9 +97,9 @@ defmodule Pleroma.Web.Endpoint do
|
|||
at: "/pleroma/admin",
|
||||
frontend_type: :admin,
|
||||
gzip: true,
|
||||
cache_control_for_etags: @static_cache_control,
|
||||
cache_control_for_etags: @static_cache_disabled,
|
||||
headers: %{
|
||||
"cache-control" => @static_cache_control
|
||||
"cache-control" => @static_cache_disabled
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -92,9 +114,9 @@ defmodule Pleroma.Web.Endpoint do
|
|||
only: Pleroma.Constants.static_only_files(),
|
||||
# credo:disable-for-previous-line Credo.Check.Readability.MaxLineLength
|
||||
gzip: true,
|
||||
cache_control_for_etags: @static_cache_control,
|
||||
cache_control_for_etags: @static_cache_disabled,
|
||||
headers: %{
|
||||
"cache-control" => @static_cache_control
|
||||
"cache-control" => @static_cache_disabled
|
||||
}
|
||||
)
|
||||
|
||||
|
|
|
@ -11,28 +11,21 @@ defmodule Pleroma.Web.MastodonAPI.WebsocketHandler do
|
|||
alias Pleroma.Web.Streamer
|
||||
alias Pleroma.Web.StreamerView
|
||||
|
||||
@behaviour :cowboy_websocket
|
||||
@behaviour Phoenix.Socket.Transport
|
||||
|
||||
# Client ping period.
|
||||
@tick :timer.seconds(30)
|
||||
# Cowboy timeout period.
|
||||
@timeout :timer.seconds(60)
|
||||
# Hibernate every X messages
|
||||
@hibernate_every 100
|
||||
|
||||
def init(%{qs: qs} = req, state) do
|
||||
with params <- Enum.into(:cow_qs.parse_qs(qs), %{}),
|
||||
sec_websocket <- :cowboy_req.header("sec-websocket-protocol", req, nil),
|
||||
access_token <- Map.get(params, "access_token"),
|
||||
{:ok, user, oauth_token} <- authenticate_request(access_token, sec_websocket),
|
||||
{:ok, topic} <- Streamer.get_topic(params["stream"], user, oauth_token, params) do
|
||||
req =
|
||||
if sec_websocket do
|
||||
:cowboy_req.set_resp_header("sec-websocket-protocol", sec_websocket, req)
|
||||
else
|
||||
req
|
||||
end
|
||||
@impl Phoenix.Socket.Transport
|
||||
def child_spec(_opts), do: :ignore
|
||||
|
||||
# This only prepares the connection and is not in the process yet
|
||||
@impl Phoenix.Socket.Transport
|
||||
def connect(%{params: params} = transport_info) do
|
||||
with access_token <- Map.get(params, "access_token"),
|
||||
{:ok, user, oauth_token} <- authenticate_request(access_token),
|
||||
{:ok, topic} <-
|
||||
Streamer.get_topic(params["stream"], user, oauth_token, params) do
|
||||
topics =
|
||||
if topic do
|
||||
[topic]
|
||||
|
@ -40,41 +33,40 @@ defmodule Pleroma.Web.MastodonAPI.WebsocketHandler do
|
|||
[]
|
||||
end
|
||||
|
||||
{:cowboy_websocket, req,
|
||||
%{user: user, topics: topics, oauth_token: oauth_token, count: 0, timer: nil},
|
||||
%{idle_timeout: @timeout}}
|
||||
state = %{
|
||||
user: user,
|
||||
topics: topics,
|
||||
oauth_token: oauth_token,
|
||||
count: 0,
|
||||
timer: nil
|
||||
}
|
||||
|
||||
{:ok, state}
|
||||
else
|
||||
{:error, :bad_topic} ->
|
||||
Logger.debug("#{__MODULE__} bad topic #{inspect(req)}")
|
||||
req = :cowboy_req.reply(404, req)
|
||||
{:ok, req, state}
|
||||
Logger.debug("#{__MODULE__} bad topic #{inspect(transport_info)}")
|
||||
|
||||
{:error, :bad_topic}
|
||||
|
||||
{:error, :unauthorized} ->
|
||||
Logger.debug("#{__MODULE__} authentication error: #{inspect(req)}")
|
||||
req = :cowboy_req.reply(401, req)
|
||||
{:ok, req, state}
|
||||
Logger.debug("#{__MODULE__} authentication error: #{inspect(transport_info)}")
|
||||
{:error, :unauthorized}
|
||||
end
|
||||
end
|
||||
|
||||
def websocket_init(state) do
|
||||
Logger.debug(
|
||||
"#{__MODULE__} accepted websocket connection for user #{(state.user || %{id: "anonymous"}).id}, topics #{state.topics}"
|
||||
)
|
||||
|
||||
# All subscriptions/links and messages cannot be created
|
||||
# until the processed is launched with init/1
|
||||
@impl Phoenix.Socket.Transport
|
||||
def init(state) do
|
||||
Enum.each(state.topics, fn topic -> Streamer.add_socket(topic, state.oauth_token) end)
|
||||
{:ok, %{state | timer: timer()}}
|
||||
|
||||
Process.send_after(self(), :ping, @tick)
|
||||
|
||||
{:ok, state}
|
||||
end
|
||||
|
||||
# Client's Pong frame.
|
||||
def websocket_handle(:pong, state) do
|
||||
if state.timer, do: Process.cancel_timer(state.timer)
|
||||
{:ok, %{state | timer: timer()}}
|
||||
end
|
||||
|
||||
# We only receive pings for now
|
||||
def websocket_handle(:ping, state), do: {:ok, state}
|
||||
|
||||
def websocket_handle({:text, text}, state) do
|
||||
@impl Phoenix.Socket.Transport
|
||||
def handle_in({text, [opcode: :text]}, state) do
|
||||
with {:ok, %{} = event} <- Jason.decode(text) do
|
||||
handle_client_event(event, state)
|
||||
else
|
||||
|
@ -84,50 +76,47 @@ defmodule Pleroma.Web.MastodonAPI.WebsocketHandler do
|
|||
end
|
||||
end
|
||||
|
||||
def websocket_handle(frame, state) do
|
||||
def handle_in(frame, state) do
|
||||
Logger.error("#{__MODULE__} received frame: #{inspect(frame)}")
|
||||
{:ok, state}
|
||||
end
|
||||
|
||||
def websocket_info({:render_with_user, view, template, item, topic}, state) do
|
||||
@impl Phoenix.Socket.Transport
|
||||
def handle_info({:render_with_user, view, template, item, topic}, state) do
|
||||
user = %User{} = User.get_cached_by_ap_id(state.user.ap_id)
|
||||
|
||||
unless Streamer.filtered_by_user?(user, item) do
|
||||
websocket_info({:text, view.render(template, item, user, topic)}, %{state | user: user})
|
||||
message = view.render(template, item, user, topic)
|
||||
{:push, {:text, message}, %{state | user: user}}
|
||||
else
|
||||
{:ok, state}
|
||||
end
|
||||
end
|
||||
|
||||
def websocket_info({:text, message}, state) do
|
||||
# If the websocket processed X messages, force an hibernate/GC.
|
||||
# We don't hibernate at every message to balance CPU usage/latency with RAM usage.
|
||||
if state.count > @hibernate_every do
|
||||
{:reply, {:text, message}, %{state | count: 0}, :hibernate}
|
||||
else
|
||||
{:reply, {:text, message}, %{state | count: state.count + 1}}
|
||||
end
|
||||
def handle_info({:text, text}, state) do
|
||||
{:push, {:text, text}, state}
|
||||
end
|
||||
|
||||
# Ping tick. We don't re-queue a timer there, it is instead queued when :pong is received.
|
||||
# As we hibernate there, reset the count to 0.
|
||||
# If the client misses :pong, Cowboy will automatically timeout the connection after
|
||||
# `@idle_timeout`.
|
||||
def websocket_info(:tick, state) do
|
||||
{:reply, :ping, %{state | timer: nil, count: 0}, :hibernate}
|
||||
def handle_info(:ping, state) do
|
||||
Process.send_after(self(), :ping, @tick)
|
||||
|
||||
{:push, {:ping, ""}, state}
|
||||
end
|
||||
|
||||
def websocket_info(:close, state) do
|
||||
{:stop, state}
|
||||
def handle_info(:close, state) do
|
||||
{:stop, {:closed, 'connection closed by server'}, state}
|
||||
end
|
||||
|
||||
# State can be `[]` only in case we terminate before switching to websocket,
|
||||
# we already log errors for these cases in `init/1`, so just do nothing here
|
||||
def terminate(_reason, _req, []), do: :ok
|
||||
def handle_info(msg, state) do
|
||||
Logger.debug("#{__MODULE__} received info: #{inspect(msg)}")
|
||||
|
||||
def terminate(reason, _req, state) do
|
||||
{:ok, state}
|
||||
end
|
||||
|
||||
@impl Phoenix.Socket.Transport
|
||||
def terminate(reason, state) do
|
||||
Logger.debug(
|
||||
"#{__MODULE__} terminating websocket connection for user #{(state.user || %{id: "anonymous"}).id}, topics #{state.topics || "?"}: #{inspect(reason)}"
|
||||
"#{__MODULE__} terminating websocket connection for user #{(state.user || %{id: "anonymous"}).id}, topics #{state.topics || "?"}: #{inspect(reason)})"
|
||||
)
|
||||
|
||||
Enum.each(state.topics, fn topic -> Streamer.remove_socket(topic) end)
|
||||
|
@ -135,16 +124,13 @@ defmodule Pleroma.Web.MastodonAPI.WebsocketHandler do
|
|||
end
|
||||
|
||||
# Public streams without authentication.
|
||||
defp authenticate_request(nil, nil) do
|
||||
defp authenticate_request(nil) do
|
||||
{:ok, nil, nil}
|
||||
end
|
||||
|
||||
# Authenticated streams.
|
||||
defp authenticate_request(access_token, sec_websocket) do
|
||||
token = access_token || sec_websocket
|
||||
|
||||
with true <- is_bitstring(token),
|
||||
oauth_token = %Token{user_id: user_id} <- Repo.get_by(Token, token: token),
|
||||
defp authenticate_request(access_token) do
|
||||
with oauth_token = %Token{user_id: user_id} <- Repo.get_by(Token, token: access_token),
|
||||
user = %User{} <- User.get_cached_by_id(user_id) do
|
||||
{:ok, user, oauth_token}
|
||||
else
|
||||
|
@ -152,36 +138,32 @@ defmodule Pleroma.Web.MastodonAPI.WebsocketHandler do
|
|||
end
|
||||
end
|
||||
|
||||
defp timer do
|
||||
Process.send_after(self(), :tick, @tick)
|
||||
end
|
||||
|
||||
defp handle_client_event(%{"type" => "subscribe", "stream" => _topic} = params, state) do
|
||||
with {_, {:ok, topic}} <-
|
||||
{:topic, Streamer.get_topic(params["stream"], state.user, state.oauth_token, params)},
|
||||
{_, false} <- {:subscribed, topic in state.topics} do
|
||||
Streamer.add_socket(topic, state.oauth_token)
|
||||
|
||||
{[
|
||||
{:text,
|
||||
StreamerView.render("pleroma_respond.json", %{type: "subscribe", result: "success"})}
|
||||
], %{state | topics: [topic | state.topics]}}
|
||||
message =
|
||||
StreamerView.render("pleroma_respond.json", %{type: "subscribe", result: "success"})
|
||||
|
||||
{:reply, :ok, {:text, message}, %{state | topics: [topic | state.topics]}}
|
||||
else
|
||||
{:subscribed, true} ->
|
||||
{[
|
||||
{:text,
|
||||
StreamerView.render("pleroma_respond.json", %{type: "subscribe", result: "ignored"})}
|
||||
], state}
|
||||
message =
|
||||
StreamerView.render("pleroma_respond.json", %{type: "subscribe", result: "ignored"})
|
||||
|
||||
{:reply, :error, {:text, message}, state}
|
||||
|
||||
{:topic, {:error, error}} ->
|
||||
{[
|
||||
{:text,
|
||||
StreamerView.render("pleroma_respond.json", %{
|
||||
type: "subscribe",
|
||||
result: "error",
|
||||
error: error
|
||||
})}
|
||||
], state}
|
||||
message =
|
||||
StreamerView.render("pleroma_respond.json", %{
|
||||
type: "subscribe",
|
||||
result: "error",
|
||||
error: error
|
||||
})
|
||||
|
||||
{:reply, :error, {:text, message}, state}
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -191,26 +173,26 @@ defmodule Pleroma.Web.MastodonAPI.WebsocketHandler do
|
|||
{_, true} <- {:subscribed, topic in state.topics} do
|
||||
Streamer.remove_socket(topic)
|
||||
|
||||
{[
|
||||
{:text,
|
||||
StreamerView.render("pleroma_respond.json", %{type: "unsubscribe", result: "success"})}
|
||||
], %{state | topics: List.delete(state.topics, topic)}}
|
||||
message =
|
||||
StreamerView.render("pleroma_respond.json", %{type: "unsubscribe", result: "success"})
|
||||
|
||||
{:reply, :ok, {:text, message}, %{state | topics: List.delete(state.topics, topic)}}
|
||||
else
|
||||
{:subscribed, false} ->
|
||||
{[
|
||||
{:text,
|
||||
StreamerView.render("pleroma_respond.json", %{type: "unsubscribe", result: "ignored"})}
|
||||
], state}
|
||||
message =
|
||||
StreamerView.render("pleroma_respond.json", %{type: "unsubscribe", result: "ignored"})
|
||||
|
||||
{:reply, :error, {:text, message}, state}
|
||||
|
||||
{:topic, {:error, error}} ->
|
||||
{[
|
||||
{:text,
|
||||
StreamerView.render("pleroma_respond.json", %{
|
||||
type: "unsubscribe",
|
||||
result: "error",
|
||||
error: error
|
||||
})}
|
||||
], state}
|
||||
message =
|
||||
StreamerView.render("pleroma_respond.json", %{
|
||||
type: "unsubscribe",
|
||||
result: "error",
|
||||
error: error
|
||||
})
|
||||
|
||||
{:reply, :error, {:text, message}, state}
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -219,39 +201,47 @@ defmodule Pleroma.Web.MastodonAPI.WebsocketHandler do
|
|||
state
|
||||
) do
|
||||
with {:auth, nil, nil} <- {:auth, state.user, state.oauth_token},
|
||||
{:ok, user, oauth_token} <- authenticate_request(access_token, nil) do
|
||||
{[
|
||||
{:text,
|
||||
StreamerView.render("pleroma_respond.json", %{
|
||||
type: "pleroma:authenticate",
|
||||
result: "success"
|
||||
})}
|
||||
], %{state | user: user, oauth_token: oauth_token}}
|
||||
{:ok, user, oauth_token} <- authenticate_request(access_token) do
|
||||
message =
|
||||
StreamerView.render("pleroma_respond.json", %{
|
||||
type: "pleroma:authenticate",
|
||||
result: "success"
|
||||
})
|
||||
|
||||
{:reply, :ok, {:text, message}, %{state | user: user, oauth_token: oauth_token}}
|
||||
else
|
||||
{:auth, _, _} ->
|
||||
{[
|
||||
{:text,
|
||||
StreamerView.render("pleroma_respond.json", %{
|
||||
type: "pleroma:authenticate",
|
||||
result: "error",
|
||||
error: :already_authenticated
|
||||
})}
|
||||
], state}
|
||||
message =
|
||||
StreamerView.render("pleroma_respond.json", %{
|
||||
type: "pleroma:authenticate",
|
||||
result: "error",
|
||||
error: :already_authenticated
|
||||
})
|
||||
|
||||
{:reply, :error, {:text, message}, state}
|
||||
|
||||
_ ->
|
||||
{[
|
||||
{:text,
|
||||
StreamerView.render("pleroma_respond.json", %{
|
||||
type: "pleroma:authenticate",
|
||||
result: "error",
|
||||
error: :unauthorized
|
||||
})}
|
||||
], state}
|
||||
message =
|
||||
StreamerView.render("pleroma_respond.json", %{
|
||||
type: "pleroma:authenticate",
|
||||
result: "error",
|
||||
error: :unauthorized
|
||||
})
|
||||
|
||||
{:reply, :error, {:text, message}, state}
|
||||
end
|
||||
end
|
||||
|
||||
defp handle_client_event(params, state) do
|
||||
Logger.error("#{__MODULE__} received unknown event: #{inspect(params)}")
|
||||
{[], state}
|
||||
{:ok, state}
|
||||
end
|
||||
|
||||
def handle_error(conn, :unauthorized) do
|
||||
Plug.Conn.send_resp(conn, 401, "Unauthorized")
|
||||
end
|
||||
|
||||
def handle_error(conn, _reason) do
|
||||
Plug.Conn.send_resp(conn, 404, "Not Found")
|
||||
end
|
||||
end
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
<div class="account-header__avatar" style="background-image: url('<%= Pleroma.User.avatar_url(@user) %>')"></div>
|
||||
<div class="account-header__meta">
|
||||
<div class="account-header__display-name"><%= @user.name %></div>
|
||||
<div class="account-header__nickname">@<%= @user.nickname %>@<%= Pleroma.User.get_host(@user) %></div>
|
||||
<div class="account-header__nickname">@<%= Pleroma.User.full_nickname(@user.nickname) %></div>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
|
|
|
@ -28,7 +28,7 @@ defmodule Pleroma.Workers.BackgroundWorker do
|
|||
def perform(%Job{args: %{"op" => op, "user_id" => user_id, "identifiers" => identifiers}})
|
||||
when op in ["blocks_import", "follow_import", "mutes_import"] do
|
||||
user = User.get_cached_by_id(user_id)
|
||||
{:ok, User.Import.perform(String.to_atom(op), user, identifiers)}
|
||||
{:ok, User.Import.perform(String.to_existing_atom(op), user, identifiers)}
|
||||
end
|
||||
|
||||
def perform(%Job{
|
||||
|
|
3
mix.exs
3
mix.exs
|
@ -137,7 +137,7 @@ defmodule Pleroma.Mixfile do
|
|||
{:calendar, "~> 1.0"},
|
||||
{:cachex, "~> 3.2"},
|
||||
{:poison, "~> 3.0", override: true},
|
||||
{:tesla, "~> 1.4.0", override: true},
|
||||
{:tesla, "~> 1.8.0"},
|
||||
{:castore, "~> 0.1"},
|
||||
{:cowlib, "~> 2.9", override: true},
|
||||
{:gun, "~> 2.0.0-rc.1", override: true},
|
||||
|
@ -188,6 +188,7 @@ defmodule Pleroma.Mixfile do
|
|||
{:exile,
|
||||
git: "https://git.pleroma.social/pleroma/elixir-libraries/exile.git",
|
||||
ref: "0d6337cf68e7fbc8a093cae000955aa93b067f91"},
|
||||
{:bandit, "~> 1.2"},
|
||||
|
||||
## dev & test
|
||||
{:ex_doc, "~> 0.22", only: :dev, runtime: false},
|
||||
|
|
6
mix.lock
6
mix.lock
|
@ -1,5 +1,6 @@
|
|||
%{
|
||||
"accept": {:hex, :accept, "0.3.5", "b33b127abca7cc948bbe6caa4c263369abf1347cfa9d8e699c6d214660f10cd1", [:rebar3], [], "hexpm", "11b18c220bcc2eab63b5470c038ef10eb6783bcb1fcdb11aa4137defa5ac1bb8"},
|
||||
"bandit": {:hex, :bandit, "1.2.1", "aa485b4ac175065b8e0fb5864ddd5dd7b50d52336b36f61c82f484c3718b3d15", [:mix], [{:hpax, "~> 0.1.1", [hex: :hpax, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:thousand_island, "~> 1.0", [hex: :thousand_island, repo: "hexpm", optional: false]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "27393e590a407f1b7d51c5fee4737f139fe224a30449ce25061eac70f763896b"},
|
||||
"base62": {:hex, :base62, "1.2.2", "85c6627eb609317b70f555294045895ffaaeb1758666ab9ef9ca38865b11e629", [:mix], [{:custom_base, "~> 0.2.1", [hex: :custom_base, repo: "hexpm", optional: false]}], "hexpm", "d41336bda8eaa5be197f1e4592400513ee60518e5b9f4dcf38f4b4dae6f377bb"},
|
||||
"bbcode_pleroma": {:hex, :bbcode_pleroma, "0.2.0", "d36f5bca6e2f62261c45be30fa9b92725c0655ad45c99025cb1c3e28e25803ef", [:mix], [{:nimble_parsec, "~> 0.5", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "19851074419a5fedb4ef49e1f01b30df504bb5dbb6d6adfc135238063bebd1c3"},
|
||||
"bcrypt_elixir": {:hex, :bcrypt_elixir, "2.3.1", "5114d780459a04f2b4aeef52307de23de961b69e13a5cd98a911e39fda13f420", [:make, :mix], [{:comeonin, "~> 5.3", [hex: :comeonin, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "42182d5f46764def15bf9af83739e3bf4ad22661b1c34fc3e88558efced07279"},
|
||||
|
@ -51,7 +52,7 @@
|
|||
"fast_html": {:hex, :fast_html, "2.2.0", "6c5ef1be087a4ed613b0379c13f815c4d11742b36b67bb52cee7859847c84520", [: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", "064c4f23b4a6168f9187dac8984b056f2c531bb0787f559fd6a8b34b38aefbae"},
|
||||
"fast_sanitize": {:hex, :fast_sanitize, "0.2.3", "67b93dfb34e302bef49fec3aaab74951e0f0602fd9fa99085987af05bd91c7a5", [:mix], [{:fast_html, "~> 2.0", [hex: :fast_html, repo: "hexpm", optional: false]}, {:plug, "~> 1.8", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "e8ad286d10d0386e15d67d0ee125245ebcfbc7d7290b08712ba9013c8c5e56e2"},
|
||||
"file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"},
|
||||
"finch": {:hex, :finch, "0.17.0", "17d06e1d44d891d20dbd437335eebe844e2426a0cd7e3a3e220b461127c73f70", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: false]}, {:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.3", [hex: :mint, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.4 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 0.2.6 or ~> 1.0", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "8d014a661bb6a437263d4b5abf0bcbd3cf0deb26b1e8596f2a271d22e48934c7"},
|
||||
"finch": {:hex, :finch, "0.18.0", "944ac7d34d0bd2ac8998f79f7a811b21d87d911e77a786bc5810adb75632ada4", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: false]}, {:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.3", [hex: :mint, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.4 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 0.2.6 or ~> 1.0", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "69f5045b042e531e53edc2574f15e25e735b522c37e2ddb766e15b979e03aa65"},
|
||||
"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.35.2", "87f8c75ed8654b9635b311774308b2760b47e9a579dabf2e4d5f1e1d42c39e0b", [:mix], [], "hexpm", "6b05289a8e9eac475f644f09c2e4ba7e19201fd002b89c28c1293e7bd16773d9"},
|
||||
"gen_smtp": {:hex, :gen_smtp, "0.15.0", "9f51960c17769b26833b50df0b96123605a8024738b62db747fece14eb2fbfcc", [:rebar3], [], "hexpm", "29bd14a88030980849c7ed2447b8db6d6c9278a28b11a44cafe41b791205440f"},
|
||||
|
@ -132,7 +133,8 @@
|
|||
"telemetry_metrics": {:hex, :telemetry_metrics, "0.6.2", "2caabe9344ec17eafe5403304771c3539f3b6e2f7fb6a6f602558c825d0d0bfb", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "9b43db0dc33863930b9ef9d27137e78974756f5f198cae18409970ed6fa5b561"},
|
||||
"telemetry_metrics_prometheus_core": {:hex, :telemetry_metrics_prometheus_core, "1.2.0", "b583c3f18508f5c5561b674d16cf5d9afd2ea3c04505b7d92baaeac93c1b8260", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:telemetry_metrics, "~> 0.6", [hex: :telemetry_metrics, repo: "hexpm", optional: false]}], "hexpm", "9cba950e1c4733468efbe3f821841f34ac05d28e7af7798622f88ecdbbe63ea3"},
|
||||
"telemetry_poller": {:hex, :telemetry_poller, "1.0.0", "db91bb424e07f2bb6e73926fcafbfcbcb295f0193e0a00e825e589a0a47e8453", [:rebar3], [{:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "b3a24eafd66c3f42da30fc3ca7dda1e9d546c12250a2d60d7b81d264fbec4f6e"},
|
||||
"tesla": {:hex, :tesla, "1.4.4", "bb89aa0c9745190930366f6a2ac612cdf2d0e4d7fff449861baa7875afd797b2", [: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 or ~> 2.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 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "d5503a49f9dec1b287567ea8712d085947e247cb11b06bc54adb05bfde466457"},
|
||||
"tesla": {:hex, :tesla, "1.8.0", "d511a4f5c5e42538d97eef7c40ec4f3e44effdc5068206f42ed859e09e51d1fd", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:exjsx, ">= 3.0.0", [hex: :exjsx, repo: "hexpm", optional: true]}, {:finch, "~> 0.13", [hex: :finch, repo: "hexpm", optional: true]}, {:fuse, "~> 2.4", [hex: :fuse, repo: "hexpm", optional: true]}, {:gun, ">= 1.0.0", [hex: :gun, repo: "hexpm", optional: true]}, {:hackney, "~> 1.6", [hex: :hackney, repo: "hexpm", optional: true]}, {:ibrowse, "4.4.2", [hex: :ibrowse, repo: "hexpm", optional: true]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: true]}, {:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.0", [hex: :mint, repo: "hexpm", optional: true]}, {:msgpax, "~> 2.3", [hex: :msgpax, repo: "hexpm", optional: true]}, {:poison, ">= 1.0.0", [hex: :poison, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "10501f360cd926a309501287470372af1a6e1cbed0f43949203a4c13300bc79f"},
|
||||
"thousand_island": {:hex, :thousand_island, "1.3.2", "bc27f9afba6e1a676dd36507d42e429935a142cf5ee69b8e3f90bff1383943cd", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "0e085b93012cd1057b378fce40cbfbf381ff6d957a382bfdd5eca1a98eec2535"},
|
||||
"timex": {:hex, :timex, "3.7.7", "3ed093cae596a410759104d878ad7b38e78b7c2151c6190340835515d4a46b8a", [: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", "0ec4b09f25fe311321f9fc04144a7e3affe48eb29481d7a5583849b6c4dfa0a7"},
|
||||
"toml": {:hex, :toml, "0.7.0", "fbcd773caa937d0c7a02c301a1feea25612720ac3fa1ccb8bfd9d30d822911de", [:mix], [], "hexpm", "0690246a2478c1defd100b0c9b89b4ea280a22be9a7b313a8a058a2408a2fa70"},
|
||||
"trailing_format_plug": {:hex, :trailing_format_plug, "0.0.7", "64b877f912cf7273bed03379936df39894149e35137ac9509117e59866e10e45", [:mix], [{:plug, "> 0.12.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "bd4fde4c15f3e993a999e019d64347489b91b7a9096af68b2bdadd192afa693f"},
|
||||
|
|
|
@ -3,16 +3,16 @@ msgstr ""
|
|||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2021-09-18 09:07+0000\n"
|
||||
"PO-Revision-Date: 2021-09-19 09:45+0000\n"
|
||||
"Last-Translator: Ryo Ueno <r.ueno.nfive@gmail.com>\n"
|
||||
"PO-Revision-Date: 2024-02-16 11:49+0000\n"
|
||||
"Last-Translator: SyoBoN <syobon@syobon.net>\n"
|
||||
"Language-Team: Japanese <https://translate.pleroma.social/projects/pleroma/"
|
||||
"pleroma/ja/>\n"
|
||||
"pleroma-backend-domain-errors/ja/>\n"
|
||||
"Language: ja\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||
"X-Generator: Weblate 4.6.2\n"
|
||||
"X-Generator: Weblate 4.13.1\n"
|
||||
|
||||
## This file is a PO Template file.
|
||||
##
|
||||
|
@ -25,11 +25,11 @@ msgstr ""
|
|||
## effect: edit them in PO (`.po`) files instead.
|
||||
## From Ecto.Changeset.cast/4
|
||||
msgid "can't be blank"
|
||||
msgstr ""
|
||||
msgstr "空にすることはできません"
|
||||
|
||||
## From Ecto.Changeset.unique_constraint/3
|
||||
msgid "has already been taken"
|
||||
msgstr ""
|
||||
msgstr "すでに使用されています"
|
||||
|
||||
## From Ecto.Changeset.put_change/3
|
||||
msgid "is invalid"
|
||||
|
@ -37,7 +37,7 @@ msgstr ""
|
|||
|
||||
## From Ecto.Changeset.validate_format/3
|
||||
msgid "has invalid format"
|
||||
msgstr ""
|
||||
msgstr "書式が正しくありません"
|
||||
|
||||
## From Ecto.Changeset.validate_subset/3
|
||||
msgid "has an invalid entry"
|
||||
|
@ -45,7 +45,7 @@ msgstr ""
|
|||
|
||||
## From Ecto.Changeset.validate_exclusion/3
|
||||
msgid "is reserved"
|
||||
msgstr ""
|
||||
msgstr "予約されています"
|
||||
|
||||
## From Ecto.Changeset.validate_confirmation/3
|
||||
msgid "does not match confirmation"
|
||||
|
@ -61,23 +61,23 @@ msgstr ""
|
|||
## From Ecto.Changeset.validate_length/3
|
||||
msgid "should be %{count} character(s)"
|
||||
msgid_plural "should be %{count} character(s)"
|
||||
msgstr[0] ""
|
||||
msgstr[0] "%{count}文字である必要があります"
|
||||
|
||||
msgid "should have %{count} item(s)"
|
||||
msgid_plural "should have %{count} item(s)"
|
||||
msgstr[0] ""
|
||||
msgstr[0] "%{count}個の要素を含む必要があります"
|
||||
|
||||
msgid "should be at least %{count} character(s)"
|
||||
msgid_plural "should be at least %{count} character(s)"
|
||||
msgstr[0] ""
|
||||
msgstr[0] "%{count}文字以上である必要があります"
|
||||
|
||||
msgid "should have at least %{count} item(s)"
|
||||
msgid_plural "should have at least %{count} item(s)"
|
||||
msgstr[0] ""
|
||||
msgstr[0] "%{count}個以上の要素を含む必要があります"
|
||||
|
||||
msgid "should be at most %{count} character(s)"
|
||||
msgid_plural "should be at most %{count} character(s)"
|
||||
msgstr[0] ""
|
||||
msgstr[0] "%{count}文字以下である必要があります"
|
||||
|
||||
msgid "should have at most %{count} item(s)"
|
||||
msgid_plural "should have at most %{count} item(s)"
|
||||
|
@ -102,7 +102,7 @@ msgstr "%{number}でなければなりません"
|
|||
#: lib/pleroma/web/common_api/common_api.ex:505
|
||||
#, elixir-format
|
||||
msgid "Account not found"
|
||||
msgstr "アカウントがありません"
|
||||
msgstr "アカウントが見つかりません"
|
||||
|
||||
#: lib/pleroma/web/common_api/common_api.ex:339
|
||||
#, elixir-format
|
||||
|
@ -226,7 +226,7 @@ msgstr ""
|
|||
#: lib/pleroma/web/common_api/utils.ex:414
|
||||
#, elixir-format
|
||||
msgid "Invalid password."
|
||||
msgstr ""
|
||||
msgstr "パスワードが間違っています。"
|
||||
|
||||
#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:220
|
||||
#, elixir-format
|
||||
|
@ -236,7 +236,7 @@ msgstr ""
|
|||
#: lib/pleroma/web/twitter_api/twitter_api.ex:109
|
||||
#, elixir-format
|
||||
msgid "Kocaptcha service unavailable"
|
||||
msgstr ""
|
||||
msgstr "Kocaptchaが利用できません"
|
||||
|
||||
#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:112
|
||||
#, elixir-format
|
||||
|
@ -259,12 +259,12 @@ msgstr ""
|
|||
#: lib/pleroma/web/feed/user_controller.ex:71 lib/pleroma/web/ostatus/ostatus_controller.ex:143
|
||||
#, elixir-format
|
||||
msgid "Not found"
|
||||
msgstr ""
|
||||
msgstr "見つかりません"
|
||||
|
||||
#: lib/pleroma/web/common_api/common_api.ex:331
|
||||
#, elixir-format
|
||||
msgid "Poll's author can't vote"
|
||||
msgstr ""
|
||||
msgstr "作成者は投票できません"
|
||||
|
||||
#: lib/pleroma/web/mastodon_api/controllers/fallback_controller.ex:20
|
||||
#: lib/pleroma/web/mastodon_api/controllers/poll_controller.ex:37 lib/pleroma/web/mastodon_api/controllers/poll_controller.ex:49
|
||||
|
@ -284,17 +284,17 @@ msgstr ""
|
|||
#: lib/pleroma/web/common_api/activity_draft.ex:107
|
||||
#, elixir-format
|
||||
msgid "The message visibility must be direct"
|
||||
msgstr ""
|
||||
msgstr "公開範囲は「ダイレクト」でなければいけません"
|
||||
|
||||
#: lib/pleroma/web/common_api/utils.ex:573
|
||||
#, elixir-format
|
||||
msgid "The status is over the character limit"
|
||||
msgstr ""
|
||||
msgstr "文字数制限を超過しています"
|
||||
|
||||
#: lib/pleroma/plugs/ensure_public_or_authenticated_plug.ex:31
|
||||
#, elixir-format
|
||||
msgid "This resource requires authentication."
|
||||
msgstr ""
|
||||
msgstr "認証が必要です。"
|
||||
|
||||
#: lib/pleroma/plugs/rate_limiter/rate_limiter.ex:206
|
||||
#, elixir-format
|
||||
|
@ -304,7 +304,7 @@ msgstr ""
|
|||
#: lib/pleroma/web/common_api/common_api.ex:356
|
||||
#, elixir-format
|
||||
msgid "Too many choices"
|
||||
msgstr ""
|
||||
msgstr "選択肢が多すぎます"
|
||||
|
||||
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:443
|
||||
#, elixir-format
|
||||
|
@ -320,7 +320,7 @@ msgstr ""
|
|||
#: lib/pleroma/web/oauth/oauth_controller.ex:308
|
||||
#, elixir-format
|
||||
msgid "Your account is currently disabled"
|
||||
msgstr ""
|
||||
msgstr "あなたのアカウントは無効化されています"
|
||||
|
||||
#: lib/pleroma/web/oauth/oauth_controller.ex:183
|
||||
#: lib/pleroma/web/oauth/oauth_controller.ex:331
|
||||
|
@ -331,23 +331,23 @@ msgstr ""
|
|||
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:390
|
||||
#, elixir-format
|
||||
msgid "can't read inbox of %{nickname} as %{as_nickname}"
|
||||
msgstr ""
|
||||
msgstr "%{nickname}のinboxを%{as_nickname}として閲覧することはできません"
|
||||
|
||||
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:473
|
||||
#, elixir-format
|
||||
msgid "can't update outbox of %{nickname} as %{as_nickname}"
|
||||
msgstr ""
|
||||
msgstr "%{nickname}のoutboxを%{as_nickname}として更新することはできません"
|
||||
|
||||
#: lib/pleroma/web/common_api/common_api.ex:471
|
||||
#, elixir-format
|
||||
msgid "conversation is already muted"
|
||||
msgstr ""
|
||||
msgstr "この会話はミュート済みです"
|
||||
|
||||
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:314
|
||||
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:492
|
||||
#, elixir-format
|
||||
msgid "error"
|
||||
msgstr ""
|
||||
msgstr "エラー"
|
||||
|
||||
#: lib/pleroma/web/pleroma_api/controllers/mascot_controller.ex:32
|
||||
#, elixir-format
|
||||
|
@ -357,7 +357,7 @@ msgstr ""
|
|||
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:62
|
||||
#, elixir-format
|
||||
msgid "not found"
|
||||
msgstr ""
|
||||
msgstr "見つかりません"
|
||||
|
||||
#: lib/pleroma/web/oauth/oauth_controller.ex:394
|
||||
#, elixir-format
|
||||
|
@ -382,28 +382,28 @@ msgstr ""
|
|||
#: lib/pleroma/web/oauth/oauth_controller.ex:410
|
||||
#, elixir-format
|
||||
msgid "Failed to authenticate: %{message}."
|
||||
msgstr ""
|
||||
msgstr "認証に失敗しました: %{message}。"
|
||||
|
||||
#: lib/pleroma/web/oauth/oauth_controller.ex:441
|
||||
#, elixir-format
|
||||
msgid "Failed to set up user account."
|
||||
msgstr ""
|
||||
msgstr "ユーザーアカウントの設定に失敗しました。"
|
||||
|
||||
#: lib/pleroma/plugs/oauth_scopes_plug.ex:38
|
||||
#, elixir-format
|
||||
msgid "Insufficient permissions: %{permissions}."
|
||||
msgstr ""
|
||||
msgstr "%{permissions}の権限が必要です。"
|
||||
|
||||
#: lib/pleroma/plugs/uploaded_media.ex:104
|
||||
#, elixir-format
|
||||
msgid "Internal Error"
|
||||
msgstr ""
|
||||
msgstr "内部エラー"
|
||||
|
||||
#: lib/pleroma/web/oauth/fallback_controller.ex:22
|
||||
#: lib/pleroma/web/oauth/fallback_controller.ex:29
|
||||
#, elixir-format
|
||||
msgid "Invalid Username/Password"
|
||||
msgstr ""
|
||||
msgstr "ユーザー名/パスワードが間違っています"
|
||||
|
||||
#: lib/pleroma/web/twitter_api/twitter_api.ex:118
|
||||
#, elixir-format
|
||||
|
@ -423,7 +423,7 @@ msgstr ""
|
|||
#: lib/pleroma/web/oauth/fallback_controller.ex:14
|
||||
#, elixir-format
|
||||
msgid "Unknown error, please check the details and try again."
|
||||
msgstr ""
|
||||
msgstr "不明なエラーです。詳細を確認し、再度試行してください。"
|
||||
|
||||
#: lib/pleroma/web/oauth/oauth_controller.ex:119
|
||||
#: lib/pleroma/web/oauth/oauth_controller.ex:158
|
||||
|
@ -449,17 +449,17 @@ msgstr ""
|
|||
#: lib/pleroma/web/twitter_api/twitter_api.ex:103
|
||||
#, elixir-format
|
||||
msgid "CAPTCHA Error"
|
||||
msgstr ""
|
||||
msgstr "CAPTCHAエラー"
|
||||
|
||||
#: lib/pleroma/web/common_api/common_api.ex:290
|
||||
#, elixir-format
|
||||
msgid "Could not add reaction emoji"
|
||||
msgstr ""
|
||||
msgstr "リアクションを追加できません"
|
||||
|
||||
#: lib/pleroma/web/common_api/common_api.ex:301
|
||||
#, elixir-format
|
||||
msgid "Could not remove reaction emoji"
|
||||
msgstr ""
|
||||
msgstr "リアクションを解除できません"
|
||||
|
||||
#: lib/pleroma/web/twitter_api/twitter_api.ex:129
|
||||
#, elixir-format
|
||||
|
@ -469,7 +469,7 @@ msgstr ""
|
|||
#: lib/pleroma/web/mastodon_api/controllers/list_controller.ex:92
|
||||
#, elixir-format
|
||||
msgid "List not found"
|
||||
msgstr ""
|
||||
msgstr "リストが見つかりません"
|
||||
|
||||
#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:123
|
||||
#, elixir-format
|
||||
|
@ -480,7 +480,7 @@ msgstr ""
|
|||
#: lib/pleroma/web/oauth/oauth_controller.ex:321
|
||||
#, elixir-format
|
||||
msgid "Password reset is required"
|
||||
msgstr ""
|
||||
msgstr "パスワードのリセットが必要です"
|
||||
|
||||
#: lib/pleroma/tests/auth_test_controller.ex:9
|
||||
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:6 lib/pleroma/web/admin_api/controllers/admin_api_controller.ex:6
|
||||
|
@ -522,7 +522,7 @@ msgstr ""
|
|||
#: lib/pleroma/plugs/ensure_authenticated_plug.ex:28
|
||||
#, elixir-format
|
||||
msgid "Two-factor authentication enabled, you must use a access token."
|
||||
msgstr ""
|
||||
msgstr "二段階認証が有効になっています。アクセストークンが必要です。"
|
||||
|
||||
#: lib/pleroma/web/pleroma_api/controllers/emoji_pack_controller.ex:210
|
||||
#, elixir-format
|
||||
|
@ -552,7 +552,7 @@ msgstr ""
|
|||
#: lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex:61
|
||||
#, elixir-format
|
||||
msgid "Web push subscription is disabled on this Pleroma instance"
|
||||
msgstr ""
|
||||
msgstr "このPleromaインスタンスではプッシュ通知が利用できません"
|
||||
|
||||
#: lib/pleroma/web/admin_api/controllers/admin_api_controller.ex:451
|
||||
#, elixir-format
|
||||
|
@ -562,19 +562,19 @@ msgstr ""
|
|||
#: lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex:126
|
||||
#, elixir-format
|
||||
msgid "authorization required for timeline view"
|
||||
msgstr ""
|
||||
msgstr "タイムラインを閲覧するには認証が必要です"
|
||||
|
||||
#: lib/pleroma/web/mastodon_api/controllers/fallback_controller.ex:24
|
||||
#, elixir-format
|
||||
msgid "Access denied"
|
||||
msgstr ""
|
||||
msgstr "アクセスが拒否されました"
|
||||
|
||||
#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:282
|
||||
#, elixir-format
|
||||
msgid "This API requires an authenticated user"
|
||||
msgstr ""
|
||||
msgstr "このAPIを利用するには認証が必要です"
|
||||
|
||||
#: lib/pleroma/plugs/user_is_admin_plug.ex:21
|
||||
#, elixir-format
|
||||
msgid "User is not an admin."
|
||||
msgstr ""
|
||||
msgstr "ユーザーは管理者ではありません。"
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
{"@context":"https://www.w3.org/ns/activitystreams","type":"Note","id":"https://cc.mkdir.uk/ap/note/e5d1d0a1-1ab3-4498-9949-588e3fdea286","attributedTo":"https://cc.mkdir.uk/ap/acct/hiira","inReplyTo":"","quoteUrl":"","content":"おはコンー","published":"2024-01-19T22:08:05Z","to":["https://www.w3.org/ns/activitystreams#Public"],"tag":null,"attachment":[],"object":null}
|
|
@ -268,17 +268,6 @@ defmodule Pleroma.Integration.MastodonWebsocketTest do
|
|||
end)
|
||||
end
|
||||
|
||||
test "accepts valid token on Sec-WebSocket-Protocol header", %{token: token} do
|
||||
assert {:ok, _} = start_socket("?stream=user", [{"Sec-WebSocket-Protocol", token.token}])
|
||||
|
||||
capture_log(fn ->
|
||||
assert {:error, %WebSockex.RequestError{code: 401}} =
|
||||
start_socket("?stream=user", [{"Sec-WebSocket-Protocol", "I am a friend"}])
|
||||
|
||||
Process.sleep(30)
|
||||
end)
|
||||
end
|
||||
|
||||
test "accepts valid token on client-sent event", %{token: token} do
|
||||
assert {:ok, pid} = start_socket()
|
||||
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2024 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.MapsTest do
|
||||
use Pleroma.DataCase, async: true
|
||||
|
||||
alias Pleroma.Maps
|
||||
|
||||
describe "filter_empty_values/1" do
|
||||
assert %{"bar" => "b", "ray" => ["foo"], "objs" => %{"a" => "b"}} ==
|
||||
Maps.filter_empty_values(%{
|
||||
"foo" => nil,
|
||||
"fooz" => "",
|
||||
"bar" => "b",
|
||||
"rei" => [],
|
||||
"ray" => ["foo"],
|
||||
"obj" => %{},
|
||||
"objs" => %{"a" => "b"}
|
||||
})
|
||||
end
|
||||
end
|
|
@ -93,6 +93,17 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.ArticleNotePageValidatorTest
|
|||
%{valid?: true} = ArticleNotePageValidator.cast_and_validate(note)
|
||||
end
|
||||
|
||||
test "a Note from Convergence AP Bridge validates" do
|
||||
insert(:user, ap_id: "https://cc.mkdir.uk/ap/acct/hiira")
|
||||
|
||||
note =
|
||||
"test/fixtures/ccworld-ap-bridge_note.json"
|
||||
|> File.read!()
|
||||
|> Jason.decode!()
|
||||
|
||||
%{valid?: true} = ArticleNotePageValidator.cast_and_validate(note)
|
||||
end
|
||||
|
||||
test "a note with an attachment should work", _ do
|
||||
insert(:user, %{ap_id: "https://owncast.localhost.localdomain/federation/user/streamer"})
|
||||
|
||||
|
|
Loading…
Reference in New Issue