Compare commits
7 Commits
feature/sa
...
nsfw-api-m
Author | SHA1 | Date | |
---|---|---|---|
|
3a03d9b65f | ||
|
a704d5499c | ||
|
c802c3055e | ||
|
b293c14a1b | ||
|
2b3dfbb42f | ||
|
f15d419062 | ||
|
718e8e1edb |
52
CHANGELOG.md
52
CHANGELOG.md
@ -8,50 +8,17 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||
|
||||
### Changed
|
||||
|
||||
### Added
|
||||
|
||||
### Fixed
|
||||
|
||||
### Removed
|
||||
|
||||
## 2.4.1 - 2021-08-29
|
||||
|
||||
### Changed
|
||||
- Make `mix pleroma.database set_text_search_config` run concurrently and indefinitely
|
||||
|
||||
### Added
|
||||
- AdminAPI: Missing configuration description for StealEmojiPolicy
|
||||
|
||||
### Fixed
|
||||
- MastodonAPI: Stream out Create activities
|
||||
- MRF ObjectAgePolicy: Fix pattern matching on "published"
|
||||
- TwitterAPI: Make `change_password` and `change_email` require params on body instead of query
|
||||
- Subscription(Bell) Notifications: Don't create from Pipeline Ingested replies
|
||||
- AdminAPI: Fix rendering reports containing a `nil` object
|
||||
- Mastodon API: Activity Search fallbacks on status fetching after a DB Timeout/Error
|
||||
- Mastodon API: Fix crash in Streamer related to reblogging
|
||||
- AdminAPI: List available frontends when `static/frontends` folder is missing
|
||||
- Make activity search properly use language-aware GIN indexes
|
||||
- AdminAPI: Fix suggestions for MRF Policies
|
||||
|
||||
## 2.4.0 - 2021-08-08
|
||||
|
||||
### Changed
|
||||
|
||||
- **Breaking:** Configuration: `:chat, enabled` moved to `:shout, enabled` and `:instance, chat_limit` moved to `:shout, limit`
|
||||
- Support for Erlang/OTP 24
|
||||
- The `application` metadata returned with statuses is no longer hardcoded. Apps that want to display these details will now have valid data for new posts after this change.
|
||||
- HTTPSecurityPlug now sends a response header to opt out of Google's FLoC (Federated Learning of Cohorts) targeted advertising.
|
||||
- Email address is now returned if requesting user is the owner of the user account so it can be exposed in client and FE user settings UIs.
|
||||
- Improved Twittercard and OpenGraph meta tag generation including thumbnails and image dimension metadata when available.
|
||||
- AdminAPI: sort users so the newest are at the top.
|
||||
- ActivityPub Client-to-Server(C2S): Limitation on the type of Activity/Object are lifted as they are now passed through ObjectValidators
|
||||
|
||||
### Added
|
||||
|
||||
- MRF (`FollowBotPolicy`): New MRF Policy which makes a designated local Bot account attempt to follow all users in public Notes received by your instance. Users who require approving follower requests or have #nobot in their profile are excluded.
|
||||
- Return OAuth token `id` (primary key) in POST `/oauth/token`.
|
||||
- AdminAPI: return `created_at` date with users.
|
||||
- `AnalyzeMetadata` upload filter for extracting image/video attachment dimensions and generating blurhashes for images. Blurhashes for videos are not generated at this time.
|
||||
- Attachment dimensions and blurhashes are federated when available.
|
||||
- Pinned posts federation
|
||||
@ -59,11 +26,15 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||
### Fixed
|
||||
- Don't crash so hard when email settings are invalid.
|
||||
- Checking activated Upload Filters for required commands.
|
||||
- Remote users can no longer reappear after being deleted.
|
||||
- Deactivated users may now be deleted.
|
||||
- Mix task `pleroma.database prune_objects`
|
||||
- Fixed rendering of JSON errors on ActivityPub endpoints.
|
||||
- Linkify: Parsing crash with URLs ending in unbalanced closed paren, no path separator, and no query parameters
|
||||
|
||||
### Removed
|
||||
- **Breaking**: Remove deprecated `/api/qvitter/statuses/notifications/read` (replaced by `/api/v1/pleroma/notifications/read`)
|
||||
|
||||
## Unreleased (Patch)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Try to save exported ConfigDB settings (migrate_from_db) in the system temp directory if default location is not writable.
|
||||
- Uploading custom instance thumbnail via AdminAPI/AdminFE generated invalid URL to the image
|
||||
- Applying ConcurrentLimiter settings via AdminAPI
|
||||
@ -72,10 +43,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||
- MRF (`SimplePolicy`): Embedded objects are now checked. If any embedded object would be rejected, its parent is rejected. This fixes Announces leaking posts from blocked domains.
|
||||
- Fixed some Markdown issues, including trailing slash in links.
|
||||
|
||||
### Removed
|
||||
- **Breaking**: Remove deprecated `/api/qvitter/statuses/notifications/read` (replaced by `/api/v1/pleroma/notifications/read`)
|
||||
|
||||
## [2.3.0] - 2021-03-01
|
||||
## [2.3.0] - 2020-03-01
|
||||
|
||||
### Security
|
||||
|
||||
@ -176,7 +144,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||
- Mastodon API: Support for expires_in/expires_at in the Filters.
|
||||
</details>
|
||||
|
||||
## [2.2.2] - 2021-01-18
|
||||
## [2.2.2] - 2020-01-18
|
||||
|
||||
### Fixed
|
||||
|
||||
|
@ -408,6 +408,13 @@ config :pleroma, :mrf_object_age,
|
||||
threshold: 604_800,
|
||||
actions: [:delist, :strip_followers]
|
||||
|
||||
config :pleroma, :mrf_nsfw_api,
|
||||
url: "http://127.0.0.1:5000/",
|
||||
threshold: 0.7,
|
||||
mark_sensitive: true,
|
||||
unlist: false,
|
||||
reject: false
|
||||
|
||||
config :pleroma, :mrf_follow_bot, follower_nickname: nil
|
||||
|
||||
config :pleroma, :rich_media,
|
||||
@ -460,7 +467,7 @@ config :pleroma, :shout,
|
||||
enabled: true,
|
||||
limit: 5_000
|
||||
|
||||
config :phoenix, :format_encoders, json: Jason, "activity+json": Jason
|
||||
config :phoenix, :format_encoders, json: Jason
|
||||
|
||||
config :phoenix, :json_library, Jason
|
||||
|
||||
|
@ -5,7 +5,7 @@ Pleroma's full text search feature is powered by PostgreSQL's native [text searc
|
||||
|
||||
## Setup and test the new search config
|
||||
|
||||
In most cases, you would need an extension installed to support parsing CJK text. Here are a few extensions you may choose from, or you are more than welcome to share additional ones you found working for you with the rest of Pleroma community.
|
||||
In most cases, you would need an extension installed to support parsing CJK text. Here are a few extension you may choose from, or you are more than welcome to share additional ones you found working for you with the rest of Pleroma community.
|
||||
|
||||
* [a generic n-gram parser](https://github.com/huangjimmy/pg_cjk_parser) supports Simplifed/Traditional Chinese, Japanese, and Korean
|
||||
* [a Korean parser](https://github.com/i0seph/textsearch_ko) based on mecab
|
||||
@ -34,7 +34,7 @@ Check output of the query, and see if it matches your expectation.
|
||||
mix pleroma.database set_text_search_config YOUR.CONFIG
|
||||
```
|
||||
|
||||
Note: index update may take a while, and it can be done while the instance is up and running, so you may restart db connection as soon as you see `Recreate index` in task output.
|
||||
Note: index update may take a while.
|
||||
|
||||
## Restart database connection
|
||||
Since some changes above will only apply with a new database connection, you will have to restart either Pleroma or PostgreSQL process, or use `pg_terminate_backend` SQL command without restarting either.
|
||||
|
@ -1,14 +1,29 @@
|
||||
# Installing on Alpine Linux
|
||||
|
||||
{! backend/installation/otp_vs_from_source_source.include !}
|
||||
|
||||
## Installation
|
||||
|
||||
This guide is a step-by-step installation guide for Alpine Linux. The instructions were verified against Alpine v3.10 standard image. You might miss additional dependencies if you use `netboot` instead.
|
||||
|
||||
It assumes that you have administrative rights, either as root or a user with [sudo permissions](https://www.linode.com/docs/tools-reference/custom-kernels-distros/install-alpine-linux-on-your-linode/#configuration). If you want to run this guide with root, ignore the `sudo` at the beginning of the lines, unless it calls a user like `sudo -Hu pleroma`; in this case, use `su -l <username> -s $SHELL -c 'command'` instead.
|
||||
|
||||
{! backend/installation/generic_dependencies.include !}
|
||||
### Required packages
|
||||
|
||||
* `postgresql`
|
||||
* `elixir`
|
||||
* `erlang`
|
||||
* `erlang-parsetools`
|
||||
* `erlang-xmerl`
|
||||
* `git`
|
||||
* `file-dev`
|
||||
* Development Tools
|
||||
* `cmake`
|
||||
|
||||
#### Optional packages used in this guide
|
||||
|
||||
* `nginx` (preferred, example configs for other reverse proxies can be found in the repo)
|
||||
* `certbot` (or any other ACME client for Let’s Encrypt certificates)
|
||||
* `ImageMagick`
|
||||
* `ffmpeg`
|
||||
* `exiftool`
|
||||
|
||||
### Prepare the system
|
||||
|
||||
|
@ -1,7 +1,4 @@
|
||||
# Installing on Arch Linux
|
||||
|
||||
{! backend/installation/otp_vs_from_source_source.include !}
|
||||
|
||||
## Installation
|
||||
|
||||
This guide will assume that you have administrative rights, either as root or a user with [sudo permissions](https://wiki.archlinux.org/index.php/Sudo). If you want to run this guide with root, ignore the `sudo` at the beginning of the lines, unless it calls a user like `sudo -Hu pleroma`; in this case, use `su <username> -s $SHELL -c 'command'` instead.
|
||||
|
@ -1,12 +1,27 @@
|
||||
# Installing on Debian Based Distributions
|
||||
|
||||
{! backend/installation/otp_vs_from_source_source.include !}
|
||||
|
||||
## Installation
|
||||
|
||||
This guide will assume you are on Debian 11 (“bullseye”) or later. This guide should also work with Ubuntu 18.04 (“Bionic Beaver”) and later. It also assumes that you have administrative rights, either as root or a user with [sudo permissions](https://www.digitalocean.com/community/tutorials/how-to-add-delete-and-grant-sudo-privileges-to-users-on-a-debian-vps). If you want to run this guide with root, ignore the `sudo` at the beginning of the lines, unless it calls a user like `sudo -Hu pleroma`; in this case, use `su <username> -s $SHELL -c 'command'` instead.
|
||||
This guide will assume you are on Debian Stretch. This guide should also work with Ubuntu 16.04 and 18.04. It also assumes that you have administrative rights, either as root or a user with [sudo permissions](https://www.digitalocean.com/community/tutorials/how-to-add-delete-and-grant-sudo-privileges-to-users-on-a-debian-vps). If you want to run this guide with root, ignore the `sudo` at the beginning of the lines, unless it calls a user like `sudo -Hu pleroma`; in this case, use `su <username> -s $SHELL -c 'command'` instead.
|
||||
|
||||
{! backend/installation/generic_dependencies.include !}
|
||||
### Required packages
|
||||
|
||||
* `postgresql` (9.6+, Ubuntu 16.04 comes with 9.5, you can get a newer version from [here](https://www.postgresql.org/download/linux/ubuntu/))
|
||||
* `postgresql-contrib` (9.6+, same situtation as above)
|
||||
* `elixir` (1.8+, Follow the guide to install from the Erlang Solutions repo or use [asdf](https://github.com/asdf-vm/asdf) as the pleroma user)
|
||||
* `erlang-dev`
|
||||
* `erlang-nox`
|
||||
* `libmagic-dev`
|
||||
* `git`
|
||||
* `build-essential`
|
||||
* `cmake`
|
||||
|
||||
#### Optional packages used in this guide
|
||||
|
||||
* `nginx` (preferred, example configs for other reverse proxies can be found in the repo)
|
||||
* `certbot` (or any other ACME client for Let’s Encrypt certificates)
|
||||
* `ImageMagick`
|
||||
* `ffmpeg`
|
||||
* `exiftool`
|
||||
|
||||
### Prepare the system
|
||||
|
||||
@ -25,14 +40,20 @@ sudo apt install git build-essential postgresql postgresql-contrib cmake libmagi
|
||||
|
||||
### Install Elixir and Erlang
|
||||
|
||||
* Install Elixir and Erlang (you might need to use backports or [asdf](https://github.com/asdf-vm/asdf) on old systems):
|
||||
* Download and add the Erlang repository:
|
||||
|
||||
```shell
|
||||
wget -P /tmp/ https://packages.erlang-solutions.com/erlang-solutions_2.0_all.deb
|
||||
sudo dpkg -i /tmp/erlang-solutions_2.0_all.deb
|
||||
```
|
||||
|
||||
* Install Elixir and Erlang:
|
||||
|
||||
```shell
|
||||
sudo apt update
|
||||
sudo apt install elixir erlang-dev erlang-nox
|
||||
```
|
||||
|
||||
|
||||
### Optional packages: [`docs/installation/optional/media_graphics_packages.md`](../installation/optional/media_graphics_packages.md)
|
||||
|
||||
```shell
|
||||
|
@ -2,9 +2,7 @@
|
||||
|
||||
This document was written for FreeBSD 12.1, but should be work on future releases.
|
||||
|
||||
{! backend/installation/generic_dependencies.include !}
|
||||
|
||||
## Installing software used in this guide
|
||||
## Required software
|
||||
|
||||
This assumes the target system has `pkg(8)`.
|
||||
|
||||
|
@ -1,16 +0,0 @@
|
||||
## Required dependencies
|
||||
|
||||
* PostgreSQL 9.6+
|
||||
* Elixir 1.9+
|
||||
* Erlang OTP 22.2+
|
||||
* git
|
||||
* file / libmagic
|
||||
* gcc (clang might also work)
|
||||
* GNU make
|
||||
* CMake
|
||||
|
||||
## Optionnal dependencies
|
||||
|
||||
* ImageMagick
|
||||
* FFmpeg
|
||||
* exiftool
|
@ -1,12 +1,11 @@
|
||||
# Installing on Gentoo GNU/Linux
|
||||
|
||||
{! backend/installation/otp_vs_from_source_source.include !}
|
||||
|
||||
## Installation
|
||||
|
||||
This guide will assume that you have administrative rights, either as root or a user with [sudo permissions](https://wiki.gentoo.org/wiki/Sudo). Lines that begin with `#` indicate that they should be run as the superuser. Lines using `$` should be run as the indicated user, e.g. `pleroma$` should be run as the `pleroma` user.
|
||||
|
||||
{! backend/installation/generic_dependencies.include !}
|
||||
### Configuring your hostname (optional)
|
||||
|
||||
If you would like your prompt to permanently include your host/domain, change `/etc/conf.d/hostname` to your hostname. You can reboot or use the `hostname` command to make immediate changes.
|
||||
|
||||
### Your make.conf, package.use, and USE flags
|
||||
|
||||
|
@ -1,8 +1,7 @@
|
||||
# Switching a from-source install to OTP releases
|
||||
|
||||
{! backend/installation/otp_vs_from_source.include !}
|
||||
|
||||
In this guide we cover how you can migrate from a from source installation to one using OTP releases.
|
||||
## What are OTP releases?
|
||||
OTP releases are as close as you can get to binary releases with Erlang/Elixir. The release is self-contained, and provides everything needed to boot it, it is easily administered via the provided shell script to open up a remote console, start/stop/restart the release, start in the background, send remote commands, and more.
|
||||
|
||||
## Pre-requisites
|
||||
You will be running commands as root. If you aren't root already, please elevate your priviledges by executing `sudo su`/`su`.
|
||||
|
@ -1,8 +1,6 @@
|
||||
# Installing on NetBSD
|
||||
|
||||
{! backend/installation/generic_dependencies.include !}
|
||||
|
||||
## Installing software used in this guide
|
||||
## Required software
|
||||
|
||||
pkgin should have been installed by the NetBSD installer if you selected
|
||||
the right options. If it isn't installed, install it using pkg_add.
|
||||
|
@ -4,11 +4,19 @@ This guide describes the installation and configuration of pleroma (and the requ
|
||||
|
||||
For any additional information regarding commands and configuration files mentioned here, check the man pages [online](https://man.openbsd.org/) or directly on your server with the man command.
|
||||
|
||||
{! backend/installation/generic_dependencies.include !}
|
||||
|
||||
### Preparing the system
|
||||
#### Required software
|
||||
|
||||
The following packages need to be installed:
|
||||
|
||||
* elixir
|
||||
* gmake
|
||||
* git
|
||||
* postgresql-server
|
||||
* postgresql-contrib
|
||||
* cmake
|
||||
* ffmpeg
|
||||
* ImageMagick
|
||||
|
||||
To install them, run the following command (with doas or as root):
|
||||
|
||||
```
|
||||
|
@ -1,9 +1,5 @@
|
||||
# Installing on Linux using OTP releases
|
||||
|
||||
{! backend/installation/otp_vs_from_source.include !}
|
||||
|
||||
This guide covers a installation using an OTP release. To install Pleroma from source, please check out the corresponding guide for your distro.
|
||||
|
||||
## Pre-requisites
|
||||
* A machine running Linux with GNU (e.g. Debian, Ubuntu) or musl (e.g. Alpine) libc and `x86_64`, `aarch64` or `armv7l` CPU, you have root access to. If you are not sure if it's compatible see [Detecting flavour section](#detecting-flavour) below
|
||||
* A (sub)domain pointed to the machine
|
||||
@ -35,7 +31,7 @@ Other than things bundled in the OTP release Pleroma depends on:
|
||||
|
||||
=== "Alpine"
|
||||
```
|
||||
awk 'NR==2' /etc/apk/repositories | sed 's/main/community/' | tee -a /etc/apk/repositories
|
||||
echo "http://nl.alpinelinux.org/alpine/latest-stable/community" >> /etc/apk/repositories
|
||||
apk update
|
||||
apk add curl unzip ncurses postgresql postgresql-contrib nginx certbot file-dev
|
||||
```
|
||||
@ -54,6 +50,7 @@ Per [`docs/installation/optional/media_graphics_packages.md`](optional/media_gra
|
||||
|
||||
=== "Alpine"
|
||||
```
|
||||
echo "http://nl.alpinelinux.org/alpine/latest-stable/community" >> /etc/apk/repositories
|
||||
apk update
|
||||
apk add imagemagick ffmpeg exiftool
|
||||
```
|
||||
|
@ -1,3 +0,0 @@
|
||||
## OTP releases vs from-source installations
|
||||
|
||||
There are two ways to install Pleroma. You can use OTP releases or do a from-source installation. OTP releases are as close as you can get to binary releases with Erlang/Elixir. The release is self-contained, and provides everything needed to boot it, it is easily administered via the provided shell script to open up a remote console, start/stop/restart the release, start in the background, send remote commands, and more. With from source installations you install Pleroma from source, meaning you have to install certain dependencies like Erlang+Elixir and compile Pleroma yourself.
|
@ -1,3 +0,0 @@
|
||||
{! backend/installation/otp_vs_from_source.include !}
|
||||
|
||||
This guide covers a from-source installation. To install using OTP releases, please check out [the OTP guide](./otp_en.md).
|
15
installation/nsfw-api.service
Normal file
15
installation/nsfw-api.service
Normal file
@ -0,0 +1,15 @@
|
||||
[Unit]
|
||||
Description=NSFW API
|
||||
After=docker.service
|
||||
Requires=docker.service
|
||||
|
||||
[Service]
|
||||
TimeoutStartSec=0
|
||||
Restart=always
|
||||
ExecStartPre=-/usr/bin/docker stop %n
|
||||
ExecStartPre=-/usr/bin/docker rm %n
|
||||
ExecStartPre=/usr/bin/docker pull eugencepoi/nsfw_api:latest
|
||||
ExecStart=/usr/bin/docker run --rm -p 127.0.0.1:5000:5000/tcp --env PORT=5000 --name %n eugencepoi/nsfw_api:latest
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
@ -209,9 +209,7 @@ defmodule Mix.Tasks.Pleroma.Database do
|
||||
new.fts_content := to_tsvector(new.data->>'content');
|
||||
RETURN new;
|
||||
END
|
||||
$$ LANGUAGE plpgsql",
|
||||
[],
|
||||
timeout: :infinity
|
||||
$$ LANGUAGE plpgsql"
|
||||
)
|
||||
|
||||
shell_info("Refresh RUM index")
|
||||
@ -221,9 +219,7 @@ defmodule Mix.Tasks.Pleroma.Database do
|
||||
|
||||
Ecto.Adapters.SQL.query!(
|
||||
Pleroma.Repo,
|
||||
"CREATE INDEX CONCURRENTLY objects_fts ON objects USING gin(to_tsvector('#{tsconfig}', data->>'content')); ",
|
||||
[],
|
||||
timeout: :infinity
|
||||
"CREATE INDEX objects_fts ON objects USING gin(to_tsvector('#{tsconfig}', data->>'content')); "
|
||||
)
|
||||
end
|
||||
|
||||
|
@ -292,8 +292,7 @@ defmodule Pleroma.Activity do
|
||||
get_in_reply_to_activity_from_object(Object.normalize(activity, fetch: false))
|
||||
end
|
||||
|
||||
def normalize(%Activity{data: %{"id" => ap_id}}), do: get_by_ap_id_with_object(ap_id)
|
||||
def normalize(%{"id" => ap_id}), do: get_by_ap_id_with_object(ap_id)
|
||||
def normalize(obj) when is_map(obj), do: get_by_ap_id_with_object(obj["id"])
|
||||
def normalize(ap_id) when is_binary(ap_id), do: get_by_ap_id_with_object(ap_id)
|
||||
def normalize(_), do: nil
|
||||
|
||||
@ -314,15 +313,13 @@ defmodule Pleroma.Activity do
|
||||
|
||||
def delete_all_by_object_ap_id(_), do: nil
|
||||
|
||||
defp purge_web_resp_cache(%Activity{data: %{"id" => id}} = activity) when is_binary(id) do
|
||||
with %{path: path} <- URI.parse(id) do
|
||||
@cachex.del(:web_resp_cache, path)
|
||||
end
|
||||
|
||||
defp purge_web_resp_cache(%Activity{} = activity) do
|
||||
%{path: path} = URI.parse(activity.data["id"])
|
||||
@cachex.del(:web_resp_cache, path)
|
||||
activity
|
||||
end
|
||||
|
||||
defp purge_web_resp_cache(activity), do: activity
|
||||
defp purge_web_resp_cache(nil), do: nil
|
||||
|
||||
def follow_accepted?(
|
||||
%Activity{data: %{"type" => "Follow", "object" => followed_ap_id}} = activity
|
||||
|
@ -26,23 +26,19 @@ defmodule Pleroma.Activity.Search do
|
||||
:plain
|
||||
end
|
||||
|
||||
try do
|
||||
Activity
|
||||
|> Activity.with_preloaded_object()
|
||||
|> Activity.restrict_deactivated_users()
|
||||
|> restrict_public()
|
||||
|> query_with(index_type, search_query, search_function)
|
||||
|> maybe_restrict_local(user)
|
||||
|> maybe_restrict_author(author)
|
||||
|> maybe_restrict_blocked(user)
|
||||
|> Pagination.fetch_paginated(
|
||||
%{"offset" => offset, "limit" => limit, "skip_order" => index_type == :rum},
|
||||
:offset
|
||||
)
|
||||
|> maybe_fetch(user, search_query)
|
||||
rescue
|
||||
_ -> maybe_fetch([], user, search_query)
|
||||
end
|
||||
Activity
|
||||
|> Activity.with_preloaded_object()
|
||||
|> Activity.restrict_deactivated_users()
|
||||
|> restrict_public()
|
||||
|> query_with(index_type, search_query, search_function)
|
||||
|> maybe_restrict_local(user)
|
||||
|> maybe_restrict_author(author)
|
||||
|> maybe_restrict_blocked(user)
|
||||
|> Pagination.fetch_paginated(
|
||||
%{"offset" => offset, "limit" => limit, "skip_order" => index_type == :rum},
|
||||
:offset
|
||||
)
|
||||
|> maybe_fetch(user, search_query)
|
||||
end
|
||||
|
||||
def maybe_restrict_author(query, %User{} = author) do
|
||||
@ -65,17 +61,10 @@ defmodule Pleroma.Activity.Search do
|
||||
end
|
||||
|
||||
defp query_with(q, :gin, search_query, :plain) do
|
||||
%{rows: [[tsc]]} =
|
||||
Ecto.Adapters.SQL.query!(
|
||||
Pleroma.Repo,
|
||||
"select current_setting('default_text_search_config')::regconfig::oid;"
|
||||
)
|
||||
|
||||
from([a, o] in q,
|
||||
where:
|
||||
fragment(
|
||||
"to_tsvector(?::oid::regconfig, ?->>'content') @@ plainto_tsquery(?)",
|
||||
^tsc,
|
||||
"to_tsvector(?->>'content') @@ plainto_tsquery(?)",
|
||||
o.data,
|
||||
^search_query
|
||||
)
|
||||
@ -83,17 +72,10 @@ defmodule Pleroma.Activity.Search do
|
||||
end
|
||||
|
||||
defp query_with(q, :gin, search_query, :websearch) do
|
||||
%{rows: [[tsc]]} =
|
||||
Ecto.Adapters.SQL.query!(
|
||||
Pleroma.Repo,
|
||||
"select current_setting('default_text_search_config')::regconfig::oid;"
|
||||
)
|
||||
|
||||
from([a, o] in q,
|
||||
where:
|
||||
fragment(
|
||||
"to_tsvector(?::oid::regconfig, ?->>'content') @@ websearch_to_tsquery(?)",
|
||||
^tsc,
|
||||
"to_tsvector(?->>'content') @@ websearch_to_tsquery(?)",
|
||||
o.data,
|
||||
^search_query
|
||||
)
|
||||
|
@ -34,7 +34,7 @@ defmodule Pleroma.MFA.TOTP do
|
||||
defp default_digits, do: Config.get(@config_ns ++ [:digits])
|
||||
|
||||
defp default_issuer,
|
||||
do: Config.get(@config_ns ++ [:issuer], Config.get([:instance, :host]))
|
||||
do: Config.get(@config_ns ++ [:issuer], Config.get([:instance, :name]))
|
||||
|
||||
@doc "Creates a random Base 32 encoded string"
|
||||
def generate_secret do
|
||||
|
@ -8,6 +8,8 @@ defmodule Pleroma.Repo do
|
||||
adapter: Ecto.Adapters.Postgres,
|
||||
migration_timestamps: [type: :naive_datetime_usec]
|
||||
|
||||
use Ecto.Explain
|
||||
|
||||
import Ecto.Query
|
||||
require Logger
|
||||
|
||||
|
@ -1695,6 +1695,8 @@ defmodule Pleroma.User do
|
||||
email: nil,
|
||||
name: nil,
|
||||
password_hash: nil,
|
||||
keys: nil,
|
||||
public_key: nil,
|
||||
avatar: %{},
|
||||
tags: [],
|
||||
last_refreshed_at: nil,
|
||||
@ -1705,7 +1707,9 @@ defmodule Pleroma.User do
|
||||
follower_count: 0,
|
||||
following_count: 0,
|
||||
is_locked: false,
|
||||
is_confirmed: true,
|
||||
password_reset_pending: false,
|
||||
is_approved: true,
|
||||
registration_reason: nil,
|
||||
confirmation_token: nil,
|
||||
domain_blocks: [],
|
||||
@ -1721,53 +1725,45 @@ defmodule Pleroma.User do
|
||||
raw_fields: [],
|
||||
is_discoverable: false,
|
||||
also_known_as: []
|
||||
# id: preserved
|
||||
# ap_id: preserved
|
||||
# nickname: preserved
|
||||
})
|
||||
end
|
||||
|
||||
# Purge doesn't delete the user from the database.
|
||||
# It just nulls all its fields and deactivates it.
|
||||
# See `User.purge_user_changeset/1` above.
|
||||
defp purge(%User{} = user) do
|
||||
user
|
||||
|> purge_user_changeset()
|
||||
|> update_and_set_cache()
|
||||
end
|
||||
|
||||
def delete(users) when is_list(users) do
|
||||
for user <- users, do: delete(user)
|
||||
end
|
||||
|
||||
def delete(%User{} = user) do
|
||||
# Purge the user immediately
|
||||
purge(user)
|
||||
BackgroundWorker.enqueue("delete_user", %{"user_id" => user.id})
|
||||
end
|
||||
|
||||
# *Actually* delete the user from the DB
|
||||
defp delete_from_db(%User{} = user) do
|
||||
defp delete_and_invalidate_cache(%User{} = user) do
|
||||
invalidate_cache(user)
|
||||
Repo.delete(user)
|
||||
end
|
||||
|
||||
# If the user never finalized their account, it's safe to delete them.
|
||||
defp maybe_delete_from_db(%User{local: true, is_confirmed: false} = user),
|
||||
do: delete_from_db(user)
|
||||
defp delete_or_deactivate(%User{local: false} = user), do: delete_and_invalidate_cache(user)
|
||||
|
||||
defp maybe_delete_from_db(%User{local: true, is_approved: false} = user),
|
||||
do: delete_from_db(user)
|
||||
defp delete_or_deactivate(%User{local: true} = user) do
|
||||
status = account_status(user)
|
||||
|
||||
defp maybe_delete_from_db(user), do: {:ok, user}
|
||||
case status do
|
||||
:confirmation_pending ->
|
||||
delete_and_invalidate_cache(user)
|
||||
|
||||
:approval_pending ->
|
||||
delete_and_invalidate_cache(user)
|
||||
|
||||
_ ->
|
||||
user
|
||||
|> purge_user_changeset()
|
||||
|> update_and_set_cache()
|
||||
end
|
||||
end
|
||||
|
||||
def perform(:force_password_reset, user), do: force_password_reset(user)
|
||||
|
||||
@spec perform(atom(), User.t()) :: {:ok, User.t()}
|
||||
def perform(:delete, %User{} = user) do
|
||||
# Purge the user again, in case perform/2 is called directly
|
||||
purge(user)
|
||||
|
||||
# Remove all relationships
|
||||
user
|
||||
|> get_followers()
|
||||
@ -1785,9 +1781,10 @@ defmodule Pleroma.User do
|
||||
|
||||
delete_user_activities(user)
|
||||
delete_notifications_from_user_activities(user)
|
||||
|
||||
delete_outgoing_pending_follow_requests(user)
|
||||
|
||||
maybe_delete_from_db(user)
|
||||
delete_or_deactivate(user)
|
||||
end
|
||||
|
||||
def perform(:set_activation_async, user, status), do: set_activation(user, status)
|
||||
|
@ -53,18 +53,15 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
||||
{recipients, to, cc}
|
||||
end
|
||||
|
||||
defp check_actor_can_insert(%{"type" => "Delete"}), do: true
|
||||
defp check_actor_can_insert(%{"type" => "Undo"}), do: true
|
||||
defp check_actor_is_active(nil), do: true
|
||||
|
||||
defp check_actor_can_insert(%{"actor" => actor}) when is_binary(actor) do
|
||||
defp check_actor_is_active(actor) when is_binary(actor) do
|
||||
case User.get_cached_by_ap_id(actor) do
|
||||
%User{is_active: true} -> true
|
||||
_ -> false
|
||||
end
|
||||
end
|
||||
|
||||
defp check_actor_can_insert(_), do: true
|
||||
|
||||
defp check_remote_limit(%{"object" => %{"content" => content}}) when not is_nil(content) do
|
||||
limit = Config.get([:instance, :remote_limit])
|
||||
String.length(content) <= limit
|
||||
@ -91,7 +88,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
||||
|
||||
defp increase_replies_count_if_reply(_create_data), do: :noop
|
||||
|
||||
@object_types ~w[ChatMessage Question Answer Audio Video Event Article Note Page]
|
||||
@object_types ~w[ChatMessage Question Answer Audio Video Event Article Note]
|
||||
@impl true
|
||||
def persist(%{"type" => type} = object, meta) when type in @object_types do
|
||||
with {:ok, object} <- Object.create(object) do
|
||||
@ -120,7 +117,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
||||
def insert(map, local \\ true, fake \\ false, bypass_actor_check \\ false) when is_map(map) do
|
||||
with nil <- Activity.normalize(map),
|
||||
map <- lazy_put_activity_defaults(map, fake),
|
||||
{_, true} <- {:actor_check, bypass_actor_check || check_actor_can_insert(map)},
|
||||
{_, true} <- {:actor_check, bypass_actor_check || check_actor_is_active(map["actor"])},
|
||||
{_, true} <- {:remote_limit_pass, check_remote_limit(map)},
|
||||
{:ok, map} <- MRF.filter(map),
|
||||
{recipients, _, _} = get_recipients(map),
|
||||
|
@ -11,6 +11,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|
||||
alias Pleroma.Object.Fetcher
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||
alias Pleroma.Web.ActivityPub.Builder
|
||||
alias Pleroma.Web.ActivityPub.InternalFetchActor
|
||||
alias Pleroma.Web.ActivityPub.ObjectView
|
||||
alias Pleroma.Web.ActivityPub.Pipeline
|
||||
@ -402,90 +403,83 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|
||||
|> json(err)
|
||||
end
|
||||
|
||||
defp fix_user_message(%User{ap_id: actor}, %{"type" => "Create", "object" => object} = activity)
|
||||
when is_map(object) do
|
||||
length =
|
||||
[object["content"], object["summary"], object["name"]]
|
||||
|> Enum.filter(&is_binary(&1))
|
||||
|> Enum.join("")
|
||||
|> String.length()
|
||||
defp handle_user_activity(
|
||||
%User{} = user,
|
||||
%{"type" => "Create", "object" => %{"type" => "Note"} = object} = params
|
||||
) do
|
||||
content = if is_binary(object["content"]), do: object["content"], else: ""
|
||||
name = if is_binary(object["name"]), do: object["name"], else: ""
|
||||
summary = if is_binary(object["summary"]), do: object["summary"], else: ""
|
||||
length = String.length(content <> name <> summary)
|
||||
|
||||
limit = Pleroma.Config.get([:instance, :limit])
|
||||
|
||||
if length < limit do
|
||||
if length > Pleroma.Config.get([:instance, :limit]) do
|
||||
{:error, dgettext("errors", "Note is over the character limit")}
|
||||
else
|
||||
object =
|
||||
object
|
||||
|> Transmogrifier.strip_internal_fields()
|
||||
|> Map.put("attributedTo", actor)
|
||||
|> Map.put("actor", actor)
|
||||
|> Map.put("id", Utils.generate_object_id())
|
||||
|> Map.merge(Map.take(params, ["to", "cc"]))
|
||||
|> Map.put("attributedTo", user.ap_id)
|
||||
|> Transmogrifier.fix_object()
|
||||
|
||||
{:ok, Map.put(activity, "object", object)}
|
||||
else
|
||||
{:error,
|
||||
dgettext(
|
||||
"errors",
|
||||
"Character limit (%{limit} characters) exceeded, contains %{length} characters",
|
||||
limit: limit,
|
||||
length: length
|
||||
)}
|
||||
ActivityPub.create(%{
|
||||
to: params["to"],
|
||||
actor: user,
|
||||
context: object["context"],
|
||||
object: object,
|
||||
additional: Map.take(params, ["cc"])
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
defp fix_user_message(
|
||||
%User{ap_id: actor} = user,
|
||||
%{"type" => "Delete", "object" => object} = activity
|
||||
) do
|
||||
with {_, %Object{data: object_data}} <- {:normalize, Object.normalize(object, fetch: false)},
|
||||
{_, true} <- {:permission, user.is_moderator || actor == object_data["actor"]} do
|
||||
defp handle_user_activity(%User{} = user, %{"type" => "Delete"} = params) do
|
||||
with %Object{} = object <- Object.normalize(params["object"], fetch: false),
|
||||
true <- user.is_moderator || user.ap_id == object.data["actor"],
|
||||
{:ok, delete_data, _} <- Builder.delete(user, object.data["id"]),
|
||||
{:ok, delete, _} <- Pipeline.common_pipeline(delete_data, local: true) do
|
||||
{:ok, delete}
|
||||
else
|
||||
_ -> {:error, dgettext("errors", "Can't delete object")}
|
||||
end
|
||||
end
|
||||
|
||||
defp handle_user_activity(%User{} = user, %{"type" => "Like"} = params) do
|
||||
with %Object{} = object <- Object.normalize(params["object"], fetch: false),
|
||||
{_, {:ok, like_object, meta}} <- {:build_object, Builder.like(user, object)},
|
||||
{_, {:ok, %Activity{} = activity, _meta}} <-
|
||||
{:common_pipeline,
|
||||
Pipeline.common_pipeline(like_object, Keyword.put(meta, :local, true))} do
|
||||
{:ok, activity}
|
||||
else
|
||||
{:normalize, _} ->
|
||||
{:error, "No such object found"}
|
||||
|
||||
{:permission, _} ->
|
||||
{:forbidden, "You can't delete this object"}
|
||||
_ -> {:error, dgettext("errors", "Can't like object")}
|
||||
end
|
||||
end
|
||||
|
||||
defp fix_user_message(%User{}, activity) do
|
||||
{:ok, activity}
|
||||
defp handle_user_activity(_, _) do
|
||||
{:error, dgettext("errors", "Unhandled activity type")}
|
||||
end
|
||||
|
||||
def update_outbox(
|
||||
%{assigns: %{user: %User{nickname: nickname, ap_id: actor} = user}} = conn,
|
||||
%{assigns: %{user: %User{nickname: nickname} = user}} = conn,
|
||||
%{"nickname" => nickname} = params
|
||||
) do
|
||||
actor = user.ap_id
|
||||
|
||||
params =
|
||||
params
|
||||
|> Map.drop(["nickname"])
|
||||
|> Map.put("id", Utils.generate_activity_id())
|
||||
|> Map.drop(["id"])
|
||||
|> Map.put("actor", actor)
|
||||
|> Transmogrifier.fix_addressing()
|
||||
|
||||
with {:ok, params} <- fix_user_message(user, params),
|
||||
{:ok, activity, _} <- Pipeline.common_pipeline(params, local: true),
|
||||
%Activity{data: activity_data} <- Activity.normalize(activity) do
|
||||
with {:ok, %Activity{} = activity} <- handle_user_activity(user, params) do
|
||||
conn
|
||||
|> put_status(:created)
|
||||
|> put_resp_header("location", activity_data["id"])
|
||||
|> json(activity_data)
|
||||
|> put_resp_header("location", activity.data["id"])
|
||||
|> json(activity.data)
|
||||
else
|
||||
{:forbidden, message} ->
|
||||
conn
|
||||
|> put_status(:forbidden)
|
||||
|> json(message)
|
||||
|
||||
{:error, message} ->
|
||||
conn
|
||||
|> put_status(:bad_request)
|
||||
|> json(message)
|
||||
|
||||
e ->
|
||||
Logger.warn(fn -> "AP C2S: #{inspect(e)}" end)
|
||||
|
||||
conn
|
||||
|> put_status(:bad_request)
|
||||
|> json("Bad Request")
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -21,7 +21,7 @@ defmodule Pleroma.Web.ActivityPub.MRF do
|
||||
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: {:list_behaviour_implementations, Pleroma.Web.ActivityPub.MRF.Policy}
|
||||
suggestions: {:list_behaviour_implementations, Pleroma.Web.ActivityPub.MRF}
|
||||
},
|
||||
%{
|
||||
key: :transparency,
|
||||
|
265
lib/pleroma/web/activity_pub/mrf/nsfw_api_policy.ex
Normal file
265
lib/pleroma/web/activity_pub/mrf/nsfw_api_policy.ex
Normal file
@ -0,0 +1,265 @@
|
||||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.ActivityPub.MRF.NsfwApiPolicy do
|
||||
@moduledoc """
|
||||
Hide, delete, or mark sensitive NSFW content with artificial intelligence.
|
||||
|
||||
Requires a NSFW API server, configured like so:
|
||||
|
||||
config :pleroma, Pleroma.Web.ActivityPub.MRF.NsfwMRF,
|
||||
url: "http://127.0.0.1:5000/",
|
||||
threshold: 0.7,
|
||||
mark_sensitive: true,
|
||||
unlist: false,
|
||||
reject: false
|
||||
|
||||
The NSFW API server must implement an HTTP endpoint like this:
|
||||
|
||||
curl http://localhost:5000/?url=https://fedi.com/images/001.jpg
|
||||
|
||||
Returning a response like this:
|
||||
|
||||
{"score", 0.314}
|
||||
|
||||
Where a score is 0-1, with `1` being definitely NSFW.
|
||||
|
||||
A good API server is here: https://github.com/EugenCepoi/nsfw_api
|
||||
You can run it with Docker with a one-liner:
|
||||
|
||||
docker run -it -p 127.0.0.1:5000:5000/tcp --env PORT=5000 eugencepoi/nsfw_api:latest
|
||||
|
||||
Options:
|
||||
|
||||
- `url`: Base URL of the API server. Default: "http://127.0.0.1:5000/"
|
||||
- `threshold`: Lowest score to take action on. Default: `0.7`
|
||||
- `mark_sensitive`: Mark sensitive all detected NSFW content? Default: `true`
|
||||
- `unlist`: Unlist all detected NSFW content? Default: `false`
|
||||
- `reject`: Reject all detected NSFW content (takes precedence)? Default: `false`
|
||||
"""
|
||||
alias Pleroma.Config
|
||||
alias Pleroma.Constants
|
||||
alias Pleroma.HTTP
|
||||
alias Pleroma.User
|
||||
|
||||
require Logger
|
||||
require Pleroma.Constants
|
||||
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF.Policy
|
||||
@policy :mrf_nsfw_api
|
||||
|
||||
def build_request_url(url) do
|
||||
Config.get([@policy, :url])
|
||||
|> URI.parse()
|
||||
|> fix_path()
|
||||
|> Map.put(:query, "url=#{url}")
|
||||
|> URI.to_string()
|
||||
end
|
||||
|
||||
def parse_url(url) do
|
||||
request = build_request_url(url)
|
||||
|
||||
with {:ok, %Tesla.Env{body: body}} <- HTTP.get(request) do
|
||||
Jason.decode(body)
|
||||
else
|
||||
error ->
|
||||
Logger.warn("""
|
||||
[NsfwApiPolicy]: The API server failed. Skipping.
|
||||
#{inspect(error)}
|
||||
""")
|
||||
|
||||
error
|
||||
end
|
||||
end
|
||||
|
||||
def check_url_nsfw(url) when is_binary(url) do
|
||||
threshold = Config.get([@policy, :threshold])
|
||||
|
||||
case parse_url(url) do
|
||||
{:ok, %{"score" => score}} when score >= threshold ->
|
||||
{:nsfw, %{url: url, score: score, threshold: threshold}}
|
||||
|
||||
{:ok, %{"score" => score}} ->
|
||||
{:sfw, %{url: url, score: score, threshold: threshold}}
|
||||
|
||||
_ ->
|
||||
{:sfw, %{url: url, score: nil, threshold: threshold}}
|
||||
end
|
||||
end
|
||||
|
||||
def check_url_nsfw(%{"href" => url}) when is_binary(url) do
|
||||
check_url_nsfw(url)
|
||||
end
|
||||
|
||||
def check_url_nsfw(url) do
|
||||
threshold = Config.get([@policy, :threshold])
|
||||
{:sfw, %{url: url, score: nil, threshold: threshold}}
|
||||
end
|
||||
|
||||
def check_attachment_nsfw(%{"url" => urls} = attachment) when is_list(urls) do
|
||||
if Enum.all?(urls, &match?({:sfw, _}, check_url_nsfw(&1))) do
|
||||
{:sfw, attachment}
|
||||
else
|
||||
{:nsfw, attachment}
|
||||
end
|
||||
end
|
||||
|
||||
def check_attachment_nsfw(%{"url" => url} = attachment) when is_binary(url) do
|
||||
case check_url_nsfw(url) do
|
||||
{:sfw, _} -> {:sfw, attachment}
|
||||
{:nsfw, _} -> {:nsfw, attachment}
|
||||
end
|
||||
end
|
||||
|
||||
def check_attachment_nsfw(attachment), do: {:sfw, attachment}
|
||||
|
||||
def check_object_nsfw(%{"attachment" => attachments} = object) when is_list(attachments) do
|
||||
if Enum.all?(attachments, &match?({:sfw, _}, check_attachment_nsfw(&1))) do
|
||||
{:sfw, object}
|
||||
else
|
||||
{:nsfw, object}
|
||||
end
|
||||
end
|
||||
|
||||
def check_object_nsfw(%{"object" => %{} = child_object} = object) do
|
||||
case check_object_nsfw(child_object) do
|
||||
{:sfw, _} -> {:sfw, object}
|
||||
{:nsfw, _} -> {:nsfw, object}
|
||||
end
|
||||
end
|
||||
|
||||
def check_object_nsfw(object), do: {:sfw, object}
|
||||
|
||||
@impl true
|
||||
def filter(object) do
|
||||
with {:sfw, object} <- check_object_nsfw(object) do
|
||||
{:ok, object}
|
||||
else
|
||||
{:nsfw, _data} -> handle_nsfw(object)
|
||||
_ -> {:reject, "NSFW: Attachment rejected"}
|
||||
end
|
||||
end
|
||||
|
||||
defp handle_nsfw(object) do
|
||||
if Config.get([@policy, :reject]) do
|
||||
{:reject, object}
|
||||
else
|
||||
{:ok,
|
||||
object
|
||||
|> maybe_unlist()
|
||||
|> maybe_mark_sensitive()}
|
||||
end
|
||||
end
|
||||
|
||||
defp maybe_unlist(object) do
|
||||
if Config.get([@policy, :unlist]) do
|
||||
unlist(object)
|
||||
else
|
||||
object
|
||||
end
|
||||
end
|
||||
|
||||
defp maybe_mark_sensitive(object) do
|
||||
if Config.get([@policy, :mark_sensitive]) do
|
||||
mark_sensitive(object)
|
||||
else
|
||||
object
|
||||
end
|
||||
end
|
||||
|
||||
def unlist(%{"to" => to, "cc" => cc, "actor" => actor} = object) do
|
||||
with %User{} = user <- User.get_cached_by_ap_id(actor) do
|
||||
to =
|
||||
[user.follower_address | to]
|
||||
|> List.delete(Constants.as_public())
|
||||
|> Enum.uniq()
|
||||
|
||||
cc =
|
||||
[Constants.as_public() | cc]
|
||||
|> List.delete(user.follower_address)
|
||||
|> Enum.uniq()
|
||||
|
||||
object
|
||||
|> Map.put("to", to)
|
||||
|> Map.put("cc", cc)
|
||||
else
|
||||
_ -> raise "[NsfwApiPolicy]: Could not find user #{actor}"
|
||||
end
|
||||
end
|
||||
|
||||
def mark_sensitive(%{"object" => child_object} = object) when is_map(child_object) do
|
||||
Map.put(object, "object", mark_sensitive(child_object))
|
||||
end
|
||||
|
||||
def mark_sensitive(object) when is_map(object) do
|
||||
tags = (object["tag"] || []) ++ ["nsfw"]
|
||||
|
||||
object
|
||||
|> Map.put("tag", tags)
|
||||
|> Map.put("sensitive", true)
|
||||
end
|
||||
|
||||
# Hackney needs a trailing slash
|
||||
defp fix_path(%URI{path: path} = uri) when is_binary(path) do
|
||||
path = String.trim_trailing(path, "/") <> "/"
|
||||
Map.put(uri, :path, path)
|
||||
end
|
||||
|
||||
defp fix_path(%URI{path: nil} = uri), do: Map.put(uri, :path, "/")
|
||||
|
||||
@impl true
|
||||
def describe do
|
||||
options = %{
|
||||
threshold: Config.get([@policy, :threshold]),
|
||||
mark_sensitive: Config.get([@policy, :mark_sensitive]),
|
||||
unlist: Config.get([@policy, :unlist]),
|
||||
reject: Config.get([@policy, :reject])
|
||||
}
|
||||
|
||||
{:ok, %{@policy => options}}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def config_description do
|
||||
%{
|
||||
key: @policy,
|
||||
related_policy: to_string(__MODULE__),
|
||||
label: "NSFW API Policy",
|
||||
description:
|
||||
"Hide, delete, or mark sensitive NSFW content with artificial intelligence. Requires running an external API server.",
|
||||
children: [
|
||||
%{
|
||||
key: :url,
|
||||
type: :string,
|
||||
description: "Base URL of the API server.",
|
||||
suggestions: ["http://127.0.0.1:5000/"]
|
||||
},
|
||||
%{
|
||||
key: :threshold,
|
||||
type: :float,
|
||||
description: "Lowest score to take action on. Between 0 and 1.",
|
||||
suggestions: [0.7]
|
||||
},
|
||||
%{
|
||||
key: :mark_sensitive,
|
||||
type: :boolean,
|
||||
description: "Mark sensitive all detected NSFW content?",
|
||||
suggestions: [true]
|
||||
},
|
||||
%{
|
||||
key: :unlist,
|
||||
type: :boolean,
|
||||
description: "Unlist sensitive all detected NSFW content?",
|
||||
suggestions: [false]
|
||||
},
|
||||
%{
|
||||
key: :reject,
|
||||
type: :boolean,
|
||||
description: "Reject sensitive all detected NSFW content (takes precedence)?",
|
||||
suggestions: [false]
|
||||
}
|
||||
]
|
||||
}
|
||||
end
|
||||
end
|
@ -49,8 +49,6 @@ defmodule Pleroma.Web.ActivityPub.MRF.ObjectAgePolicy do
|
||||
message
|
||||
|> Map.put("to", to)
|
||||
|> Map.put("cc", cc)
|
||||
|> Kernel.put_in(["object", "to"], to)
|
||||
|> Kernel.put_in(["object", "cc"], cc)
|
||||
|
||||
{:ok, message}
|
||||
else
|
||||
@ -72,8 +70,6 @@ defmodule Pleroma.Web.ActivityPub.MRF.ObjectAgePolicy do
|
||||
message
|
||||
|> Map.put("to", to)
|
||||
|> Map.put("cc", cc)
|
||||
|> Kernel.put_in(["object", "to"], to)
|
||||
|> Kernel.put_in(["object", "cc"], cc)
|
||||
|
||||
{:ok, message}
|
||||
else
|
||||
@ -86,7 +82,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.ObjectAgePolicy do
|
||||
end
|
||||
|
||||
@impl true
|
||||
def filter(%{"type" => "Create", "object" => %{"published" => _}} = message) do
|
||||
def filter(%{"type" => "Create", "published" => _} = message) do
|
||||
with actions <- Config.get([:mrf_object_age, :actions]),
|
||||
{:reject, _} <- check_date(message),
|
||||
{:ok, message} <- check_reject(message, actions),
|
||||
|
@ -93,51 +93,6 @@ defmodule Pleroma.Web.ActivityPub.MRF.StealEmojiPolicy do
|
||||
def filter(message), do: {:ok, message}
|
||||
|
||||
@impl true
|
||||
@spec config_description :: %{
|
||||
children: [
|
||||
%{
|
||||
description: <<_::272, _::_*256>>,
|
||||
key: :hosts | :rejected_shortcodes | :size_limit,
|
||||
suggestions: [any(), ...],
|
||||
type: {:list, :string} | {:list, :string} | :integer
|
||||
},
|
||||
...
|
||||
],
|
||||
description: <<_::448>>,
|
||||
key: :mrf_steal_emoji,
|
||||
label: <<_::80>>,
|
||||
related_policy: <<_::352>>
|
||||
}
|
||||
def config_description do
|
||||
%{
|
||||
key: :mrf_steal_emoji,
|
||||
related_policy: "Pleroma.Web.ActivityPub.MRF.StealEmojiPolicy",
|
||||
label: "MRF Emojis",
|
||||
description: "Steals emojis from selected instances when it sees them.",
|
||||
children: [
|
||||
%{
|
||||
key: :hosts,
|
||||
type: {:list, :string},
|
||||
description: "List of hosts to steal emojis from",
|
||||
suggestions: [""]
|
||||
},
|
||||
%{
|
||||
key: :rejected_shortcodes,
|
||||
type: {:list, :string},
|
||||
description: "Regex-list of shortcodes to reject",
|
||||
suggestions: [""]
|
||||
},
|
||||
%{
|
||||
key: :size_limit,
|
||||
type: :integer,
|
||||
description: "File size limit (in bytes), checked before an emoji is saved to the disk",
|
||||
suggestions: ["100000"]
|
||||
}
|
||||
]
|
||||
}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def describe do
|
||||
{:ok, %{}}
|
||||
end
|
||||
|
@ -20,7 +20,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do
|
||||
alias Pleroma.Web.ActivityPub.ObjectValidators.AddRemoveValidator
|
||||
alias Pleroma.Web.ActivityPub.ObjectValidators.AnnounceValidator
|
||||
alias Pleroma.Web.ActivityPub.ObjectValidators.AnswerValidator
|
||||
alias Pleroma.Web.ActivityPub.ObjectValidators.ArticleNotePageValidator
|
||||
alias Pleroma.Web.ActivityPub.ObjectValidators.ArticleNoteValidator
|
||||
alias Pleroma.Web.ActivityPub.ObjectValidators.AudioVideoValidator
|
||||
alias Pleroma.Web.ActivityPub.ObjectValidators.BlockValidator
|
||||
alias Pleroma.Web.ActivityPub.ObjectValidators.ChatMessageValidator
|
||||
@ -102,7 +102,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do
|
||||
%{"type" => "Create", "object" => %{"type" => objtype} = object} = create_activity,
|
||||
meta
|
||||
)
|
||||
when objtype in ~w[Question Answer Audio Video Event Article Note Page] do
|
||||
when objtype in ~w[Question Answer Audio Video Event Article Note] do
|
||||
with {:ok, object_data} <- cast_and_apply(object),
|
||||
meta = Keyword.put(meta, :object_data, object_data |> stringify_keys),
|
||||
{:ok, create_activity} <-
|
||||
@ -115,16 +115,15 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do
|
||||
end
|
||||
|
||||
def validate(%{"type" => type} = object, meta)
|
||||
when type in ~w[Event Question Audio Video Article Note Page] do
|
||||
when type in ~w[Event Question Audio Video Article Note] do
|
||||
validator =
|
||||
case type do
|
||||
"Event" -> EventValidator
|
||||
"Question" -> QuestionValidator
|
||||
"Audio" -> AudioVideoValidator
|
||||
"Video" -> AudioVideoValidator
|
||||
"Article" -> ArticleNotePageValidator
|
||||
"Note" -> ArticleNotePageValidator
|
||||
"Page" -> ArticleNotePageValidator
|
||||
"Article" -> ArticleNoteValidator
|
||||
"Note" -> ArticleNoteValidator
|
||||
end
|
||||
|
||||
with {:ok, object} <-
|
||||
@ -176,8 +175,6 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do
|
||||
end
|
||||
end
|
||||
|
||||
def validate(o, m), do: {:error, {:validator_not_set, {o, m}}}
|
||||
|
||||
def cast_and_apply(%{"type" => "ChatMessage"} = object) do
|
||||
ChatMessageValidator.cast_and_apply(object)
|
||||
end
|
||||
@ -198,8 +195,8 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do
|
||||
EventValidator.cast_and_apply(object)
|
||||
end
|
||||
|
||||
def cast_and_apply(%{"type" => type} = object) when type in ~w[Article Note Page] do
|
||||
ArticleNotePageValidator.cast_and_apply(object)
|
||||
def cast_and_apply(%{"type" => type} = object) when type in ~w[Article Note] do
|
||||
ArticleNoteValidator.cast_and_apply(object)
|
||||
end
|
||||
|
||||
def cast_and_apply(o), do: {:error, {:validator_not_set, o}}
|
||||
|
@ -2,7 +2,7 @@
|
||||
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.ActivityPub.ObjectValidators.ArticleNotePageValidator do
|
||||
defmodule Pleroma.Web.ActivityPub.ObjectValidators.ArticleNoteValidator do
|
||||
use Ecto.Schema
|
||||
|
||||
alias Pleroma.EctoType.ActivityPub.ObjectValidators
|
||||
@ -113,7 +113,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.ArticleNotePageValidator do
|
||||
|
||||
defp validate_data(data_cng) do
|
||||
data_cng
|
||||
|> validate_inclusion(:type, ["Article", "Note", "Page"])
|
||||
|> validate_inclusion(:type, ["Article", "Note"])
|
||||
|> validate_required([:id, :actor, :attributedTo, :type, :context, :context_id])
|
||||
|> CommonValidations.validate_any_presence([:cc, :to])
|
||||
|> CommonValidations.validate_fields_match([:actor, :attributedTo])
|
@ -7,7 +7,6 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.DeleteValidator do
|
||||
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.EctoType.ActivityPub.ObjectValidators
|
||||
alias Pleroma.User
|
||||
|
||||
import Ecto.Changeset
|
||||
import Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations
|
||||
@ -58,7 +57,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.DeleteValidator do
|
||||
cng
|
||||
|> validate_required([:id, :type, :actor, :to, :cc, :object])
|
||||
|> validate_inclusion(:type, ["Delete"])
|
||||
|> validate_delete_actor(:actor)
|
||||
|> validate_actor_presence()
|
||||
|> validate_modification_rights()
|
||||
|> validate_object_or_user_presence(allowed_types: @deletable_types)
|
||||
|> add_deleted_activity_id()
|
||||
@ -73,13 +72,4 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.DeleteValidator do
|
||||
|> cast_data
|
||||
|> validate_data
|
||||
end
|
||||
|
||||
defp validate_delete_actor(cng, field_name) do
|
||||
validate_change(cng, field_name, fn field_name, actor ->
|
||||
case User.get_cached_by_ap_id(actor) do
|
||||
%User{} -> []
|
||||
_ -> [{field_name, "can't find user"}]
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
@ -7,7 +7,6 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.UndoValidator do
|
||||
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.EctoType.ActivityPub.ObjectValidators
|
||||
alias Pleroma.User
|
||||
|
||||
import Ecto.Changeset
|
||||
import Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations
|
||||
@ -43,7 +42,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.UndoValidator do
|
||||
data_cng
|
||||
|> validate_inclusion(:type, ["Undo"])
|
||||
|> validate_required([:id, :type, :object, :actor, :to, :cc])
|
||||
|> validate_undo_actor(:actor)
|
||||
|> validate_actor_presence()
|
||||
|> validate_object_presence()
|
||||
|> validate_undo_rights()
|
||||
end
|
||||
@ -60,13 +59,4 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.UndoValidator do
|
||||
_ -> cng
|
||||
end
|
||||
end
|
||||
|
||||
defp validate_undo_actor(cng, field_name) do
|
||||
validate_change(cng, field_name, fn field_name, actor ->
|
||||
case User.get_cached_by_ap_id(actor) do
|
||||
%User{} -> []
|
||||
_ -> [{field_name, "can't find user"}]
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
@ -10,6 +10,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.FollowingRelationship
|
||||
@ -224,8 +225,6 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
|
||||
meta
|
||||
|> add_notifications(notifications)
|
||||
|
||||
ap_streamer().stream_out(activity)
|
||||
|
||||
{:ok, activity, meta}
|
||||
else
|
||||
e -> Repo.rollback(e)
|
||||
@ -246,7 +245,9 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
|
||||
if !User.is_internal_user?(user) do
|
||||
Notification.create_notifications(object)
|
||||
|
||||
ap_streamer().stream_out(object)
|
||||
object
|
||||
|> Topics.get_activity_topics()
|
||||
|> Streamer.stream(object)
|
||||
end
|
||||
|
||||
{:ok, object, meta}
|
||||
@ -436,7 +437,7 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
|
||||
end
|
||||
|
||||
def handle_object_creation(%{"type" => objtype} = object, meta)
|
||||
when objtype in ~w[Audio Video Question Event Article Note Page] do
|
||||
when objtype in ~w[Audio Video Question Event Article Note] do
|
||||
with {:ok, object, meta} <- Pipeline.common_pipeline(object, meta) do
|
||||
{:ok, object, meta}
|
||||
end
|
||||
|
@ -353,6 +353,29 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
||||
end)
|
||||
end
|
||||
|
||||
# Compatibility wrapper for Mastodon votes
|
||||
defp handle_create(%{"object" => %{"type" => "Answer"}} = data, _user) do
|
||||
handle_incoming(data)
|
||||
end
|
||||
|
||||
defp handle_create(%{"object" => object} = data, user) do
|
||||
%{
|
||||
to: data["to"],
|
||||
object: object,
|
||||
actor: user,
|
||||
context: object["context"],
|
||||
local: false,
|
||||
published: data["published"],
|
||||
additional:
|
||||
Map.take(data, [
|
||||
"cc",
|
||||
"directMessage",
|
||||
"id"
|
||||
])
|
||||
}
|
||||
|> ActivityPub.create()
|
||||
end
|
||||
|
||||
def handle_incoming(data, options \\ [])
|
||||
|
||||
# Flag objects are placed ahead of the ID check because Mastodon 2.8 and earlier send them
|
||||
@ -384,6 +407,43 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
||||
def handle_incoming(%{"id" => id}, _options) when is_binary(id) and byte_size(id) < 8,
|
||||
do: :error
|
||||
|
||||
# TODO: validate those with a Ecto scheme
|
||||
# - tags
|
||||
# - emoji
|
||||
def handle_incoming(
|
||||
%{"type" => "Create", "object" => %{"type" => "Page"} = object} = data,
|
||||
options
|
||||
) do
|
||||
actor = Containment.get_actor(data)
|
||||
|
||||
with nil <- Activity.get_create_by_object_ap_id(object["id"]),
|
||||
{:ok, %User{} = user} <- User.get_or_fetch_by_ap_id(actor) do
|
||||
data =
|
||||
data
|
||||
|> Map.put("object", fix_object(object, options))
|
||||
|> Map.put("actor", actor)
|
||||
|> fix_addressing()
|
||||
|
||||
with {:ok, created_activity} <- handle_create(data, user) do
|
||||
reply_depth = (options[:depth] || 0) + 1
|
||||
|
||||
if Federator.allowed_thread_distance?(reply_depth) do
|
||||
for reply_id <- replies(object) do
|
||||
Pleroma.Workers.RemoteFetcherWorker.enqueue("fetch_remote", %{
|
||||
"id" => reply_id,
|
||||
"depth" => reply_depth
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
{:ok, created_activity}
|
||||
end
|
||||
else
|
||||
%Activity{} = activity -> {:ok, activity}
|
||||
_e -> :error
|
||||
end
|
||||
end
|
||||
|
||||
def handle_incoming(
|
||||
%{"type" => "Listen", "object" => %{"type" => "Audio"} = object} = data,
|
||||
options
|
||||
@ -447,7 +507,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
||||
%{"type" => "Create", "object" => %{"type" => objtype, "id" => obj_id}} = data,
|
||||
options
|
||||
)
|
||||
when objtype in ~w{Question Answer ChatMessage Audio Video Event Article Note Page} do
|
||||
when objtype in ~w{Question Answer ChatMessage Audio Video Event Article Note} do
|
||||
fetch_options = Keyword.put(options, :depth, (options[:depth] || 0) + 1)
|
||||
|
||||
object =
|
||||
|
@ -57,7 +57,6 @@ defmodule Pleroma.Web.ActivityPub.Visibility do
|
||||
def is_list?(_), do: false
|
||||
|
||||
@spec visible_for_user?(Object.t() | Activity.t() | nil, User.t() | nil) :: boolean()
|
||||
def visible_for_user?(%Object{data: %{"type" => "Tombstone"}}, _), do: false
|
||||
def visible_for_user?(%Activity{actor: ap_id}, %User{ap_id: ap_id}), do: true
|
||||
def visible_for_user?(%Object{data: %{"actor" => ap_id}}, %User{ap_id: ap_id}), do: true
|
||||
def visible_for_user?(nil, _), do: false
|
||||
|
@ -35,12 +35,6 @@ defmodule Pleroma.Web.AdminAPI.FrontendController do
|
||||
end
|
||||
|
||||
defp installed do
|
||||
frontend_directory = Pleroma.Frontend.dir()
|
||||
|
||||
if File.exists?(frontend_directory) do
|
||||
File.ls!(frontend_directory)
|
||||
else
|
||||
[]
|
||||
end
|
||||
File.ls!(Pleroma.Frontend.dir())
|
||||
end
|
||||
end
|
||||
|
@ -13,9 +13,7 @@ defmodule Pleroma.Web.AdminAPI.Report do
|
||||
account = User.get_cached_by_ap_id(account_ap_id)
|
||||
|
||||
statuses =
|
||||
status_ap_ids
|
||||
|> Enum.reject(&is_nil(&1))
|
||||
|> Enum.map(fn
|
||||
Enum.map(status_ap_ids, fn
|
||||
act when is_map(act) -> Activity.get_by_ap_id_with_object(act["id"])
|
||||
act when is_binary(act) -> Activity.get_by_ap_id_with_object(act)
|
||||
end)
|
||||
|
@ -17,7 +17,7 @@ defmodule Pleroma.Web.AdminAPI.Search do
|
||||
|> Map.drop([:page, :page_size])
|
||||
|> Map.put(:invisible, false)
|
||||
|> User.Query.build()
|
||||
|> order_by(desc: :id)
|
||||
|> order_by([u], u.nickname)
|
||||
|
||||
paginated_query =
|
||||
User.Query.paginate(query, params[:page] || 1, params[:page_size] || @page_size)
|
||||
|
@ -8,7 +8,6 @@ defmodule Pleroma.Web.AdminAPI.AccountView do
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.AdminAPI
|
||||
alias Pleroma.Web.AdminAPI.AccountView
|
||||
alias Pleroma.Web.CommonAPI
|
||||
alias Pleroma.Web.MastodonAPI
|
||||
alias Pleroma.Web.MediaProxy
|
||||
|
||||
@ -82,8 +81,7 @@ defmodule Pleroma.Web.AdminAPI.AccountView do
|
||||
"is_approved" => user.is_approved,
|
||||
"url" => user.uri || user.ap_id,
|
||||
"registration_reason" => user.registration_reason,
|
||||
"actor_type" => user.actor_type,
|
||||
"created_at" => CommonAPI.Utils.to_masto_date(user.inserted_at)
|
||||
"actor_type" => user.actor_type
|
||||
}
|
||||
end
|
||||
|
||||
|
@ -24,7 +24,6 @@ defmodule Pleroma.Web.ApiSpec.MediaOperation do
|
||||
requestBody: Helpers.request_body("Parameters", create_request()),
|
||||
responses: %{
|
||||
200 => Operation.response("Media", "application/json", Attachment),
|
||||
400 => Operation.response("Media", "application/json", ApiError),
|
||||
401 => Operation.response("Media", "application/json", ApiError),
|
||||
422 => Operation.response("Media", "application/json", ApiError)
|
||||
}
|
||||
@ -122,7 +121,6 @@ defmodule Pleroma.Web.ApiSpec.MediaOperation do
|
||||
requestBody: Helpers.request_body("Parameters", create_request()),
|
||||
responses: %{
|
||||
202 => Operation.response("Media", "application/json", Attachment),
|
||||
400 => Operation.response("Media", "application/json", ApiError),
|
||||
422 => Operation.response("Media", "application/json", ApiError),
|
||||
500 => Operation.response("Media", "application/json", ApiError)
|
||||
}
|
||||
|
@ -8,8 +8,6 @@ defmodule Pleroma.Web.ApiSpec.TwitterUtilOperation do
|
||||
alias Pleroma.Web.ApiSpec.Schemas.ApiError
|
||||
alias Pleroma.Web.ApiSpec.Schemas.BooleanLike
|
||||
|
||||
import Pleroma.Web.ApiSpec.Helpers
|
||||
|
||||
def open_api_operation(action) do
|
||||
operation = String.to_existing_atom("#{action}_operation")
|
||||
apply(__MODULE__, operation, [])
|
||||
@ -65,7 +63,17 @@ defmodule Pleroma.Web.ApiSpec.TwitterUtilOperation do
|
||||
summary: "Change account password",
|
||||
security: [%{"oAuth" => ["write:accounts"]}],
|
||||
operationId: "UtilController.change_password",
|
||||
requestBody: request_body("Parameters", change_password_request(), required: true),
|
||||
parameters: [
|
||||
Operation.parameter(:password, :query, :string, "Current password", required: true),
|
||||
Operation.parameter(:new_password, :query, :string, "New password", required: true),
|
||||
Operation.parameter(
|
||||
:new_password_confirmation,
|
||||
:query,
|
||||
:string,
|
||||
"New password, confirmation",
|
||||
required: true
|
||||
)
|
||||
],
|
||||
responses: %{
|
||||
200 =>
|
||||
Operation.response("Success", "application/json", %Schema{
|
||||
@ -78,30 +86,17 @@ defmodule Pleroma.Web.ApiSpec.TwitterUtilOperation do
|
||||
}
|
||||
end
|
||||
|
||||
defp change_password_request do
|
||||
%Schema{
|
||||
title: "ChangePasswordRequest",
|
||||
description: "POST body for changing the account's passowrd",
|
||||
type: :object,
|
||||
required: [:password, :new_password, :new_password_confirmation],
|
||||
properties: %{
|
||||
password: %Schema{type: :string, description: "Current password"},
|
||||
new_password: %Schema{type: :string, description: "New password"},
|
||||
new_password_confirmation: %Schema{
|
||||
type: :string,
|
||||
description: "New password, confirmation"
|
||||
}
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def change_email_operation do
|
||||
%Operation{
|
||||
tags: ["Account credentials"],
|
||||
summary: "Change account email",
|
||||
security: [%{"oAuth" => ["write:accounts"]}],
|
||||
operationId: "UtilController.change_email",
|
||||
requestBody: request_body("Parameters", change_email_request(), required: true),
|
||||
parameters: [
|
||||
Operation.parameter(:password, :query, :string, "Current password", required: true),
|
||||
Operation.parameter(:email, :query, :string, "New email", required: true)
|
||||
],
|
||||
requestBody: nil,
|
||||
responses: %{
|
||||
200 =>
|
||||
Operation.response("Success", "application/json", %Schema{
|
||||
@ -114,19 +109,6 @@ defmodule Pleroma.Web.ApiSpec.TwitterUtilOperation do
|
||||
}
|
||||
end
|
||||
|
||||
defp change_email_request do
|
||||
%Schema{
|
||||
title: "ChangeEmailRequest",
|
||||
description: "POST body for changing the account's email",
|
||||
type: :object,
|
||||
required: [:email, :password],
|
||||
properties: %{
|
||||
email: %Schema{type: :string, description: "New email"},
|
||||
password: %Schema{type: :string, description: "Current password"}
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def update_notificaton_settings_operation do
|
||||
%Operation{
|
||||
tags: ["Accounts"],
|
||||
|
@ -412,14 +412,19 @@ defmodule Pleroma.Web.CommonAPI.Utils do
|
||||
|
||||
def maybe_notify_mentioned_recipients(recipients, _), do: recipients
|
||||
|
||||
# Do not notify subscribers if author is making a reply
|
||||
def maybe_notify_subscribers(recipients, %Activity{
|
||||
object: %Object{data: %{"inReplyTo" => _ap_id}}
|
||||
}) do
|
||||
recipients
|
||||
end
|
||||
|
||||
def maybe_notify_subscribers(
|
||||
recipients,
|
||||
%Activity{data: %{"actor" => actor, "type" => "Create"}} = activity
|
||||
) do
|
||||
# Do not notify subscribers if author is making a reply
|
||||
with %Object{data: object} <- Object.normalize(activity, fetch: false),
|
||||
nil <- object["inReplyTo"],
|
||||
%User{} = user <- User.get_cached_by_ap_id(actor) do
|
||||
%Activity{data: %{"actor" => actor, "type" => type}} = activity
|
||||
)
|
||||
when type == "Create" do
|
||||
with %User{} = user <- User.get_cached_by_ap_id(actor) do
|
||||
subscriber_ids =
|
||||
user
|
||||
|> User.subscriber_users()
|
||||
|
@ -102,7 +102,7 @@ defmodule Pleroma.Web.Endpoint do
|
||||
plug(Plug.Parsers,
|
||||
parsers: [
|
||||
:urlencoded,
|
||||
{:multipart, length: {Config, :get, [[:instance, :upload_limit]]}},
|
||||
{:multipart, length: Config.get([:instance, :upload_limit])},
|
||||
:json
|
||||
],
|
||||
pass: ["*/*"],
|
||||
|
@ -193,9 +193,7 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do
|
||||
|> ActivityPub.fetch_activities_bounded(following, params)
|
||||
|> Enum.reverse()
|
||||
|
||||
conn
|
||||
|> add_link_headers(activities)
|
||||
|> render("index.json",
|
||||
render(conn, "index.json",
|
||||
activities: activities,
|
||||
for: user,
|
||||
as: :activity,
|
||||
|
@ -65,19 +65,11 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
|
||||
|
||||
defp get_context_id(_), do: nil
|
||||
|
||||
# Check if the user reblogged this status
|
||||
defp reblogged?(activity, %User{ap_id: ap_id}) do
|
||||
with %Object{data: %{"announcements" => announcements}} when is_list(announcements) <-
|
||||
Object.normalize(activity, fetch: false) do
|
||||
ap_id in announcements
|
||||
else
|
||||
_ -> false
|
||||
end
|
||||
defp reblogged?(activity, user) do
|
||||
object = Object.normalize(activity, fetch: false) || %{}
|
||||
present?(user && user.ap_id in (object.data["announcements"] || []))
|
||||
end
|
||||
|
||||
# False if the user is logged out
|
||||
defp reblogged?(_activity, _user), do: false
|
||||
|
||||
def render("index.json", opts) do
|
||||
reading_user = opts[:for]
|
||||
|
||||
|
@ -41,7 +41,7 @@ defmodule Pleroma.Web.PleromaAPI.TwoFactorAuthenticationController do
|
||||
def setup(%{assigns: %{user: user}} = conn, %{"method" => "totp"} = _params) do
|
||||
with {:ok, user} <- MFA.setup_totp(user),
|
||||
%{secret: secret} = _ <- user.multi_factor_authentication_settings.totp do
|
||||
provisioning_uri = TOTP.provisioning_uri(secret, "#{user.ap_id}")
|
||||
provisioning_uri = TOTP.provisioning_uri(secret, "#{user.email}")
|
||||
|
||||
json(conn, %{provisioning_uri: provisioning_uri, key: secret})
|
||||
else
|
||||
|
@ -81,13 +81,17 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
|
||||
end
|
||||
end
|
||||
|
||||
def change_password(%{assigns: %{user: user}, body_params: body_params} = conn, %{}) do
|
||||
case CommonAPI.Utils.confirm_current_password(user, body_params.password) do
|
||||
def change_password(%{assigns: %{user: user}} = conn, %{
|
||||
password: password,
|
||||
new_password: new_password,
|
||||
new_password_confirmation: new_password_confirmation
|
||||
}) do
|
||||
case CommonAPI.Utils.confirm_current_password(user, password) do
|
||||
{:ok, user} ->
|
||||
with {:ok, _user} <-
|
||||
User.reset_password(user, %{
|
||||
password: body_params.new_password,
|
||||
password_confirmation: body_params.new_password_confirmation
|
||||
password: new_password,
|
||||
password_confirmation: new_password_confirmation
|
||||
}) do
|
||||
json(conn, %{status: "success"})
|
||||
else
|
||||
@ -104,10 +108,10 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
|
||||
end
|
||||
end
|
||||
|
||||
def change_email(%{assigns: %{user: user}, body_params: body_params} = conn, %{}) do
|
||||
case CommonAPI.Utils.confirm_current_password(user, body_params.password) do
|
||||
def change_email(%{assigns: %{user: user}} = conn, %{password: password, email: email}) do
|
||||
case CommonAPI.Utils.confirm_current_password(user, password) do
|
||||
{:ok, user} ->
|
||||
with {:ok, _user} <- User.change_email(user, body_params.email) do
|
||||
with {:ok, _user} <- User.change_email(user, email) do
|
||||
json(conn, %{status: "success"})
|
||||
else
|
||||
{:error, changeset} ->
|
||||
|
10
mix.exs
10
mix.exs
@ -4,7 +4,7 @@ defmodule Pleroma.Mixfile do
|
||||
def project do
|
||||
[
|
||||
app: :pleroma,
|
||||
version: version("2.4.1"),
|
||||
version: version("2.3.50"),
|
||||
elixir: "~> 1.9",
|
||||
elixirc_paths: elixirc_paths(Mix.env()),
|
||||
compilers: [:phoenix, :gettext] ++ Mix.compilers(),
|
||||
@ -121,7 +121,8 @@ defmodule Pleroma.Mixfile do
|
||||
{:phoenix_pubsub, "~> 2.0"},
|
||||
{:phoenix_ecto, "~> 4.0"},
|
||||
{:ecto_enum, "~> 1.4"},
|
||||
{:ecto_sql, "~> 3.6.2"},
|
||||
{:ecto_explain, "~> 0.1.2"},
|
||||
{:ecto_sql, "~> 3.4.4"},
|
||||
{:postgrex, ">= 0.15.5"},
|
||||
{:oban, "~> 2.3.4"},
|
||||
{:gettext, "~> 0.18"},
|
||||
@ -157,7 +158,7 @@ defmodule Pleroma.Mixfile do
|
||||
{:floki, "~> 0.27"},
|
||||
{:timex, "~> 3.6"},
|
||||
{:ueberauth, "~> 0.4"},
|
||||
{:linkify, "~> 0.5.1"},
|
||||
{:linkify, "~> 0.5.0"},
|
||||
{:http_signatures, "~> 0.1.0"},
|
||||
{:telemetry, "~> 0.3"},
|
||||
{:poolboy, "~> 1.5"},
|
||||
@ -198,9 +199,6 @@ defmodule Pleroma.Mixfile do
|
||||
{:eblurhash, "~> 1.1.0"},
|
||||
{:open_api_spex, "~> 3.10"},
|
||||
|
||||
# indirect dependency version override
|
||||
{:plug, "~> 1.10.4", override: true},
|
||||
|
||||
## dev & test
|
||||
{:ex_doc, "~> 0.22", only: :dev, runtime: false},
|
||||
{:ex_machina, "~> 2.4", only: :test},
|
||||
|
9
mix.lock
9
mix.lock
@ -30,9 +30,10 @@
|
||||
"earmark": {:hex, :earmark, "1.4.15", "2c7f924bf495ec1f65bd144b355d0949a05a254d0ec561740308a54946a67888", [:mix], [{:earmark_parser, ">= 1.4.13", [hex: :earmark_parser, repo: "hexpm", optional: false]}], "hexpm", "3b1209b85bc9f3586f370f7c363f6533788fb4e51db23aa79565875e7f9999ee"},
|
||||
"earmark_parser": {:hex, :earmark_parser, "1.4.13", "0c98163e7d04a15feb62000e1a891489feb29f3d10cb57d4f845c405852bbef8", [:mix], [], "hexpm", "d602c26af3a0af43d2f2645613f65841657ad6efc9f0e361c3b6c06b578214ba"},
|
||||
"eblurhash": {:hex, :eblurhash, "1.1.0", "e10ccae762598507ebfacf0b645ed49520f2afa3e7e9943e73a91117dffce415", [:rebar3], [], "hexpm", "2e6b889d09fddd374e3c5ac57c486138768763264e99ac1074ae5fa7fc9ab51d"},
|
||||
"ecto": {:hex, :ecto, "3.6.2", "efdf52acfc4ce29249bab5417415bd50abd62db7b0603b8bab0d7b996548c2bc", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "efad6dfb04e6f986b8a3047822b0f826d9affe8e4ebdd2aeedbfcb14fd48884e"},
|
||||
"ecto": {:hex, :ecto, "3.4.6", "08f7afad3257d6eb8613309af31037e16c36808dfda5a3cd0cb4e9738db030e4", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "6f13a9e2a62e75c2dcfc7207bfc65645ab387af8360db4c89fee8b5a4bf3f70b"},
|
||||
"ecto_enum": {:hex, :ecto_enum, "1.4.0", "d14b00e04b974afc69c251632d1e49594d899067ee2b376277efd8233027aec8", [:mix], [{:ecto, ">= 3.0.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:ecto_sql, "> 3.0.0", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:mariaex, ">= 0.0.0", [hex: :mariaex, repo: "hexpm", optional: true]}, {:postgrex, ">= 0.0.0", [hex: :postgrex, repo: "hexpm", optional: true]}], "hexpm", "8fb55c087181c2b15eee406519dc22578fa60dd82c088be376d0010172764ee4"},
|
||||
"ecto_sql": {:hex, :ecto_sql, "3.6.2", "9526b5f691701a5181427634c30655ac33d11e17e4069eff3ae1176c764e0ba3", [:mix], [{:db_connection, "~> 2.2", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.6.2", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.4.0 or ~> 0.5.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.15.0 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "5ec9d7e6f742ea39b63aceaea9ac1d1773d574ea40df5a53ef8afbd9242fdb6b"},
|
||||
"ecto_explain": {:hex, :ecto_explain, "0.1.2", "a9d504cbd4adc809911f796d5ef7ebb17a576a6d32286c3d464c015bd39d5541", [:mix], [], "hexpm", "1d0e7798ae30ecf4ce34e912e5354a0c1c832b7ebceba39298270b9a9f316330"},
|
||||
"ecto_sql": {:hex, :ecto_sql, "3.4.5", "30161f81b167d561a9a2df4329c10ae05ff36eca7ccc84628f2c8b9fa1e43323", [:mix], [{:db_connection, "~> 2.2", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.4.3", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.3.0 or ~> 0.4.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.15.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.0", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "31990c6a3579b36a3c0841d34a94c275e727de8b84f58509da5f1b2032c98ac2"},
|
||||
"eimp": {:hex, :eimp, "1.0.14", "fc297f0c7e2700457a95a60c7010a5f1dcb768a083b6d53f49cd94ab95a28f22", [:rebar3], [{:p1_utils, "1.0.18", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "501133f3112079b92d9e22da8b88bf4f0e13d4d67ae9c15c42c30bd25ceb83b6"},
|
||||
"elixir_make": {:hex, :elixir_make, "0.6.2", "7dffacd77dec4c37b39af867cedaabb0b59f6a871f89722c25b28fcd4bd70530", [:mix], [], "hexpm", "03e49eadda22526a7e5279d53321d1cced6552f344ba4e03e619063de75348d9"},
|
||||
"esshd": {:hex, :esshd, "0.1.1", "d4dd4c46698093a40a56afecce8a46e246eb35463c457c246dacba2e056f31b5", [:mix], [], "hexpm", "d73e341e3009d390aa36387dc8862860bf9f874c94d9fd92ade2926376f49981"},
|
||||
@ -67,7 +68,7 @@
|
||||
"jose": {:hex, :jose, "1.11.1", "59da64010c69aad6cde2f5b9248b896b84472e99bd18f246085b7b9fe435dcdb", [:mix, :rebar3], [], "hexpm", "078f6c9fb3cd2f4cfafc972c814261a7d1e8d2b3685c0a76eb87e158efff1ac5"},
|
||||
"jumper": {:hex, :jumper, "1.0.1", "3c00542ef1a83532b72269fab9f0f0c82bf23a35e27d278bfd9ed0865cecabff", [:mix], [], "hexpm", "318c59078ac220e966d27af3646026db9b5a5e6703cb2aa3e26bcfaba65b7433"},
|
||||
"libring": {:hex, :libring, "1.4.0", "41246ba2f3fbc76b3971f6bce83119dfec1eee17e977a48d8a9cfaaf58c2a8d6", [:mix], [], "hexpm"},
|
||||
"linkify": {:hex, :linkify, "0.5.1", "6dc415cbc948b2f6ecec7cb226aab7ba9d3a1815bb501ae33e042334d707ecee", [:mix], [], "hexpm", "a3128c7e22fada4aa7214009501d8131e1fa3faf2f0a68b33dba379dc84ff944"},
|
||||
"linkify": {:hex, :linkify, "0.5.0", "e0ea8de73ff44742d6a889721221f4c4eccaad5284957ee9832ffeb347602d54", [:mix], [], "hexpm", "4ccd958350aee7c51c89e21f05b15d30596ebbba707e051d21766be1809df2d7"},
|
||||
"majic": {:git, "https://git.pleroma.social/pleroma/elixir-libraries/majic.git", "289cda1b6d0d70ccb2ba508a2b0bd24638db2880", [ref: "289cda1b6d0d70ccb2ba508a2b0bd24638db2880"]},
|
||||
"makeup": {:hex, :makeup, "1.0.5", "d5a830bc42c9800ce07dd97fa94669dfb93d3bf5fcf6ea7a0c67b2e0e4a7f26c", [:mix], [{:nimble_parsec, "~> 0.5 or ~> 1.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "cfa158c02d3f5c0c665d0af11512fed3fba0144cf1aadee0f2ce17747fba2ca9"},
|
||||
"makeup_elixir": {:hex, :makeup_elixir, "0.14.1", "4f0e96847c63c17841d42c08107405a005a2680eb9c7ccadfd757bd31dabccfb", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "f2438b1a80eaec9ede832b5c41cd4f373b38fd7aa33e3b22d9db79e640cbde11"},
|
||||
@ -94,7 +95,7 @@
|
||||
"phoenix_html": {:hex, :phoenix_html, "2.14.3", "51f720d0d543e4e157ff06b65de38e13303d5778a7919bcc696599e5934271b8", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "efd697a7fff35a13eeeb6b43db884705cba353a1a41d127d118fda5f90c8e80f"},
|
||||
"phoenix_pubsub": {:hex, :phoenix_pubsub, "2.0.0", "a1ae76717bb168cdeb10ec9d92d1480fec99e3080f011402c0a2d68d47395ffb", [:mix], [], "hexpm", "c52d948c4f261577b9c6fa804be91884b381a7f8f18450c5045975435350f771"},
|
||||
"phoenix_swoosh": {:hex, :phoenix_swoosh, "0.3.3", "039435dd975f7e55953525b88f1d596f26c6141412584c16f4db109708a8ee68", [:mix], [{:hackney, "~> 1.9", [hex: :hackney, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:swoosh, "~> 1.0", [hex: :swoosh, repo: "hexpm", optional: false]}], "hexpm", "4a540cea32e05356541737033d666ee7fea7700eb2101bf76783adbfe06601cd"},
|
||||
"plug": {:hex, :plug, "1.10.4", "41eba7d1a2d671faaf531fa867645bd5a3dce0957d8e2a3f398ccff7d2ef017f", [:mix], [{:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "ad1e233fe73d2eec56616568d260777b67f53148a999dc2d048f4eb9778fe4a0"},
|
||||
"plug": {:hex, :plug, "1.11.1", "f2992bac66fdae679453c9e86134a4201f6f43a687d8ff1cd1b2862d53c80259", [:mix], [{:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "23524e4fefbb587c11f0833b3910bfb414bf2e2534d61928e920f54e3a1b881f"},
|
||||
"plug_cowboy": {:hex, :plug_cowboy, "2.5.0", "51c998f788c4e68fc9f947a5eba8c215fbb1d63a520f7604134cab0270ea6513", [:mix], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:cowboy_telemetry, "~> 0.3", [hex: :cowboy_telemetry, repo: "hexpm", optional: false]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "5b2c8925a5e2587446f33810a58c01e66b3c345652eeec809b76ba007acde71a"},
|
||||
"plug_crypto": {:hex, :plug_crypto, "1.2.2", "05654514ac717ff3a1843204b424477d9e60c143406aa94daf2274fdd280794d", [:mix], [], "hexpm", "87631c7ad914a5a445f0a3809f99b079113ae4ed4b867348dd9eec288cecb6db"},
|
||||
"plug_static_index_html": {:hex, :plug_static_index_html, "1.0.0", "840123d4d3975585133485ea86af73cb2600afd7f2a976f9f5fd8b3808e636a0", [:mix], [{:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "79fd4fcf34d110605c26560cbae8f23c603ec4158c08298bd4360fdea90bb5cf"},
|
||||
|
File diff suppressed because one or more lines are too long
1
priv/static/adminfe/app.6fb984d1.css
Normal file
1
priv/static/adminfe/app.6fb984d1.css
Normal file
File diff suppressed because one or more lines are too long
1
priv/static/adminfe/chunk-0537.76929cff.css
Normal file
1
priv/static/adminfe/chunk-0537.76929cff.css
Normal file
File diff suppressed because one or more lines are too long
@ -1 +0,0 @@
|
||||
.error-page-container[data-v-09709f1e]{min-height:100%;width:100%;background-color:#2d3a4b;overflow:hidden}.error-page-container .buttons-group[data-v-09709f1e]{margin-top:4em}.error-page-container .el-icon-warning[data-v-09709f1e]{font-size:4.2em;color:#eee;margin:0 auto}.error-page-container .error-page[data-v-09709f1e]{width:45rem;max-width:100%;margin:16rem auto;text-align:center}.error-page-container .error-title[data-v-09709f1e]{color:#eee}
|
1
priv/static/adminfe/chunk-1e1e.5980e665.css
Normal file
1
priv/static/adminfe/chunk-1e1e.5980e665.css
Normal file
@ -0,0 +1 @@
|
||||
.moderation-log-container[data-v-ab8fe5e2]{margin:0 15px}h1[data-v-ab8fe5e2]{margin:0}.el-timeline[data-v-ab8fe5e2]{margin:25px 45px 0 0;padding:0}.moderation-log-date-panel[data-v-ab8fe5e2]{width:350px}.moderation-log-header-container[data-v-ab8fe5e2]{-webkit-box-align:center;-ms-flex-align:center;align-items:center;margin:10px 0 15px}.moderation-log-header-container[data-v-ab8fe5e2],.moderation-log-nav-container[data-v-ab8fe5e2]{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-ab8fe5e2]{width:350px}.moderation-log-user-select[data-v-ab8fe5e2]{margin:0 0 20px;width:350px}.reboot-button[data-v-ab8fe5e2]{padding:10px;margin:0;width:145px}.router-link[data-v-ab8fe5e2]{text-decoration:none}.search-container[data-v-ab8fe5e2]{text-align:right}.pagination[data-v-ab8fe5e2]{text-align:center}@media only screen and (max-width:480px){h1[data-v-ab8fe5e2]{font-size:24px}.moderation-log-date-panel[data-v-ab8fe5e2]{width:100%}.moderation-log-user-select[data-v-ab8fe5e2]{margin:0 0 10px;width:55%}.moderation-log-search[data-v-ab8fe5e2]{width:40%}}@media only screen and (max-width:801px) and (min-width:481px){.moderation-log-date-panel[data-v-ab8fe5e2]{width:55%}.moderation-log-user-select[data-v-ab8fe5e2]{margin:0 0 10px;width:55%}.moderation-log-search[data-v-ab8fe5e2]{width:40%}}
|
@ -1 +0,0 @@
|
||||
.error-page-container[data-v-6c40cae5]{min-height:100%;width:100%;background-color:#2d3a4b;overflow:hidden}.error-page-container .buttons-group[data-v-6c40cae5]{margin-top:4em}.error-page-container .el-icon-warning[data-v-6c40cae5]{font-size:4.2em;color:#eee;margin:0 auto}.error-page-container .error-page[data-v-6c40cae5]{width:45rem;max-width:100%;margin:16rem auto;text-align:center}.error-page-container .error-title[data-v-6c40cae5]{color:#eee}
|
1
priv/static/adminfe/chunk-4770.20caaae1.css
Normal file
1
priv/static/adminfe/chunk-4770.20caaae1.css
Normal file
File diff suppressed because one or more lines are too long
@ -1 +0,0 @@
|
||||
.follow-relay{width:350px;margin-right:7px}.relays-container{margin:0 15px}.relays-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}@media only screen and (max-width:480px){.follow-relay{width:75%;margin-right:5px}.follow-relay input{width:100%}.follow-relay-container{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;margin:0 5px}.relays-container{margin:0 10px}}
|
@ -1 +0,0 @@
|
||||
.router-link{text-decoration:none}.moderation-log-container[data-v-a9880f26]{margin:0 15px}h1[data-v-a9880f26]{margin:0}.el-timeline[data-v-a9880f26]{margin:25px 45px 0 0;padding:0}.moderation-log-date-panel[data-v-a9880f26]{width:350px}.moderation-log-header-container[data-v-a9880f26]{-webkit-box-align:center;-ms-flex-align:center;align-items:center;margin:10px 0 15px}.moderation-log-header-container[data-v-a9880f26],.moderation-log-nav-container[data-v-a9880f26]{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-a9880f26]{width:350px}.moderation-log-user-select[data-v-a9880f26]{margin:0 0 20px;width:350px}.reboot-button[data-v-a9880f26]{padding:10px;margin:0;width:145px}.pagination[data-v-a9880f26]{text-align:center}@media only screen and (max-width:480px){h1[data-v-a9880f26]{font-size:24px}.moderation-log-date-panel[data-v-a9880f26]{width:100%}.moderation-log-user-select[data-v-a9880f26]{margin:0 0 10px;width:55%}.moderation-log-search[data-v-a9880f26]{width:40%}}@media only screen and (max-width:801px) and (min-width:481px){.moderation-log-date-panel[data-v-a9880f26]{width:55%}.moderation-log-user-select[data-v-a9880f26]{margin:0 0 10px;width:55%}.moderation-log-search[data-v-a9880f26]{width:40%}}
|
1
priv/static/adminfe/chunk-68ea9.892994aa.css
Normal file
1
priv/static/adminfe/chunk-68ea9.892994aa.css
Normal file
@ -0,0 +1 @@
|
||||
.wscn-http404-container[data-v-1d6b2d2a]{-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%);position:absolute;top:40%;left:50%}.wscn-http404[data-v-1d6b2d2a]{position:relative;width:1200px;padding:0 50px;overflow:hidden}.wscn-http404 .pic-404[data-v-1d6b2d2a]{position:relative;float:left;width:600px;overflow:hidden}.wscn-http404 .pic-404__parent[data-v-1d6b2d2a]{width:100%}.wscn-http404 .pic-404__child[data-v-1d6b2d2a]{position:absolute}.wscn-http404 .pic-404__child.left[data-v-1d6b2d2a]{width:80px;top:17px;left:220px;opacity:0;-webkit-animation-name:cloudLeft-data-v-1d6b2d2a;animation-name:cloudLeft-data-v-1d6b2d2a;-webkit-animation-duration:2s;animation-duration:2s;-webkit-animation-timing-function:linear;animation-timing-function:linear;-webkit-animation-fill-mode:forwards;animation-fill-mode:forwards;-webkit-animation-delay:1s;animation-delay:1s}.wscn-http404 .pic-404__child.mid[data-v-1d6b2d2a]{width:46px;top:10px;left:420px;opacity:0;-webkit-animation-name:cloudMid-data-v-1d6b2d2a;animation-name:cloudMid-data-v-1d6b2d2a;-webkit-animation-duration:2s;animation-duration:2s;-webkit-animation-timing-function:linear;animation-timing-function:linear;-webkit-animation-fill-mode:forwards;animation-fill-mode:forwards;-webkit-animation-delay:1.2s;animation-delay:1.2s}.wscn-http404 .pic-404__child.right[data-v-1d6b2d2a]{width:62px;top:100px;left:500px;opacity:0;-webkit-animation-name:cloudRight-data-v-1d6b2d2a;animation-name:cloudRight-data-v-1d6b2d2a;-webkit-animation-duration:2s;animation-duration:2s;-webkit-animation-timing-function:linear;animation-timing-function:linear;-webkit-animation-fill-mode:forwards;animation-fill-mode:forwards;-webkit-animation-delay:1s;animation-delay:1s}@-webkit-keyframes cloudLeft-data-v-1d6b2d2a{0%{top:17px;left:220px;opacity:0}20%{top:33px;left:188px;opacity:1}80%{top:81px;left:92px;opacity:1}to{top:97px;left:60px;opacity:0}}@keyframes cloudLeft-data-v-1d6b2d2a{0%{top:17px;left:220px;opacity:0}20%{top:33px;left:188px;opacity:1}80%{top:81px;left:92px;opacity:1}to{top:97px;left:60px;opacity:0}}@-webkit-keyframes cloudMid-data-v-1d6b2d2a{0%{top:10px;left:420px;opacity:0}20%{top:40px;left:360px;opacity:1}70%{top:130px;left:180px;opacity:1}to{top:160px;left:120px;opacity:0}}@keyframes cloudMid-data-v-1d6b2d2a{0%{top:10px;left:420px;opacity:0}20%{top:40px;left:360px;opacity:1}70%{top:130px;left:180px;opacity:1}to{top:160px;left:120px;opacity:0}}@-webkit-keyframes cloudRight-data-v-1d6b2d2a{0%{top:100px;left:500px;opacity:0}20%{top:120px;left:460px;opacity:1}80%{top:180px;left:340px;opacity:1}to{top:200px;left:300px;opacity:0}}@keyframes cloudRight-data-v-1d6b2d2a{0%{top:100px;left:500px;opacity:0}20%{top:120px;left:460px;opacity:1}80%{top:180px;left:340px;opacity:1}to{top:200px;left:300px;opacity:0}}.wscn-http404 .bullshit[data-v-1d6b2d2a]{position:relative;float:left;width:300px;padding:30px 0;overflow:hidden}.wscn-http404 .bullshit__oops[data-v-1d6b2d2a]{font-size:32px;line-height:40px;color:#1482f0;margin-bottom:20px;-webkit-animation-fill-mode:forwards;animation-fill-mode:forwards}.wscn-http404 .bullshit__headline[data-v-1d6b2d2a],.wscn-http404 .bullshit__oops[data-v-1d6b2d2a]{font-weight:700;opacity:0;-webkit-animation-name:slideUp-data-v-1d6b2d2a;animation-name:slideUp-data-v-1d6b2d2a;-webkit-animation-duration:.5s;animation-duration:.5s}.wscn-http404 .bullshit__headline[data-v-1d6b2d2a]{font-size:20px;line-height:24px;color:#222;margin-bottom:10px;-webkit-animation-delay:.1s;animation-delay:.1s;-webkit-animation-fill-mode:forwards;animation-fill-mode:forwards}.wscn-http404 .bullshit__info[data-v-1d6b2d2a]{font-size:13px;line-height:21px;color:grey;margin-bottom:30px;-webkit-animation-delay:.2s;animation-delay:.2s;-webkit-animation-fill-mode:forwards;animation-fill-mode:forwards}.wscn-http404 .bullshit__info[data-v-1d6b2d2a],.wscn-http404 .bullshit__return-home[data-v-1d6b2d2a]{opacity:0;-webkit-animation-name:slideUp-data-v-1d6b2d2a;animation-name:slideUp-data-v-1d6b2d2a;-webkit-animation-duration:.5s;animation-duration:.5s}.wscn-http404 .bullshit__return-home[data-v-1d6b2d2a]{display:block;float:left;width:165px;height:36px;background:#1482f0;border-radius:100px;text-align:center;color:#fff;font-size:14px;line-height:36px;cursor:pointer;-webkit-animation-delay:.3s;animation-delay:.3s;-webkit-animation-fill-mode:forwards;animation-fill-mode:forwards}@-webkit-keyframes slideUp-data-v-1d6b2d2a{0%{-webkit-transform:translateY(60px);transform:translateY(60px);opacity:0}to{-webkit-transform:translateY(0);transform:translateY(0);opacity:1}}@keyframes slideUp-data-v-1d6b2d2a{0%{-webkit-transform:translateY(60px);transform:translateY(60px);opacity:0}to{-webkit-transform:translateY(0);transform:translateY(0);opacity:1}}
|
1
priv/static/adminfe/chunk-6e81.687d5046.css
Normal file
1
priv/static/adminfe/chunk-6e81.687d5046.css
Normal file
@ -0,0 +1 @@
|
||||
.errPage-container[data-v-ab9be52c]{width:800px;max-width:100%;margin:100px auto}.errPage-container .pan-back-btn[data-v-ab9be52c]{background:#008489;color:#fff;border:none!important}.errPage-container .pan-gif[data-v-ab9be52c]{margin:0 auto;display:block}.errPage-container .pan-img[data-v-ab9be52c]{display:block;margin:0 auto;width:100%}.errPage-container .text-jumbo[data-v-ab9be52c]{font-size:60px;font-weight:700;color:#484848}.errPage-container .list-unstyled[data-v-ab9be52c]{font-size:14px}.errPage-container .list-unstyled li[data-v-ab9be52c]{padding-bottom:5px}.errPage-container .list-unstyled a[data-v-ab9be52c]{color:#008489;text-decoration:none}.errPage-container .list-unstyled a[data-v-ab9be52c]:hover{text-decoration:underline}
|
File diff suppressed because one or more lines are too long
@ -1 +1 @@
|
||||
.invites-container .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:15px}.invites-container .create-invite-token{text-align:left;width:350px;padding:10px}.invites-container .create-new-token-dialog a{margin-bottom:3px}.invites-container .create-new-token-dialog .el-card__body{padding:10px 20px}.invites-container .el-dialog__body{padding:5px 20px 0}.invites-container h1{margin:0}.invites-container .icon{margin-right:5px}.invites-container .invite-link-container{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;-webkit-box-align:baseline;-ms-flex-align:baseline;align-items:baseline}.invites-container .invite-link-container button{margin-left:15px}.invites-container .invite-token-table{width:100%;margin:0 15px}.invites-container .invite-via-email{text-align:left;width:350px;padding:10px}.invites-container .invite-via-email-dialog{width:50%}.invites-container .invites-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;margin:10px 15px}.invites-container .info{color:#666;font-size:13px;line-height:22px;margin:0 0 10px}.invites-container .new-token-card .el-form-item{margin:0}.invites-container .reboot-button{padding:10px;margin:0;width:145px}@media only screen and (max-width:480px){.invites-container .actions-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;-webkit-box-align:center;-ms-flex-align:center;align-items:center;margin:15px 10px 7px}.invites-container .cell{padding:0}.invites-container .create-invite-token{width:100%}.invites-container .create-new-token-dialog{width:85%}.invites-container .el-date-editor{width:150px}.invites-container .el-dialog__body{padding:5px 15px 0}.invites-container h1{margin:0}.invites-container .invite-token-table{width:100%;margin:0 5px;font-size:12px;font-weight:500}.invites-container .invite-via-email{width:100%;margin:10px 0 0}.invites-container .invite-via-email-dialog{width:85%}.invites-container .invites-header-container{margin:0 10px}.invites-container .info{margin:0 0 10px 5px}.invites-container th .cell{padding:0}.create-invite-token,.invite-via-email{width:100%}}
|
||||
.invites-container .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:15px}.invites-container .create-invite-token{text-align:left;width:350px;padding:10px}.invites-container .create-new-token-dialog{width:50%}.invites-container .create-new-token-dialog a{margin-bottom:3px}.invites-container .create-new-token-dialog .el-card__body{padding:10px 20px}.invites-container .el-dialog__body{padding:5px 20px 0}.invites-container h1{margin:0}.invites-container .icon{margin-right:5px}.invites-container .invite-token-table{width:100%;margin:0 15px}.invites-container .invite-via-email{text-align:left;width:350px;padding:10px}.invites-container .invite-via-email-dialog{width:50%}.invites-container .invites-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;margin:10px 15px}.invites-container .info{color:#666;font-size:13px;line-height:22px;margin:0 0 10px}.invites-container .new-token-card .el-form-item{margin:0}.invites-container .reboot-button{padding:10px;margin:0;width:145px}@media only screen and (max-width:480px){.invites-container .actions-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;-webkit-box-align:center;-ms-flex-align:center;align-items:center;margin:15px 10px 7px}.invites-container .cell{padding:0}.invites-container .create-invite-token{width:100%}.invites-container .create-new-token-dialog{width:85%}.invites-container .el-date-editor{width:150px}.invites-container .el-dialog__body{padding:5px 15px 0}.invites-container h1{margin:0}.invites-container .invite-token-table{width:100%;margin:0 5px;font-size:12px;font-weight:500}.invites-container .invite-via-email{width:100%;margin:10px 0 0}.invites-container .invite-via-email-dialog{width:85%}.invites-container .invites-header-container{margin:0 10px}.invites-container .info{margin:0 0 10px 5px}.invites-container th .cell{padding:0}.create-invite-token,.invite-via-email{width:100%}}
|
@ -1 +1 @@
|
||||
@supports (-webkit-mask:none) and (not (cater-color:#fff)){.login-container .el-input input{color:#fff}.login-container .el-input input:first-line{color:#eee}}.login-container .el-input{display:inline-block;height:47px;width:85%}.login-container .el-input input{background:transparent;border:0;-webkit-appearance:none;border-radius:0;padding:12px 5px 12px 15px;color:#eee;height:47px;caret-color:#fff}.login-container .el-input input:-webkit-autofill{-webkit-box-shadow:0 0 0 1000px #283443 inset!important;-webkit-text-fill-color:#fff!important}.login-container .el-form-item{border:1px solid hsla(0,0%,100%,.1);background:rgba(0,0,0,.1);border-radius:5px;color:#454545}.login-container .login-button{width:100%;margin:0 0 10px}.login-container .omit-host-note{color:#596f8c;font-size:.8em;font-style:italic;margin:-20px 0 15px;padding:3px 0 0 15px}.login-container[data-v-5aafa9c0]{min-height:100%;width:100%;background-color:#2d3a4b;overflow:hidden}.login-container .login-form[data-v-5aafa9c0]{position:relative;width:520px;max-width:100%;padding:160px 35px 0;margin:0 auto;overflow:hidden}.login-container .tips[data-v-5aafa9c0]{font-size:14px;color:#fff;margin-bottom:10px}.login-container .tips span[data-v-5aafa9c0]:first-of-type{margin-right:16px}.login-container .svg-container[data-v-5aafa9c0]{padding:6px 5px 6px 15px;color:#889aa4;vertical-align:middle;width:30px;display:inline-block}.login-container .title-container[data-v-5aafa9c0]{position:relative}.login-container .title-container .title[data-v-5aafa9c0]{font-size:26px;color:#eee;margin:0 auto 40px;text-align:center;font-weight:700}.login-container .title-container .set-language[data-v-5aafa9c0]{color:#fff;position:absolute;top:3px;font-size:18px;right:0;cursor:pointer}.login-container .show-pwd[data-v-5aafa9c0]{position:absolute;right:10px;top:7px;font-size:16px;color:#889aa4;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.login-container .thirdparty-button[data-v-5aafa9c0]{position:absolute;right:0;bottom:6px}
|
||||
@supports (-webkit-mask:none) and (not (cater-color:#fff)){.login-container .el-input input{color:#fff}.login-container .el-input input:first-line{color:#eee}}.login-container .el-input{display:inline-block;height:47px;width:85%}.login-container .el-input input{background:transparent;border:0;-webkit-appearance:none;border-radius:0;padding:12px 5px 12px 15px;color:#eee;height:47px;caret-color:#fff}.login-container .el-input input:-webkit-autofill{-webkit-box-shadow:0 0 0 1000px #283443 inset!important;-webkit-text-fill-color:#fff!important}.login-container .el-form-item{border:1px solid hsla(0,0%,100%,.1);background:rgba(0,0,0,.1);border-radius:5px;color:#454545}.login-container .login-button{width:100%;margin:0 0 10px}.login-container .omit-host-note{color:#596f8c;font-size:.8em;font-style:italic;margin:-20px 0 15px;padding:3px 0 0 15px}.login-container[data-v-5bb13616]{min-height:100%;width:100%;background-color:#2d3a4b;overflow:hidden}.login-container .login-form[data-v-5bb13616]{position:relative;width:520px;max-width:100%;padding:160px 35px 0;margin:0 auto;overflow:hidden}.login-container .tips[data-v-5bb13616]{font-size:14px;color:#fff;margin-bottom:10px}.login-container .tips span[data-v-5bb13616]:first-of-type{margin-right:16px}.login-container .svg-container[data-v-5bb13616]{padding:6px 5px 6px 15px;color:#889aa4;vertical-align:middle;width:30px;display:inline-block}.login-container .title-container[data-v-5bb13616]{position:relative}.login-container .title-container .title[data-v-5bb13616]{font-size:26px;color:#eee;margin:0 auto 40px;text-align:center;font-weight:700}.login-container .title-container .set-language[data-v-5bb13616]{color:#fff;position:absolute;top:3px;font-size:18px;right:0;cursor:pointer}.login-container .show-pwd[data-v-5bb13616]{position:absolute;right:10px;top:7px;font-size:16px;color:#889aa4;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.login-container .thirdparty-button[data-v-5bb13616]{position:absolute;right:0;bottom:6px}
|
@ -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.f1f2be85.css rel=stylesheet><link href=chunk-libs.74976a6a.css rel=stylesheet><link href=app.143a1409.css rel=stylesheet></head><body><div id=app></div><script type=text/javascript src=static/js/runtime.2a586239.js></script><script type=text/javascript src=static/js/chunk-elementUI.4c32a355.js></script><script type=text/javascript src=static/js/chunk-libs.55b24a78.js></script><script type=text/javascript src=static/js/app.f02f5ebc.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.f77689d7.css rel=stylesheet><link href=chunk-libs.5cf7f50a.css rel=stylesheet><link href=app.6fb984d1.css rel=stylesheet></head><body><div id=app></div><script type=text/javascript src=static/js/runtime.6b30c658.js></script><script type=text/javascript src=static/js/chunk-elementUI.21957ec8.js></script><script type=text/javascript src=static/js/chunk-libs.5ca2c8e8.js></script><script type=text/javascript src=static/js/app.1428845f.js></script></body></html>
|
BIN
priv/static/adminfe/static/img/401.089007e.gif
Normal file
BIN
priv/static/adminfe/static/img/401.089007e.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 160 KiB |
BIN
priv/static/adminfe/static/img/404.a57b6f3.png
Normal file
BIN
priv/static/adminfe/static/img/404.a57b6f3.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 96 KiB |
2
priv/static/adminfe/static/js/app.1428845f.js
Normal file
2
priv/static/adminfe/static/js/app.1428845f.js
Normal file
File diff suppressed because one or more lines are too long
1
priv/static/adminfe/static/js/app.1428845f.js.map
Normal file
1
priv/static/adminfe/static/js/app.1428845f.js.map
Normal file
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
2
priv/static/adminfe/static/js/chunk-0537.d0eef370.js
Normal file
2
priv/static/adminfe/static/js/chunk-0537.d0eef370.js
Normal file
File diff suppressed because one or more lines are too long
1
priv/static/adminfe/static/js/chunk-0537.d0eef370.js.map
Normal file
1
priv/static/adminfe/static/js/chunk-0537.d0eef370.js.map
Normal file
File diff suppressed because one or more lines are too long
@ -1,2 +0,0 @@
|
||||
(window.webpackJsonp=window.webpackJsonp||[]).push([["chunk-0c60"],{"/yZL":function(t,r,o){"use strict";var e=o("3dKJ");o.n(e).a},"3dKJ":function(t,r,o){},"UUO+":function(t,r,o){"use strict";o.r(r);var e={name:"Page401",methods:{back:function(){this.$route.query.noGoBack?this.$router.push({path:"/login"}):this.$router.go(-1)},login:function(){this.$router.push({path:"/login"})}}},s=(o("/yZL"),o("KHd+")),n=Object(s.a)(e,function(){var t=this,r=t.$createElement,o=t._self._c||r;return o("div",{staticClass:"error-page-container"},[o("div",{staticClass:"error-page"},[o("i",{staticClass:"el-icon-warning"}),t._v(" "),o("h1",{staticClass:"error-title"},[t._v(t._s(t.$t("errLog.error401")))]),t._v(" "),o("h2",{staticClass:"error-title"},[t._v(t._s(t.$t("errLog.unauth")))]),t._v(" "),o("div",{staticClass:"buttons-group"},[o("el-button",{on:{click:t.back}},[t._v(t._s(t.$t("errLog.back")))]),t._v(" "),o("el-button",{attrs:{type:"primary"},on:{click:t.login}},[t._v(t._s(t.$t("errLog.login")))])],1)])])},[],!1,null,"09709f1e",null);n.options.__file="401.vue";r.default=n.exports}}]);
|
||||
//# sourceMappingURL=chunk-0c60.9b12ac3f.js.map
|
File diff suppressed because one or more lines are too long
2
priv/static/adminfe/static/js/chunk-170f.e1d6aac3.js
Normal file
2
priv/static/adminfe/static/js/chunk-170f.e1d6aac3.js
Normal file
File diff suppressed because one or more lines are too long
1
priv/static/adminfe/static/js/chunk-170f.e1d6aac3.js.map
Normal file
1
priv/static/adminfe/static/js/chunk-170f.e1d6aac3.js.map
Normal file
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
1
priv/static/adminfe/static/js/chunk-176e.f64cb745.js.map
Normal file
1
priv/static/adminfe/static/js/chunk-176e.f64cb745.js.map
Normal file
File diff suppressed because one or more lines are too long
2
priv/static/adminfe/static/js/chunk-1e1e.37f6f555.js
Normal file
2
priv/static/adminfe/static/js/chunk-1e1e.37f6f555.js
Normal file
File diff suppressed because one or more lines are too long
1
priv/static/adminfe/static/js/chunk-1e1e.37f6f555.js.map
Normal file
1
priv/static/adminfe/static/js/chunk-1e1e.37f6f555.js.map
Normal file
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
2
priv/static/adminfe/static/js/chunk-35b1.50c1449b.js
Normal file
2
priv/static/adminfe/static/js/chunk-35b1.50c1449b.js
Normal file
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
Block a user