Merge branch 'develop' into issue/1790-updated-oban
This commit is contained in:
commit
4695bdd81b
|
@ -14,7 +14,7 @@
|
|||
* Pleroma version (could be found in the "Version" tab of settings in Pleroma-FE):
|
||||
* Elixir version (`elixir -v` for from source installations, N/A for OTP):
|
||||
* Operating system:
|
||||
* PostgreSQL version (`postgres -V`):
|
||||
* PostgreSQL version (`psql -V`):
|
||||
|
||||
|
||||
### Bug description
|
||||
|
|
18
CHANGELOG.md
18
CHANGELOG.md
|
@ -11,9 +11,23 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||
- Using the `only_media` filter on timelines will now exclude reblog media
|
||||
- MFR policy to set global expiration for all local Create activities
|
||||
- OGP rich media parser merged with TwitterCard
|
||||
- Configuration: `:instance, rewrite_policy` moved to `:mrf, policies`, `:instance, :mrf_transparency` moved to `:mrf, :transparency`, `:instance, :mrf_transparency_exclusions` moved to `:mrf, :transparency_exclusions`. Old config namespace is deprecated.
|
||||
|
||||
<details>
|
||||
<summary>API Changes</summary>
|
||||
|
||||
- **Breaking:** Emoji API: changed methods and renamed routes.
|
||||
- Streaming: Repeats of a user's posts will no longer be pushed to the user's stream.
|
||||
- Mastodon API: Added `pleroma.metadata.fields_limits` to /api/v1/instance
|
||||
- Mastodon API: On deletion, returns the original post text.
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Admin API Changes</summary>
|
||||
|
||||
- Status visibility stats: now can return stats per instance.
|
||||
|
||||
- Mix task to refresh counter cache (`mix pleroma.refresh_counter_cache`)
|
||||
</details>
|
||||
|
||||
### Removed
|
||||
|
@ -40,6 +54,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||
|
||||
<details>
|
||||
<summary>API Changes</summary>
|
||||
- Mastodon API: Add pleroma.parents_visible field to statuses.
|
||||
- Mastodon API: Extended `/api/v1/instance`.
|
||||
- Mastodon API: Support for `include_types` in `/api/v1/notifications`.
|
||||
- Mastodon API: Added `/api/v1/notifications/:id/dismiss` endpoint.
|
||||
|
@ -96,6 +111,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||
2. Run database migrations (inside Pleroma directory):
|
||||
- OTP: `./bin/pleroma_ctl migrate`
|
||||
- From Source: `mix ecto.migrate`
|
||||
3. Reset status visibility counters (inside Pleroma directory):
|
||||
- OTP: `./bin/pleroma_ctl refresh_counter_cache`
|
||||
- From Source: `mix pleroma.refresh_counter_cache`
|
||||
|
||||
|
||||
## [2.0.2] - 2020-04-08
|
||||
|
|
|
@ -210,7 +210,6 @@ config :pleroma, :instance,
|
|||
Pleroma.Web.ActivityPub.Publisher
|
||||
],
|
||||
allow_relay: true,
|
||||
rewrite_policy: Pleroma.Web.ActivityPub.MRF.NoOpPolicy,
|
||||
public: true,
|
||||
quarantined_instances: [],
|
||||
managed_config: true,
|
||||
|
@ -221,8 +220,6 @@ config :pleroma, :instance,
|
|||
"text/markdown",
|
||||
"text/bbcode"
|
||||
],
|
||||
mrf_transparency: true,
|
||||
mrf_transparency_exclusions: [],
|
||||
autofollowed_nicknames: [],
|
||||
max_pinned_statuses: 1,
|
||||
attachment_links: false,
|
||||
|
@ -437,6 +434,12 @@ config :pleroma, Pleroma.Web.Metadata,
|
|||
],
|
||||
unfurl_nsfw: false
|
||||
|
||||
config :pleroma, Pleroma.Web.Preload,
|
||||
providers: [
|
||||
Pleroma.Web.Preload.Providers.Instance,
|
||||
Pleroma.Web.Preload.Providers.StatusNet
|
||||
]
|
||||
|
||||
config :pleroma, :http_security,
|
||||
enabled: true,
|
||||
sts: false,
|
||||
|
@ -692,6 +695,15 @@ config :pleroma, :restrict_unauthenticated,
|
|||
|
||||
config :pleroma, Pleroma.Web.ApiSpec.CastAndValidate, strict: false
|
||||
|
||||
config :pleroma, :mrf,
|
||||
policies: Pleroma.Web.ActivityPub.MRF.NoOpPolicy,
|
||||
transparency: true,
|
||||
transparency_exclusions: []
|
||||
|
||||
config :tzdata, :http_client, Pleroma.HTTP.Tzdata
|
||||
|
||||
config :ex_aws, http_client: Pleroma.HTTP.ExAws
|
||||
|
||||
# Import environment specific config. This must remain at the bottom
|
||||
# of this file so it overrides the configuration defined above.
|
||||
import_config "#{Mix.env()}.exs"
|
||||
|
|
|
@ -40,12 +40,13 @@ config :pleroma, :config_description, [
|
|||
key: :link_name,
|
||||
type: :boolean,
|
||||
description:
|
||||
"If enabled, a name parameter will be added to the url of the upload. For example `https://instance.tld/media/imagehash.png?name=realname.png`."
|
||||
"If enabled, a name parameter will be added to the URL of the upload. For example `https://instance.tld/media/imagehash.png?name=realname.png`."
|
||||
},
|
||||
%{
|
||||
key: :base_url,
|
||||
label: "Base URL",
|
||||
type: :string,
|
||||
description: "Base url for the uploads, needed if you use CDN",
|
||||
description: "Base URL for the uploads, needed if you use CDN",
|
||||
suggestions: [
|
||||
"https://cdn-host.com"
|
||||
]
|
||||
|
@ -58,6 +59,7 @@ config :pleroma, :config_description, [
|
|||
},
|
||||
%{
|
||||
key: :proxy_opts,
|
||||
label: "Proxy Options",
|
||||
type: :keyword,
|
||||
description: "Options for Pleroma.ReverseProxy",
|
||||
suggestions: [
|
||||
|
@ -85,6 +87,7 @@ config :pleroma, :config_description, [
|
|||
},
|
||||
%{
|
||||
key: :http,
|
||||
label: "HTTP",
|
||||
type: :keyword,
|
||||
description: "HTTP options",
|
||||
children: [
|
||||
|
@ -193,7 +196,9 @@ config :pleroma, :config_description, [
|
|||
%{
|
||||
key: :args,
|
||||
type: [:string, {:list, :string}, {:list, :tuple}],
|
||||
description: "List of actions for the mogrify command",
|
||||
description:
|
||||
"List of actions for the mogrify command. It's possible to add self-written settings as string. " <>
|
||||
"For example `[\"auto-orient\", \"strip\", {\"resize\", \"3840x1080>\"}]` string will be parsed into list of the settings.",
|
||||
suggestions: [
|
||||
"strip",
|
||||
"auto-orient",
|
||||
|
@ -479,6 +484,7 @@ config :pleroma, :config_description, [
|
|||
%{
|
||||
group: :pleroma,
|
||||
key: :uri_schemes,
|
||||
label: "URI Schemes",
|
||||
type: :group,
|
||||
description: "URI schemes related settings",
|
||||
children: [
|
||||
|
@ -651,17 +657,17 @@ config :pleroma, :config_description, [
|
|||
key: :invites_enabled,
|
||||
type: :boolean,
|
||||
description:
|
||||
"Enable user invitations for admins (depends on `registrations_open` being disabled)."
|
||||
"Enable user invitations for admins (depends on `registrations_open` being disabled)"
|
||||
},
|
||||
%{
|
||||
key: :account_activation_required,
|
||||
type: :boolean,
|
||||
description: "Require users to confirm their emails before signing in."
|
||||
description: "Require users to confirm their emails before signing in"
|
||||
},
|
||||
%{
|
||||
key: :federating,
|
||||
type: :boolean,
|
||||
description: "Enable federation with other instances."
|
||||
description: "Enable federation with other instances"
|
||||
},
|
||||
%{
|
||||
key: :federation_incoming_replies_max_depth,
|
||||
|
@ -679,7 +685,7 @@ config :pleroma, :config_description, [
|
|||
label: "Fed. reachability timeout days",
|
||||
type: :integer,
|
||||
description:
|
||||
"Timeout (in days) of each external federation target being unreachable prior to pausing federating to it.",
|
||||
"Timeout (in days) of each external federation target being unreachable prior to pausing federating to it",
|
||||
suggestions: [
|
||||
7
|
||||
]
|
||||
|
@ -689,17 +695,6 @@ config :pleroma, :config_description, [
|
|||
type: :boolean,
|
||||
description: "Enable Pleroma's Relay, which makes it possible to follow a whole instance"
|
||||
},
|
||||
%{
|
||||
key: :rewrite_policy,
|
||||
type: [:module, {:list, :module}],
|
||||
description:
|
||||
"A list of enabled MRF policies. Module names are shortened (removed leading `Pleroma.Web.ActivityPub.MRF.` part), but on adding custom module you need to use full name.",
|
||||
suggestions:
|
||||
Generator.list_modules_in_dir(
|
||||
"lib/pleroma/web/activity_pub/mrf",
|
||||
"Elixir.Pleroma.Web.ActivityPub.MRF."
|
||||
)
|
||||
},
|
||||
%{
|
||||
key: :public,
|
||||
type: :boolean,
|
||||
|
@ -742,23 +737,6 @@ config :pleroma, :config_description, [
|
|||
"text/bbcode"
|
||||
]
|
||||
},
|
||||
%{
|
||||
key: :mrf_transparency,
|
||||
label: "MRF transparency",
|
||||
type: :boolean,
|
||||
description:
|
||||
"Make the content of your Message Rewrite Facility settings public (via nodeinfo)"
|
||||
},
|
||||
%{
|
||||
key: :mrf_transparency_exclusions,
|
||||
label: "MRF transparency exclusions",
|
||||
type: {:list, :string},
|
||||
description:
|
||||
"Exclude specific instance names from MRF transparency. The use of the exclusions feature will be disclosed in nodeinfo as a boolean value.",
|
||||
suggestions: [
|
||||
"exclusion.com"
|
||||
]
|
||||
},
|
||||
%{
|
||||
key: :extended_nickname_format,
|
||||
type: :boolean,
|
||||
|
@ -829,6 +807,7 @@ config :pleroma, :config_description, [
|
|||
},
|
||||
%{
|
||||
key: :safe_dm_mentions,
|
||||
label: "Safe DM mentions",
|
||||
type: :boolean,
|
||||
description:
|
||||
"If enabled, only mentions at the beginning of a post will be used to address people in direct messages." <>
|
||||
|
@ -868,7 +847,7 @@ config :pleroma, :config_description, [
|
|||
%{
|
||||
key: :skip_thread_containment,
|
||||
type: :boolean,
|
||||
description: "Skip filtering out broken threads. Default: enabled"
|
||||
description: "Skip filtering out broken threads. Default: enabled."
|
||||
},
|
||||
%{
|
||||
key: :limit_to_local_content,
|
||||
|
@ -932,6 +911,7 @@ config :pleroma, :config_description, [
|
|||
children: [
|
||||
%{
|
||||
key: :totp,
|
||||
label: "TOTP settings",
|
||||
type: :keyword,
|
||||
description: "TOTP settings",
|
||||
suggestions: [digits: 6, period: 30],
|
||||
|
@ -948,7 +928,7 @@ config :pleroma, :config_description, [
|
|||
type: :integer,
|
||||
suggestions: [30],
|
||||
description:
|
||||
"a period for which the TOTP code will be valid, in seconds. Defaults to 30 seconds."
|
||||
"A period for which the TOTP code will be valid, in seconds. Defaults to 30 seconds."
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -962,7 +942,7 @@ config :pleroma, :config_description, [
|
|||
key: :number,
|
||||
type: :integer,
|
||||
suggestions: [5],
|
||||
description: "number of backup codes to generate."
|
||||
description: "Number of backup codes to generate."
|
||||
},
|
||||
%{
|
||||
key: :length,
|
||||
|
@ -979,7 +959,7 @@ config :pleroma, :config_description, [
|
|||
key: :instance_thumbnail,
|
||||
type: :string,
|
||||
description:
|
||||
"The instance thumbnail image. It will appear in [Pleroma Instances](http://distsn.org/pleroma-instances.html)",
|
||||
"The instance thumbnail can be any image that represents your instance and is used by some apps or services when they display information about your instance.",
|
||||
suggestions: ["/instance/thumbnail.jpeg"]
|
||||
}
|
||||
]
|
||||
|
@ -1002,6 +982,7 @@ config :pleroma, :config_description, [
|
|||
group: :logger,
|
||||
type: :group,
|
||||
key: :ex_syslogger,
|
||||
label: "ExSyslogger",
|
||||
description: "ExSyslogger-related settings",
|
||||
children: [
|
||||
%{
|
||||
|
@ -1020,7 +1001,7 @@ config :pleroma, :config_description, [
|
|||
%{
|
||||
key: :format,
|
||||
type: :string,
|
||||
description: "Default: \"$date $time [$level] $levelpad$node $metadata $message\".",
|
||||
description: "Default: \"$date $time [$level] $levelpad$node $metadata $message\"",
|
||||
suggestions: ["$metadata[$level] $message"]
|
||||
},
|
||||
%{
|
||||
|
@ -1034,6 +1015,7 @@ config :pleroma, :config_description, [
|
|||
group: :logger,
|
||||
type: :group,
|
||||
key: :console,
|
||||
label: "Console Logger",
|
||||
description: "Console logger settings",
|
||||
children: [
|
||||
%{
|
||||
|
@ -1045,7 +1027,7 @@ config :pleroma, :config_description, [
|
|||
%{
|
||||
key: :format,
|
||||
type: :string,
|
||||
description: "Default: \"$date $time [$level] $levelpad$node $metadata $message\".",
|
||||
description: "Default: \"$date $time [$level] $levelpad$node $metadata $message\"",
|
||||
suggestions: ["$metadata[$level] $message"]
|
||||
},
|
||||
%{
|
||||
|
@ -1058,6 +1040,7 @@ config :pleroma, :config_description, [
|
|||
%{
|
||||
group: :quack,
|
||||
type: :group,
|
||||
label: "Quack Logger",
|
||||
description: "Quack-related settings",
|
||||
children: [
|
||||
%{
|
||||
|
@ -1168,19 +1151,19 @@ config :pleroma, :config_description, [
|
|||
key: :greentext,
|
||||
label: "Greentext",
|
||||
type: :boolean,
|
||||
description: "Enables green text on lines prefixed with the > character."
|
||||
description: "Enables green text on lines prefixed with the > character"
|
||||
},
|
||||
%{
|
||||
key: :hideFilteredStatuses,
|
||||
label: "Hide Filtered Statuses",
|
||||
type: :boolean,
|
||||
description: "Hides filtered statuses from timelines."
|
||||
description: "Hides filtered statuses from timelines"
|
||||
},
|
||||
%{
|
||||
key: :hideMutedPosts,
|
||||
label: "Hide Muted Posts",
|
||||
type: :boolean,
|
||||
description: "Hides muted statuses from timelines."
|
||||
description: "Hides muted statuses from timelines"
|
||||
},
|
||||
%{
|
||||
key: :hidePostStats,
|
||||
|
@ -1192,7 +1175,7 @@ config :pleroma, :config_description, [
|
|||
key: :hideSitename,
|
||||
label: "Hide Sitename",
|
||||
type: :boolean,
|
||||
description: "Hides instance name from PleromaFE banner."
|
||||
description: "Hides instance name from PleromaFE banner"
|
||||
},
|
||||
%{
|
||||
key: :hideUserStats,
|
||||
|
@ -1237,14 +1220,14 @@ config :pleroma, :config_description, [
|
|||
label: "NSFW Censor Image",
|
||||
type: :string,
|
||||
description:
|
||||
"URL of the image to use for hiding NSFW media attachments in the timeline.",
|
||||
"URL of the image to use for hiding NSFW media attachments in the timeline",
|
||||
suggestions: ["/static/img/nsfw.74818f9.png"]
|
||||
},
|
||||
%{
|
||||
key: :postContentType,
|
||||
label: "Post Content Type",
|
||||
type: {:dropdown, :atom},
|
||||
description: "Default post formatting option.",
|
||||
description: "Default post formatting option",
|
||||
suggestions: ["text/plain", "text/html", "text/markdown", "text/bbcode"]
|
||||
},
|
||||
%{
|
||||
|
@ -1273,14 +1256,14 @@ config :pleroma, :config_description, [
|
|||
key: :sidebarRight,
|
||||
label: "Sidebar on Right",
|
||||
type: :boolean,
|
||||
description: "Change alignment of sidebar and panels to the right."
|
||||
description: "Change alignment of sidebar and panels to the right"
|
||||
},
|
||||
%{
|
||||
key: :showFeaturesPanel,
|
||||
label: "Show instance features panel",
|
||||
type: :boolean,
|
||||
description:
|
||||
"Enables panel displaying functionality of the instance on the About page."
|
||||
"Enables panel displaying functionality of the instance on the About page"
|
||||
},
|
||||
%{
|
||||
key: :showInstanceSpecificPanel,
|
||||
|
@ -1338,7 +1321,7 @@ config :pleroma, :config_description, [
|
|||
key: :mascots,
|
||||
type: {:keyword, :map},
|
||||
description:
|
||||
"Keyword of mascots, each element must contain both an url and a mime_type key",
|
||||
"Keyword of mascots, each element must contain both an URL and a mime_type key",
|
||||
suggestions: [
|
||||
pleroma_fox_tan: %{
|
||||
url: "/images/pleroma-fox-tan-smol.png",
|
||||
|
@ -1362,7 +1345,7 @@ config :pleroma, :config_description, [
|
|||
%{
|
||||
key: :default_user_avatar,
|
||||
type: :string,
|
||||
description: "URL of the default user avatar.",
|
||||
description: "URL of the default user avatar",
|
||||
suggestions: ["/images/avi.png"]
|
||||
}
|
||||
]
|
||||
|
@ -1372,7 +1355,7 @@ config :pleroma, :config_description, [
|
|||
key: :manifest,
|
||||
type: :group,
|
||||
description:
|
||||
"This section describe PWA manifest instance-specific values. Currently this option relate only for MastoFE",
|
||||
"This section describe PWA manifest instance-specific values. Currently this option relate only for MastoFE.",
|
||||
children: [
|
||||
%{
|
||||
key: :icons,
|
||||
|
@ -1408,10 +1391,49 @@ config :pleroma, :config_description, [
|
|||
},
|
||||
%{
|
||||
group: :pleroma,
|
||||
key: :mrf_simple,
|
||||
label: "MRF simple",
|
||||
key: :mrf,
|
||||
tab: :mrf,
|
||||
label: "MRF",
|
||||
type: :group,
|
||||
description: "Message Rewrite Facility",
|
||||
description: "General MRF settings",
|
||||
children: [
|
||||
%{
|
||||
key: :policies,
|
||||
type: [:module, {:list, :module}],
|
||||
description:
|
||||
"A list of MRF policies enabled. Module names are shortened (removed leading `Pleroma.Web.ActivityPub.MRF.` part), but on adding custom module you need to use full name.",
|
||||
suggestions:
|
||||
Generator.list_modules_in_dir(
|
||||
"lib/pleroma/web/activity_pub/mrf",
|
||||
"Elixir.Pleroma.Web.ActivityPub.MRF."
|
||||
)
|
||||
},
|
||||
%{
|
||||
key: :transparency,
|
||||
label: "MRF transparency",
|
||||
type: :boolean,
|
||||
description:
|
||||
"Make the content of your Message Rewrite Facility settings public (via nodeinfo)"
|
||||
},
|
||||
%{
|
||||
key: :transparency_exclusions,
|
||||
label: "MRF transparency exclusions",
|
||||
type: {:list, :string},
|
||||
description:
|
||||
"Exclude specific instance names from MRF transparency. The use of the exclusions feature will be disclosed in nodeinfo as a boolean value.",
|
||||
suggestions: [
|
||||
"exclusion.com"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
%{
|
||||
group: :pleroma,
|
||||
key: :mrf_simple,
|
||||
tab: :mrf,
|
||||
label: "MRF Simple",
|
||||
type: :group,
|
||||
description: "Simple ingress policies",
|
||||
children: [
|
||||
%{
|
||||
key: :media_removal,
|
||||
|
@ -1430,7 +1452,7 @@ config :pleroma, :config_description, [
|
|||
key: :federated_timeline_removal,
|
||||
type: {:list, :string},
|
||||
description:
|
||||
"List of instances to remove from Federated (aka The Whole Known Network) Timeline",
|
||||
"List of instances to remove from the Federated (aka The Whole Known Network) Timeline",
|
||||
suggestions: ["example.com", "*.example.com"]
|
||||
},
|
||||
%{
|
||||
|
@ -1474,14 +1496,15 @@ config :pleroma, :config_description, [
|
|||
%{
|
||||
group: :pleroma,
|
||||
key: :mrf_activity_expiration,
|
||||
tab: :mrf,
|
||||
label: "MRF Activity Expiration Policy",
|
||||
type: :group,
|
||||
description: "Adds expiration to all local Create Note activities",
|
||||
description: "Adds automatic expiration to all local activities",
|
||||
children: [
|
||||
%{
|
||||
key: :days,
|
||||
type: :integer,
|
||||
description: "Default global expiration time for all local Create activities (in days)",
|
||||
description: "Default global expiration time for all local activities (in days)",
|
||||
suggestions: [90, 365]
|
||||
}
|
||||
]
|
||||
|
@ -1489,7 +1512,8 @@ config :pleroma, :config_description, [
|
|||
%{
|
||||
group: :pleroma,
|
||||
key: :mrf_subchain,
|
||||
label: "MRF subchain",
|
||||
tab: :mrf,
|
||||
label: "MRF Subchain",
|
||||
type: :group,
|
||||
description:
|
||||
"This policy processes messages through an alternate pipeline when a given message matches certain criteria." <>
|
||||
|
@ -1510,9 +1534,9 @@ config :pleroma, :config_description, [
|
|||
%{
|
||||
group: :pleroma,
|
||||
key: :mrf_rejectnonpublic,
|
||||
description:
|
||||
"MRF RejectNonPublic settings. RejectNonPublic drops posts with non-public visibility settings.",
|
||||
label: "MRF reject non public",
|
||||
tab: :mrf,
|
||||
description: "RejectNonPublic drops posts with non-public visibility settings.",
|
||||
label: "MRF Reject Non Public",
|
||||
type: :group,
|
||||
children: [
|
||||
%{
|
||||
|
@ -1531,16 +1555,17 @@ config :pleroma, :config_description, [
|
|||
%{
|
||||
group: :pleroma,
|
||||
key: :mrf_hellthread,
|
||||
label: "MRF hellthread",
|
||||
tab: :mrf,
|
||||
label: "MRF Hellthread",
|
||||
type: :group,
|
||||
description: "Block messages with too much mentions",
|
||||
description: "Block messages with excessive user mentions",
|
||||
children: [
|
||||
%{
|
||||
key: :delist_threshold,
|
||||
type: :integer,
|
||||
description:
|
||||
"Number of mentioned users after which the message gets delisted (the message can still be seen, " <>
|
||||
" but it will not show up in public timelines and mentioned users won't get notifications about it). Set to 0 to disable.",
|
||||
"Number of mentioned users after which the message gets removed from timelines and" <>
|
||||
"disables notifications. Set to 0 to disable.",
|
||||
suggestions: [10]
|
||||
},
|
||||
%{
|
||||
|
@ -1555,7 +1580,8 @@ config :pleroma, :config_description, [
|
|||
%{
|
||||
group: :pleroma,
|
||||
key: :mrf_keyword,
|
||||
label: "MRF keyword",
|
||||
tab: :mrf,
|
||||
label: "MRF Keyword",
|
||||
type: :group,
|
||||
description: "Reject or Word-Replace messages with a keyword or regex",
|
||||
children: [
|
||||
|
@ -1585,14 +1611,15 @@ config :pleroma, :config_description, [
|
|||
%{
|
||||
group: :pleroma,
|
||||
key: :mrf_mention,
|
||||
label: "MRF mention",
|
||||
tab: :mrf,
|
||||
label: "MRF Mention",
|
||||
type: :group,
|
||||
description: "Block messages which mention a user",
|
||||
description: "Block messages which mention a specific user",
|
||||
children: [
|
||||
%{
|
||||
key: :actors,
|
||||
type: {:list, :string},
|
||||
description: "A list of actors for which any post mentioning them will be dropped.",
|
||||
description: "A list of actors for which any post mentioning them will be dropped",
|
||||
suggestions: ["actor1", "actor2"]
|
||||
}
|
||||
]
|
||||
|
@ -1600,7 +1627,8 @@ config :pleroma, :config_description, [
|
|||
%{
|
||||
group: :pleroma,
|
||||
key: :mrf_vocabulary,
|
||||
label: "MRF vocabulary",
|
||||
tab: :mrf,
|
||||
label: "MRF Vocabulary",
|
||||
type: :group,
|
||||
description: "Filter messages which belong to certain activity vocabularies",
|
||||
children: [
|
||||
|
@ -1608,14 +1636,14 @@ config :pleroma, :config_description, [
|
|||
key: :accept,
|
||||
type: {:list, :string},
|
||||
description:
|
||||
"A list of ActivityStreams terms to accept. If empty, all supported messages are accepted",
|
||||
"A list of ActivityStreams terms to accept. If empty, all supported messages are accepted.",
|
||||
suggestions: ["Create", "Follow", "Mention", "Announce", "Like"]
|
||||
},
|
||||
%{
|
||||
key: :reject,
|
||||
type: {:list, :string},
|
||||
description:
|
||||
"A list of ActivityStreams terms to reject. If empty, no messages are rejected",
|
||||
"A list of ActivityStreams terms to reject. If empty, no messages are rejected.",
|
||||
suggestions: ["Create", "Follow", "Mention", "Announce", "Like"]
|
||||
}
|
||||
]
|
||||
|
@ -1645,6 +1673,7 @@ config :pleroma, :config_description, [
|
|||
},
|
||||
%{
|
||||
key: :base_url,
|
||||
label: "Base URL",
|
||||
type: :string,
|
||||
description:
|
||||
"The base URL to access a user-uploaded file. Useful when you want to proxy the media files via another host/CDN fronts.",
|
||||
|
@ -1677,6 +1706,7 @@ config :pleroma, :config_description, [
|
|||
},
|
||||
%{
|
||||
key: :proxy_opts,
|
||||
label: "Proxy Options",
|
||||
type: :keyword,
|
||||
description: "Options for Pleroma.ReverseProxy",
|
||||
suggestions: [
|
||||
|
@ -1704,6 +1734,7 @@ config :pleroma, :config_description, [
|
|||
},
|
||||
%{
|
||||
key: :http,
|
||||
label: "HTTP",
|
||||
type: :keyword,
|
||||
description: "HTTP options",
|
||||
children: [
|
||||
|
@ -1799,6 +1830,7 @@ config :pleroma, :config_description, [
|
|||
},
|
||||
%{
|
||||
key: :ip,
|
||||
label: "IP",
|
||||
type: :tuple,
|
||||
description: "IP address to bind to",
|
||||
suggestions: [{0, 0, 0, 0}]
|
||||
|
@ -1812,7 +1844,7 @@ config :pleroma, :config_description, [
|
|||
%{
|
||||
key: :dstport,
|
||||
type: :integer,
|
||||
description: "Port advertised in urls (optional, defaults to port)",
|
||||
description: "Port advertised in URLs (optional, defaults to port)",
|
||||
suggestions: [9999]
|
||||
}
|
||||
]
|
||||
|
@ -1820,6 +1852,7 @@ config :pleroma, :config_description, [
|
|||
%{
|
||||
group: :pleroma,
|
||||
key: :activitypub,
|
||||
label: "ActivityPub",
|
||||
type: :group,
|
||||
description: "ActivityPub-related settings",
|
||||
children: [
|
||||
|
@ -1842,7 +1875,7 @@ config :pleroma, :config_description, [
|
|||
key: :note_replies_output_limit,
|
||||
type: :integer,
|
||||
description:
|
||||
"The number of Note replies' URIs to be included with outgoing federation (`5` to match Mastodon hardcoded value, `0` to disable the output)."
|
||||
"The number of Note replies' URIs to be included with outgoing federation (`5` to match Mastodon hardcoded value, `0` to disable the output)"
|
||||
},
|
||||
%{
|
||||
key: :follow_handshake_timeout,
|
||||
|
@ -1855,6 +1888,7 @@ config :pleroma, :config_description, [
|
|||
%{
|
||||
group: :pleroma,
|
||||
key: :http_security,
|
||||
label: "HTTP security",
|
||||
type: :group,
|
||||
description: "HTTP security settings",
|
||||
children: [
|
||||
|
@ -1893,7 +1927,7 @@ config :pleroma, :config_description, [
|
|||
key: :report_uri,
|
||||
label: "Report URI",
|
||||
type: :string,
|
||||
description: "Adds the specified url to report-uri and report-to group in CSP header",
|
||||
description: "Adds the specified URL to report-uri and report-to group in CSP header",
|
||||
suggestions: ["https://example.com/report-uri"]
|
||||
}
|
||||
]
|
||||
|
@ -1901,9 +1935,10 @@ config :pleroma, :config_description, [
|
|||
%{
|
||||
group: :web_push_encryption,
|
||||
key: :vapid_details,
|
||||
label: "Vapid Details",
|
||||
type: :group,
|
||||
description:
|
||||
"Web Push Notifications configuration. You can use the mix task mix web_push.gen.keypair to generate it",
|
||||
"Web Push Notifications configuration. You can use the mix task mix web_push.gen.keypair to generate it.",
|
||||
children: [
|
||||
%{
|
||||
key: :subject,
|
||||
|
@ -1970,6 +2005,7 @@ config :pleroma, :config_description, [
|
|||
},
|
||||
%{
|
||||
group: :pleroma,
|
||||
label: "Pleroma Admin Token",
|
||||
type: :group,
|
||||
description:
|
||||
"Allows to set a token that can be used to authenticate with the admin api without using an actual user by giving it as the `admin_token` parameter",
|
||||
|
@ -1977,7 +2013,7 @@ config :pleroma, :config_description, [
|
|||
%{
|
||||
key: :admin_token,
|
||||
type: :string,
|
||||
description: "Token",
|
||||
description: "Admin token",
|
||||
suggestions: ["We recommend a secure random string or UUID"]
|
||||
}
|
||||
]
|
||||
|
@ -2135,24 +2171,24 @@ config :pleroma, :config_description, [
|
|||
key: :rich_media,
|
||||
type: :group,
|
||||
description:
|
||||
"If enabled the instance will parse metadata from attached links to generate link previews.",
|
||||
"If enabled the instance will parse metadata from attached links to generate link previews",
|
||||
children: [
|
||||
%{
|
||||
key: :enabled,
|
||||
type: :boolean,
|
||||
description: "Enables RichMedia parsing of URLs."
|
||||
description: "Enables RichMedia parsing of URLs"
|
||||
},
|
||||
%{
|
||||
key: :ignore_hosts,
|
||||
type: {:list, :string},
|
||||
description: "List of hosts which will be ignored by the metadata parser.",
|
||||
description: "List of hosts which will be ignored by the metadata parser",
|
||||
suggestions: ["accounts.google.com", "xss.website"]
|
||||
},
|
||||
%{
|
||||
key: :ignore_tld,
|
||||
label: "Ignore TLD",
|
||||
type: {:list, :string},
|
||||
description: "List TLDs (top-level domains) which will ignore for parse metadata.",
|
||||
description: "List TLDs (top-level domains) which will ignore for parse metadata",
|
||||
suggestions: ["local", "localdomain", "lan"]
|
||||
},
|
||||
%{
|
||||
|
@ -2180,31 +2216,32 @@ config :pleroma, :config_description, [
|
|||
%{
|
||||
group: :auto_linker,
|
||||
key: :opts,
|
||||
label: "Auto Linker",
|
||||
type: :group,
|
||||
description: "Configuration for the auto_linker library",
|
||||
children: [
|
||||
%{
|
||||
key: :class,
|
||||
type: [:string, false],
|
||||
description: "Specify the class to be added to the generated link. Disable to clear",
|
||||
description: "Specify the class to be added to the generated link. Disable to clear.",
|
||||
suggestions: ["auto-linker", false]
|
||||
},
|
||||
%{
|
||||
key: :rel,
|
||||
type: [:string, false],
|
||||
description: "Override the rel attribute. Disable to clear",
|
||||
description: "Override the rel attribute. Disable to clear.",
|
||||
suggestions: ["ugc", "noopener noreferrer", false]
|
||||
},
|
||||
%{
|
||||
key: :new_window,
|
||||
type: :boolean,
|
||||
description: "Link urls will open in new window/tab"
|
||||
description: "Link URLs will open in new window/tab"
|
||||
},
|
||||
%{
|
||||
key: :truncate,
|
||||
type: [:integer, false],
|
||||
description:
|
||||
"Set to a number to truncate urls longer then the number. Truncated urls will end in `..`",
|
||||
"Set to a number to truncate URLs longer then the number. Truncated URLs will end in `..`",
|
||||
suggestions: [15, false]
|
||||
},
|
||||
%{
|
||||
|
@ -2215,7 +2252,7 @@ config :pleroma, :config_description, [
|
|||
%{
|
||||
key: :extra,
|
||||
type: :boolean,
|
||||
description: "Link urls with rarely used schemes (magnet, ipfs, irc, etc.)"
|
||||
description: "Link URLs with rarely used schemes (magnet, ipfs, irc, etc.)"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -2261,6 +2298,7 @@ config :pleroma, :config_description, [
|
|||
},
|
||||
%{
|
||||
group: :pleroma,
|
||||
label: "Pleroma Authenticator",
|
||||
type: :group,
|
||||
description: "Authenticator",
|
||||
children: [
|
||||
|
@ -2274,6 +2312,7 @@ config :pleroma, :config_description, [
|
|||
%{
|
||||
group: :pleroma,
|
||||
key: :ldap,
|
||||
label: "LDAP",
|
||||
type: :group,
|
||||
description:
|
||||
"Use LDAP for user authentication. When a user logs in to the Pleroma instance, the name and password" <>
|
||||
|
@ -2360,6 +2399,7 @@ config :pleroma, :config_description, [
|
|||
},
|
||||
%{
|
||||
key: :uid,
|
||||
label: "UID",
|
||||
type: :string,
|
||||
description:
|
||||
"LDAP attribute name to authenticate the user, e.g. when \"cn\", the filter will be \"cn=username,base\"",
|
||||
|
@ -2375,6 +2415,7 @@ config :pleroma, :config_description, [
|
|||
children: [
|
||||
%{
|
||||
key: :enforce_oauth_admin_scope_usage,
|
||||
label: "Enforce OAuth admin scope usage",
|
||||
type: :boolean,
|
||||
description:
|
||||
"OAuth admin scope requirement toggle. " <>
|
||||
|
@ -2391,6 +2432,7 @@ config :pleroma, :config_description, [
|
|||
},
|
||||
%{
|
||||
key: :oauth_consumer_template,
|
||||
label: "OAuth consumer template",
|
||||
type: :string,
|
||||
description:
|
||||
"OAuth consumer mode authentication form template. By default it's `consumer.html` which corresponds to" <>
|
||||
|
@ -2399,6 +2441,7 @@ config :pleroma, :config_description, [
|
|||
},
|
||||
%{
|
||||
key: :oauth_consumer_strategies,
|
||||
label: "OAuth consumer strategies",
|
||||
type: {:list, :string},
|
||||
description:
|
||||
"The list of enabled OAuth consumer strategies. By default it's set by OAUTH_CONSUMER_STRATEGIES environment variable." <>
|
||||
|
@ -2527,7 +2570,7 @@ config :pleroma, :config_description, [
|
|||
%{
|
||||
key: :enabled,
|
||||
type: :boolean,
|
||||
description: "enables new users admin digest email when `true`",
|
||||
description: "Enables new users admin digest email when `true`",
|
||||
suggestions: [false]
|
||||
}
|
||||
]
|
||||
|
@ -2535,6 +2578,7 @@ config :pleroma, :config_description, [
|
|||
%{
|
||||
group: :pleroma,
|
||||
key: :oauth2,
|
||||
label: "OAuth2",
|
||||
type: :group,
|
||||
description: "Configure OAuth 2 provider capabilities",
|
||||
children: [
|
||||
|
@ -2553,7 +2597,7 @@ config :pleroma, :config_description, [
|
|||
%{
|
||||
key: :clean_expired_tokens,
|
||||
type: :boolean,
|
||||
description: "Enable a background job to clean expired oauth tokens. Default: disabled."
|
||||
description: "Enable a background job to clean expired OAuth tokens. Default: disabled."
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -2637,6 +2681,7 @@ config :pleroma, :config_description, [
|
|||
},
|
||||
%{
|
||||
key: :relation_id_action,
|
||||
label: "Relation ID action",
|
||||
type: [:tuple, {:list, :tuple}],
|
||||
description: "For actions on relation with a specific user (follow, unfollow)",
|
||||
suggestions: [{1000, 10}, [{10_000, 10}, {10_000, 50}]]
|
||||
|
@ -2650,6 +2695,7 @@ config :pleroma, :config_description, [
|
|||
},
|
||||
%{
|
||||
key: :status_id_action,
|
||||
label: "Status ID action",
|
||||
type: [:tuple, {:list, :tuple}],
|
||||
description:
|
||||
"For fav / unfav or reblog / unreblog actions on the same status by the same user",
|
||||
|
@ -2665,6 +2711,7 @@ config :pleroma, :config_description, [
|
|||
},
|
||||
%{
|
||||
group: :esshd,
|
||||
label: "ESSHD",
|
||||
type: :group,
|
||||
description:
|
||||
"Before enabling this you must add :esshd to mix.exs as one of the extra_applications " <>
|
||||
|
@ -2703,8 +2750,9 @@ config :pleroma, :config_description, [
|
|||
},
|
||||
%{
|
||||
group: :mime,
|
||||
label: "Mime Types",
|
||||
type: :group,
|
||||
description: "Mime types",
|
||||
description: "Mime Types settings",
|
||||
children: [
|
||||
%{
|
||||
key: :types,
|
||||
|
@ -2763,6 +2811,7 @@ config :pleroma, :config_description, [
|
|||
%{
|
||||
group: :pleroma,
|
||||
key: :http,
|
||||
label: "HTTP",
|
||||
type: :group,
|
||||
description: "HTTP settings",
|
||||
children: [
|
||||
|
@ -2811,6 +2860,7 @@ config :pleroma, :config_description, [
|
|||
%{
|
||||
group: :pleroma,
|
||||
key: :markup,
|
||||
label: "Markup Settings",
|
||||
type: :group,
|
||||
children: [
|
||||
%{
|
||||
|
@ -2851,8 +2901,9 @@ config :pleroma, :config_description, [
|
|||
},
|
||||
%{
|
||||
group: :pleroma,
|
||||
tab: :mrf,
|
||||
key: :mrf_normalize_markup,
|
||||
label: "MRF normalize markup",
|
||||
label: "MRF Normalize Markup",
|
||||
description: "MRF NormalizeMarkup settings. Scrub configured hypertext markup.",
|
||||
type: :group,
|
||||
children: [
|
||||
|
@ -2908,6 +2959,7 @@ config :pleroma, :config_description, [
|
|||
},
|
||||
%{
|
||||
group: :cors_plug,
|
||||
label: "CORS plug config",
|
||||
type: :group,
|
||||
children: [
|
||||
%{
|
||||
|
@ -2980,6 +3032,7 @@ config :pleroma, :config_description, [
|
|||
%{
|
||||
group: :pleroma,
|
||||
key: :web_cache_ttl,
|
||||
label: "Web cache TTL",
|
||||
type: :group,
|
||||
description:
|
||||
"The expiration time for the web responses cache. Values should be in milliseconds or `nil` to disable expiration.",
|
||||
|
@ -3002,9 +3055,10 @@ config :pleroma, :config_description, [
|
|||
%{
|
||||
group: :pleroma,
|
||||
key: :static_fe,
|
||||
label: "Static FE",
|
||||
type: :group,
|
||||
description:
|
||||
"Render profiles and posts using server-generated HTML that is viewable without using JavaScript.",
|
||||
"Render profiles and posts using server-generated HTML that is viewable without using JavaScript",
|
||||
children: [
|
||||
%{
|
||||
key: :enabled,
|
||||
|
@ -3022,18 +3076,18 @@ config :pleroma, :config_description, [
|
|||
%{
|
||||
key: :post_title,
|
||||
type: :map,
|
||||
description: "Configure title rendering.",
|
||||
description: "Configure title rendering",
|
||||
children: [
|
||||
%{
|
||||
key: :max_length,
|
||||
type: :integer,
|
||||
description: "Maximum number of characters before truncating title.",
|
||||
description: "Maximum number of characters before truncating title",
|
||||
suggestions: [100]
|
||||
},
|
||||
%{
|
||||
key: :omission,
|
||||
type: :string,
|
||||
description: "Replacement which will be used after truncating string.",
|
||||
description: "Replacement which will be used after truncating string",
|
||||
suggestions: ["..."]
|
||||
}
|
||||
]
|
||||
|
@ -3043,8 +3097,11 @@ config :pleroma, :config_description, [
|
|||
%{
|
||||
group: :pleroma,
|
||||
key: :mrf_object_age,
|
||||
label: "MRF Object Age",
|
||||
tab: :mrf,
|
||||
type: :group,
|
||||
description: "Rejects or delists posts based on their age when received.",
|
||||
description:
|
||||
"Rejects or delists posts based on their timestamp deviance from your server's clock.",
|
||||
children: [
|
||||
%{
|
||||
key: :threshold,
|
||||
|
@ -3057,7 +3114,7 @@ config :pleroma, :config_description, [
|
|||
type: {:list, :atom},
|
||||
description:
|
||||
"A list of actions to apply to the post. `:delist` removes the post from public timelines; " <>
|
||||
"`:strip_followers` removes followers from the ActivityPub recipient list, ensuring they won't be delivered to home timelines; " <>
|
||||
"`:strip_followers` removes followers from the ActivityPub recipient list ensuring they won't be delivered to home timelines; " <>
|
||||
"`:reject` rejects the message entirely",
|
||||
suggestions: [:delist, :strip_followers, :reject]
|
||||
}
|
||||
|
@ -3085,13 +3142,13 @@ config :pleroma, :config_description, [
|
|||
%{
|
||||
key: :workers,
|
||||
type: :integer,
|
||||
description: "Number of workers to send notifications.",
|
||||
description: "Number of workers to send notifications",
|
||||
suggestions: [3]
|
||||
},
|
||||
%{
|
||||
key: :overflow_workers,
|
||||
type: :integer,
|
||||
description: "Maximum number of workers created if pool is empty.",
|
||||
description: "Maximum number of workers created if pool is empty",
|
||||
suggestions: [2]
|
||||
}
|
||||
]
|
||||
|
|
|
@ -1118,6 +1118,10 @@ Loads json generated from `config/descriptions.exs`.
|
|||
|
||||
### Stats
|
||||
|
||||
- Query Params:
|
||||
- *optional* `instance`: **string** instance hostname (without protocol) to get stats for
|
||||
- Example: `https://mypleroma.org/api/pleroma/admin/stats?instance=lain.com`
|
||||
|
||||
- Response:
|
||||
|
||||
```json
|
||||
|
|
|
@ -27,6 +27,7 @@ Has these additional fields under the `pleroma` object:
|
|||
- `expires_at`: a datetime (iso8601) that states when the post will expire (be deleted automatically), or empty if the post won't expire
|
||||
- `thread_muted`: true if the thread the post belongs to is muted
|
||||
- `emoji_reactions`: A list with emoji / reaction maps. The format is `{name: "☕", count: 1, me: true}`. Contains no information about the reacting users, for that use the `/statuses/:id/reactions` endpoint.
|
||||
- `parent_visible`: If the parent of this post is visible to the user or not.
|
||||
|
||||
## Media Attachments
|
||||
|
||||
|
@ -51,11 +52,14 @@ The `id` parameter can also be the `nickname` of the user. This only works in th
|
|||
|
||||
Has these additional fields under the `pleroma` object:
|
||||
|
||||
- `ap_id`: nullable URL string, ActivityPub id of the user
|
||||
- `background_image`: nullable URL string, background image of the user
|
||||
- `tags`: Lists an array of tags for the user
|
||||
- `relationship{}`: Includes fields as documented for Mastodon API https://docs.joinmastodon.org/entities/relationship/
|
||||
- `relationship` (object): Includes fields as documented for Mastodon API https://docs.joinmastodon.org/entities/relationship/
|
||||
- `is_moderator`: boolean, nullable, true if user is a moderator
|
||||
- `is_admin`: boolean, nullable, true if user is an admin
|
||||
- `confirmation_pending`: boolean, true if a new user account is waiting on email confirmation to be activated
|
||||
- `hide_favorites`: boolean, true when the user has hiding favorites enabled
|
||||
- `hide_followers`: boolean, true when the user has follower hiding enabled
|
||||
- `hide_follows`: boolean, true when the user has follow hiding enabled
|
||||
- `hide_followers_count`: boolean, true when the user has follower stat hiding enabled
|
||||
|
@ -66,6 +70,7 @@ Has these additional fields under the `pleroma` object:
|
|||
- `allow_following_move`: boolean, true when the user allows automatically follow moved following accounts
|
||||
- `unread_conversation_count`: The count of unread conversations. Only returned to the account owner.
|
||||
- `unread_notifications_count`: The count of unread notifications. Only returned to the account owner.
|
||||
- `notification_settings`: object, can be absent. See `/api/pleroma/notification_settings` for the parameters/keys returned.
|
||||
|
||||
### Source
|
||||
|
||||
|
@ -223,6 +228,7 @@ Has theses additional parameters (which are the same as in Pleroma-API):
|
|||
- `background_image`: A background image that frontends can use
|
||||
- `pleroma.metadata.features`: A list of supported features
|
||||
- `pleroma.metadata.federation`: The federation restrictions of this instance
|
||||
- `pleroma.metadata.fields_limits`: A list of values detailing the length and count limitation for various instance-configurable fields.
|
||||
- `vapid_public_key`: The public key needed for push messages
|
||||
|
||||
## Markers
|
||||
|
@ -234,3 +240,43 @@ Has these additional fields under the `pleroma` object:
|
|||
## Streaming
|
||||
|
||||
There is an additional `user:pleroma_chat` stream. Incoming chat messages will make the current chat be sent to this `user` stream. The `event` of an incoming chat message is `pleroma:chat_update`. The payload is the updated chat with the incoming chat message in the `last_message` field.
|
||||
|
||||
## Not implemented
|
||||
|
||||
Pleroma is generally compatible with the Mastodon 2.7.2 API, but some newer features and non-essential features are omitted. These features usually return an HTTP 200 status code, but with an empty response. While they may be added in the future, they are considered low priority.
|
||||
|
||||
### Suggestions
|
||||
|
||||
*Added in Mastodon 2.4.3*
|
||||
|
||||
- `GET /api/v1/suggestions`: Returns an empty array, `[]`
|
||||
|
||||
### Trends
|
||||
|
||||
*Added in Mastodon 3.0.0*
|
||||
|
||||
- `GET /api/v1/trends`: Returns an empty array, `[]`
|
||||
|
||||
### Identity proofs
|
||||
|
||||
*Added in Mastodon 2.8.0*
|
||||
|
||||
- `GET /api/v1/identity_proofs`: Returns an empty array, `[]`
|
||||
|
||||
### Endorsements
|
||||
|
||||
*Added in Mastodon 2.5.0*
|
||||
|
||||
- `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
|
||||
|
||||
*Added in Mastodon 3.0.0*
|
||||
|
||||
- `GET /api/v1/featured_tags`: Returns HTTP 404
|
||||
|
|
|
@ -36,26 +36,10 @@ To add configuration to your config file, you can copy it from the base config.
|
|||
* `federation_incoming_replies_max_depth`: Max. depth of reply-to activities fetching on incoming federation, to prevent out-of-memory situations while fetching very long threads. If set to `nil`, threads of any depth will be fetched. Lower this value if you experience out-of-memory crashes.
|
||||
* `federation_reachability_timeout_days`: Timeout (in days) of each external federation target being unreachable prior to pausing federating to it.
|
||||
* `allow_relay`: Enable Pleroma’s Relay, which makes it possible to follow a whole instance.
|
||||
* `rewrite_policy`: Message Rewrite Policy, either one or a list. Here are the ones available by default:
|
||||
* `Pleroma.Web.ActivityPub.MRF.NoOpPolicy`: Doesn’t modify activities (default).
|
||||
* `Pleroma.Web.ActivityPub.MRF.DropPolicy`: Drops all activities. It generally doesn’t makes sense to use in production.
|
||||
* `Pleroma.Web.ActivityPub.MRF.SimplePolicy`: Restrict the visibility of activities from certain instances (See [`:mrf_simple`](#mrf_simple)).
|
||||
* `Pleroma.Web.ActivityPub.MRF.TagPolicy`: Applies policies to individual users based on tags, which can be set using pleroma-fe/admin-fe/any other app that supports Pleroma Admin API. For example it allows marking posts from individual users nsfw (sensitive).
|
||||
* `Pleroma.Web.ActivityPub.MRF.SubchainPolicy`: Selectively runs other MRF policies when messages match (See [`:mrf_subchain`](#mrf_subchain)).
|
||||
* `Pleroma.Web.ActivityPub.MRF.RejectNonPublic`: Drops posts with non-public visibility settings (See [`:mrf_rejectnonpublic`](#mrf_rejectnonpublic)).
|
||||
* `Pleroma.Web.ActivityPub.MRF.EnsureRePrepended`: Rewrites posts to ensure that replies to posts with subjects do not have an identical subject and instead begin with re:.
|
||||
* `Pleroma.Web.ActivityPub.MRF.AntiLinkSpamPolicy`: Rejects posts from likely spambots by rejecting posts from new users that contain links.
|
||||
* `Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy`: Crawls attachments using their MediaProxy URLs so that the MediaProxy cache is primed.
|
||||
* `Pleroma.Web.ActivityPub.MRF.MentionPolicy`: Drops posts mentioning configurable users. (See [`:mrf_mention`](#mrf_mention)).
|
||||
* `Pleroma.Web.ActivityPub.MRF.VocabularyPolicy`: Restricts activities to a configured set of vocabulary. (See [`:mrf_vocabulary`](#mrf_vocabulary)).
|
||||
* `Pleroma.Web.ActivityPub.MRF.ObjectAgePolicy`: Rejects or delists posts based on their age when received. (See [`:mrf_object_age`](#mrf_object_age)).
|
||||
* `Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicy`: Adds expiration to all local Create activities (see [`:mrf_activity_expiration`](#mrf_activity_expiration)).
|
||||
* `public`: Makes the client API in authenticated mode-only except for user-profiles. Useful for disabling the Local Timeline and The Whole Known Network.
|
||||
* `quarantined_instances`: List of ActivityPub instances where private(DMs, followers-only) activities will not be send.
|
||||
* `managed_config`: Whenether the config for pleroma-fe is configured in [:frontend_configurations](#frontend_configurations) or in ``static/config.json``.
|
||||
* `allowed_post_formats`: MIME-type list of formats allowed to be posted (transformed into HTML).
|
||||
* `mrf_transparency`: Make the content of your Message Rewrite Facility settings public (via nodeinfo).
|
||||
* `mrf_transparency_exclusions`: Exclude specific instance names from MRF transparency. The use of the exclusions feature will be disclosed in nodeinfo as a boolean value.
|
||||
* `extended_nickname_format`: Set to `true` to use extended local nicknames format (allows underscores/dashes). This will break federation with
|
||||
older software for theses nicknames.
|
||||
* `max_pinned_statuses`: The maximum number of pinned statuses. `0` will disable the feature.
|
||||
|
@ -78,11 +62,30 @@ To add configuration to your config file, you can copy it from the base config.
|
|||
* `external_user_synchronization`: Enabling following/followers counters synchronization for external users.
|
||||
* `cleanup_attachments`: Remove attachments along with statuses. Does not affect duplicate files and attachments without status. Enabling this will increase load to database when deleting statuses on larger instances.
|
||||
|
||||
## Message rewrite facility
|
||||
|
||||
### :mrf
|
||||
* `policies`: Message Rewrite Policy, either one or a list. Here are the ones available by default:
|
||||
* `Pleroma.Web.ActivityPub.MRF.NoOpPolicy`: Doesn’t modify activities (default).
|
||||
* `Pleroma.Web.ActivityPub.MRF.DropPolicy`: Drops all activities. It generally doesn’t makes sense to use in production.
|
||||
* `Pleroma.Web.ActivityPub.MRF.SimplePolicy`: Restrict the visibility of activities from certains instances (See [`:mrf_simple`](#mrf_simple)).
|
||||
* `Pleroma.Web.ActivityPub.MRF.TagPolicy`: Applies policies to individual users based on tags, which can be set using pleroma-fe/admin-fe/any other app that supports Pleroma Admin API. For example it allows marking posts from individual users nsfw (sensitive).
|
||||
* `Pleroma.Web.ActivityPub.MRF.SubchainPolicy`: Selectively runs other MRF policies when messages match (See [`:mrf_subchain`](#mrf_subchain)).
|
||||
* `Pleroma.Web.ActivityPub.MRF.RejectNonPublic`: Drops posts with non-public visibility settings (See [`:mrf_rejectnonpublic`](#mrf_rejectnonpublic)).
|
||||
* `Pleroma.Web.ActivityPub.MRF.EnsureRePrepended`: Rewrites posts to ensure that replies to posts with subjects do not have an identical subject and instead begin with re:.
|
||||
* `Pleroma.Web.ActivityPub.MRF.AntiLinkSpamPolicy`: Rejects posts from likely spambots by rejecting posts from new users that contain links.
|
||||
* `Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy`: Crawls attachments using their MediaProxy URLs so that the MediaProxy cache is primed.
|
||||
* `Pleroma.Web.ActivityPub.MRF.MentionPolicy`: Drops posts mentioning configurable users. (See [`:mrf_mention`](#mrf_mention)).
|
||||
* `Pleroma.Web.ActivityPub.MRF.VocabularyPolicy`: Restricts activities to a configured set of vocabulary. (See [`:mrf_vocabulary`](#mrf_vocabulary)).
|
||||
* `Pleroma.Web.ActivityPub.MRF.ObjectAgePolicy`: Rejects or delists posts based on their age when received. (See [`:mrf_object_age`](#mrf_object_age)).
|
||||
* `transparency`: Make the content of your Message Rewrite Facility settings public (via nodeinfo).
|
||||
* `transparency_exclusions`: Exclude specific instance names from MRF transparency. The use of the exclusions feature will be disclosed in nodeinfo as a boolean value.
|
||||
|
||||
## Federation
|
||||
### MRF policies
|
||||
|
||||
!!! note
|
||||
Configuring MRF policies is not enough for them to take effect. You have to enable them by specifying their module in `rewrite_policy` under [:instance](#instance) section.
|
||||
Configuring MRF policies is not enough for them to take effect. You have to enable them by specifying their module in `policies` under [:mrf](#mrf) section.
|
||||
|
||||
#### :mrf_simple
|
||||
* `media_removal`: List of instances to remove media from.
|
||||
|
@ -969,13 +972,13 @@ config :pleroma, :database_config_whitelist, [
|
|||
|
||||
Restrict access for unauthenticated users to timelines (public and federate), user profiles and statuses.
|
||||
|
||||
* `timelines` - public and federated timelines
|
||||
* `local` - public timeline
|
||||
* `timelines`: public and federated timelines
|
||||
* `local`: public timeline
|
||||
* `federated`
|
||||
* `profiles` - user profiles
|
||||
* `profiles`: user profiles
|
||||
* `local`
|
||||
* `remote`
|
||||
* `activities` - statuses
|
||||
* `activities`: statuses
|
||||
* `local`
|
||||
* `remote`
|
||||
|
||||
|
|
|
@ -34,9 +34,9 @@ config :pleroma, :instance,
|
|||
To use `SimplePolicy`, you must enable it. Do so by adding the following to your `:instance` config object, so that it looks like this:
|
||||
|
||||
```elixir
|
||||
config :pleroma, :instance,
|
||||
config :pleroma, :mrf,
|
||||
[...]
|
||||
rewrite_policy: Pleroma.Web.ActivityPub.MRF.SimplePolicy
|
||||
policies: Pleroma.Web.ActivityPub.MRF.SimplePolicy
|
||||
```
|
||||
|
||||
Once `SimplePolicy` is enabled, you can configure various groups in the `:mrf_simple` config object. These groups are:
|
||||
|
@ -58,8 +58,8 @@ Servers should be configured as lists.
|
|||
This example will enable `SimplePolicy`, block media from `illegalporn.biz`, mark media as NSFW from `porn.biz` and `porn.business`, reject messages from `spam.com`, remove messages from `spam.university` from the federated timeline and block reports (flags) from `whiny.whiner`:
|
||||
|
||||
```elixir
|
||||
config :pleroma, :instance,
|
||||
rewrite_policy: [Pleroma.Web.ActivityPub.MRF.SimplePolicy]
|
||||
config :pleroma, :mrf,
|
||||
policies: [Pleroma.Web.ActivityPub.MRF.SimplePolicy]
|
||||
|
||||
config :pleroma, :mrf_simple,
|
||||
media_removal: ["illegalporn.biz"],
|
||||
|
@ -75,7 +75,7 @@ The effects of MRF policies can be very drastic. It is important to use this fun
|
|||
|
||||
## Writing your own MRF Policy
|
||||
|
||||
As discussed above, the MRF system is a modular system that supports pluggable policies. This means that an admin may write a custom MRF policy in Elixir or any other language that runs on the Erlang VM, by specifying the module name in the `rewrite_policy` config setting.
|
||||
As discussed above, the MRF system is a modular system that supports pluggable policies. This means that an admin may write a custom MRF policy in Elixir or any other language that runs on the Erlang VM, by specifying the module name in the `policies` config setting.
|
||||
|
||||
For example, here is a sample policy module which rewrites all messages to "new message content":
|
||||
|
||||
|
@ -125,8 +125,8 @@ end
|
|||
If you save this file as `lib/pleroma/web/activity_pub/mrf/rewrite_policy.ex`, it will be included when you next rebuild Pleroma. You can enable it in the configuration like so:
|
||||
|
||||
```elixir
|
||||
config :pleroma, :instance,
|
||||
rewrite_policy: [
|
||||
config :pleroma, :mrf,
|
||||
policies: [
|
||||
Pleroma.Web.ActivityPub.MRF.SimplePolicy,
|
||||
Pleroma.Web.ActivityPub.MRF.RewritePolicy
|
||||
]
|
||||
|
|
|
@ -33,6 +33,6 @@ as soon as the post is received by your instance.
|
|||
Add to your `prod.secret.exs`:
|
||||
|
||||
```
|
||||
config :pleroma, :instance,
|
||||
rewrite_policy: [Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy]
|
||||
config :pleroma, :mrf,
|
||||
policies: [Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy]
|
||||
```
|
||||
|
|
|
@ -17,30 +17,53 @@ defmodule Mix.Tasks.Pleroma.RefreshCounterCache do
|
|||
def run([]) do
|
||||
Mix.Pleroma.start_pleroma()
|
||||
|
||||
["public", "unlisted", "private", "direct"]
|
||||
|> Enum.each(fn visibility ->
|
||||
count = status_visibility_count_query(visibility)
|
||||
name = "status_visibility_#{visibility}"
|
||||
CounterCache.set(name, count)
|
||||
Mix.Pleroma.shell_info("Set #{name} to #{count}")
|
||||
instances =
|
||||
Activity
|
||||
|> distinct([a], true)
|
||||
|> select([a], fragment("split_part(?, '/', 3)", a.actor))
|
||||
|> Repo.all()
|
||||
|
||||
instances
|
||||
|> Enum.with_index(1)
|
||||
|> Enum.each(fn {instance, i} ->
|
||||
counters = instance_counters(instance)
|
||||
CounterCache.set(instance, counters)
|
||||
|
||||
Mix.Pleroma.shell_info(
|
||||
"[#{i}/#{length(instances)}] Setting #{instance} counters: #{inspect(counters)}"
|
||||
)
|
||||
end)
|
||||
|
||||
Mix.Pleroma.shell_info("Done")
|
||||
end
|
||||
|
||||
defp status_visibility_count_query(visibility) do
|
||||
defp instance_counters(instance) do
|
||||
counters = %{"public" => 0, "unlisted" => 0, "private" => 0, "direct" => 0}
|
||||
|
||||
Activity
|
||||
|> where(
|
||||
|> where([a], fragment("(? ->> 'type'::text) = 'Create'", a.data))
|
||||
|> where([a], fragment("split_part(?, '/', 3) = ?", a.actor, ^instance))
|
||||
|> select(
|
||||
[a],
|
||||
fragment(
|
||||
"activity_visibility(?, ?, ?) = ?",
|
||||
{fragment(
|
||||
"activity_visibility(?, ?, ?)",
|
||||
a.actor,
|
||||
a.recipients,
|
||||
a.data,
|
||||
^visibility
|
||||
a.data
|
||||
), count(a.id)}
|
||||
)
|
||||
|> group_by(
|
||||
[a],
|
||||
fragment(
|
||||
"activity_visibility(?, ?, ?)",
|
||||
a.actor,
|
||||
a.recipients,
|
||||
a.data
|
||||
)
|
||||
)
|
||||
|> where([a], fragment("(? ->> 'type'::text) = 'Create'", a.data))
|
||||
|> Repo.aggregate(:count, :id, timeout: :timer.minutes(30))
|
||||
|> Repo.all(timeout: :timer.minutes(30))
|
||||
|> Enum.reduce(counters, fn {visibility, count}, acc ->
|
||||
Map.put(acc, visibility, count)
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -167,7 +167,9 @@ defmodule Pleroma.ConfigDB do
|
|||
end)
|
||||
end
|
||||
|
||||
@spec delete(map()) :: {:ok, ConfigDB.t()} | {:error, Changeset.t()}
|
||||
@spec delete(ConfigDB.t() | map()) :: {:ok, ConfigDB.t()} | {:error, Changeset.t()}
|
||||
def delete(%ConfigDB{} = config), do: Repo.delete(config)
|
||||
|
||||
def delete(params) do
|
||||
search_opts = Map.delete(params, :subkeys)
|
||||
|
||||
|
|
|
@ -3,9 +3,23 @@
|
|||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Config.DeprecationWarnings do
|
||||
alias Pleroma.Config
|
||||
|
||||
require Logger
|
||||
alias Pleroma.Config
|
||||
|
||||
@type config_namespace() :: [atom()]
|
||||
@type config_map() :: {config_namespace(), config_namespace(), String.t()}
|
||||
|
||||
@mrf_config_map [
|
||||
{[:instance, :rewrite_policy], [:mrf, :policies],
|
||||
"\n* `config :pleroma, :instance, rewrite_policy` is now `config :pleroma, :mrf, policies`"},
|
||||
{[:instance, :mrf_transparency], [:mrf, :transparency],
|
||||
"\n* `config :pleroma, :instance, mrf_transparency` is now `config :pleroma, :mrf, transparency`"},
|
||||
{[:instance, :mrf_transparency_exclusions], [:mrf, :transparency_exclusions],
|
||||
"\n* `config :pleroma, :instance, mrf_transparency_exclusions` is now `config :pleroma, :mrf, transparency_exclusions`"}
|
||||
]
|
||||
|
||||
def check_hellthread_threshold do
|
||||
if Config.get([:mrf_hellthread, :threshold]) do
|
||||
Logger.warn("""
|
||||
|
@ -39,5 +53,35 @@ defmodule Pleroma.Config.DeprecationWarnings do
|
|||
def warn do
|
||||
check_hellthread_threshold()
|
||||
mrf_user_allowlist()
|
||||
check_old_mrf_config()
|
||||
end
|
||||
|
||||
def check_old_mrf_config do
|
||||
warning_preface = """
|
||||
!!!DEPRECATION WARNING!!!
|
||||
Your config is using old namespaces for MRF configuration. They should work for now, but you are advised to change to new namespaces to prevent possible issues later:
|
||||
"""
|
||||
|
||||
move_namespace_and_warn(@mrf_config_map, warning_preface)
|
||||
end
|
||||
|
||||
@spec move_namespace_and_warn([config_map()], String.t()) :: :ok
|
||||
def move_namespace_and_warn(config_map, warning_preface) do
|
||||
warning =
|
||||
Enum.reduce(config_map, "", fn
|
||||
{old, new, err_msg}, acc ->
|
||||
old_config = Config.get(old)
|
||||
|
||||
if old_config do
|
||||
Config.put(new, old_config)
|
||||
acc <> err_msg
|
||||
else
|
||||
acc
|
||||
end
|
||||
end)
|
||||
|
||||
if warning != "" do
|
||||
Logger.warn(warning_preface <> warning)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -10,32 +10,70 @@ defmodule Pleroma.CounterCache do
|
|||
import Ecto.Query
|
||||
|
||||
schema "counter_cache" do
|
||||
field(:name, :string)
|
||||
field(:count, :integer)
|
||||
field(:instance, :string)
|
||||
field(:public, :integer)
|
||||
field(:unlisted, :integer)
|
||||
field(:private, :integer)
|
||||
field(:direct, :integer)
|
||||
end
|
||||
|
||||
def changeset(struct, params) do
|
||||
struct
|
||||
|> cast(params, [:name, :count])
|
||||
|> validate_required([:name])
|
||||
|> unique_constraint(:name)
|
||||
|> cast(params, [:instance, :public, :unlisted, :private, :direct])
|
||||
|> validate_required([:instance])
|
||||
|> unique_constraint(:instance)
|
||||
end
|
||||
|
||||
def get_as_map(names) when is_list(names) do
|
||||
def get_by_instance(instance) do
|
||||
CounterCache
|
||||
|> where([cc], cc.name in ^names)
|
||||
|> Repo.all()
|
||||
|> Enum.group_by(& &1.name, & &1.count)
|
||||
|> Map.new(fn {k, v} -> {k, hd(v)} end)
|
||||
|> select([c], %{
|
||||
"public" => c.public,
|
||||
"unlisted" => c.unlisted,
|
||||
"private" => c.private,
|
||||
"direct" => c.direct
|
||||
})
|
||||
|> where([c], c.instance == ^instance)
|
||||
|> Repo.one()
|
||||
|> case do
|
||||
nil -> %{"public" => 0, "unlisted" => 0, "private" => 0, "direct" => 0}
|
||||
val -> val
|
||||
end
|
||||
end
|
||||
|
||||
def set(name, count) do
|
||||
def get_sum do
|
||||
CounterCache
|
||||
|> select([c], %{
|
||||
"public" => type(sum(c.public), :integer),
|
||||
"unlisted" => type(sum(c.unlisted), :integer),
|
||||
"private" => type(sum(c.private), :integer),
|
||||
"direct" => type(sum(c.direct), :integer)
|
||||
})
|
||||
|> Repo.one()
|
||||
end
|
||||
|
||||
def set(instance, values) do
|
||||
params =
|
||||
Enum.reduce(
|
||||
["public", "private", "unlisted", "direct"],
|
||||
%{"instance" => instance},
|
||||
fn param, acc ->
|
||||
Map.put_new(acc, param, Map.get(values, param, 0))
|
||||
end
|
||||
)
|
||||
|
||||
%CounterCache{}
|
||||
|> changeset(%{"name" => name, "count" => count})
|
||||
|> changeset(params)
|
||||
|> Repo.insert(
|
||||
on_conflict: [set: [count: count]],
|
||||
on_conflict: [
|
||||
set: [
|
||||
public: params["public"],
|
||||
private: params["private"],
|
||||
unlisted: params["unlisted"],
|
||||
direct: params["direct"]
|
||||
]
|
||||
],
|
||||
returning: true,
|
||||
conflict_target: :name
|
||||
conflict_target: :instance
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -45,6 +45,7 @@ defmodule Pleroma.Emoji.Pack do
|
|||
shortcodes =
|
||||
pack.files
|
||||
|> Map.keys()
|
||||
|> Enum.sort()
|
||||
|> paginate(opts[:page], opts[:page_size])
|
||||
|
||||
pack = Map.put(pack, :files, Map.take(pack.files, shortcodes))
|
||||
|
|
|
@ -124,6 +124,7 @@ defmodule Pleroma.FollowingRelationship do
|
|||
|> join(:inner, [r], f in assoc(r, :follower))
|
||||
|> where([r], r.state == ^:follow_pending)
|
||||
|> where([r], r.following_id == ^id)
|
||||
|> where([r, f], f.deactivated != true)
|
||||
|> select([r, f], f)
|
||||
|> Repo.all()
|
||||
end
|
||||
|
|
|
@ -109,7 +109,7 @@ defmodule Pleroma.HTML do
|
|||
result =
|
||||
content
|
||||
|> Floki.parse_fragment!()
|
||||
|> Floki.filter_out("a.mention,a.hashtag,a[rel~=\"tag\"]")
|
||||
|> Floki.filter_out("a.mention,a.hashtag,a.attachment,a[rel~=\"tag\"]")
|
||||
|> Floki.attribute("a", "href")
|
||||
|> Enum.at(0)
|
||||
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.HTTP.ExAws do
|
||||
@moduledoc false
|
||||
|
||||
@behaviour ExAws.Request.HttpClient
|
||||
|
||||
alias Pleroma.HTTP
|
||||
|
||||
@impl true
|
||||
def request(method, url, body \\ "", headers \\ [], http_opts \\ []) do
|
||||
case HTTP.request(method, url, body, headers, http_opts) do
|
||||
{:ok, env} ->
|
||||
{:ok, %{status_code: env.status, headers: env.headers, body: env.body}}
|
||||
|
||||
{:error, reason} ->
|
||||
{:error, %{reason: reason}}
|
||||
end
|
||||
end
|
||||
end
|
|
@ -16,6 +16,7 @@ defmodule Pleroma.HTTP do
|
|||
require Logger
|
||||
|
||||
@type t :: __MODULE__
|
||||
@type method() :: :get | :post | :put | :delete | :head
|
||||
|
||||
@doc """
|
||||
Performs GET request.
|
||||
|
@ -28,6 +29,9 @@ defmodule Pleroma.HTTP do
|
|||
def get(nil, _, _), do: nil
|
||||
def get(url, headers, options), do: request(:get, url, "", headers, options)
|
||||
|
||||
@spec head(Request.url(), Request.headers(), keyword()) :: {:ok, Env.t()} | {:error, any()}
|
||||
def head(url, headers \\ [], options \\ []), do: request(:head, url, "", headers, options)
|
||||
|
||||
@doc """
|
||||
Performs POST request.
|
||||
|
||||
|
@ -42,7 +46,7 @@ defmodule Pleroma.HTTP do
|
|||
Builds and performs http request.
|
||||
|
||||
# Arguments:
|
||||
`method` - :get, :post, :put, :delete
|
||||
`method` - :get, :post, :put, :delete, :head
|
||||
`url` - full url
|
||||
`body` - request body
|
||||
`headers` - a keyworld list of headers, e.g. `[{"content-type", "text/plain"}]`
|
||||
|
@ -52,7 +56,7 @@ defmodule Pleroma.HTTP do
|
|||
`{:ok, %Tesla.Env{}}` or `{:error, error}`
|
||||
|
||||
"""
|
||||
@spec request(atom(), Request.url(), String.t(), Request.headers(), keyword()) ::
|
||||
@spec request(method(), Request.url(), String.t(), Request.headers(), keyword()) ::
|
||||
{:ok, Env.t()} | {:error, any()}
|
||||
def request(method, url, body, headers, options) when is_binary(url) do
|
||||
uri = URI.parse(url)
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.HTTP.Tzdata do
|
||||
@moduledoc false
|
||||
|
||||
@behaviour Tzdata.HTTPClient
|
||||
|
||||
alias Pleroma.HTTP
|
||||
|
||||
@impl true
|
||||
def get(url, headers, options) do
|
||||
with {:ok, %Tesla.Env{} = env} <- HTTP.get(url, headers, options) do
|
||||
{:ok, {env.status, env.headers, env.body}}
|
||||
end
|
||||
end
|
||||
|
||||
@impl true
|
||||
def head(url, headers, options) do
|
||||
with {:ok, %Tesla.Env{} = env} <- HTTP.head(url, headers, options) do
|
||||
{:ok, {env.status, env.headers}}
|
||||
end
|
||||
end
|
||||
end
|
|
@ -3,7 +3,6 @@
|
|||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.MigrationHelper.NotificationBackfill do
|
||||
alias Pleroma.Notification
|
||||
alias Pleroma.Object
|
||||
alias Pleroma.Repo
|
||||
alias Pleroma.User
|
||||
|
@ -25,18 +24,27 @@ defmodule Pleroma.MigrationHelper.NotificationBackfill do
|
|||
|> type_from_activity()
|
||||
|
||||
notification
|
||||
|> Notification.changeset(%{type: type})
|
||||
|> Ecto.Changeset.change(%{type: type})
|
||||
|> Repo.update()
|
||||
end)
|
||||
end
|
||||
|
||||
defp get_by_ap_id(ap_id) do
|
||||
q =
|
||||
from(u in User,
|
||||
select: u.id
|
||||
)
|
||||
|
||||
Repo.get_by(q, ap_id: ap_id)
|
||||
end
|
||||
|
||||
# This is copied over from Notifications to keep this stable.
|
||||
defp type_from_activity(%{data: %{"type" => type}} = activity) do
|
||||
case type do
|
||||
"Follow" ->
|
||||
accepted_function = fn activity ->
|
||||
with %User{} = follower <- User.get_by_ap_id(activity.data["actor"]),
|
||||
%User{} = followed <- User.get_by_ap_id(activity.data["object"]) do
|
||||
with %User{} = follower <- get_by_ap_id(activity.data["actor"]),
|
||||
%User{} = followed <- get_by_ap_id(activity.data["object"]) do
|
||||
Pleroma.FollowingRelationship.following?(follower, followed)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -367,6 +367,7 @@ defmodule Pleroma.Notification do
|
|||
do_send = do_send && user in enabled_receivers
|
||||
create_notification(activity, user, do_send)
|
||||
end)
|
||||
|> Enum.reject(&is_nil/1)
|
||||
|
||||
{:ok, notifications}
|
||||
end
|
||||
|
|
|
@ -83,8 +83,8 @@ defmodule Pleroma.Object.Fetcher do
|
|||
{:transmogrifier, {:error, {:reject, nil}}} ->
|
||||
{:reject, nil}
|
||||
|
||||
{:transmogrifier, _} ->
|
||||
{:error, "Transmogrifier failure."}
|
||||
{:transmogrifier, _} = e ->
|
||||
{:error, e}
|
||||
|
||||
{:object, data, nil} ->
|
||||
reinject_object(%Object{}, data)
|
||||
|
|
|
@ -97,20 +97,11 @@ defmodule Pleroma.Stats do
|
|||
}
|
||||
end
|
||||
|
||||
def get_status_visibility_count do
|
||||
counter_cache =
|
||||
CounterCache.get_as_map([
|
||||
"status_visibility_public",
|
||||
"status_visibility_private",
|
||||
"status_visibility_unlisted",
|
||||
"status_visibility_direct"
|
||||
])
|
||||
|
||||
%{
|
||||
public: counter_cache["status_visibility_public"] || 0,
|
||||
unlisted: counter_cache["status_visibility_unlisted"] || 0,
|
||||
private: counter_cache["status_visibility_private"] || 0,
|
||||
direct: counter_cache["status_visibility_direct"] || 0
|
||||
}
|
||||
def get_status_visibility_count(instance \\ nil) do
|
||||
if is_nil(instance) do
|
||||
CounterCache.get_sum()
|
||||
else
|
||||
CounterCache.get_by_instance(instance)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -115,7 +115,7 @@ defmodule Pleroma.User do
|
|||
field(:is_moderator, :boolean, default: false)
|
||||
field(:is_admin, :boolean, default: false)
|
||||
field(:show_role, :boolean, default: true)
|
||||
field(:settings, :map, default: nil)
|
||||
field(:mastofe_settings, :map, default: nil)
|
||||
field(:uri, ObjectValidators.Uri, default: nil)
|
||||
field(:hide_followers_count, :boolean, default: false)
|
||||
field(:hide_follows_count, :boolean, default: false)
|
||||
|
@ -1309,7 +1309,8 @@ defmodule Pleroma.User do
|
|||
|
||||
unsubscribe(blocked, blocker)
|
||||
|
||||
if following?(blocked, blocker), do: unfollow(blocked, blocker)
|
||||
unfollowing_blocked = Config.get([:activitypub, :unfollow_blocked], true)
|
||||
if unfollowing_blocked && following?(blocked, blocker), do: unfollow(blocked, blocker)
|
||||
|
||||
{:ok, blocker} = update_follower_count(blocker)
|
||||
{:ok, blocker, _} = Participation.mark_all_as_read(blocker, blocked)
|
||||
|
@ -1527,8 +1528,7 @@ defmodule Pleroma.User do
|
|||
blocked_identifiers,
|
||||
fn blocked_identifier ->
|
||||
with {:ok, %User{} = blocked} <- get_or_fetch(blocked_identifier),
|
||||
{:ok, _user_block} <- block(blocker, blocked),
|
||||
{:ok, _} <- ActivityPub.block(blocker, blocked) do
|
||||
{:ok, _block} <- CommonAPI.block(blocker, blocked) do
|
||||
blocked
|
||||
else
|
||||
err ->
|
||||
|
@ -2118,8 +2118,8 @@ defmodule Pleroma.User do
|
|||
|
||||
def mastodon_settings_update(user, settings) do
|
||||
user
|
||||
|> cast(%{settings: settings}, [:settings])
|
||||
|> validate_required([:settings])
|
||||
|> cast(%{mastofe_settings: settings}, [:mastofe_settings])
|
||||
|> validate_required([:mastofe_settings])
|
||||
|> update_and_set_cache()
|
||||
end
|
||||
|
||||
|
|
|
@ -52,6 +52,7 @@ defmodule Pleroma.User.Search do
|
|||
|> base_query(following)
|
||||
|> filter_blocked_user(for_user)
|
||||
|> filter_invisible_users()
|
||||
|> filter_internal_users()
|
||||
|> filter_blocked_domains(for_user)
|
||||
|> fts_search(query_string)
|
||||
|> trigram_rank(query_string)
|
||||
|
@ -109,6 +110,10 @@ defmodule Pleroma.User.Search do
|
|||
from(q in query, where: q.invisible == false)
|
||||
end
|
||||
|
||||
defp filter_internal_users(query) do
|
||||
from(q in query, where: q.actor_type != "Application")
|
||||
end
|
||||
|
||||
defp filter_blocked_user(query, %User{} = blocker) do
|
||||
query
|
||||
|> join(:left, [u], b in Pleroma.UserRelationship,
|
||||
|
|
|
@ -321,28 +321,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
end
|
||||
end
|
||||
|
||||
@spec update(map()) :: {:ok, Activity.t()} | {:error, any()}
|
||||
def update(%{to: to, cc: cc, actor: actor, object: object} = params) do
|
||||
local = !(params[:local] == false)
|
||||
activity_id = params[:activity_id]
|
||||
|
||||
data =
|
||||
%{
|
||||
"to" => to,
|
||||
"cc" => cc,
|
||||
"type" => "Update",
|
||||
"actor" => actor,
|
||||
"object" => object
|
||||
}
|
||||
|> Maps.put_if_present("id", activity_id)
|
||||
|
||||
with {:ok, activity} <- insert(data, local),
|
||||
_ <- notify_and_stream(activity),
|
||||
:ok <- maybe_federate(activity) do
|
||||
{:ok, activity}
|
||||
end
|
||||
end
|
||||
|
||||
@spec follow(User.t(), User.t(), String.t() | nil, boolean(), keyword()) ::
|
||||
{:ok, Activity.t()} | {:error, any()}
|
||||
def follow(follower, followed, activity_id \\ nil, local \\ true, opts \\ []) do
|
||||
|
@ -388,33 +366,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
end
|
||||
end
|
||||
|
||||
@spec block(User.t(), User.t(), String.t() | nil, boolean()) ::
|
||||
{:ok, Activity.t()} | {:error, any()}
|
||||
def block(blocker, blocked, activity_id \\ nil, local \\ true) do
|
||||
with {:ok, result} <-
|
||||
Repo.transaction(fn -> do_block(blocker, blocked, activity_id, local) end) do
|
||||
result
|
||||
end
|
||||
end
|
||||
|
||||
defp do_block(blocker, blocked, activity_id, local) do
|
||||
unfollow_blocked = Config.get([:activitypub, :unfollow_blocked])
|
||||
|
||||
if unfollow_blocked and fetch_latest_follow(blocker, blocked) do
|
||||
unfollow(blocker, blocked, nil, local)
|
||||
end
|
||||
|
||||
block_data = make_block_data(blocker, blocked, activity_id)
|
||||
|
||||
with {:ok, activity} <- insert(block_data, local),
|
||||
_ <- notify_and_stream(activity),
|
||||
:ok <- maybe_federate(activity) do
|
||||
{:ok, activity}
|
||||
else
|
||||
{:error, error} -> Repo.rollback(error)
|
||||
end
|
||||
end
|
||||
|
||||
@spec flag(map()) :: {:ok, Activity.t()} | {:error, any()}
|
||||
def flag(
|
||||
%{
|
||||
|
@ -1420,6 +1371,16 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
end
|
||||
end
|
||||
|
||||
def maybe_handle_clashing_nickname(nickname) do
|
||||
with %User{} = old_user <- User.get_by_nickname(nickname) do
|
||||
Logger.info("Found an old user for #{nickname}, ap id is #{old_user.ap_id}, renaming.")
|
||||
|
||||
old_user
|
||||
|> User.remote_user_changeset(%{nickname: "#{old_user.id}.#{old_user.nickname}"})
|
||||
|> User.update_and_set_cache()
|
||||
end
|
||||
end
|
||||
|
||||
def make_user_from_ap_id(ap_id) do
|
||||
user = User.get_cached_by_ap_id(ap_id)
|
||||
|
||||
|
@ -1432,6 +1393,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
|> User.remote_user_changeset(data)
|
||||
|> User.update_and_set_cache()
|
||||
else
|
||||
maybe_handle_clashing_nickname(data[:nickname])
|
||||
|
||||
data
|
||||
|> User.remote_user_changeset()
|
||||
|> Repo.insert()
|
||||
|
|
|
@ -123,6 +123,33 @@ defmodule Pleroma.Web.ActivityPub.Builder do
|
|||
end
|
||||
end
|
||||
|
||||
# Retricted to user updates for now, always public
|
||||
@spec update(User.t(), Object.t()) :: {:ok, map(), keyword()}
|
||||
def update(actor, object) do
|
||||
to = [Pleroma.Constants.as_public(), actor.follower_address]
|
||||
|
||||
{:ok,
|
||||
%{
|
||||
"id" => Utils.generate_activity_id(),
|
||||
"type" => "Update",
|
||||
"actor" => actor.ap_id,
|
||||
"object" => object,
|
||||
"to" => to
|
||||
}, []}
|
||||
end
|
||||
|
||||
@spec block(User.t(), User.t()) :: {:ok, map(), keyword()}
|
||||
def block(blocker, blocked) do
|
||||
{:ok,
|
||||
%{
|
||||
"id" => Utils.generate_activity_id(),
|
||||
"type" => "Block",
|
||||
"actor" => blocker.ap_id,
|
||||
"object" => blocked.ap_id,
|
||||
"to" => [blocked.ap_id]
|
||||
}, []}
|
||||
end
|
||||
|
||||
@spec announce(User.t(), Object.t(), keyword()) :: {:ok, map(), keyword()}
|
||||
def announce(actor, object, options \\ []) do
|
||||
public? = Keyword.get(options, :public, false)
|
||||
|
|
|
@ -16,7 +16,7 @@ defmodule Pleroma.Web.ActivityPub.MRF do
|
|||
def filter(%{} = object), do: get_policies() |> filter(object)
|
||||
|
||||
def get_policies do
|
||||
Pleroma.Config.get([:instance, :rewrite_policy], []) |> get_policies()
|
||||
Pleroma.Config.get([:mrf, :policies], []) |> get_policies()
|
||||
end
|
||||
|
||||
defp get_policies(policy) when is_atom(policy), do: [policy]
|
||||
|
@ -51,7 +51,7 @@ defmodule Pleroma.Web.ActivityPub.MRF do
|
|||
get_policies()
|
||||
|> Enum.map(fn policy -> to_string(policy) |> String.split(".") |> List.last() end)
|
||||
|
||||
exclusions = Pleroma.Config.get([:instance, :mrf_transparency_exclusions])
|
||||
exclusions = Pleroma.Config.get([:mrf, :transparency_exclusions])
|
||||
|
||||
base =
|
||||
%{
|
||||
|
|
|
@ -27,11 +27,14 @@ defmodule Pleroma.Web.ActivityPub.MRF.AntiLinkSpamPolicy do
|
|||
|
||||
@impl true
|
||||
def filter(%{"type" => "Create", "actor" => actor, "object" => object} = message) do
|
||||
with {:ok, %User{} = u} <- User.get_or_fetch_by_ap_id(actor),
|
||||
with {:ok, %User{local: false} = u} <- User.get_or_fetch_by_ap_id(actor),
|
||||
{:contains_links, true} <- {:contains_links, contains_links?(object)},
|
||||
{:old_user, true} <- {:old_user, old_user?(u)} do
|
||||
{:ok, message}
|
||||
else
|
||||
{:ok, %User{local: true}} ->
|
||||
{:ok, message}
|
||||
|
||||
{:contains_links, false} ->
|
||||
{:ok, message}
|
||||
|
||||
|
|
|
@ -3,21 +3,23 @@
|
|||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.ActivityPub.MRF
|
||||
@moduledoc "Filter activities depending on their origin instance"
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF
|
||||
|
||||
alias Pleroma.Config
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.ActivityPub.MRF
|
||||
|
||||
require Pleroma.Constants
|
||||
|
||||
defp check_accept(%{host: actor_host} = _actor_info, object) do
|
||||
accepts =
|
||||
Pleroma.Config.get([:mrf_simple, :accept])
|
||||
Config.get([:mrf_simple, :accept])
|
||||
|> MRF.subdomains_regex()
|
||||
|
||||
cond do
|
||||
accepts == [] -> {:ok, object}
|
||||
actor_host == Pleroma.Config.get([Pleroma.Web.Endpoint, :url, :host]) -> {:ok, object}
|
||||
actor_host == Config.get([Pleroma.Web.Endpoint, :url, :host]) -> {:ok, object}
|
||||
MRF.subdomain_match?(accepts, actor_host) -> {:ok, object}
|
||||
true -> {:reject, nil}
|
||||
end
|
||||
|
@ -25,7 +27,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
|
|||
|
||||
defp check_reject(%{host: actor_host} = _actor_info, object) do
|
||||
rejects =
|
||||
Pleroma.Config.get([:mrf_simple, :reject])
|
||||
Config.get([:mrf_simple, :reject])
|
||||
|> MRF.subdomains_regex()
|
||||
|
||||
if MRF.subdomain_match?(rejects, actor_host) do
|
||||
|
@ -41,7 +43,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
|
|||
)
|
||||
when length(child_attachment) > 0 do
|
||||
media_removal =
|
||||
Pleroma.Config.get([:mrf_simple, :media_removal])
|
||||
Config.get([:mrf_simple, :media_removal])
|
||||
|> MRF.subdomains_regex()
|
||||
|
||||
object =
|
||||
|
@ -65,7 +67,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
|
|||
} = object
|
||||
) do
|
||||
media_nsfw =
|
||||
Pleroma.Config.get([:mrf_simple, :media_nsfw])
|
||||
Config.get([:mrf_simple, :media_nsfw])
|
||||
|> MRF.subdomains_regex()
|
||||
|
||||
object =
|
||||
|
@ -85,7 +87,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
|
|||
|
||||
defp check_ftl_removal(%{host: actor_host} = _actor_info, object) do
|
||||
timeline_removal =
|
||||
Pleroma.Config.get([:mrf_simple, :federated_timeline_removal])
|
||||
Config.get([:mrf_simple, :federated_timeline_removal])
|
||||
|> MRF.subdomains_regex()
|
||||
|
||||
object =
|
||||
|
@ -108,7 +110,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
|
|||
|
||||
defp check_report_removal(%{host: actor_host} = _actor_info, %{"type" => "Flag"} = object) do
|
||||
report_removal =
|
||||
Pleroma.Config.get([:mrf_simple, :report_removal])
|
||||
Config.get([:mrf_simple, :report_removal])
|
||||
|> MRF.subdomains_regex()
|
||||
|
||||
if MRF.subdomain_match?(report_removal, actor_host) do
|
||||
|
@ -122,7 +124,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
|
|||
|
||||
defp check_avatar_removal(%{host: actor_host} = _actor_info, %{"icon" => _icon} = object) do
|
||||
avatar_removal =
|
||||
Pleroma.Config.get([:mrf_simple, :avatar_removal])
|
||||
Config.get([:mrf_simple, :avatar_removal])
|
||||
|> MRF.subdomains_regex()
|
||||
|
||||
if MRF.subdomain_match?(avatar_removal, actor_host) do
|
||||
|
@ -136,7 +138,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
|
|||
|
||||
defp check_banner_removal(%{host: actor_host} = _actor_info, %{"image" => _image} = object) do
|
||||
banner_removal =
|
||||
Pleroma.Config.get([:mrf_simple, :banner_removal])
|
||||
Config.get([:mrf_simple, :banner_removal])
|
||||
|> MRF.subdomains_regex()
|
||||
|
||||
if MRF.subdomain_match?(banner_removal, actor_host) do
|
||||
|
@ -197,10 +199,10 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
|
|||
|
||||
@impl true
|
||||
def describe do
|
||||
exclusions = Pleroma.Config.get([:instance, :mrf_transparency_exclusions])
|
||||
exclusions = Config.get([:mrf, :transparency_exclusions])
|
||||
|
||||
mrf_simple =
|
||||
Pleroma.Config.get(:mrf_simple)
|
||||
Config.get(:mrf_simple)
|
||||
|> Enum.map(fn {k, v} -> {k, Enum.reject(v, fn v -> v in exclusions end)} end)
|
||||
|> Enum.into(%{})
|
||||
|
||||
|
|
|
@ -13,16 +13,47 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do
|
|||
alias Pleroma.Object
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.ActivityPub.ObjectValidators.AnnounceValidator
|
||||
alias Pleroma.Web.ActivityPub.ObjectValidators.BlockValidator
|
||||
alias Pleroma.Web.ActivityPub.ObjectValidators.ChatMessageValidator
|
||||
alias Pleroma.Web.ActivityPub.ObjectValidators.CreateChatMessageValidator
|
||||
alias Pleroma.Web.ActivityPub.ObjectValidators.DeleteValidator
|
||||
alias Pleroma.Web.ActivityPub.ObjectValidators.EmojiReactValidator
|
||||
alias Pleroma.Web.ActivityPub.ObjectValidators.LikeValidator
|
||||
alias Pleroma.Web.ActivityPub.ObjectValidators.UndoValidator
|
||||
alias Pleroma.Web.ActivityPub.ObjectValidators.UpdateValidator
|
||||
|
||||
@spec validate(map(), keyword()) :: {:ok, map(), keyword()} | {:error, any()}
|
||||
def validate(object, meta)
|
||||
|
||||
def validate(%{"type" => "Block"} = block_activity, meta) do
|
||||
with {:ok, block_activity} <-
|
||||
block_activity
|
||||
|> BlockValidator.cast_and_validate()
|
||||
|> Ecto.Changeset.apply_action(:insert) do
|
||||
block_activity = stringify_keys(block_activity)
|
||||
outgoing_blocks = Pleroma.Config.get([:activitypub, :outgoing_blocks])
|
||||
|
||||
meta =
|
||||
if !outgoing_blocks do
|
||||
Keyword.put(meta, :do_not_federate, true)
|
||||
else
|
||||
meta
|
||||
end
|
||||
|
||||
{:ok, block_activity, meta}
|
||||
end
|
||||
end
|
||||
|
||||
def validate(%{"type" => "Update"} = update_activity, meta) do
|
||||
with {:ok, update_activity} <-
|
||||
update_activity
|
||||
|> UpdateValidator.cast_and_validate()
|
||||
|> Ecto.Changeset.apply_action(:insert) do
|
||||
update_activity = stringify_keys(update_activity)
|
||||
{:ok, update_activity, meta}
|
||||
end
|
||||
end
|
||||
|
||||
def validate(%{"type" => "Undo"} = object, meta) do
|
||||
with {:ok, object} <-
|
||||
object
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.ActivityPub.ObjectValidators.BlockValidator do
|
||||
use Ecto.Schema
|
||||
|
||||
alias Pleroma.EctoType.ActivityPub.ObjectValidators
|
||||
|
||||
import Ecto.Changeset
|
||||
import Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations
|
||||
|
||||
@primary_key false
|
||||
|
||||
embedded_schema do
|
||||
field(:id, ObjectValidators.ObjectID, primary_key: true)
|
||||
field(:type, :string)
|
||||
field(:actor, ObjectValidators.ObjectID)
|
||||
field(:to, ObjectValidators.Recipients, default: [])
|
||||
field(:cc, ObjectValidators.Recipients, default: [])
|
||||
field(:object, ObjectValidators.ObjectID)
|
||||
end
|
||||
|
||||
def cast_data(data) do
|
||||
%__MODULE__{}
|
||||
|> cast(data, __schema__(:fields))
|
||||
end
|
||||
|
||||
def validate_data(cng) do
|
||||
cng
|
||||
|> validate_required([:id, :type, :actor, :to, :cc, :object])
|
||||
|> validate_inclusion(:type, ["Block"])
|
||||
|> validate_actor_presence()
|
||||
|> validate_actor_presence(field_name: :object)
|
||||
end
|
||||
|
||||
def cast_and_validate(data) do
|
||||
data
|
||||
|> cast_data
|
||||
|> validate_data
|
||||
end
|
||||
end
|
|
@ -0,0 +1,59 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.ActivityPub.ObjectValidators.UpdateValidator do
|
||||
use Ecto.Schema
|
||||
|
||||
alias Pleroma.EctoType.ActivityPub.ObjectValidators
|
||||
|
||||
import Ecto.Changeset
|
||||
import Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations
|
||||
|
||||
@primary_key false
|
||||
|
||||
embedded_schema do
|
||||
field(:id, ObjectValidators.ObjectID, primary_key: true)
|
||||
field(:type, :string)
|
||||
field(:actor, ObjectValidators.ObjectID)
|
||||
field(:to, ObjectValidators.Recipients, default: [])
|
||||
field(:cc, ObjectValidators.Recipients, default: [])
|
||||
# In this case, we save the full object in this activity instead of just a
|
||||
# reference, so we can always see what was actually changed by this.
|
||||
field(:object, :map)
|
||||
end
|
||||
|
||||
def cast_data(data) do
|
||||
%__MODULE__{}
|
||||
|> cast(data, __schema__(:fields))
|
||||
end
|
||||
|
||||
def validate_data(cng) do
|
||||
cng
|
||||
|> validate_required([:id, :type, :actor, :to, :cc, :object])
|
||||
|> validate_inclusion(:type, ["Update"])
|
||||
|> validate_actor_presence()
|
||||
|> validate_updating_rights()
|
||||
end
|
||||
|
||||
def cast_and_validate(data) do
|
||||
data
|
||||
|> cast_data
|
||||
|> validate_data
|
||||
end
|
||||
|
||||
# For now we only support updating users, and here the rule is easy:
|
||||
# object id == actor id
|
||||
def validate_updating_rights(cng) do
|
||||
with actor = get_field(cng, :actor),
|
||||
object = get_field(cng, :object),
|
||||
{:ok, object_id} <- ObjectValidators.ObjectID.cast(object),
|
||||
true <- actor == object_id do
|
||||
cng
|
||||
else
|
||||
_e ->
|
||||
cng
|
||||
|> add_error(:object, "Can't be updated by this actor")
|
||||
end
|
||||
end
|
||||
end
|
|
@ -6,6 +6,7 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
|
|||
collection, and so on.
|
||||
"""
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.Activity.Ir.Topics
|
||||
alias Pleroma.Chat
|
||||
alias Pleroma.Chat.MessageReference
|
||||
alias Pleroma.Notification
|
||||
|
@ -20,6 +21,41 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
|
|||
|
||||
def handle(object, meta \\ [])
|
||||
|
||||
# Tasks this handles:
|
||||
# - Unfollow and block
|
||||
def handle(
|
||||
%{data: %{"type" => "Block", "object" => blocked_user, "actor" => blocking_user}} =
|
||||
object,
|
||||
meta
|
||||
) do
|
||||
with %User{} = blocker <- User.get_cached_by_ap_id(blocking_user),
|
||||
%User{} = blocked <- User.get_cached_by_ap_id(blocked_user) do
|
||||
User.block(blocker, blocked)
|
||||
end
|
||||
|
||||
{:ok, object, meta}
|
||||
end
|
||||
|
||||
# Tasks this handles:
|
||||
# - Update the user
|
||||
#
|
||||
# For a local user, we also get a changeset with the full information, so we
|
||||
# can update non-federating, non-activitypub settings as well.
|
||||
def handle(%{data: %{"type" => "Update", "object" => updated_object}} = object, meta) do
|
||||
if changeset = Keyword.get(meta, :user_update_changeset) do
|
||||
changeset
|
||||
|> User.update_and_set_cache()
|
||||
else
|
||||
{:ok, new_user_data} = ActivityPub.user_data_from_user_object(updated_object)
|
||||
|
||||
User.get_by_ap_id(updated_object["id"])
|
||||
|> User.remote_user_changeset(new_user_data)
|
||||
|> User.update_and_set_cache()
|
||||
end
|
||||
|
||||
{:ok, object, meta}
|
||||
end
|
||||
|
||||
# Tasks this handles:
|
||||
# - Add like to object
|
||||
# - Set up notification
|
||||
|
@ -62,7 +98,10 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
|
|||
|
||||
if !User.is_internal_user?(user) do
|
||||
Notification.create_notifications(object)
|
||||
ActivityPub.stream_out(object)
|
||||
|
||||
object
|
||||
|> Topics.get_activity_topics()
|
||||
|> Streamer.stream(object)
|
||||
end
|
||||
|
||||
{:ok, object, meta}
|
||||
|
|
|
@ -446,12 +446,9 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
|||
when objtype in ["Article", "Event", "Note", "Video", "Page", "Question", "Answer", "Audio"] do
|
||||
actor = Containment.get_actor(data)
|
||||
|
||||
data =
|
||||
Map.put(data, "actor", actor)
|
||||
|> fix_addressing
|
||||
|
||||
with nil <- Activity.get_create_by_object_ap_id(object["id"]),
|
||||
{:ok, %User{} = user} <- User.get_or_fetch_by_ap_id(data["actor"]) do
|
||||
{:ok, %User{} = user} <- User.get_or_fetch_by_ap_id(actor),
|
||||
data <- Map.put(data, "actor", actor) |> fix_addressing() do
|
||||
object = fix_object(object, options)
|
||||
|
||||
params = %{
|
||||
|
@ -673,7 +670,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
|||
end
|
||||
|
||||
def handle_incoming(%{"type" => type} = data, _options)
|
||||
when type in ["Like", "EmojiReact", "Announce"] do
|
||||
when type in ~w{Like EmojiReact Announce} do
|
||||
with :ok <- ObjectValidator.fetch_actor_and_object(data),
|
||||
{:ok, activity, _meta} <-
|
||||
Pipeline.common_pipeline(data, local: false) do
|
||||
|
@ -684,35 +681,13 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
|||
end
|
||||
|
||||
def handle_incoming(
|
||||
%{"type" => "Update", "object" => %{"type" => object_type} = object, "actor" => actor_id} =
|
||||
data,
|
||||
%{"type" => type} = data,
|
||||
_options
|
||||
)
|
||||
when object_type in [
|
||||
"Person",
|
||||
"Application",
|
||||
"Service",
|
||||
"Organization"
|
||||
] do
|
||||
with %User{ap_id: ^actor_id} = actor <- User.get_cached_by_ap_id(object["id"]) do
|
||||
{:ok, new_user_data} = ActivityPub.user_data_from_user_object(object)
|
||||
|
||||
actor
|
||||
|> User.remote_user_changeset(new_user_data)
|
||||
|> User.update_and_set_cache()
|
||||
|
||||
ActivityPub.update(%{
|
||||
local: false,
|
||||
to: data["to"] || [],
|
||||
cc: data["cc"] || [],
|
||||
object: object,
|
||||
actor: actor_id,
|
||||
activity_id: data["id"]
|
||||
})
|
||||
else
|
||||
e ->
|
||||
Logger.error(e)
|
||||
:error
|
||||
when type in ~w{Update Block} do
|
||||
with {:ok, %User{}} <- ObjectValidator.fetch_actor(data),
|
||||
{:ok, activity, _} <- Pipeline.common_pipeline(data, local: false) do
|
||||
{:ok, activity}
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -788,21 +763,6 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
|||
end
|
||||
end
|
||||
|
||||
def handle_incoming(
|
||||
%{"type" => "Block", "object" => blocked, "actor" => blocker, "id" => id} = _data,
|
||||
_options
|
||||
) do
|
||||
with %User{local: true} = blocked = User.get_cached_by_ap_id(blocked),
|
||||
{:ok, %User{} = blocker} = User.get_or_fetch_by_ap_id(blocker),
|
||||
{:ok, activity} <- ActivityPub.block(blocker, blocked, id, false) do
|
||||
User.unfollow(blocker, blocked)
|
||||
User.block(blocker, blocked)
|
||||
{:ok, activity}
|
||||
else
|
||||
_e -> :error
|
||||
end
|
||||
end
|
||||
|
||||
def handle_incoming(
|
||||
%{
|
||||
"type" => "Move",
|
||||
|
|
|
@ -47,6 +47,10 @@ defmodule Pleroma.Web.ActivityPub.Visibility do
|
|||
@spec visible_for_user?(Activity.t(), User.t() | nil) :: boolean()
|
||||
def visible_for_user?(%{actor: ap_id}, %User{ap_id: ap_id}), do: true
|
||||
|
||||
def visible_for_user?(nil, _), do: false
|
||||
|
||||
def visible_for_user?(%{data: %{"listMessage" => _}}, nil), do: false
|
||||
|
||||
def visible_for_user?(%{data: %{"listMessage" => list_ap_id}} = activity, %User{} = user) do
|
||||
user.ap_id in activity.data["to"] ||
|
||||
list_ap_id
|
||||
|
@ -54,8 +58,6 @@ defmodule Pleroma.Web.ActivityPub.Visibility do
|
|||
|> Pleroma.List.member?(user)
|
||||
end
|
||||
|
||||
def visible_for_user?(%{data: %{"listMessage" => _}}, nil), do: false
|
||||
|
||||
def visible_for_user?(%{local: local} = activity, nil) do
|
||||
cfg_key =
|
||||
if local,
|
||||
|
|
|
@ -643,10 +643,10 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
|||
json(conn, "")
|
||||
end
|
||||
|
||||
def stats(conn, _) do
|
||||
count = Stats.get_status_visibility_count()
|
||||
def stats(conn, params) do
|
||||
counters = Stats.get_status_visibility_count(params["instance"])
|
||||
|
||||
json(conn, %{"status_visibility" => count})
|
||||
json(conn, %{"status_visibility" => counters})
|
||||
end
|
||||
|
||||
defp page_params(params) do
|
||||
|
|
|
@ -40,7 +40,7 @@ defmodule Pleroma.Web.ApiSpec.CastAndValidate do
|
|||
|> List.first()
|
||||
|
||||
_ ->
|
||||
nil
|
||||
"application/json"
|
||||
end
|
||||
|
||||
private_data = Map.put(private_data, :operation_id, operation_id)
|
||||
|
|
|
@ -84,7 +84,7 @@ defmodule Pleroma.Web.ApiSpec.StatusOperation do
|
|||
operationId: "StatusController.delete",
|
||||
parameters: [id_param()],
|
||||
responses: %{
|
||||
200 => empty_object_response(),
|
||||
200 => status_response(),
|
||||
403 => Operation.response("Forbidden", "application/json", ApiError),
|
||||
404 => Operation.response("Not Found", "application/json", ApiError)
|
||||
}
|
||||
|
|
|
@ -40,20 +40,53 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Account do
|
|||
pleroma: %Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
allow_following_move: %Schema{type: :boolean},
|
||||
background_image: %Schema{type: :string, nullable: true},
|
||||
allow_following_move: %Schema{
|
||||
type: :boolean,
|
||||
description: "whether the user allows automatically follow moved following accounts"
|
||||
},
|
||||
background_image: %Schema{type: :string, nullable: true, format: :uri},
|
||||
chat_token: %Schema{type: :string},
|
||||
confirmation_pending: %Schema{type: :boolean},
|
||||
confirmation_pending: %Schema{
|
||||
type: :boolean,
|
||||
description:
|
||||
"whether the user account is waiting on email confirmation to be activated"
|
||||
},
|
||||
hide_favorites: %Schema{type: :boolean},
|
||||
hide_followers_count: %Schema{type: :boolean},
|
||||
hide_followers: %Schema{type: :boolean},
|
||||
hide_follows_count: %Schema{type: :boolean},
|
||||
hide_follows: %Schema{type: :boolean},
|
||||
is_admin: %Schema{type: :boolean},
|
||||
is_moderator: %Schema{type: :boolean},
|
||||
hide_followers_count: %Schema{
|
||||
type: :boolean,
|
||||
description: "whether the user has follower stat hiding enabled"
|
||||
},
|
||||
hide_followers: %Schema{
|
||||
type: :boolean,
|
||||
description: "whether the user has follower hiding enabled"
|
||||
},
|
||||
hide_follows_count: %Schema{
|
||||
type: :boolean,
|
||||
description: "whether the user has follow stat hiding enabled"
|
||||
},
|
||||
hide_follows: %Schema{
|
||||
type: :boolean,
|
||||
description: "whether the user has follow hiding enabled"
|
||||
},
|
||||
is_admin: %Schema{
|
||||
type: :boolean,
|
||||
description: "whether the user is an admin of the local instance"
|
||||
},
|
||||
is_moderator: %Schema{
|
||||
type: :boolean,
|
||||
description: "whether the user is a moderator of the local instance"
|
||||
},
|
||||
skip_thread_containment: %Schema{type: :boolean},
|
||||
tags: %Schema{type: :array, items: %Schema{type: :string}},
|
||||
unread_conversation_count: %Schema{type: :integer},
|
||||
tags: %Schema{
|
||||
type: :array,
|
||||
items: %Schema{type: :string},
|
||||
description:
|
||||
"List of tags being used for things like extra roles or moderation(ie. marking all media as nsfw all)."
|
||||
},
|
||||
unread_conversation_count: %Schema{
|
||||
type: :integer,
|
||||
description: "The count of unread conversations. Only returned to the account owner."
|
||||
},
|
||||
notification_settings: %Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
|
@ -66,7 +99,9 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Account do
|
|||
},
|
||||
relationship: AccountRelationship,
|
||||
settings_store: %Schema{
|
||||
type: :object
|
||||
type: :object,
|
||||
description:
|
||||
"A generic map of settings for frontends. Opaque to the backend. Only returned in `verify_credentials` and `update_credentials`"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -74,16 +109,32 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Account do
|
|||
type: :object,
|
||||
properties: %{
|
||||
fields: %Schema{type: :array, items: AccountField},
|
||||
note: %Schema{type: :string},
|
||||
note: %Schema{
|
||||
type: :string,
|
||||
description:
|
||||
"Plaintext version of the bio without formatting applied by the backend, used for editing the bio."
|
||||
},
|
||||
privacy: VisibilityScope,
|
||||
sensitive: %Schema{type: :boolean},
|
||||
pleroma: %Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
actor_type: ActorType,
|
||||
discoverable: %Schema{type: :boolean},
|
||||
no_rich_text: %Schema{type: :boolean},
|
||||
show_role: %Schema{type: :boolean}
|
||||
discoverable: %Schema{
|
||||
type: :boolean,
|
||||
description:
|
||||
"whether the user allows discovery of the account in search results and other services."
|
||||
},
|
||||
no_rich_text: %Schema{
|
||||
type: :boolean,
|
||||
description:
|
||||
"whether the HTML tags for rich-text formatting are stripped from all statuses requested from the API."
|
||||
},
|
||||
show_role: %Schema{
|
||||
type: :boolean,
|
||||
description:
|
||||
"whether the user wants their role (e.g admin, moderator) to be shown"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -62,6 +62,11 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Status do
|
|||
}
|
||||
},
|
||||
content: %Schema{type: :string, format: :html, description: "HTML-encoded status content"},
|
||||
text: %Schema{
|
||||
type: :string,
|
||||
description: "Original unformatted content in plain text",
|
||||
nullable: true
|
||||
},
|
||||
created_at: %Schema{
|
||||
type: :string,
|
||||
format: "date-time",
|
||||
|
@ -184,6 +189,10 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Status do
|
|||
thread_muted: %Schema{
|
||||
type: :boolean,
|
||||
description: "`true` if the thread the post belongs to is muted"
|
||||
},
|
||||
parent_visible: %Schema{
|
||||
type: :boolean,
|
||||
description: "`true` if the parent post is visible to the user"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -186,6 +186,7 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do
|
|||
draft.poll
|
||||
)
|
||||
|> Map.put("emoji", emoji)
|
||||
|> Map.put("source", draft.status)
|
||||
|
||||
%__MODULE__{draft | object: object}
|
||||
end
|
||||
|
|
|
@ -25,6 +25,13 @@ defmodule Pleroma.Web.CommonAPI do
|
|||
require Pleroma.Constants
|
||||
require Logger
|
||||
|
||||
def block(blocker, blocked) do
|
||||
with {:ok, block_data, _} <- Builder.block(blocker, blocked),
|
||||
{:ok, block, _} <- Pipeline.common_pipeline(block_data, local: true) do
|
||||
{:ok, block}
|
||||
end
|
||||
end
|
||||
|
||||
def post_chat_message(%User{} = user, %User{} = recipient, content, opts \\ []) do
|
||||
with maybe_attachment <- opts[:media_id] && Object.get_by_id(opts[:media_id]),
|
||||
:ok <- validate_chat_content_length(content, !!maybe_attachment),
|
||||
|
|
|
@ -9,6 +9,7 @@ defmodule Fallback.RedirectController do
|
|||
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.Metadata
|
||||
alias Pleroma.Web.Preload
|
||||
|
||||
def api_not_implemented(conn, _params) do
|
||||
conn
|
||||
|
@ -16,16 +17,7 @@ defmodule Fallback.RedirectController do
|
|||
|> json(%{error: "Not implemented"})
|
||||
end
|
||||
|
||||
def redirector(conn, _params, code \\ 200)
|
||||
|
||||
# redirect to admin section
|
||||
# /pleroma/admin -> /pleroma/admin/
|
||||
#
|
||||
def redirector(conn, %{"path" => ["pleroma", "admin"]} = _, _code) do
|
||||
redirect(conn, to: "/pleroma/admin/")
|
||||
end
|
||||
|
||||
def redirector(conn, _params, code) do
|
||||
def redirector(conn, _params, code \\ 200) do
|
||||
conn
|
||||
|> put_resp_content_type("text/html")
|
||||
|> send_file(code, index_file_path())
|
||||
|
@ -43,28 +35,33 @@ defmodule Fallback.RedirectController do
|
|||
def redirector_with_meta(conn, params) do
|
||||
{:ok, index_content} = File.read(index_file_path())
|
||||
|
||||
tags =
|
||||
try do
|
||||
Metadata.build_tags(params)
|
||||
rescue
|
||||
e ->
|
||||
Logger.error(
|
||||
"Metadata rendering for #{conn.request_path} failed.\n" <>
|
||||
Exception.format(:error, e, __STACKTRACE__)
|
||||
)
|
||||
tags = build_tags(conn, params)
|
||||
preloads = preload_data(conn, params)
|
||||
|
||||
""
|
||||
end
|
||||
|
||||
response = String.replace(index_content, "<!--server-generated-meta-->", tags)
|
||||
response =
|
||||
index_content
|
||||
|> String.replace("<!--server-generated-meta-->", tags <> preloads)
|
||||
|
||||
conn
|
||||
|> put_resp_content_type("text/html")
|
||||
|> send_resp(200, response)
|
||||
end
|
||||
|
||||
def index_file_path do
|
||||
Pleroma.Plugs.InstanceStatic.file_path("index.html")
|
||||
def redirector_with_preload(conn, %{"path" => ["pleroma", "admin"]}) do
|
||||
redirect(conn, to: "/pleroma/admin/")
|
||||
end
|
||||
|
||||
def redirector_with_preload(conn, params) do
|
||||
{:ok, index_content} = File.read(index_file_path())
|
||||
preloads = preload_data(conn, params)
|
||||
|
||||
response =
|
||||
index_content
|
||||
|> String.replace("<!--server-generated-meta-->", preloads)
|
||||
|
||||
conn
|
||||
|> put_resp_content_type("text/html")
|
||||
|> send_resp(200, response)
|
||||
end
|
||||
|
||||
def registration_page(conn, params) do
|
||||
|
@ -76,4 +73,36 @@ defmodule Fallback.RedirectController do
|
|||
|> put_status(204)
|
||||
|> text("")
|
||||
end
|
||||
|
||||
defp index_file_path do
|
||||
Pleroma.Plugs.InstanceStatic.file_path("index.html")
|
||||
end
|
||||
|
||||
defp build_tags(conn, params) do
|
||||
try do
|
||||
Metadata.build_tags(params)
|
||||
rescue
|
||||
e ->
|
||||
Logger.error(
|
||||
"Metadata rendering for #{conn.request_path} failed.\n" <>
|
||||
Exception.format(:error, e, __STACKTRACE__)
|
||||
)
|
||||
|
||||
""
|
||||
end
|
||||
end
|
||||
|
||||
defp preload_data(conn, params) do
|
||||
try do
|
||||
Preload.build_tags(conn, params)
|
||||
rescue
|
||||
e ->
|
||||
Logger.error(
|
||||
"Preloading for #{conn.request_path} failed.\n" <>
|
||||
Exception.format(:error, e, __STACKTRACE__)
|
||||
)
|
||||
|
||||
""
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -20,6 +20,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
|||
alias Pleroma.Plugs.RateLimiter
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||
alias Pleroma.Web.ActivityPub.Builder
|
||||
alias Pleroma.Web.ActivityPub.Pipeline
|
||||
alias Pleroma.Web.CommonAPI
|
||||
alias Pleroma.Web.MastodonAPI.ListView
|
||||
alias Pleroma.Web.MastodonAPI.MastodonAPI
|
||||
|
@ -182,34 +184,39 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
|||
end)
|
||||
|> Maps.put_if_present(:actor_type, params[:actor_type])
|
||||
|
||||
changeset = User.update_changeset(user, user_params)
|
||||
|
||||
with {:ok, user} <- User.update_and_set_cache(changeset) do
|
||||
user
|
||||
|> build_update_activity_params()
|
||||
|> ActivityPub.update()
|
||||
|
||||
render(conn, "show.json", user: user, for: user, with_pleroma_settings: true)
|
||||
# What happens here:
|
||||
#
|
||||
# We want to update the user through the pipeline, but the ActivityPub
|
||||
# update information is not quite enough for this, because this also
|
||||
# contains local settings that don't federate and don't even appear
|
||||
# in the Update activity.
|
||||
#
|
||||
# So we first build the normal local changeset, then apply it to the
|
||||
# user data, but don't persist it. With this, we generate the object
|
||||
# data for our update activity. We feed this and the changeset as meta
|
||||
# inforation into the pipeline, where they will be properly updated and
|
||||
# federated.
|
||||
with changeset <- User.update_changeset(user, user_params),
|
||||
{:ok, unpersisted_user} <- Ecto.Changeset.apply_action(changeset, :update),
|
||||
updated_object <-
|
||||
Pleroma.Web.ActivityPub.UserView.render("user.json", user: user)
|
||||
|> Map.delete("@context"),
|
||||
{:ok, update_data, []} <- Builder.update(user, updated_object),
|
||||
{:ok, _update, _} <-
|
||||
Pipeline.common_pipeline(update_data,
|
||||
local: true,
|
||||
user_update_changeset: changeset
|
||||
) do
|
||||
render(conn, "show.json",
|
||||
user: unpersisted_user,
|
||||
for: unpersisted_user,
|
||||
with_pleroma_settings: true
|
||||
)
|
||||
else
|
||||
_e -> render_error(conn, :forbidden, "Invalid request")
|
||||
end
|
||||
end
|
||||
|
||||
# Hotfix, handling will be redone with the pipeline
|
||||
defp build_update_activity_params(user) do
|
||||
object =
|
||||
Pleroma.Web.ActivityPub.UserView.render("user.json", user: user)
|
||||
|> Map.delete("@context")
|
||||
|
||||
%{
|
||||
local: true,
|
||||
to: [user.follower_address],
|
||||
cc: [],
|
||||
object: object,
|
||||
actor: user.ap_id
|
||||
}
|
||||
end
|
||||
|
||||
defp normalize_fields_attributes(fields) do
|
||||
if Enum.all?(fields, &is_tuple/1) do
|
||||
Enum.map(fields, fn {_, v} -> v end)
|
||||
|
@ -378,8 +385,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
|||
|
||||
@doc "POST /api/v1/accounts/:id/block"
|
||||
def block(%{assigns: %{user: blocker, account: blocked}} = conn, _params) do
|
||||
with {:ok, _user_block} <- User.block(blocker, blocked),
|
||||
{:ok, _activity} <- ActivityPub.block(blocker, blocked) do
|
||||
with {:ok, _activity} <- CommonAPI.block(blocker, blocked) do
|
||||
render(conn, "relationship.json", user: blocker, target: blocked)
|
||||
else
|
||||
{:error, message} -> json_response(conn, :forbidden, %{error: message})
|
||||
|
|
|
@ -44,6 +44,7 @@ defmodule Pleroma.Web.MastodonAPI.SearchController do
|
|||
def search(conn, params), do: do_search(:v1, conn, params)
|
||||
|
||||
defp do_search(version, %{assigns: %{user: user}} = conn, %{q: query} = params) do
|
||||
query = String.trim(query)
|
||||
options = search_options(params, user)
|
||||
timeout = Keyword.get(Repo.config(), :timeout, 15_000)
|
||||
default_values = %{"statuses" => [], "accounts" => [], "hashtags" => []}
|
||||
|
|
|
@ -200,11 +200,18 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do
|
|||
|
||||
@doc "DELETE /api/v1/statuses/:id"
|
||||
def delete(%{assigns: %{user: user}} = conn, %{id: id}) do
|
||||
with {:ok, %Activity{}} <- CommonAPI.delete(id, user) do
|
||||
json(conn, %{})
|
||||
with %Activity{} = activity <- Activity.get_by_id_with_object(id),
|
||||
render <-
|
||||
try_render(conn, "show.json",
|
||||
activity: activity,
|
||||
for: user,
|
||||
with_direct_conversation_id: true,
|
||||
with_source: true
|
||||
),
|
||||
{:ok, %Activity{}} <- CommonAPI.delete(id, user) do
|
||||
render
|
||||
else
|
||||
{:error, :not_found} = e -> e
|
||||
_e -> render_error(conn, :forbidden, "Can't delete this post")
|
||||
_e -> {:error, :not_found}
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -36,8 +36,10 @@ defmodule Pleroma.Web.MastodonAPI.InstanceView do
|
|||
background_image: Keyword.get(instance, :background_image),
|
||||
pleroma: %{
|
||||
metadata: %{
|
||||
account_activation_required: Keyword.get(instance, :account_activation_required),
|
||||
features: features(),
|
||||
federation: federation()
|
||||
federation: federation(),
|
||||
fields_limits: fields_limits()
|
||||
},
|
||||
vapid_public_key: Keyword.get(Pleroma.Web.Push.vapid_config(), :public_key)
|
||||
}
|
||||
|
@ -78,7 +80,7 @@ defmodule Pleroma.Web.MastodonAPI.InstanceView do
|
|||
def federation do
|
||||
quarantined = Config.get([:instance, :quarantined_instances], [])
|
||||
|
||||
if Config.get([:instance, :mrf_transparency]) do
|
||||
if Config.get([:mrf, :transparency]) do
|
||||
{:ok, data} = MRF.describe()
|
||||
|
||||
data
|
||||
|
@ -88,4 +90,13 @@ defmodule Pleroma.Web.MastodonAPI.InstanceView do
|
|||
end
|
||||
|> Map.put(:enabled, Config.get([:instance, :federating]))
|
||||
end
|
||||
|
||||
def fields_limits do
|
||||
%{
|
||||
max_fields: Config.get([:instance, :max_account_fields]),
|
||||
max_remote_fields: Config.get([:instance, :max_remote_account_fields]),
|
||||
name_length: Config.get([:instance, :account_field_name_length]),
|
||||
value_length: Config.get([:instance, :account_field_value_length])
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
|
@ -21,7 +21,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
|
|||
alias Pleroma.Web.MastodonAPI.StatusView
|
||||
alias Pleroma.Web.MediaProxy
|
||||
|
||||
import Pleroma.Web.ActivityPub.Visibility, only: [get_visibility: 1]
|
||||
import Pleroma.Web.ActivityPub.Visibility, only: [get_visibility: 1, visible_for_user?: 2]
|
||||
|
||||
# TODO: Add cached version.
|
||||
defp get_replied_to_activities([]), do: %{}
|
||||
|
@ -333,6 +333,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
|
|||
reblog: nil,
|
||||
card: card,
|
||||
content: content_html,
|
||||
text: opts[:with_source] && object.data["source"],
|
||||
created_at: created_at,
|
||||
reblogs_count: announcement_count,
|
||||
replies_count: object.data["repliesCount"] || 0,
|
||||
|
@ -364,7 +365,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
|
|||
expires_at: expires_at,
|
||||
direct_conversation_id: direct_conversation_id,
|
||||
thread_muted: thread_muted?,
|
||||
emoji_reactions: emoji_reactions
|
||||
emoji_reactions: emoji_reactions,
|
||||
parent_visible: visible_for_user?(reply_to, opts[:for])
|
||||
}
|
||||
}
|
||||
end
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.Nodeinfo.Nodeinfo do
|
||||
alias Pleroma.Config
|
||||
alias Pleroma.Stats
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.Federator.Publisher
|
||||
alias Pleroma.Web.MastodonAPI.InstanceView
|
||||
|
||||
# returns a nodeinfo 2.0 map, since 2.1 just adds a repository field
|
||||
# under software.
|
||||
def get_nodeinfo("2.0") do
|
||||
stats = Stats.get_stats()
|
||||
|
||||
staff_accounts =
|
||||
User.all_superusers()
|
||||
|> Enum.map(fn u -> u.ap_id end)
|
||||
|
||||
federation = InstanceView.federation()
|
||||
features = InstanceView.features()
|
||||
|
||||
%{
|
||||
version: "2.0",
|
||||
software: %{
|
||||
name: Pleroma.Application.name() |> String.downcase(),
|
||||
version: Pleroma.Application.version()
|
||||
},
|
||||
protocols: Publisher.gather_nodeinfo_protocol_names(),
|
||||
services: %{
|
||||
inbound: [],
|
||||
outbound: []
|
||||
},
|
||||
openRegistrations: Config.get([:instance, :registrations_open]),
|
||||
usage: %{
|
||||
users: %{
|
||||
total: Map.get(stats, :user_count, 0)
|
||||
},
|
||||
localPosts: Map.get(stats, :status_count, 0)
|
||||
},
|
||||
metadata: %{
|
||||
nodeName: Config.get([:instance, :name]),
|
||||
nodeDescription: Config.get([:instance, :description]),
|
||||
private: !Config.get([:instance, :public], true),
|
||||
suggestions: %{
|
||||
enabled: false
|
||||
},
|
||||
staffAccounts: staff_accounts,
|
||||
federation: federation,
|
||||
pollLimits: Config.get([:instance, :poll_limits]),
|
||||
postFormats: Config.get([:instance, :allowed_post_formats]),
|
||||
uploadLimits: %{
|
||||
general: Config.get([:instance, :upload_limit]),
|
||||
avatar: Config.get([:instance, :avatar_upload_limit]),
|
||||
banner: Config.get([:instance, :banner_upload_limit]),
|
||||
background: Config.get([:instance, :background_upload_limit])
|
||||
},
|
||||
fieldsLimits: %{
|
||||
maxFields: Config.get([:instance, :max_account_fields]),
|
||||
maxRemoteFields: Config.get([:instance, :max_remote_account_fields]),
|
||||
nameLength: Config.get([:instance, :account_field_name_length]),
|
||||
valueLength: Config.get([:instance, :account_field_value_length])
|
||||
},
|
||||
accountActivationRequired: Config.get([:instance, :account_activation_required], false),
|
||||
invitesEnabled: Config.get([:instance, :invites_enabled], false),
|
||||
mailerEnabled: Config.get([Pleroma.Emails.Mailer, :enabled], false),
|
||||
features: features,
|
||||
restrictedNicknames: Config.get([Pleroma.User, :restricted_nicknames]),
|
||||
skipThreadContainment: Config.get([:instance, :skip_thread_containment], false)
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def get_nodeinfo("2.1") do
|
||||
raw_response = get_nodeinfo("2.0")
|
||||
|
||||
updated_software =
|
||||
raw_response
|
||||
|> Map.get(:software)
|
||||
|> Map.put(:repository, Pleroma.Application.repository())
|
||||
|
||||
raw_response
|
||||
|> Map.put(:software, updated_software)
|
||||
|> Map.put(:version, "2.1")
|
||||
end
|
||||
|
||||
def get_nodeinfo(_version) do
|
||||
{:error, :missing}
|
||||
end
|
||||
end
|
|
@ -5,12 +5,8 @@
|
|||
defmodule Pleroma.Web.Nodeinfo.NodeinfoController do
|
||||
use Pleroma.Web, :controller
|
||||
|
||||
alias Pleroma.Config
|
||||
alias Pleroma.Stats
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web
|
||||
alias Pleroma.Web.Federator.Publisher
|
||||
alias Pleroma.Web.MastodonAPI.InstanceView
|
||||
alias Pleroma.Web.Nodeinfo.Nodeinfo
|
||||
|
||||
def schemas(conn, _params) do
|
||||
response = %{
|
||||
|
@ -29,102 +25,20 @@ defmodule Pleroma.Web.Nodeinfo.NodeinfoController do
|
|||
json(conn, response)
|
||||
end
|
||||
|
||||
# returns a nodeinfo 2.0 map, since 2.1 just adds a repository field
|
||||
# under software.
|
||||
def raw_nodeinfo do
|
||||
stats = Stats.get_stats()
|
||||
|
||||
staff_accounts =
|
||||
User.all_superusers()
|
||||
|> Enum.map(fn u -> u.ap_id end)
|
||||
|
||||
features = InstanceView.features()
|
||||
federation = InstanceView.federation()
|
||||
|
||||
%{
|
||||
version: "2.0",
|
||||
software: %{
|
||||
name: Pleroma.Application.name() |> String.downcase(),
|
||||
version: Pleroma.Application.version()
|
||||
},
|
||||
protocols: Publisher.gather_nodeinfo_protocol_names(),
|
||||
services: %{
|
||||
inbound: [],
|
||||
outbound: []
|
||||
},
|
||||
openRegistrations: Config.get([:instance, :registrations_open]),
|
||||
usage: %{
|
||||
users: %{
|
||||
total: Map.get(stats, :user_count, 0)
|
||||
},
|
||||
localPosts: Map.get(stats, :status_count, 0)
|
||||
},
|
||||
metadata: %{
|
||||
nodeName: Config.get([:instance, :name]),
|
||||
nodeDescription: Config.get([:instance, :description]),
|
||||
private: !Config.get([:instance, :public], true),
|
||||
suggestions: %{
|
||||
enabled: false
|
||||
},
|
||||
staffAccounts: staff_accounts,
|
||||
federation: federation,
|
||||
pollLimits: Config.get([:instance, :poll_limits]),
|
||||
postFormats: Config.get([:instance, :allowed_post_formats]),
|
||||
uploadLimits: %{
|
||||
general: Config.get([:instance, :upload_limit]),
|
||||
avatar: Config.get([:instance, :avatar_upload_limit]),
|
||||
banner: Config.get([:instance, :banner_upload_limit]),
|
||||
background: Config.get([:instance, :background_upload_limit])
|
||||
},
|
||||
fieldsLimits: %{
|
||||
maxFields: Config.get([:instance, :max_account_fields]),
|
||||
maxRemoteFields: Config.get([:instance, :max_remote_account_fields]),
|
||||
nameLength: Config.get([:instance, :account_field_name_length]),
|
||||
valueLength: Config.get([:instance, :account_field_value_length])
|
||||
},
|
||||
accountActivationRequired: Config.get([:instance, :account_activation_required], false),
|
||||
invitesEnabled: Config.get([:instance, :invites_enabled], false),
|
||||
mailerEnabled: Config.get([Pleroma.Emails.Mailer, :enabled], false),
|
||||
features: features,
|
||||
restrictedNicknames: Config.get([Pleroma.User, :restricted_nicknames]),
|
||||
skipThreadContainment: Config.get([:instance, :skip_thread_containment], false)
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
# Schema definition: https://github.com/jhass/nodeinfo/blob/master/schemas/2.0/schema.json
|
||||
# and https://github.com/jhass/nodeinfo/blob/master/schemas/2.1/schema.json
|
||||
def nodeinfo(conn, %{"version" => "2.0"}) do
|
||||
def nodeinfo(conn, %{"version" => version}) do
|
||||
case Nodeinfo.get_nodeinfo(version) do
|
||||
{:error, :missing} ->
|
||||
render_error(conn, :not_found, "Nodeinfo schema version not handled")
|
||||
|
||||
node_info ->
|
||||
conn
|
||||
|> put_resp_header(
|
||||
"content-type",
|
||||
"application/json; profile=http://nodeinfo.diaspora.software/ns/schema/2.0#; charset=utf-8"
|
||||
)
|
||||
|> json(raw_nodeinfo())
|
||||
end
|
||||
|
||||
def nodeinfo(conn, %{"version" => "2.1"}) do
|
||||
raw_response = raw_nodeinfo()
|
||||
|
||||
updated_software =
|
||||
raw_response
|
||||
|> Map.get(:software)
|
||||
|> Map.put(:repository, Pleroma.Application.repository())
|
||||
|
||||
response =
|
||||
raw_response
|
||||
|> Map.put(:software, updated_software)
|
||||
|> Map.put(:version, "2.1")
|
||||
|
||||
conn
|
||||
|> put_resp_header(
|
||||
"content-type",
|
||||
"application/json; profile=http://nodeinfo.diaspora.software/ns/schema/2.1#; charset=utf-8"
|
||||
)
|
||||
|> json(response)
|
||||
end
|
||||
|
||||
def nodeinfo(conn, _) do
|
||||
render_error(conn, :not_found, "Nodeinfo schema version not handled")
|
||||
|> json(node_info)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.Preload do
|
||||
alias Phoenix.HTML
|
||||
require Logger
|
||||
|
||||
def build_tags(_conn, params) do
|
||||
preload_data =
|
||||
Enum.reduce(Pleroma.Config.get([__MODULE__, :providers], []), %{}, fn parser, acc ->
|
||||
terms =
|
||||
params
|
||||
|> parser.generate_terms()
|
||||
|> Enum.map(fn {k, v} -> {k, Base.encode64(Jason.encode!(v))} end)
|
||||
|> Enum.into(%{})
|
||||
|
||||
Map.merge(acc, terms)
|
||||
end)
|
||||
|
||||
rendered_html =
|
||||
preload_data
|
||||
|> Jason.encode!()
|
||||
|> build_script_tag()
|
||||
|> HTML.safe_to_string()
|
||||
|
||||
rendered_html
|
||||
end
|
||||
|
||||
def build_script_tag(content) do
|
||||
HTML.Tag.content_tag(:script, HTML.raw(content),
|
||||
id: "initial-results",
|
||||
type: "application/json"
|
||||
)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,50 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.Preload.Providers.Instance do
|
||||
alias Pleroma.Plugs.InstanceStatic
|
||||
alias Pleroma.Web.MastodonAPI.InstanceView
|
||||
alias Pleroma.Web.Nodeinfo.Nodeinfo
|
||||
alias Pleroma.Web.Preload.Providers.Provider
|
||||
|
||||
@behaviour Provider
|
||||
@instance_url "/api/v1/instance"
|
||||
@panel_url "/instance/panel.html"
|
||||
@nodeinfo_url "/nodeinfo/2.0.json"
|
||||
|
||||
@impl Provider
|
||||
def generate_terms(_params) do
|
||||
%{}
|
||||
|> build_info_tag()
|
||||
|> build_panel_tag()
|
||||
|> build_nodeinfo_tag()
|
||||
end
|
||||
|
||||
defp build_info_tag(acc) do
|
||||
info_data = InstanceView.render("show.json", %{})
|
||||
|
||||
Map.put(acc, @instance_url, info_data)
|
||||
end
|
||||
|
||||
defp build_panel_tag(acc) do
|
||||
instance_path = InstanceStatic.file_path(@panel_url |> to_string())
|
||||
|
||||
if File.exists?(instance_path) do
|
||||
panel_data = File.read!(instance_path)
|
||||
Map.put(acc, @panel_url, panel_data)
|
||||
else
|
||||
acc
|
||||
end
|
||||
end
|
||||
|
||||
defp build_nodeinfo_tag(acc) do
|
||||
case Nodeinfo.get_nodeinfo("2.0") do
|
||||
{:error, _} ->
|
||||
acc
|
||||
|
||||
nodeinfo_data ->
|
||||
Map.put(acc, @nodeinfo_url, nodeinfo_data)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,7 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.Preload.Providers.Provider do
|
||||
@callback generate_terms(map()) :: map()
|
||||
end
|
|
@ -0,0 +1,25 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.Preload.Providers.StatusNet do
|
||||
alias Pleroma.Web.Preload.Providers.Provider
|
||||
alias Pleroma.Web.TwitterAPI.UtilController
|
||||
|
||||
@behaviour Provider
|
||||
@config_url "/api/statusnet/config.json"
|
||||
|
||||
@impl Provider
|
||||
def generate_terms(_params) do
|
||||
%{}
|
||||
|> build_config_tag()
|
||||
end
|
||||
|
||||
defp build_config_tag(acc) do
|
||||
resp =
|
||||
Plug.Test.conn(:get, @config_url |> to_string())
|
||||
|> UtilController.config(nil)
|
||||
|
||||
Map.put(acc, @config_url, resp.resp_body)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,39 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.Preload.Providers.Timelines do
|
||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||
alias Pleroma.Web.MastodonAPI.StatusView
|
||||
alias Pleroma.Web.Preload.Providers.Provider
|
||||
|
||||
@behaviour Provider
|
||||
@public_url "/api/v1/timelines/public"
|
||||
|
||||
@impl Provider
|
||||
def generate_terms(params) do
|
||||
build_public_tag(%{}, params)
|
||||
end
|
||||
|
||||
def build_public_tag(acc, params) do
|
||||
if Pleroma.Config.get([:restrict_unauthenticated, :timelines, :federated], true) do
|
||||
acc
|
||||
else
|
||||
Map.put(acc, @public_url, public_timeline(params))
|
||||
end
|
||||
end
|
||||
|
||||
defp public_timeline(%{"path" => ["main", "all"]}), do: get_public_timeline(false)
|
||||
|
||||
defp public_timeline(_params), do: get_public_timeline(true)
|
||||
|
||||
defp get_public_timeline(local_only) do
|
||||
activities =
|
||||
ActivityPub.fetch_public_activities(%{
|
||||
type: ["Create"],
|
||||
local_only: local_only
|
||||
})
|
||||
|
||||
StatusView.render("index.json", activities: activities, for: nil, as: :activity)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,26 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.Preload.Providers.User do
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.MastodonAPI.AccountView
|
||||
alias Pleroma.Web.Preload.Providers.Provider
|
||||
|
||||
@behaviour Provider
|
||||
@account_url_base "/api/v1/accounts"
|
||||
|
||||
@impl Provider
|
||||
def generate_terms(%{user: user}) do
|
||||
build_accounts_tag(%{}, user)
|
||||
end
|
||||
|
||||
def generate_terms(_params), do: %{}
|
||||
|
||||
def build_accounts_tag(acc, %User{} = user) do
|
||||
account_data = AccountView.render("show.json", %{user: user, for: user})
|
||||
Map.put(acc, "#{@account_url_base}/#{user.id}", account_data)
|
||||
end
|
||||
|
||||
def build_accounts_tag(acc, _), do: acc
|
||||
end
|
|
@ -726,7 +726,7 @@ defmodule Pleroma.Web.Router do
|
|||
get("/registration/:token", RedirectController, :registration_page)
|
||||
get("/:maybe_nickname_or_id", RedirectController, :redirector_with_meta)
|
||||
get("/api*path", RedirectController, :api_not_implemented)
|
||||
get("/*path", RedirectController, :redirector)
|
||||
get("/*path", RedirectController, :redirector_with_preload)
|
||||
|
||||
options("/*path", RedirectController, :empty)
|
||||
end
|
||||
|
|
|
@ -116,6 +116,7 @@ defmodule Pleroma.Web.Streamer do
|
|||
true <-
|
||||
Enum.all?([blocked_ap_ids, muted_ap_ids], &(item.actor not in &1)),
|
||||
true <- item.data["type"] != "Announce" || item.actor not in reblog_muted_ap_ids,
|
||||
true <- !(item.data["type"] == "Announce" && parent.data["actor"] == user.ap_id),
|
||||
true <- Enum.all?([blocked_ap_ids, muted_ap_ids], &(parent.data["actor"] not in &1)),
|
||||
true <- MapSet.disjoint?(recipients, recipient_blocks),
|
||||
%{host: item_host} <- URI.parse(item.actor),
|
||||
|
|
|
@ -15,6 +15,7 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
|
|||
alias Pleroma.User
|
||||
alias Pleroma.Web
|
||||
alias Pleroma.Web.CommonAPI
|
||||
alias Pleroma.Web.TwitterAPI.UtilView
|
||||
alias Pleroma.Web.WebFinger
|
||||
|
||||
plug(Pleroma.Web.FederatingPlug when action == :remote_subscribe)
|
||||
|
@ -90,17 +91,7 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
|
|||
|
||||
def config(%{assigns: %{format: "xml"}} = conn, _params) do
|
||||
instance = Pleroma.Config.get(:instance)
|
||||
|
||||
response = """
|
||||
<config>
|
||||
<site>
|
||||
<name>#{Keyword.get(instance, :name)}</name>
|
||||
<site>#{Web.base_url()}</site>
|
||||
<textlimit>#{Keyword.get(instance, :limit)}</textlimit>
|
||||
<closed>#{!Keyword.get(instance, :registrations_open)}</closed>
|
||||
</site>
|
||||
</config>
|
||||
"""
|
||||
response = UtilView.status_net_config(instance)
|
||||
|
||||
conn
|
||||
|> put_resp_content_type("application/xml")
|
||||
|
|
|
@ -5,4 +5,18 @@
|
|||
defmodule Pleroma.Web.TwitterAPI.UtilView do
|
||||
use Pleroma.Web, :view
|
||||
import Phoenix.HTML.Form
|
||||
alias Pleroma.Web
|
||||
|
||||
def status_net_config(instance) do
|
||||
"""
|
||||
<config>
|
||||
<site>
|
||||
<name>#{Keyword.get(instance, :name)}</name>
|
||||
<site>#{Web.base_url()}</site>
|
||||
<textlimit>#{Keyword.get(instance, :limit)}</textlimit>
|
||||
<closed>#{!Keyword.get(instance, :registrations_open)}</closed>
|
||||
</site>
|
||||
</config>
|
||||
"""
|
||||
end
|
||||
end
|
||||
|
|
|
@ -86,7 +86,7 @@ defmodule Pleroma.Web.MastoFEView do
|
|||
"video\/mp4"
|
||||
]
|
||||
},
|
||||
settings: user.settings || @default_settings,
|
||||
settings: user.mastofe_settings || @default_settings,
|
||||
push_subscription: nil,
|
||||
accounts: %{user.id => render(AccountView, "show.json", user: user, for: user)},
|
||||
custom_emojis: render(CustomEmojiView, "index.json", custom_emojis: custom_emojis),
|
||||
|
|
7
mix.exs
7
mix.exs
|
@ -117,7 +117,7 @@ defmodule Pleroma.Mixfile do
|
|||
defp deps do
|
||||
[
|
||||
{:phoenix, "~> 1.4.8"},
|
||||
{:tzdata, "~> 0.5.21"},
|
||||
{:tzdata, "~> 1.0.3"},
|
||||
{:plug_cowboy, "~> 2.0"},
|
||||
{:phoenix_pubsub, "~> 1.1"},
|
||||
{:phoenix_ecto, "~> 4.0"},
|
||||
|
@ -159,7 +159,10 @@ defmodule Pleroma.Mixfile do
|
|||
{:cors_plug, "~> 1.5"},
|
||||
{:ex_doc, "~> 0.21", only: :dev, runtime: false},
|
||||
{:web_push_encryption, "~> 0.2.1"},
|
||||
{:swoosh, "~> 0.23.2"},
|
||||
{:swoosh,
|
||||
git: "https://github.com/swoosh/swoosh",
|
||||
ref: "c96e0ca8a00d8f211ec1f042a4626b09f249caa5",
|
||||
override: true},
|
||||
{:phoenix_swoosh, "~> 0.2"},
|
||||
{:gen_smtp, "~> 0.13"},
|
||||
{:websocket_client, git: "https://github.com/jeremyong/websocket_client.git", only: :test},
|
||||
|
|
4
mix.lock
4
mix.lock
|
@ -104,13 +104,13 @@
|
|||
"sleeplocks": {:hex, :sleeplocks, "1.1.1", "3d462a0639a6ef36cc75d6038b7393ae537ab394641beb59830a1b8271faeed3", [:rebar3], [], "hexpm", "84ee37aeff4d0d92b290fff986d6a95ac5eedf9b383fadfd1d88e9b84a1c02e1"},
|
||||
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.6", "cf344f5692c82d2cd7554f5ec8fd961548d4fd09e7d22f5b62482e5aeaebd4b0", [:make, :mix, :rebar3], [], "hexpm", "bdb0d2471f453c88ff3908e7686f86f9be327d065cc1ec16fa4540197ea04680"},
|
||||
"sweet_xml": {:hex, :sweet_xml, "0.6.6", "fc3e91ec5dd7c787b6195757fbcf0abc670cee1e4172687b45183032221b66b8", [:mix], [], "hexpm", "2e1ec458f892ffa81f9f8386e3f35a1af6db7a7a37748a64478f13163a1f3573"},
|
||||
"swoosh": {:hex, :swoosh, "0.23.5", "bfd9404bbf5069b1be2ffd317923ce57e58b332e25dbca2a35dedd7820dfee5a", [:mix], [{:cowboy, "~> 1.0.1 or ~> 1.1 or ~> 2.4", [hex: :cowboy, repo: "hexpm", optional: true]}, {:gen_smtp, "~> 0.13", [hex: :gen_smtp, repo: "hexpm", optional: true]}, {:hackney, "~> 1.9", [hex: :hackney, repo: "hexpm", optional: false]}, {: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", "e3928e1d2889a308aaf3e42755809ac21cffd77cb58eef01cbfdab4ce2fd1e21"},
|
||||
"swoosh": {:git, "https://github.com/swoosh/swoosh", "c96e0ca8a00d8f211ec1f042a4626b09f249caa5", [ref: "c96e0ca8a00d8f211ec1f042a4626b09f249caa5"]},
|
||||
"syslog": {:hex, :syslog, "1.1.0", "6419a232bea84f07b56dc575225007ffe34d9fdc91abe6f1b2f254fd71d8efc2", [:rebar3], [], "hexpm", "4c6a41373c7e20587be33ef841d3de6f3beba08519809329ecc4d27b15b659e1"},
|
||||
"telemetry": {:hex, :telemetry, "0.4.2", "2808c992455e08d6177322f14d3bdb6b625fbcfd233a73505870d8738a2f4599", [:rebar3], [], "hexpm", "2d1419bd9dda6a206d7b5852179511722e2b18812310d304620c7bd92a13fcef"},
|
||||
"tesla": {:git, "https://git.pleroma.social/pleroma/elixir-libraries/tesla.git", "61b7503cef33f00834f78ddfafe0d5d9dec2270b", [ref: "61b7503cef33f00834f78ddfafe0d5d9dec2270b"]},
|
||||
"timex": {:hex, :timex, "3.6.1", "efdf56d0e67a6b956cc57774353b0329c8ab7726766a11547e529357ffdc1d56", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:gettext, "~> 0.10", [hex: :gettext, repo: "hexpm", optional: false]}, {:tzdata, "~> 0.1.8 or ~> 0.5 or ~> 1.0.0", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm", "f354efb2400dd7a80fd9eb6c8419068c4f632da4ac47f3d8822d6e33f08bc852"},
|
||||
"trailing_format_plug": {:hex, :trailing_format_plug, "0.0.7", "64b877f912cf7273bed03379936df39894149e35137ac9509117e59866e10e45", [:mix], [{:plug, "> 0.12.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "bd4fde4c15f3e993a999e019d64347489b91b7a9096af68b2bdadd192afa693f"},
|
||||
"tzdata": {:hex, :tzdata, "0.5.22", "f2ba9105117ee0360eae2eca389783ef7db36d533899b2e84559404dbc77ebb8", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "cd66c8a1e6a9e121d1f538b01bef459334bb4029a1ffb4eeeb5e4eae0337e7b6"},
|
||||
"tzdata": {:hex, :tzdata, "1.0.3", "73470ad29dde46e350c60a66e6b360d3b99d2d18b74c4c349dbebbc27a09a3eb", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "a6e1ee7003c4d04ecbd21dd3ec690d4c6662db5d3bbdd7262d53cdf5e7c746c1"},
|
||||
"ueberauth": {:hex, :ueberauth, "0.6.2", "25a31111249d60bad8b65438b2306a4dc91f3208faa62f5a8c33e8713989b2e8", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "db9fbfb5ac707bc4f85a297758406340bf0358b4af737a88113c1a9eee120ac7"},
|
||||
"unicode_util_compat": {:hex, :unicode_util_compat, "0.5.0", "8516502659002cec19e244ebd90d312183064be95025a319a6c7e89f4bccd65b", [:rebar3], [], "hexpm", "d48d002e15f5cc105a696cf2f1bbb3fc72b4b770a184d8420c8db20da2674b38"},
|
||||
"unsafe": {:hex, :unsafe, "1.0.1", "a27e1874f72ee49312e0a9ec2e0b27924214a05e3ddac90e91727bc76f8613d8", [:mix], [], "hexpm", "6c7729a2d214806450d29766abc2afaa7a2cbecf415be64f36a6691afebb50e5"},
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
defmodule Pleroma.Repo.Migrations.MrfConfigMoveFromInstanceNamespace do
|
||||
use Ecto.Migration
|
||||
|
||||
alias Pleroma.ConfigDB
|
||||
|
||||
@old_keys [:rewrite_policy, :mrf_transparency, :mrf_transparency_exclusions]
|
||||
def change do
|
||||
config = ConfigDB.get_by_params(%{group: :pleroma, key: :instance})
|
||||
|
||||
if config do
|
||||
mrf =
|
||||
config.value
|
||||
|> Keyword.take(@old_keys)
|
||||
|> Keyword.new(fn
|
||||
{:rewrite_policy, policies} -> {:policies, policies}
|
||||
{:mrf_transparency, transparency} -> {:transparency, transparency}
|
||||
{:mrf_transparency_exclusions, exclusions} -> {:transparency_exclusions, exclusions}
|
||||
end)
|
||||
|
||||
if mrf != [] do
|
||||
{:ok, _} =
|
||||
%ConfigDB{}
|
||||
|> ConfigDB.changeset(%{group: :pleroma, key: :mrf, value: mrf})
|
||||
|> Pleroma.Repo.insert()
|
||||
|
||||
new_instance = Keyword.drop(config.value, @old_keys)
|
||||
|
||||
if new_instance != [] do
|
||||
{:ok, _} =
|
||||
config
|
||||
|> ConfigDB.changeset(%{value: new_instance})
|
||||
|> Pleroma.Repo.update()
|
||||
else
|
||||
{:ok, _} = ConfigDB.delete(config)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,143 @@
|
|||
defmodule Pleroma.Repo.Migrations.UpdateCounterCacheTable do
|
||||
use Ecto.Migration
|
||||
|
||||
@function_name "update_status_visibility_counter_cache"
|
||||
@trigger_name "status_visibility_counter_cache_trigger"
|
||||
|
||||
def up do
|
||||
execute("drop trigger if exists #{@trigger_name} on activities")
|
||||
execute("drop function if exists #{@function_name}()")
|
||||
drop_if_exists(unique_index(:counter_cache, [:name]))
|
||||
drop_if_exists(table(:counter_cache))
|
||||
|
||||
create_if_not_exists table(:counter_cache) do
|
||||
add(:instance, :string, null: false)
|
||||
add(:direct, :bigint, null: false, default: 0)
|
||||
add(:private, :bigint, null: false, default: 0)
|
||||
add(:unlisted, :bigint, null: false, default: 0)
|
||||
add(:public, :bigint, null: false, default: 0)
|
||||
end
|
||||
|
||||
create_if_not_exists(unique_index(:counter_cache, [:instance]))
|
||||
|
||||
"""
|
||||
CREATE OR REPLACE FUNCTION #{@function_name}()
|
||||
RETURNS TRIGGER AS
|
||||
$$
|
||||
DECLARE
|
||||
hostname character varying(255);
|
||||
visibility_new character varying(64);
|
||||
visibility_old character varying(64);
|
||||
actor character varying(255);
|
||||
BEGIN
|
||||
IF TG_OP = 'DELETE' THEN
|
||||
actor := OLD.actor;
|
||||
ELSE
|
||||
actor := NEW.actor;
|
||||
END IF;
|
||||
hostname := split_part(actor, '/', 3);
|
||||
IF TG_OP = 'INSERT' THEN
|
||||
visibility_new := activity_visibility(NEW.actor, NEW.recipients, NEW.data);
|
||||
IF NEW.data->>'type' = 'Create'
|
||||
AND visibility_new IN ('public', 'unlisted', 'private', 'direct') THEN
|
||||
EXECUTE format('INSERT INTO "counter_cache" ("instance", %1$I) VALUES ($1, 1)
|
||||
ON CONFLICT ("instance") DO
|
||||
UPDATE SET %1$I = "counter_cache".%1$I + 1', visibility_new)
|
||||
USING hostname;
|
||||
END IF;
|
||||
RETURN NEW;
|
||||
ELSIF TG_OP = 'UPDATE' THEN
|
||||
visibility_new := activity_visibility(NEW.actor, NEW.recipients, NEW.data);
|
||||
visibility_old := activity_visibility(OLD.actor, OLD.recipients, OLD.data);
|
||||
IF (NEW.data->>'type' = 'Create')
|
||||
AND (OLD.data->>'type' = 'Create')
|
||||
AND visibility_new != visibility_old
|
||||
AND visibility_new IN ('public', 'unlisted', 'private', 'direct') THEN
|
||||
EXECUTE format('UPDATE "counter_cache" SET
|
||||
%1$I = greatest("counter_cache".%1$I - 1, 0),
|
||||
%2$I = "counter_cache".%2$I + 1
|
||||
WHERE "instance" = $1', visibility_old, visibility_new)
|
||||
USING hostname;
|
||||
END IF;
|
||||
RETURN NEW;
|
||||
ELSIF TG_OP = 'DELETE' THEN
|
||||
IF OLD.data->>'type' = 'Create' THEN
|
||||
visibility_old := activity_visibility(OLD.actor, OLD.recipients, OLD.data);
|
||||
EXECUTE format('UPDATE "counter_cache" SET
|
||||
%1$I = greatest("counter_cache".%1$I - 1, 0)
|
||||
WHERE "instance" = $1', visibility_old)
|
||||
USING hostname;
|
||||
END IF;
|
||||
RETURN OLD;
|
||||
END IF;
|
||||
END;
|
||||
$$
|
||||
LANGUAGE 'plpgsql';
|
||||
"""
|
||||
|> execute()
|
||||
|
||||
execute("DROP TRIGGER IF EXISTS #{@trigger_name} ON activities")
|
||||
|
||||
"""
|
||||
CREATE TRIGGER #{@trigger_name}
|
||||
BEFORE
|
||||
INSERT
|
||||
OR UPDATE of recipients, data
|
||||
OR DELETE
|
||||
ON activities
|
||||
FOR EACH ROW
|
||||
EXECUTE PROCEDURE #{@function_name}();
|
||||
"""
|
||||
|> execute()
|
||||
end
|
||||
|
||||
def down do
|
||||
execute("DROP TRIGGER IF EXISTS #{@trigger_name} ON activities")
|
||||
execute("DROP FUNCTION IF EXISTS #{@function_name}()")
|
||||
drop_if_exists(unique_index(:counter_cache, [:instance]))
|
||||
drop_if_exists(table(:counter_cache))
|
||||
|
||||
create_if_not_exists table(:counter_cache) do
|
||||
add(:name, :string, null: false)
|
||||
add(:count, :bigint, null: false, default: 0)
|
||||
end
|
||||
|
||||
create_if_not_exists(unique_index(:counter_cache, [:name]))
|
||||
|
||||
"""
|
||||
CREATE OR REPLACE FUNCTION #{@function_name}()
|
||||
RETURNS TRIGGER AS
|
||||
$$
|
||||
DECLARE
|
||||
BEGIN
|
||||
IF TG_OP = 'INSERT' THEN
|
||||
IF NEW.data->>'type' = 'Create' THEN
|
||||
EXECUTE 'INSERT INTO counter_cache (name, count) VALUES (''status_visibility_' || activity_visibility(NEW.actor, NEW.recipients, NEW.data) || ''', 1) ON CONFLICT (name) DO UPDATE SET count = counter_cache.count + 1';
|
||||
END IF;
|
||||
RETURN NEW;
|
||||
ELSIF TG_OP = 'UPDATE' THEN
|
||||
IF (NEW.data->>'type' = 'Create') and (OLD.data->>'type' = 'Create') and activity_visibility(NEW.actor, NEW.recipients, NEW.data) != activity_visibility(OLD.actor, OLD.recipients, OLD.data) THEN
|
||||
EXECUTE 'INSERT INTO counter_cache (name, count) VALUES (''status_visibility_' || activity_visibility(NEW.actor, NEW.recipients, NEW.data) || ''', 1) ON CONFLICT (name) DO UPDATE SET count = counter_cache.count + 1';
|
||||
EXECUTE 'update counter_cache SET count = counter_cache.count - 1 where count > 0 and name = ''status_visibility_' || activity_visibility(OLD.actor, OLD.recipients, OLD.data) || ''';';
|
||||
END IF;
|
||||
RETURN NEW;
|
||||
ELSIF TG_OP = 'DELETE' THEN
|
||||
IF OLD.data->>'type' = 'Create' THEN
|
||||
EXECUTE 'update counter_cache SET count = counter_cache.count - 1 where count > 0 and name = ''status_visibility_' || activity_visibility(OLD.actor, OLD.recipients, OLD.data) || ''';';
|
||||
END IF;
|
||||
RETURN OLD;
|
||||
END IF;
|
||||
END;
|
||||
$$
|
||||
LANGUAGE 'plpgsql';
|
||||
"""
|
||||
|> execute()
|
||||
|
||||
"""
|
||||
CREATE TRIGGER #{@trigger_name} BEFORE INSERT OR UPDATE of recipients, data OR DELETE ON activities
|
||||
FOR EACH ROW
|
||||
EXECUTE PROCEDURE #{@function_name}();
|
||||
"""
|
||||
|> execute()
|
||||
end
|
||||
end
|
|
@ -0,0 +1,11 @@
|
|||
defmodule Pleroma.Repo.Migrations.RenameUserSettingsCol do
|
||||
use Ecto.Migration
|
||||
|
||||
def up do
|
||||
rename(table(:users), :settings, to: :mastofe_settings)
|
||||
end
|
||||
|
||||
def down do
|
||||
rename(table(:users), :mastofe_settings, to: :settings)
|
||||
end
|
||||
end
|
|
@ -0,0 +1 @@
|
|||
.select-field[data-v-78fa3c24]{width:350px}@media only screen and (max-width:480px){.select-field[data-v-78fa3c24]{width:100%;margin-bottom:5px}}.el-dialog__body{padding:20px}.create-account-form-item{margin-bottom:20px}.create-account-form-item-without-margin{margin-bottom:0}@media only screen and (max-width:480px){.create-user-dialog{width:85%}.create-account-form-item{margin-bottom:20px}.el-dialog__body{padding:20px}}.moderate-user-button{text-align:left;width:350px;padding:10px}.moderate-user-button-container{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between}@media only screen and (max-width:480px){.moderate-user-button{width:100%}}.actions-button{text-align:left;width:350px;padding:10px}.actions-container{display:-webkit-box;display:-ms-flexbox;display:flex;height:36px;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;-webkit-box-align:center;-ms-flex-align:center;align-items:center;margin:0 15px 10px}.actions-container .el-dropdown{margin-left:10px}.active-tag{color:#409eff;font-weight:700}.active-tag .el-icon-check{color:#409eff;float:right;margin:7px 0 0 15px}.el-dropdown-link:hover{cursor:pointer;color:#409eff}.create-account>.el-icon-plus{margin-right:5px}.password-reset-token{margin:0 0 14px}.password-reset-token-dialog{width:50%}.reset-password-link{text-decoration:underline}.users-header-container{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between}.users-container h1{margin:10px 0 0 15px;height:40px}.users-container .el-table__row:hover{cursor:pointer}.users-container .pagination{margin:25px 0;text-align:center}.users-container .reboot-button{margin:0 15px 0 0;padding:10px;width:145px}.users-container .search{width:350px;float:right;margin-left:10px}.users-container .filter-container{display:-webkit-box;display:-ms-flexbox;display:flex;height:36px;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;-webkit-box-align:center;-ms-flex-align:center;align-items:center;margin:15px}.users-container .user-count{color:grey;font-size:28px}@media only screen and (max-width:480px){.password-reset-token-dialog{width:85%}.users-container h1{margin:0}.users-container .actions-button{width:100%}.users-container .actions-container{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;margin:0 10px 7px}.users-container .el-icon-arrow-down{font-size:12px}.users-container .search{width:100%;margin-left:0}.users-container .filter-container{display:-webkit-box;display:-ms-flexbox;display:flex;height:82px;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;margin:0 10px}.users-container .el-tag{width:30px;display:inline-block;margin-bottom:4px;font-weight:700}.users-container .el-tag.el-tag--danger,.users-container .el-tag.el-tag--success{padding-left:8px}.users-container .reboot-button{margin:0}.users-container .users-header-container{margin:7px 10px 12px}.users-container .user-count{color:grey;font-size:22px}}@media only screen and (max-width:801px) and (min-width:481px){.actions-button,.search{width:49%}}
|
|
@ -0,0 +1 @@
|
|||
.actions-button[data-v-2d9f3c5e]{text-align:left;width:350px;padding:10px}.actions-button-container[data-v-2d9f3c5e]{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between}.el-dropdown[data-v-2d9f3c5e]{float:right}.el-icon-edit[data-v-2d9f3c5e]{margin-right:5px}.tag-container[data-v-2d9f3c5e]{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.tag-text[data-v-2d9f3c5e]{padding-right:20px}.no-hover[data-v-2d9f3c5e]:hover{color:#606266;background-color:#fff;cursor:auto}
|
|
@ -0,0 +1 @@
|
|||
.router-link{text-decoration:none}.moderation-log-container[data-v-60b585cf]{margin:0 15px}h1[data-v-60b585cf]{margin:0}.el-timeline[data-v-60b585cf]{margin:25px 45px 0 0;padding:0}.moderation-log-date-panel[data-v-60b585cf]{width:350px}.moderation-log-header-container[data-v-60b585cf]{-webkit-box-align:center;-ms-flex-align:center;align-items:center;margin:10px 0 15px}.moderation-log-header-container[data-v-60b585cf],.moderation-log-nav-container[data-v-60b585cf]{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between}.moderation-log-search[data-v-60b585cf]{width:350px}.moderation-log-user-select[data-v-60b585cf]{margin:0 0 20px;width:350px}.reboot-button[data-v-60b585cf]{padding:10px;margin:0;width:145px}.search-container[data-v-60b585cf]{text-align:right}.pagination[data-v-60b585cf]{text-align:center}@media only screen and (max-width:480px){h1[data-v-60b585cf]{font-size:24px}.moderation-log-date-panel[data-v-60b585cf]{width:100%}.moderation-log-user-select[data-v-60b585cf]{margin:0 0 10px;width:55%}.moderation-log-search[data-v-60b585cf]{width:40%}}@media only screen and (max-width:801px) and (min-width:481px){.moderation-log-date-panel[data-v-60b585cf]{width:55%}.moderation-log-user-select[data-v-60b585cf]{margin:0 0 10px;width:55%}.moderation-log-search[data-v-60b585cf]{width:40%}}
|
|
@ -0,0 +1 @@
|
|||
.statuses-container{padding:0 15px}.statuses-container h1{margin:10px 0 15px}.statuses-container .status-container{margin:0 0 10px}.statuses-header-container .el-button.is-plain:focus,.statuses-header-container .el-button.is-plain:hover{border-color:#dcdfe6;color:#606266;cursor:default}.checkbox-container{margin-bottom:15px}.filter-container{display:-webkit-box;display:-ms-flexbox;display:flex;height:36px;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;-webkit-box-align:center;-ms-flex-align:center;align-items:center;margin:22px 0 15px}.reboot-button{padding:10px;margin:0;width:145px}.select-instance{width:396px}.statuses-header,.statuses-header-container{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between}.statuses-pagination{padding:15px 0;text-align:center}@media only screen and (max-width:480px){.checkbox-container{margin-bottom:10px}.filter-container{display:-webkit-box;display:-ms-flexbox;display:flex;height:36px;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;margin:10px 0}.select-field{width:100%;margin-bottom:5px}.select-instance{width:100%}.statuses-header-container{-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;-webkit-box-align:start;-ms-flex-align:start;align-items:flex-start}.statuses-header-container .el-button-group{width:100%}.statuses-header-container .el-button{padding:10px 6.5px;width:50%}.statuses-header-container .el-button-group>.el-button:first-child{border-bottom-left-radius:0}.statuses-header-container .el-button-group>.el-button:not(:first-child):not(:last-child).private-button{border-top-right-radius:4px}.statuses-header-container .el-button-group>.el-button:not(:first-child):not(:last-child).public-button{border-bottom-left-radius:4px;border-top:#fff}.statuses-header-container .el-button-group>.el-button:last-child{border-top-right-radius:0;border-top:#fff}.statuses-header-container .reboot-button{margin:10px 0 0}}
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -0,0 +1 @@
|
|||
.moderate-user-button{text-align:left;width:350px;padding:10px}.moderate-user-button-container{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between}@media only screen and (max-width:480px){.moderate-user-button{width:100%}}.security-settings-container{display:-webkit-box;display:-ms-flexbox;display:flex}.security-settings-container label{width:15%;height:36px}.security-settings-modal .el-dialog__body{padding-top:10px}.security-settings-modal .el-form-item,.security-settings-modal .password-alert{margin-bottom:15px}.security-settings-modal .password-input{margin-bottom:0}.security-settings-submit-button{float:right}@media (max-width:800px){.security-settings-modal .el-dialog{width:90%}}.security-settings-modal .el-alert .el-alert__description{word-break:break-word;font-size:1em}.security-settings-modal .form-text{display:block;margin-top:.25rem;color:#909399}header{-webkit-box-align:center;-ms-flex-align:center;align-items:center;display:-webkit-box;display:-ms-flexbox;display:flex;margin:22px 0;padding-left:15px}header h1{margin:0 0 0 10px}table{margin:10px 0 0 15px}table .name-col{width:150px}.avatar-name-container{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.avatar-name-container .el-icon-top-right{font-size:2em;line-height:36px;color:#606266}.invalid{color:grey}.el-table--border:after,.el-table--group:after,.el-table:before{background-color:transparent}.image{width:20%}.image img{width:100%}.invalid-user-tag{font-size:14px;width:inherit;height:auto;text-align:center;word-wrap:break-word;white-space:normal}.left-header-container{-webkit-box-align:center;-ms-flex-align:center;align-items:center;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between}.no-statuses{margin-left:28px;color:#606266}.password-reset-token{margin:0 0 14px}.password-reset-token-dialog{width:50%}.poll ul{list-style-type:none;padding:0;width:30%}.reboot-button{padding:10px;margin-left:10px}.recent-statuses-container{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;width:67%}.recent-statuses-header{margin-top:10px}.reset-password-link{text-decoration:underline}.security-setting-button{margin-top:20px;width:100%}.statuses{padding:0 20px 0 0}.show-private{width:200px;text-align:left;line-height:67px;margin-right:20px}.show-private-statuses{margin-left:28px;margin-bottom:20px}.recent-statuses{margin-left:28px}.user-page-header{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;margin:22px 15px 22px 20px;padding:0;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.user-page-header h1{display:inline}.user-profile-card{margin:0 20px;width:30%;height:-webkit-fit-content;height:-moz-fit-content;height:fit-content}.user-profile-container{display:-webkit-box;display:-ms-flexbox;display:flex}.user-profile-table{margin:0;width:inherit}.user-profile-tag{margin:0 4px 4px 0}@media only screen and (max-width:480px){.avatar-name-container{margin-bottom:10px}.el-timeline-item__wrapper{padding-left:18px}.password-reset-token-dialog{width:85%}.recent-statuses{margin:20px 10px 15px}.recent-statuses-container{width:100%;margin:0}.show-private-statuses{margin:0 10px 20px}.status-container{margin:0 10px}.statuses{padding-right:10px;margin-left:8px}.user-page-header{padding:0;margin:7px 15px 15px 10px}.user-page-header-container .el-dropdown{width:95%;margin:0 15px 15px 10px}.user-profile-card{margin:0 10px;width:95%}.user-profile-card td{width:80px}.user-profile-container{-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}}@media only screen and (max-width:801px) and (min-width:481px){.recent-statuses{margin:20px 10px 15px 0}.recent-statuses-container{width:97%;margin:0 20px}.show-private-statuses{margin:0 10px 20px 0}.user-page-header{padding:0;margin:7px 15px 20px 20px}.user-profile-card{margin:0 20px;width:-webkit-fit-content;width:-moz-fit-content;width:fit-content}.user-profile-container{-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}}
|
|
@ -1 +0,0 @@
|
|||
.actions-button[data-v-3850612b]{text-align:left;width:350px;padding:10px}.actions-button-container[data-v-3850612b]{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between}.el-dropdown[data-v-3850612b]{float:right}.el-icon-edit[data-v-3850612b]{margin-right:5px}.tag-container[data-v-3850612b]{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.tag-text[data-v-3850612b]{padding-right:20px}.no-hover[data-v-3850612b]:hover{color:#606266;background-color:#fff;cursor:auto}
|
|
@ -0,0 +1 @@
|
|||
.moderate-user-button{text-align:left;width:350px;padding:10px}.moderate-user-button-container{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between}@media only screen and (max-width:480px){.moderate-user-button{width:100%}}.avatar-name-container{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.avatar-name-container .el-icon-top-right{font-size:2em;line-height:36px;color:#606266}.avatar-name-header{display:-webkit-box;display:-ms-flexbox;display:flex;height:40px;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.invalid{color:grey}.no-statuses{margin-left:28px;color:#606266}.password-reset-token{margin:0 0 14px}.password-reset-token-dialog{width:50%}.reboot-button{padding:10px;margin-left:6px}.recent-statuses-container-show{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}.recent-statuses-container-show .el-timeline-item,.recent-statuses-container-show .recent-statuses{margin-left:20px}.recent-statuses-container-show .show-private-statuses{margin-left:20px;margin-bottom:20px}.reset-password-link{text-decoration:underline}.router-link{text-decoration:none}.status-container{margin:0 15px 0 20px}.statuses{padding:0 20px 0 0}.user-page-header{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;margin:22px 15px 22px 20px;padding:0;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.user-page-header h1{display:inline;margin:0 0 0 10px}@media only screen and (min-width:1824px){.status-show-container{max-width:1824px;margin:auto}}@media only screen and (max-width:480px){.avatar-name-container{margin-bottom:10px}.el-timeline-item__wrapper{padding-left:18px}.left-header-container{-webkit-box-align:center;-ms-flex-align:center;align-items:center;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between}.password-reset-token-dialog{width:85%}.recent-statuses{margin:20px 10px 15px}.recent-statuses-container-show{width:100%;margin:0 0 0 10px}.recent-statuses-container-show .el-timeline-item,.recent-statuses-container-show .recent-statuses{margin-left:0}.recent-statuses-container-show .show-private-statuses{margin:0 10px 20px 0}.status-card .el-card__body{padding:15px}.status-container{margin:0 10px}.statuses{padding-right:10px;margin-left:0}.statuses .el-timeline-item__wrapper{margin-right:10px}.user-page-header{padding:0;margin:7px 15px 5px 10px}.status-page-header-container{width:100%}.status-page-header-container .el-dropdown{width:-webkit-fill-available;width:-moz-available;width:stretch;margin:0 10px 15px}}@media only screen and (max-width:801px) and (min-width:481px){.recent-statuses-container-show{width:97%;margin:0 20px}.recent-statuses-container-show .el-timeline-item{margin-left:2px}.recent-statuses-container-show .recent-statuses{margin:20px 10px 15px 0}.recent-statuses-container-show .show-private-statuses,.show-private-statuses{margin:0 10px 20px 0}.user-page-header{padding:0;margin:7px 15px 20px 20px}}
|
|
@ -1 +0,0 @@
|
|||
.moderation-log-container[data-v-103bba83]{margin:0 15px}h1[data-v-103bba83]{margin:0}.el-timeline[data-v-103bba83]{margin:25px 45px 0 0;padding:0}.moderation-log-date-panel[data-v-103bba83]{width:350px}.moderation-log-header-container[data-v-103bba83]{-webkit-box-align:center;-ms-flex-align:center;align-items:center;margin:10px 0 15px}.moderation-log-header-container[data-v-103bba83],.moderation-log-nav-container[data-v-103bba83]{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between}.moderation-log-search[data-v-103bba83]{width:350px}.moderation-log-user-select[data-v-103bba83]{margin:0 0 20px;width:350px}.reboot-button[data-v-103bba83]{padding:10px;margin:0;width:145px}.search-container[data-v-103bba83]{text-align:right}.pagination[data-v-103bba83]{text-align:center}@media only screen and (max-width:480px){h1[data-v-103bba83]{font-size:24px}.moderation-log-date-panel[data-v-103bba83]{width:100%}.moderation-log-user-select[data-v-103bba83]{margin:0 0 10px;width:55%}.moderation-log-search[data-v-103bba83]{width:40%}}@media only screen and (max-width:801px) and (min-width:481px){.moderation-log-date-panel[data-v-103bba83]{width:55%}.moderation-log-user-select[data-v-103bba83]{margin:0 0 10px;width:55%}.moderation-log-search[data-v-103bba83]{width:40%}}
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -0,0 +1 @@
|
|||
.status-card{margin-bottom:10px;cursor:pointer}.status-card .account{line-height:26px;font-size:13px;color:#606266}.status-card .account:hover{text-decoration:underline}.status-card .deactivated{color:grey;line-height:28px;vertical-align:middle}.status-card .image{width:20%}.status-card .image img{width:100%}.status-card .router-link{text-decoration:none}.status-card .show-more-button{margin-left:5px}.status-card .status-account{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.status-card .status-avatar-img{display:inline-block;width:15px;height:15px;margin-right:5px}.status-card .status-account-name{display:inline-block;margin:0;font-size:15px;font-weight:500}.status-card .status-body{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}.status-card .status-card-header{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.status-card .status-checkbox{margin-right:7px}.status-card .status-content{font-size:15px;line-height:26px}.status-card .status-created-at{font-size:13px;color:#606266}.status-card .status-deleted{font-style:italic;margin-top:3px}.status-card .status-footer,.status-card .status-header{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.status-card .status-tags{display:inline}.status-card .status-without-content{font-style:italic}@media only screen and (max-width:480px){.el-message{min-width:80%}.el-message-box{width:80%}.status-card .el-card__header{padding:10px 17px}.status-card .el-tag{margin:3px 0}.status-card .status-account-container{margin-bottom:5px}.status-card .status-actions-button{margin:3px 0}.status-card .status-actions{width:100%;display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between}.status-card .status-footer{margin-top:10px}.status-card .status-footer,.status-card .status-header{-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;-webkit-box-align:start;-ms-flex-align:start;align-items:flex-start}.status-card .status-header{display:-webkit-box;display:-ms-flexbox;display:flex}}
|
|
@ -1 +0,0 @@
|
|||
.select-field[data-v-4bc96860]{width:350px}@media only screen and (max-width:480px){.select-field[data-v-4bc96860]{width:100%;margin-bottom:5px}}.el-dialog__body{padding:20px}.create-account-form-item{margin-bottom:20px}.create-account-form-item-without-margin{margin-bottom:0}@media only screen and (max-width:480px){.create-user-dialog{width:85%}.create-account-form-item{margin-bottom:20px}.el-dialog__body{padding:20px}}.moderate-user-button{text-align:left;width:350px;padding:10px}.moderate-user-button-container{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between}@media only screen and (max-width:480px){.moderate-user-button{width:100%}}.actions-button{text-align:left;width:350px;padding:10px}.actions-container{display:-webkit-box;display:-ms-flexbox;display:flex;height:36px;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;-webkit-box-align:center;-ms-flex-align:center;align-items:center;margin:0 15px 10px}.actions-container .el-dropdown{margin-left:10px}.active-tag{color:#409eff;font-weight:700}.active-tag .el-icon-check{color:#409eff;float:right;margin:7px 0 0 15px}.el-dropdown-link:hover{cursor:pointer;color:#409eff}.create-account>.el-icon-plus{margin-right:5px}.users-header-container{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between}.password-reset-token{margin:0 0 14px}.password-reset-token-dialog{width:50%}.reset-password-link{text-decoration:underline}.users-container h1{margin:10px 0 0 15px;height:40px}.users-container .pagination{margin:25px 0;text-align:center}.users-container .reboot-button{margin:0 15px 0 0;padding:10px;width:145px}.users-container .search{width:350px;float:right;margin-left:10px}.users-container .filter-container{display:-webkit-box;display:-ms-flexbox;display:flex;height:36px;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;-webkit-box-align:center;-ms-flex-align:center;align-items:center;margin:15px}.users-container .user-count{color:grey;font-size:28px}@media only screen and (max-width:480px){.password-reset-token-dialog{width:85%}.users-container h1{margin:0}.users-container .actions-button{width:100%}.users-container .actions-container{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;margin:0 10px 7px}.users-container .el-icon-arrow-down{font-size:12px}.users-container .search{width:100%;margin-left:0}.users-container .filter-container{display:-webkit-box;display:-ms-flexbox;display:flex;height:82px;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;margin:0 10px}.users-container .el-tag{width:30px;display:inline-block;margin-bottom:4px;font-weight:700}.users-container .el-tag.el-tag--danger,.users-container .el-tag.el-tag--success{padding-left:8px}.users-container .reboot-button{margin:0}.users-container .users-header-container{margin:7px 10px 12px}.users-container .user-count{color:grey;font-size:22px}}@media only screen and (max-width:801px) and (min-width:481px){.actions-button,.search{width:49%}}
|
File diff suppressed because one or more lines are too long
|
@ -1 +0,0 @@
|
|||
.status-card{margin-bottom:10px}.status-card .account{text-decoration:underline;line-height:26px;font-size:13px}.status-card .image{width:20%}.status-card .image img{width:100%}.status-card .show-more-button{margin-left:5px}.status-card .status-account{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.status-card .status-avatar-img{display:inline-block;width:15px;height:15px;margin-right:5px}.status-card .status-account-name{display:inline-block;margin:0;height:22px}.status-card .status-body{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}.status-card .status-checkbox{margin-right:7px}.status-card .status-content{font-size:15px;line-height:26px}.status-card .status-deleted{font-style:italic;margin-top:3px}.status-card .status-header{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between}.status-card .status-without-content{font-style:italic}@media only screen and (max-width:480px){.el-message{min-width:80%}.el-message-box{width:80%}.status-card .el-card__header{padding:10px 17px}.status-card .el-tag{margin:3px 4px 3px 0}.status-card .status-account-container{margin-bottom:5px}.status-card .status-actions-button{margin:3px 0}.status-card .status-actions{display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap}.status-card .status-header{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}}.statuses-container{padding:0 15px}.statuses-container h1{margin:10px 0 15px}.statuses-container .status-container{margin:0 0 10px}.statuses-header-container .el-button.is-plain:focus,.statuses-header-container .el-button.is-plain:hover{border-color:#dcdfe6;color:#606266;cursor:default}.checkbox-container{margin-bottom:15px}.filter-container{display:-webkit-box;display:-ms-flexbox;display:flex;height:36px;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;-webkit-box-align:center;-ms-flex-align:center;align-items:center;margin:22px 0 15px}.reboot-button{padding:10px;margin:0;width:145px}.select-instance{width:396px}.statuses-header,.statuses-header-container{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between}.statuses-pagination{padding:15px 0;text-align:center}@media only screen and (max-width:480px){.checkbox-container{margin-bottom:10px}.filter-container{display:-webkit-box;display:-ms-flexbox;display:flex;height:36px;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;margin:10px 0}.select-field{width:100%;margin-bottom:5px}.select-instance{width:100%}.statuses-header-container{-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;-webkit-box-align:start;-ms-flex-align:start;align-items:flex-start}.statuses-header-container .el-button-group{width:100%}.statuses-header-container .el-button{padding:10px 6.5px;width:50%}.statuses-header-container .el-button-group>.el-button:first-child{border-bottom-left-radius:0}.statuses-header-container .el-button-group>.el-button:not(:first-child):not(:last-child).private-button{border-top-right-radius:4px}.statuses-header-container .el-button-group>.el-button:not(:first-child):not(:last-child).public-button{border-bottom-left-radius:4px;border-top:#fff}.statuses-header-container .el-button-group>.el-button:last-child{border-top-right-radius:0;border-top:#fff}.statuses-header-container .reboot-button{margin:10px 0 0}}
|
|
@ -1 +1 @@
|
|||
<!DOCTYPE html><html><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge,chrome=1"><meta name=renderer content=webkit><meta name=viewport content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no"><title>Admin FE</title><link rel="shortcut icon" href=favicon.ico><link href=chunk-elementUI.1abbc9b8.css rel=stylesheet><link href=chunk-libs.686b5876.css rel=stylesheet><link href=app.796ca6d4.css rel=stylesheet></head><body><div id=app></div><script type=text/javascript src=static/js/runtime.b08eb412.js></script><script type=text/javascript src=static/js/chunk-elementUI.fba0efec.js></script><script type=text/javascript src=static/js/chunk-libs.b8c453ab.js></script><script type=text/javascript src=static/js/app.0146039c.js></script></body></html>
|
||||
<!DOCTYPE html><html><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge,chrome=1"><meta name=renderer content=webkit><meta name=viewport content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no"><title>Admin FE</title><link rel="shortcut icon" href=favicon.ico><link href=chunk-elementUI.1abbc9b8.css rel=stylesheet><link href=chunk-libs.686b5876.css rel=stylesheet><link href=app.01bdb34a.css rel=stylesheet></head><body><div id=app></div><script type=text/javascript src=static/js/runtime.0a70a9f5.js></script><script type=text/javascript src=static/js/chunk-elementUI.fba0efec.js></script><script type=text/javascript src=static/js/chunk-libs.b8c453ab.js></script><script type=text/javascript src=static/js/app.f220ac13.js></script></body></html>
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue