Compare commits

..

No commits in common. "2fab999373bdaf4d430499bc30c09fa4fd87be8f" and "adaca2c33d7b2414d9c9e46322be5d962f636809" have entirely different histories.

1673 changed files with 32015 additions and 82140 deletions

View File

@ -1,7 +0,0 @@
[production]
defaults
not IE 11
not dead
[development]
supports es6-module

View File

@ -1,209 +1,255 @@
version: 2.1
version: 2
orbs:
ruby: circleci/ruby@1.4.1
node: circleci/node@5.0.1
executors:
default:
parameters:
ruby-version:
type: string
aliases:
- &defaults
docker:
- image: cimg/ruby:<< parameters.ruby-version >>
environment:
- image: circleci/ruby:2.7-buster-node
environment: &ruby_environment
BUNDLE_JOBS: 3
BUNDLE_RETRY: 3
CONTINUOUS_INTEGRATION: true
BUNDLE_APP_CONFIG: ./.bundle/
BUNDLE_PATH: ./vendor/bundle/
DB_HOST: localhost
DB_USER: root
DISABLE_SIMPLECOV: true
RAILS_ENV: test
- image: cimg/postgres:14.0
ALLOW_NOPAM: true
CONTINUOUS_INTEGRATION: true
DISABLE_SIMPLECOV: true
PAM_ENABLED: true
PAM_DEFAULT_SERVICE: pam_test
PAM_CONTROLLED_SERVICE: pam_test_controlled
working_directory: ~/projects/mastodon/
- &attach_workspace
attach_workspace:
at: ~/projects/
- &persist_to_workspace
persist_to_workspace:
root: ~/projects/
paths:
- ./mastodon/
- &restore_ruby_dependencies
restore_cache:
keys:
- v3-ruby-dependencies-{{ checksum "/tmp/.ruby-version" }}-{{ checksum "Gemfile.lock" }}
- v3-ruby-dependencies-{{ checksum "/tmp/.ruby-version" }}-
- v3-ruby-dependencies-
- &install_steps
steps:
- checkout
- *attach_workspace
- restore_cache:
keys:
- v2-node-dependencies-{{ checksum "yarn.lock" }}
- v2-node-dependencies-
- run:
name: Install yarn dependencies
command: yarn install --frozen-lockfile
- save_cache:
key: v2-node-dependencies-{{ checksum "yarn.lock" }}
paths:
- ./node_modules/
- *persist_to_workspace
- &install_system_dependencies
run:
name: Install system dependencies
command: |
sudo apt-get update
sudo apt-get install -y libicu-dev libidn11-dev libprotobuf-dev protobuf-compiler
- &install_ruby_dependencies
steps:
- *attach_workspace
- *install_system_dependencies
- run:
name: Set Ruby version
command: ruby -e 'puts RUBY_VERSION' | tee /tmp/.ruby-version
- *restore_ruby_dependencies
- run:
name: Set bundler settings
command: |
bundle config --local clean 'true'
bundle config --local deployment 'true'
bundle config --local with 'pam_authentication'
bundle config --local without 'development production'
bundle config --local frozen 'true'
bundle config --local path $BUNDLE_PATH
- run:
name: Install bundler dependencies
command: bundle check || (bundle install && bundle clean)
- save_cache:
key: v3-ruby-dependencies-{{ checksum "/tmp/.ruby-version" }}-{{ checksum "Gemfile.lock" }}
paths:
- ./.bundle/
- ./vendor/bundle/
- persist_to_workspace:
root: ~/projects/
paths:
- ./mastodon/.bundle/
- ./mastodon/vendor/bundle/
- &test_steps
parallelism: 4
steps:
- *attach_workspace
- *install_system_dependencies
- run:
name: Install FFMPEG
command: sudo apt-get install -y ffmpeg
- run:
name: Load database schema
command: ./bin/rails db:create db:schema:load db:seed
- run:
name: Run rspec in parallel
command: |
bundle exec rspec --profile 10 \
--format RspecJunitFormatter \
--out test_results/rspec.xml \
--format progress \
$(circleci tests glob "spec/**/*_spec.rb" | circleci tests split --split-by=timings)
- store_test_results:
path: test_results
jobs:
install:
<<: *defaults
<<: *install_steps
install-ruby2.7:
<<: *defaults
<<: *install_ruby_dependencies
install-ruby2.6:
<<: *defaults
docker:
- image: circleci/ruby:2.6-buster-node
environment: *ruby_environment
<<: *install_ruby_dependencies
install-ruby3.0:
<<: *defaults
docker:
- image: circleci/ruby:3.0-buster-node
environment: *ruby_environment
<<: *install_ruby_dependencies
build:
<<: *defaults
steps:
- *attach_workspace
- *install_system_dependencies
- run:
name: Precompile assets
command: ./bin/rails assets:precompile
- persist_to_workspace:
root: ~/projects/
paths:
- ./mastodon/public/assets
- ./mastodon/public/packs-test/
test-migrations:
<<: *defaults
docker:
- image: circleci/ruby:2.7-buster-node
environment: *ruby_environment
- image: circleci/postgres:12.2
environment:
POSTGRES_USER: root
POSTGRES_HOST_AUTH_METHOD: trust
- image: cimg/redis:6.2
- image: circleci/redis:5-alpine
steps:
- *attach_workspace
- *install_system_dependencies
- run:
name: Create database
command: ./bin/rails db:create
- run:
name: Run migrations
command: ./bin/rails db:migrate
commands:
install-system-dependencies:
steps:
- run:
name: Install system dependencies
command: |
sudo apt-get update
sudo apt-get install -y libicu-dev libidn11-dev
install-ruby-dependencies:
parameters:
ruby-version:
type: string
steps:
- run:
command: |
bundle config clean 'true'
bundle config frozen 'true'
bundle config without 'development production'
name: Set bundler settings
- ruby/install-deps:
bundler-version: '2.3.8'
key: ruby<< parameters.ruby-version >>-gems-v1
wait-db:
steps:
- run:
command: dockerize -wait tcp://localhost:5432 -wait tcp://localhost:6379 -timeout 1m
name: Wait for PostgreSQL and Redis
jobs:
build:
test-ruby2.7:
<<: *defaults
docker:
- image: cimg/ruby:3.0-node
- image: circleci/ruby:2.7-buster-node
environment: *ruby_environment
- image: circleci/postgres:12.2
environment:
RAILS_ENV: test
steps:
- checkout
- install-system-dependencies
- install-ruby-dependencies:
ruby-version: '3.0'
- node/install-packages:
cache-version: v1
pkg-manager: yarn
- run:
command: ./bin/rails assets:precompile
name: Precompile assets
- persist_to_workspace:
paths:
- public/assets
- public/packs-test
root: .
POSTGRES_USER: root
POSTGRES_HOST_AUTH_METHOD: trust
- image: circleci/redis:5-alpine
<<: *test_steps
test:
parameters:
ruby-version:
type: string
executor:
name: default
ruby-version: << parameters.ruby-version >>
environment:
ALLOW_NOPAM: true
PAM_ENABLED: true
PAM_DEFAULT_SERVICE: pam_test
PAM_CONTROLLED_SERVICE: pam_test_controlled
parallelism: 4
steps:
- checkout
- install-system-dependencies
- run:
command: sudo apt-get install -y ffmpeg imagemagick libpam-dev
name: Install additional system dependencies
- run:
command: bundle config with 'pam_authentication'
name: Enable PAM authentication
- install-ruby-dependencies:
ruby-version: << parameters.ruby-version >>
- attach_workspace:
at: .
- wait-db
- run:
command: ./bin/rails db:create db:schema:load db:seed
name: Load database schema
- ruby/rspec-test
test-ruby2.6:
<<: *defaults
docker:
- image: circleci/ruby:2.6-buster-node
environment: *ruby_environment
- image: circleci/postgres:12.2
environment:
POSTGRES_USER: root
POSTGRES_HOST_AUTH_METHOD: trust
- image: circleci/redis:5-alpine
<<: *test_steps
test-migrations:
executor:
name: default
ruby-version: '3.0'
steps:
- checkout
- install-system-dependencies
- install-ruby-dependencies:
ruby-version: '3.0'
- wait-db
- run:
command: ./bin/rails db:create
name: Create database
- run:
command: ./bin/rails db:migrate VERSION=20171010025614
name: Run migrations up to v2.0.0
- run:
command: ./bin/rails tests:migrations:populate_v2
name: Populate database with test data
- run:
command: ./bin/rails db:migrate VERSION=20180514140000
name: Run migrations up to v2.4.0
- run:
command: ./bin/rails tests:migrations:populate_v2_4
name: Populate database with test data
- run:
command: ./bin/rails db:migrate
name: Run all remaining migrations
- run:
command: ./bin/rails tests:migrations:check_database
name: Check migration result
test-ruby3.0:
<<: *defaults
docker:
- image: circleci/ruby:3.0-buster-node
environment: *ruby_environment
- image: circleci/postgres:12.2
environment:
POSTGRES_USER: root
POSTGRES_HOST_AUTH_METHOD: trust
- image: circleci/redis:5-alpine
<<: *test_steps
test-two-step-migrations:
executor:
name: default
ruby-version: '3.0'
test-webui:
<<: *defaults
docker:
- image: circleci/node:14-buster
steps:
- checkout
- install-system-dependencies
- install-ruby-dependencies:
ruby-version: '3.0'
- wait-db
- *attach_workspace
- run:
command: ./bin/rails db:create
name: Create database
- run:
command: ./bin/rails db:migrate VERSION=20171010025614
name: Run migrations up to v2.0.0
- run:
command: ./bin/rails tests:migrations:populate_v2
name: Populate database with test data
- run:
command: ./bin/rails db:migrate VERSION=20180514140000
name: Run pre-deployment migrations up to v2.4.0
environment:
SKIP_POST_DEPLOYMENT_MIGRATIONS: true
- run:
command: ./bin/rails tests:migrations:populate_v2_4
name: Populate database with test data
- run:
command: ./bin/rails db:migrate
name: Run all pre-deployment migrations
environment:
SKIP_POST_DEPLOYMENT_MIGRATIONS: true
- run:
command: ./bin/rails db:migrate
name: Run all post-deployment remaining migrations
- run:
command: ./bin/rails tests:migrations:check_database
name: Check migration result
name: Run jest
command: yarn test:jest
workflows:
version: 2
build-and-test:
jobs:
- build
- test:
matrix:
parameters:
ruby-version:
- '2.7'
- '3.0'
name: test-ruby<< matrix.ruby-version >>
- install
- install-ruby2.7:
requires:
- build
- install
- install-ruby2.6:
requires:
- install
- install-ruby2.7
- install-ruby3.0:
requires:
- install
- install-ruby2.7
- build:
requires:
- install-ruby2.7
- test-migrations:
requires:
- build
- test-two-step-migrations:
- install-ruby2.7
- test-ruby2.7:
requires:
- install-ruby2.7
- build
- node/run:
cache-version: v1
name: test-webui
pkg-manager: yarn
- test-ruby2.6:
requires:
- install-ruby2.6
- build
version: lts
yarn-run: test:jest
- test-ruby3.0:
requires:
- install-ruby3.0
- build
- test-webui:
requires:
- install

View File

@ -1,4 +1,4 @@
version: '2'
version: "2"
checks:
argument-count:
enabled: false
@ -34,8 +34,5 @@ plugins:
sass-lint:
enabled: true
exclude_patterns:
- spec/
- vendor/asset/
- app/javascript/mastodon/locales/**/*.json
- config/locales/**/*.yml
- spec/
- vendor/asset

View File

@ -1,24 +0,0 @@
# [Choice] Ruby version (use -bullseye variants on local arm64/Apple Silicon): 3, 3.1, 3.0, 2, 2.7, 2.6, 3-bullseye, 3.1-bullseye, 3.0-bullseye, 2-bullseye, 2.7-bullseye, 2.6-bullseye, 3-buster, 3.1-buster, 3.0-buster, 2-buster, 2.7-buster, 2.6-buster
ARG VARIANT=3.1-bullseye
FROM mcr.microsoft.com/vscode/devcontainers/ruby:${VARIANT}
# Install Rails
# RUN gem install rails webdrivers
# Default value to allow debug server to serve content over GitHub Codespace's port forwarding service
# The value is a comma-separated list of allowed domains
ENV RAILS_DEVELOPMENT_HOSTS=".githubpreview.dev"
# [Choice] Node.js version: lts/*, 16, 14, 12, 10
ARG NODE_VERSION="lts/*"
RUN su vscode -c "source /usr/local/share/nvm/nvm.sh && nvm install ${NODE_VERSION} 2>&1"
# [Optional] Uncomment this section to install additional OS packages.
RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
&& apt-get -y install --no-install-recommends libicu-dev libidn11-dev ffmpeg imagemagick libpam-dev
# [Optional] Uncomment this line to install additional gems.
RUN gem install foreman
# [Optional] Uncomment this line to install global node packages.
RUN su vscode -c "source /usr/local/share/nvm/nvm.sh && npm install -g yarn" 2>&1

View File

@ -1,27 +0,0 @@
{
"name": "Mastodon",
"dockerComposeFile": "docker-compose.yml",
"service": "app",
"workspaceFolder": "/workspaces/mastodon",
// Set *default* container specific settings.json values on container create.
"settings": {},
// Add the IDs of extensions you want installed when the container is created.
"extensions": [
"EditorConfig.EditorConfig",
"dbaeumer.vscode-eslint",
"rebornix.Ruby",
"webben.browserslist"
],
// Use 'forwardPorts' to make a list of ports inside the container available locally.
// This can be used to network with other containers or the host.
"forwardPorts": [3000, 4000],
// Use 'postCreateCommand' to run commands after the container is created.
"postCreateCommand": "bundle install --path vendor/bundle && yarn install && ./bin/rails db:setup",
// Comment out to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root.
"remoteUser": "vscode"
}

View File

@ -1,83 +0,0 @@
version: '3'
services:
app:
build:
context: .
dockerfile: Dockerfile
args:
# Update 'VARIANT' to pick a version of Ruby: 3, 3.1, 3.0, 2, 2.7, 2.6
# Append -bullseye or -buster to pin to an OS version.
# Use -bullseye variants on local arm64/Apple Silicon.
VARIANT: '3.0-bullseye'
# Optional Node.js version to install
NODE_VERSION: '14'
volumes:
- ..:/workspaces/mastodon:cached
environment:
RAILS_ENV: development
NODE_ENV: development
REDIS_HOST: redis
REDIS_PORT: '6379'
DB_HOST: db
DB_USER: postgres
DB_PASS: postgres
DB_PORT: '5432'
ES_ENABLED: 'true'
ES_HOST: es
ES_PORT: '9200'
# Overrides default command so things don't shut down after the process ends.
command: sleep infinity
networks:
- external_network
- internal_network
user: vscode
db:
image: postgres:14-alpine
restart: unless-stopped
volumes:
- postgres-data:/var/lib/postgresql/data
environment:
POSTGRES_USER: postgres
POSTGRES_DB: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_HOST_AUTH_METHOD: trust
networks:
- internal_network
redis:
image: redis:6-alpine
restart: unless-stopped
volumes:
- redis-data:/data
networks:
- internal_network
es:
image: docker.elastic.co/elasticsearch/elasticsearch-oss:7.10.2
restart: unless-stopped
environment:
ES_JAVA_OPTS: -Xms512m -Xmx512m
cluster.name: es-mastodon
discovery.type: single-node
bootstrap.memory_lock: 'true'
volumes:
- es-data:/usr/share/elasticsearch/data
networks:
- internal_network
ulimits:
memlock:
soft: -1
hard: -1
volumes:
postgres-data:
redis-data:
es-data:
networks:
external_network:
internal_network:
internal: true

View File

@ -15,7 +15,6 @@ vendor/bundle
*.swp
*~
postgres
postgres14
redis
elasticsearch
chart

View File

@ -13,7 +13,7 @@ DB_PORT=5432
# DATABASE_URL=postgresql://$DATA_DB_USER:$DATA_DB_PASS@$DATA_DB_HOST/gonano
# Optional Elasticsearch configuration
# Optional ElasticSearch configuration
ES_ENABLED=true
ES_HOST=$DATA_ELASTIC_HOST
ES_PORT=9200
@ -202,6 +202,10 @@ SMTP_FROM_ADDRESS=notifications@${APP_NAME}.nanoapp.io
# Name of the pam service used for checking if an user can register (pam "account" section is evaluated) (nil (disabled) by default)
# PAM_CONTROLLED_SERVICE=rpam
# Global OAuth settings (optional) :
# If you have only one strategy, you may want to enable this
# OAUTH_REDIRECT_AT_SIGN_IN=true
# Optional CAS authentication (cf. omniauth-cas) :
# CAS_ENABLED=true
# CAS_URL=https://sso.myserver.com/

View File

@ -4,12 +4,6 @@
# not demonstrate all available configuration options. Please look at
# https://docs.joinmastodon.org/admin/config/ for the full documentation.
# Note that this file accepts slightly different syntax depending on whether
# you are using `docker-compose` or not. In particular, if you use
# `docker-compose`, the value of each declared variable will be taken verbatim,
# including surrounding quotes.
# See: https://github.com/mastodon/mastodon/issues/16895
# Federation
# ----------
# This identifies your server and cannot be changed safely later
@ -56,14 +50,11 @@ DB_PASS=
DB_PORT=5432
# Elasticsearch (optional)
# ElasticSearch (optional)
# ------------------------
#ES_ENABLED=true
#ES_HOST=localhost
#ES_PORT=9200
# Authentication for ES (optional)
#ES_USER=elastic
#ES_PASS=password
# Secrets
@ -107,7 +98,7 @@ SMTP_SERVER=smtp.mailgun.org
SMTP_PORT=587
SMTP_LOGIN=
SMTP_PASSWORD=
SMTP_FROM_ADDRESS=notifications@example.com
SMTP_FROM_ADDRESS=notificatons@example.com
# File storage (optional)
@ -285,7 +276,3 @@ MAX_POLL_OPTION_CHARS=100
# Units are in bytes
MAX_EMOJI_SIZE=51200
MAX_REMOTE_EMOJI_SIZE=204800
# Optional hCaptcha support
# HCAPTCHA_SECRET_KEY=
# HCAPTCHA_SITE_KEY=

View File

@ -79,11 +79,6 @@ module.exports = {
'no-irregular-whitespace': 'error',
'no-mixed-spaces-and-tabs': 'warn',
'no-nested-ternary': 'warn',
'no-restricted-properties': [
'error',
{ property: 'substring', message: 'Use .slice instead of .substring.' },
{ property: 'substr', message: 'Use .slice instead of .substr.' },
],
'no-trailing-spaces': 'warn',
'no-undef': 'error',
'no-unreachable': 'error',

32
.github/CODEOWNERS vendored 100644
View File

@ -0,0 +1,32 @@
# CODEOWNERS for mastodon/mastodon
# Translators
# To add translator, copy these lines, replace `fr` with appropriate language code and replace `@żelipapą` with user's GitHub nickname preceded by `@` sign or e-mail address.
# /app/javascript/mastodon/locales/fr.json @żelipapą
# /app/views/user_mailer/*.fr.html.erb @żelipapą
# /app/views/user_mailer/*.fr.text.erb @żelipapą
# /config/locales/*.fr.yml @żelipapą
# /config/locales/fr.yml @żelipapą
# Polish
/app/javascript/mastodon/locales/pl.json @m4sk1n
/app/views/user_mailer/*.pl.html.erb @m4sk1n
/app/views/user_mailer/*.pl.text.erb @m4sk1n
/config/locales/*.pl.yml @m4sk1n
/config/locales/pl.yml @m4sk1n
# French
/app/javascript/mastodon/locales/fr.json @aldarone
/app/javascript/mastodon/locales/whitelist_fr.json @aldarone
/app/views/user_mailer/*.fr.html.erb @aldarone
/app/views/user_mailer/*.fr.text.erb @aldarone
/config/locales/*.fr.yml @aldarone
/config/locales/fr.yml @aldarone
# Dutch
/app/javascript/mastodon/locales/nl.json @jeroenpraat
/app/javascript/mastodon/locales/whitelist_nl.json @jeroenpraat
/app/views/user_mailer/*.nl.html.erb @jeroenpraat
/app/views/user_mailer/*.nl.text.erb @jeroenpraat
/config/locales/*.nl.yml @jeroenpraat
/config/locales/nl.yml @jeroenpraat

2
.github/FUNDING.yml vendored
View File

@ -1,3 +1,3 @@
patreon: mastodon
open_collective: mastodon
custom: https://sponsor.joinmastodon.org
github: [Gargron]

View File

@ -8,17 +8,6 @@ body:
Make sure that you are submitting a new bug that was not previously reported or already fixed.
Please use a concise and distinct title for the issue.
- type: textarea
attributes:
label: Steps to reproduce the problem
description: What were you trying to do?
value: |
1.
2.
3.
...
validations:
required: true
- type: input
attributes:
label: Expected behaviour
@ -31,6 +20,17 @@ body:
description: What happened?
validations:
required: true
- type: textarea
attributes:
label: Steps to reproduce the problem
description: What were you trying to do?
value: |
1.
2.
3.
...
validations:
required: true
- type: textarea
attributes:
label: Specifications

View File

@ -1,6 +1,5 @@
name: Feature Request
description: I have a suggestion
labels: suggestion
body:
- type: markdown
attributes:

View File

@ -0,0 +1,10 @@
---
name: Support
about: Ask for help with your deployment
title: DO NOT CREATE THIS ISSUE
---
We primarily use GitHub as a bug and feature tracker. For usage questions, troubleshooting of deployments and other individual technical assistance, please use one of the resources below:
- https://discourse.joinmastodon.org
- #mastodon on irc.freenode.net

View File

@ -1,8 +1,5 @@
blank_issues_enabled: false
contact_links:
- name: GitHub Discussions
url: https://github.com/mastodon/mastodon/discussions
- name: Mastodon Meta Discussion Board
url: https://discourse.joinmastodon.org/
about: Please ask and answer questions here.
- name: Bug Bounty Program
url: https://app.intigriti.com/programs/mastodon/mastodonio/detail
about: Please report security vulnerabilities here.

View File

@ -1,43 +0,0 @@
name: Build container image
on:
workflow_dispatch:
push:
branches:
- 'main'
tags:
- '*'
pull_request:
paths:
- .github/workflows/build-image.yml
- Dockerfile
jobs:
build-image:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: docker/setup-qemu-action@v1
- uses: docker/setup-buildx-action@v1
- uses: docker/login-action@v1
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
if: github.event_name != 'pull_request'
- uses: docker/metadata-action@v3
id: meta
with:
images: ghcr.io/${{ github.repository_owner }}/mastodon
flavor: |
latest=true
tags: |
type=edge,branch=main
type=match,pattern=v(.*),group=0
type=ref,event=pr
- uses: docker/build-push-action@v2
with:
context: .
platforms: linux/amd64,linux/arm64
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
cache-from: type=registry,ref=ghcr.io/${{ github.repository_owner }}/mastodon:latest
cache-to: type=inline

View File

@ -2,9 +2,9 @@ name: Check i18n
on:
push:
branches: [main]
branches: [ main ]
pull_request:
branches: [main]
branches: [ main ]
env:
RAILS_ENV: test
@ -14,21 +14,21 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Install system dependencies
run: |
sudo apt-get update
sudo apt-get install -y libicu-dev libidn11-dev
- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: '3.0'
bundler-cache: true
- name: Check locale file normalization
run: bundle exec i18n-tasks check-normalized
- name: Check for unused strings
run: bundle exec i18n-tasks unused -l en
- name: Check for wrong string interpolations
run: bundle exec i18n-tasks check-consistent-interpolations
- name: Check that all required locale files exist
run: bundle exec rake repo:check_locales_files
- uses: actions/checkout@v2
- name: Install system dependencies
run: |
sudo apt-get update
sudo apt-get install -y libicu-dev libidn11-dev libprotobuf-dev protobuf-compiler
- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: '2.7'
bundler-cache: true
- name: Check locale file normalization
run: bundle exec i18n-tasks check-normalized
- name: Check for unused strings
run: bundle exec i18n-tasks unused -l en
- name: Check for wrong string interpolations
run: bundle exec i18n-tasks check-consistent-interpolations
- name: Check that all required locale files exist
run: bundle exec rake repo:check_locales_files

1
.gitignore vendored
View File

@ -40,7 +40,6 @@
# Ignore postgres + redis + elasticsearch volume optionally created by docker-compose
/postgres
/postgres14
/redis
/elasticsearch

View File

@ -1,78 +0,0 @@
# See https://help.github.com/articles/ignoring-files for more about ignoring files.
#
# If you find yourself ignoring temporary files generated by your text editor
# or operating system, you probably want to add a global ignore instead:
# git config --global core.excludesfile '~/.gitignore_global'
# Ignore bundler config and downloaded libraries.
/.bundle
/vendor/bundle
# Ignore the default SQLite database.
/db/*.sqlite3
/db/*.sqlite3-journal
# Ignore all logfiles and tempfiles.
.eslintcache
/log/*
!/log/.keep
/tmp
/coverage
/public/system
/public/assets
/public/packs
/public/packs-test
.env
.env.production
.env.development
/node_modules/
/build/
# Ignore Vagrant files
.vagrant/
# Ignore Capistrano customizations
/config/deploy/*
# Ignore IDE files
.vscode/
.idea/
# Ignore postgres + redis + elasticsearch volume optionally created by docker-compose
/postgres
/postgres14
/redis
/elasticsearch
# ignore Helm dependency charts
/chart/charts/*.tgz
# Ignore Apple files
.DS_Store
# Ignore vim files
*~
*.swp
# Ignore npm debug log
npm-debug.log
# Ignore yarn log files
yarn-error.log
yarn-debug.log
# Ignore vagrant log files
*-cloudimg-console.log
# Ignore Docker option files
docker-compose.override.yml
# Ignore Helm files
/chart
# Ignore emoji map file
/app/javascript/mastodon/features/emoji/emoji_map.json
# Ignore locale files
/app/javascript/mastodon/locales
/config/locales

View File

@ -1,3 +0,0 @@
module.exports = {
singleQuote: true
}

View File

@ -5,17 +5,17 @@ AllCops:
TargetRubyVersion: 2.5
NewCops: disable
Exclude:
- 'spec/**/*'
- 'db/**/*'
- 'app/views/**/*'
- 'config/**/*'
- 'bin/*'
- 'Rakefile'
- 'node_modules/**/*'
- 'Vagrantfile'
- 'vendor/**/*'
- 'lib/json_ld/*'
- 'lib/templates/**/*'
- 'spec/**/*'
- 'db/**/*'
- 'app/views/**/*'
- 'config/**/*'
- 'bin/*'
- 'Rakefile'
- 'node_modules/**/*'
- 'Vagrantfile'
- 'vendor/**/*'
- 'lib/json_ld/*'
- 'lib/templates/**/*'
Bundler/OrderedGems:
Enabled: false
@ -29,17 +29,13 @@ Layout/EmptyLineAfterMagicComment:
Layout/EmptyLineAfterGuardClause:
Enabled: false
Layout/EmptyLineBetweenDefs:
AllowAdjacentOneLineDefs: true
Layout/EmptyLinesAroundAttributeAccessor:
Enabled: true
Layout/FirstHashElementIndentation:
EnforcedStyle: consistent
Layout/HashAlignment:
Enabled: false
# EnforcedHashRocketStyle: table
# EnforcedColonStyle: table
Layout/SpaceAroundMethodCallOperator:
Enabled: true

View File

@ -1 +1 @@
3.0.3
2.7.4

File diff suppressed because it is too large Load Diff

View File

@ -4,8 +4,10 @@ libicu-dev
libidn11
libidn11-dev
libpq-dev
libprotobuf-dev
libxdamage1
libxfixes3
protobuf-compiler
zlib1g-dev
libcairo2
libcroco3

View File

@ -3,440 +3,6 @@ Changelog
All notable changes to this project will be documented in this file.
## [3.5.3] - 2022-05-26
### Added
- **Add language dropdown to compose form in web UI** ([Gargron](https://github.com/mastodon/mastodon/pull/18420), [ykzts](https://github.com/mastodon/mastodon/pull/18460))
- **Add warning for limited accounts in web UI** ([Gargron](https://github.com/mastodon/mastodon/pull/18344))
- Add `limited` attribute to accounts in REST API ([Gargron](https://github.com/mastodon/mastodon/pull/18344))
### Changed
- **Change RSS feeds** ([Gargron](https://github.com/mastodon/mastodon/pull/18356), [tribela](https://github.com/mastodon/mastodon/pull/18406))
- Titles are now date and time of post
- Bodies now render all content faithfully, including polls and emojis
- All media attachments are included with Media RSS
- Change "dangerous" to "sensitive" in privacy policy and web UI ([Gargron](https://github.com/mastodon/mastodon/pull/18515))
- Change unconfirmed accounts to not be visible in REST API ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17530))
- Change `tootctl search deploy` to improve performance ([Gargron](https://github.com/mastodon/mastodon/pull/18463), [Gargron](https://github.com/mastodon/mastodon/pull/18514))
- Change search indexing to use batches to minimize resource usage ([Gargron](https://github.com/mastodon/mastodon/pull/18451))
### Fixed
- Fix follower and other counters being able to go negative ([Gargron](https://github.com/mastodon/mastodon/pull/18517))
- Fix unnecessary query on when creating a status ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17901))
- Fix warning an account outside of a report closing all reports for that account ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/18387))
- Fix error when resolving a link that redirects to a local post ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/18314))
- Fix preferred posting language returning unusable value in REST API ([Gargron](https://github.com/mastodon/mastodon/pull/18428))
- Fix race condition error when external status is reblogged ([ykzts](https://github.com/mastodon/mastodon/pull/18424))
- Fix missing string for appeal validation error ([Gargron](https://github.com/mastodon/mastodon/pull/18410))
- Fix block/mute lists showing a follow button in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/18364))
- Fix Redis configuration not being changed by `mastodon:setup` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/18383))
- Fix streaming notifications not using quick filter logic in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/18316))
- Fix ambiguous wording on appeal actions in admin UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/18328))
- Fix floating action button obscuring last element in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/18332))
- Fix account warnings not being recorded in audit log ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/18338))
- Fix leftover icons for direct visibility statuses ([Steffo99](https://github.com/mastodon/mastodon/pull/18305))
- Fix link verification requiring case sensitivity on links ([sgolemon](https://github.com/mastodon/mastodon/pull/18320))
- Fix embeds not setting their height correctly ([rinsuki](https://github.com/mastodon/mastodon/pull/18301))
### Security
- Fix concurrent unfollowing decrementing follower count more than once ([Gargron](https://github.com/mastodon/mastodon/pull/18527))
- Fix being able to appeal a strike unlimited times ([Gargron](https://github.com/mastodon/mastodon/pull/18529))
- Fix being able to report otherwise inaccessible statuses ([Gargron](https://github.com/mastodon/mastodon/pull/18528))
- Fix empty votes arbitrarily increasing voters count in polls ([Gargron](https://github.com/mastodon/mastodon/pull/18526))
- Fix moderator identity leak when approving appeal of sensitive marked statuses ([Gargron](https://github.com/mastodon/mastodon/pull/18525))
- Fix suspended users being able to access APIs that don't require a user ([Gargron](https://github.com/mastodon/mastodon/pull/18524))
- Fix confirmation redirect to app without `Location` header ([Gargron](https://github.com/mastodon/mastodon/pull/18523))
## [3.5.2] - 2022-05-04
### Added
- Add warning on direct messages screen in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/18289))
- We already had a warning when composing a direct message, it has now been reworded to be more clear
- Same warning is now displayed when viewing sent and received direct messages
- Add ability to set approval-based registration through tootctl ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/18248))
- Add pre-filling of domain from search filter in domain allow/block admin UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/18172))
## Changed
- Change name of “Direct” visibility to “Mentioned people only” in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/18146), [Gargron](https://github.com/mastodon/mastodon/pull/18289), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/18291))
- Change trending posts to only show one post from each account ([Gargron](https://github.com/mastodon/mastodon/pull/18181))
- Change half-life of trending posts from 6 hours to 2 hours ([Gargron](https://github.com/mastodon/mastodon/pull/18182))
- Change full-text search feature to also include polls you have voted in ([tribela](https://github.com/mastodon/mastodon/pull/18070))
- Change Redis from using one connection per process, to using a connection pool ([Gargron](https://github.com/mastodon/mastodon/pull/18135), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/18160), [Gargron](https://github.com/mastodon/mastodon/pull/18171))
- Different threads no longer have to wait on a mutex over a single connection
- However, this does increase the number of Redis connections by a fair amount
- We are planning to optimize Redis use so that the pool can be made smaller in the future
## Removed
- Remove IP matching from e-mail domain blocks ([Gargron](https://github.com/mastodon/mastodon/pull/18190))
- The IPs of the blocked e-mail domain or its MX records are no longer checked
- Previously it was too easy to block e-mail providers by mistake
## Fixed
- Fix compatibility with Friendica's pinned posts ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/18254), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/18260))
- Fix error when looking up handle with surrounding spaces in REST API ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/18225))
- Fix double render error when authorizing interaction ([Gargron](https://github.com/mastodon/mastodon/pull/18203))
- Fix error when a post references an invalid media attachment ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/18211))
- Fix error when trying to revoke OAuth token without supplying a token ([Gargron](https://github.com/mastodon/mastodon/pull/18205))
- Fix error caused by missing subject in Webfinger response ([Gargron](https://github.com/mastodon/mastodon/pull/18204))
- Fix error on attempting to delete an account moderation note ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/18196))
- Fix light-mode emoji borders in web UI ([Gaelan](https://github.com/mastodon/mastodon/pull/18131))
- Fix being able to scroll away from the loading bar in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/18170))
- Fix error when a bookmark or favorite has been reported and deleted ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/18174))
- Fix being offered empty “Server rules violation” report option in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/18165))
- Fix temporary network errors preventing from authorizing interactions with remote accounts ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/18161))
- Fix incorrect link in "new trending tags" email ([cdzombak](https://github.com/mastodon/mastodon/pull/18156))
- Fix missing indexes on some foreign keys ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/18157))
- Fix n+1 query on feed merge and populate operations ([Gargron](https://github.com/mastodon/mastodon/pull/18111))
- Fix feed unmerge worker being exceptionally slow in some conditions ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/18110))
- Fix PeerTube videos appearing with an erroneous “Edited at” marker ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/18100))
- Fix instance actor being created incorrectly when running through migrations ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/18109))
- Fix web push notifications containing HTML entities ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/18071))
- Fix inconsistent parsing of `TRUSTED_PROXY_IP` ([ykzts](https://github.com/mastodon/mastodon/pull/18051))
- Fix error when fetching pinned posts ([tribela](https://github.com/mastodon/mastodon/pull/18030))
- Fix wrong optimization in feed populate operation ([dogelover911](https://github.com/mastodon/mastodon/pull/18009))
- Fix error in alias settings page ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/18004))
## [3.5.1] - 2022-04-08
### Added
- Add pagination for trending statuses in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/17976))
### Changed
- Change e-mail notifications to only be sent when recipient is offline ([Gargron](https://github.com/mastodon/mastodon/pull/17984))
- Send e-mails for mentions and follows by default again
- But only when recipient does not have push notifications through an app
- Change `website` attribute to be nullable on `Application` entity in REST API ([rinsuki](https://github.com/mastodon/mastodon/pull/17962))
### Removed
- Remove sign-in token authentication, instead send e-mail about new sign-in ([Gargron](https://github.com/mastodon/mastodon/pull/17970))
- You no longer need to enter a security code sent through e-mail
- Instead you get an e-mail about a new sign-in from an unfamiliar IP address
### Fixed
- Fix error resposes for `from` search prefix ([single-right-quote](https://github.com/mastodon/mastodon/pull/17963))
- Fix dangling language-specific trends ([Gargron](https://github.com/mastodon/mastodon/pull/17997))
- Fix extremely rare race condition when deleting a status or account ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17994))
- Fix trends returning less results per page when filtered in REST API ([Gargron](https://github.com/mastodon/mastodon/pull/17996))
- Fix pagination header on empty trends responses in REST API ([Gargron](https://github.com/mastodon/mastodon/pull/17986))
- Fix cookies secure flag being set when served over Tor ([Gargron](https://github.com/mastodon/mastodon/pull/17992))
- Fix migration error handling ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17991))
- Fix error when re-running some migrations if they get interrupted at the wrong moment ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17989))
- Fix potentially missing statuses when reconnecting to streaming API in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17981), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/17987), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/17980))
- Fix error when sending warning emails with custom text ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17983))
- Fix unset `SMTP_RETURN_PATH` environment variable causing e-mail not to send ([Gargron](https://github.com/mastodon/mastodon/pull/17982))
- Fix possible duplicate statuses in timelines in some edge cases in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17971))
- Fix spurious edits and require incoming edits to be explicitly marked as such ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17918))
- Fix error when encountering invalid pinned statuses ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17964))
- Fix inconsistency in error handling when removing a status ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17974))
- Fix admin API unconditionally requiring CSRF token ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17975))
- Fix trending tags endpoint missing `offset` param in REST API ([Gargron](https://github.com/mastodon/mastodon/pull/17973))
- Fix unusual number formatting in some locales ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17929))
- Fix `S3_FORCE_SINGLE_REQUEST` environment variable not working ([HolgerHuo](https://github.com/mastodon/mastodon/pull/17922))
- Fix failure to build assets with OpenSSL 3 ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17930))
- Fix PWA manifest using outdated routes ([HolgerHuo](https://github.com/mastodon/mastodon/pull/17921))
- Fix error when indexing statuses into Elasticsearch ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17912))
## [3.5.0] - 2022-03-30
### Added
- **Add support for incoming edited posts** ([Gargron](https://github.com/mastodon/mastodon/pull/16697), [Gargron](https://github.com/mastodon/mastodon/pull/17727), [Gargron](https://github.com/mastodon/mastodon/pull/17728), [Gargron](https://github.com/mastodon/mastodon/pull/17320), [Gargron](https://github.com/mastodon/mastodon/pull/17404), [Gargron](https://github.com/mastodon/mastodon/pull/17390), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/17335), [Gargron](https://github.com/mastodon/mastodon/pull/17696), [Gargron](https://github.com/mastodon/mastodon/pull/17745), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/17740), [Gargron](https://github.com/mastodon/mastodon/pull/17697), [Gargron](https://github.com/mastodon/mastodon/pull/17648), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/17531), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/17499), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/17498), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/17380), [Gargron](https://github.com/mastodon/mastodon/pull/17373), [Gargron](https://github.com/mastodon/mastodon/pull/17334), [Gargron](https://github.com/mastodon/mastodon/pull/17333), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/17699), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/17748))
- Previous versions remain available for perusal and comparison
- People who reblogged a post are notified when it's edited
- New REST APIs:
- `PUT /api/v1/statuses/:id`
- `GET /api/v1/statuses/:id/history`
- `GET /api/v1/statuses/:id/source`
- New streaming API event:
- `status.update`
- **Add appeals for moderator decisions** ([Gargron](https://github.com/mastodon/mastodon/pull/17364), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/17725), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/17566), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/17652), [Gargron](https://github.com/mastodon/mastodon/pull/17616), [Gargron](https://github.com/mastodon/mastodon/pull/17615), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/17554), [Gargron](https://github.com/mastodon/mastodon/pull/17523))
- All default moderator decisions now notify the affected user by e-mail
- They now link to an appeal page instead of suggesting replying to the e-mail
- They can now be found in account settings and not just e-mail
- Users can submit one appeal within 20 days of the decision
- Moderators can approve or reject the appeal
- **Add notifications for posts deleted by moderators** ([Gargron](https://github.com/mastodon/mastodon/pull/17204), [Gargron](https://github.com/mastodon/mastodon/pull/17668), [Gargron](https://github.com/mastodon/mastodon/pull/17746), [Gargron](https://github.com/mastodon/mastodon/pull/17679), [Gargron](https://github.com/mastodon/mastodon/pull/17487))
- New, redesigned report view in admin UI
- Common report actions now only take one click to complete
- Deleting posts or marking as sensitive from report now notifies user
- Reports can be categorized by reason and specific rules violated
- The reasons are automatically cited in the notifications, except for spam
- Marking posts as sensitive now federates using post editing
- **Add explore page with trending posts and links** ([Gargron](https://github.com/mastodon/mastodon/pull/17123), [Gargron](https://github.com/mastodon/mastodon/pull/17431), [Gargron](https://github.com/mastodon/mastodon/pull/16917), [Gargron](https://github.com/mastodon/mastodon/pull/17677), [Gargron](https://github.com/mastodon/mastodon/pull/16938), [Gargron](https://github.com/mastodon/mastodon/pull/17044), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/16978), [Gargron](https://github.com/mastodon/mastodon/pull/16979), [tribela](https://github.com/mastodon/mastodon/pull/17066), [Gargron](https://github.com/mastodon/mastodon/pull/17072), [Gargron](https://github.com/mastodon/mastodon/pull/17403), [noiob](https://github.com/mastodon/mastodon/pull/17624), [mayaeh](https://github.com/mastodon/mastodon/pull/17755), [mayaeh](https://github.com/mastodon/mastodon/pull/17757), [Gargron](https://github.com/mastodon/mastodon/pull/17760), [mayaeh](https://github.com/mastodon/mastodon/pull/17762))
- Hashtag trends algorithm is extended to work for posts and links
- Links are only considered if they have an adequate preview card
- Preview card generation has been improved to support structured data
- Links can only trend if the publisher (domain) has been approved
- Posts can only trend if the author has been approved
- Individual approval and rejection for posts and links is also available
- Moderators are notified about pending trends at most once every 2 hours
- Posts and link trends are language-specific
- Search page is redesigned into explore page in web UI
- Discovery tab is coming soon in official iOS and Android apps
- New REST APIs:
- `GET /api/v1/trends/links`
- `GET /api/v1/trends/statuses`
- `GET /api/v1/trends/tags` (alias of `GET /api/v1/trends`)
- `GET /api/v1/admin/trends/links`
- `GET /api/v1/admin/trends/statuses`
- `GET /api/v1/admin/trends/tags`
- **Add graphs and retention metrics to admin dashboard** ([Gargron](https://github.com/mastodon/mastodon/pull/16829), [Gargron](https://github.com/mastodon/mastodon/pull/17617), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/17570), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/16910), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/16909), [mashirozx](https://github.com/mastodon/mastodon/pull/16884), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/16854))
- Dashboard shows more numbers with development over time
- Other data such as most used interface languages and sign-up sources
- User retention graph shows how many new users stick around
- New REST APIs:
- `POST /api/v1/admin/measures`
- `POST /api/v1/admin/dimensions`
- `POST /api/v1/admin/retention`
- Add `GET /api/v1/accounts/familiar_followers` to REST API ([Gargron](https://github.com/mastodon/mastodon/pull/17700))
- Add `POST /api/v1/accounts/:id/remove_from_followers` to REST API ([noellabo](https://github.com/mastodon/mastodon/pull/16864))
- Add `category` and `rule_ids` params to `POST /api/v1/reports` IN REST API ([Gargron](https://github.com/mastodon/mastodon/pull/17492), [Gargron](https://github.com/mastodon/mastodon/pull/17682), [Gargron](https://github.com/mastodon/mastodon/pull/17713))
- `category` can be one of: `spam`, `violation`, `other` (default)
- `rule_ids` must reference `rules` returned in `GET /api/v1/instance`
- Add global `lang` param to REST API ([Gargron](https://github.com/mastodon/mastodon/pull/17464), [Gargron](https://github.com/mastodon/mastodon/pull/17592))
- Add `types` param to `GET /api/v1/notifications` in REST API ([Gargron](https://github.com/mastodon/mastodon/pull/17767))
- **Add notifications for moderators about new sign-ups** ([Gargron](https://github.com/mastodon/mastodon/pull/16953), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/17629))
- When a new user confirms e-mail, moderators receive a notification
- New notification type:
- `admin.sign_up`
- Add authentication history ([Gargron](https://github.com/mastodon/mastodon/pull/16408), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/16428), [baby-gnu](https://github.com/mastodon/mastodon/pull/16654))
- Add ability to automatically delete old posts ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16529), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/17691), [tribela](https://github.com/mastodon/mastodon/pull/16653))
- Add ability to pin private posts ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16954), [tribela](https://github.com/mastodon/mastodon/pull/17326), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/17304), [MitarashiDango](https://github.com/mastodon/mastodon/pull/17647))
- Add ability to filter search results by author using `from:` syntax ([tribela](https://github.com/mastodon/mastodon/pull/16526))
- Add ability to delete canonical email blocks in admin UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16644))
- Add ability to purge undeliverable domains in admin UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16686), [tribela](https://github.com/mastodon/mastodon/pull/17210), [tribela](https://github.com/mastodon/mastodon/pull/17741), [tribela](https://github.com/mastodon/mastodon/pull/17209))
- Add ability to disable e-mail token authentication for specific users in admin UI ([Gargron](https://github.com/mastodon/mastodon/pull/16427))
- **Add ability to suspend accounts in batches in admin UI** ([Gargron](https://github.com/mastodon/mastodon/pull/17009), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/17301), [Gargron](https://github.com/mastodon/mastodon/pull/17444))
- New, redesigned accounts list in admin UI
- Batch suspensions are meant to help clean up spam and bot accounts
- They do not generate notifications
- Add ability to filter reports by origin of target account in admin UI ([Gargron](https://github.com/mastodon/mastodon/pull/16487))
- Add support for login through OpenID Connect ([chandrn7](https://github.com/mastodon/mastodon/pull/16221))
- Add lazy loading for emoji picker in web UI ([mashirozx](https://github.com/mastodon/mastodon/pull/16907), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/17011))
- Add single option votes tooltip in polls in web UI ([Brawaru](https://github.com/mastodon/mastodon/pull/16849))
- Add confirmation modal when closing media edit modal with unsaved changes in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16518))
- Add hint about missing media attachment description in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/17845))
- Add support for fetching Create and Announce activities by URI in ActivityPub ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16383))
- Add `S3_FORCE_SINGLE_REQUEST` environment variable ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16866))
- Add `OMNIAUTH_ONLY` environment variable ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17288), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/17345))
- Add `ES_USER` and `ES_PASS` environment variables for Elasticsearch authentication ([tribela](https://github.com/mastodon/mastodon/pull/16890))
- Add `CAS_SECURITY_ASSUME_EMAIL_IS_VERIFIED` environment variable ([baby-gnu](https://github.com/mastodon/mastodon/pull/16655))
- Add ability to pass specific domains to `tootctl accounts cull` ([tribela](https://github.com/mastodon/mastodon/pull/16511))
- Add `--by-uri` option to `tootctl domains purge` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16434))
- Add `--batch-size` option to `tootctl search deploy` ([aquarla](https://github.com/mastodon/mastodon/pull/17049))
- Add `--remove-orphans` option to `tootctl statuses remove` ([noellabo](https://github.com/mastodon/mastodon/pull/17067))
### Changed
- Change design of federation pages in admin UI ([Gargron](https://github.com/mastodon/mastodon/pull/17704), [noellabo](https://github.com/mastodon/mastodon/pull/17735), [Gargron](https://github.com/mastodon/mastodon/pull/17765))
- Change design of account cards in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/17689))
- Change `follow` scope to be covered by `read` and `write` scopes in REST API ([Gargron](https://github.com/mastodon/mastodon/pull/17678))
- Change design of authorized applications page ([Gargron](https://github.com/mastodon/mastodon/pull/17656), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/17686))
- Change e-mail domain blocks to block IPs dynamically ([Gargron](https://github.com/mastodon/mastodon/pull/17635), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/17650), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/17649))
- Change report modal to include category selection in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/17565), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/17734), [Gargron](https://github.com/mastodon/mastodon/pull/17654), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/17632))
- Change reblogs to not count towards hashtag trends anymore ([Gargron](https://github.com/mastodon/mastodon/pull/17501))
- Change languages to be listed under standard instead of native name in admin UI ([Gargron](https://github.com/mastodon/mastodon/pull/17485))
- Change routing paths to use usernames in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/16171), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/16772), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/16773), [mashirozx](https://github.com/mastodon/mastodon/pull/16793), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/17060))
- Change list title input design in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17092))
- Change "Opt-in to profile directory" preference to be general discoverability preference ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16637))
- Change API rate limits to use /64 masking on IPv6 addresses ([tribela](https://github.com/mastodon/mastodon/pull/17588), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/17600), [zunda](https://github.com/mastodon/mastodon/pull/17590))
- Change allowed formats for locally uploaded custom emojis to include GIF ([rgroothuijsen](https://github.com/mastodon/mastodon/pull/17706), [Gargron](https://github.com/mastodon/mastodon/pull/17759))
- Change error message when chosen password is too long ([rgroothuijsen](https://github.com/mastodon/mastodon/pull/17082))
- Change minimum required Elasticsearch version from 6 to 7 ([noellabo](https://github.com/mastodon/mastodon/pull/16915))
### Removed
- Remove profile directory link from main navigation panel in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/17688))
- **Remove language detection through cld3** ([Gargron](https://github.com/mastodon/mastodon/pull/17478), [ykzts](https://github.com/mastodon/mastodon/pull/17539), [Gargron](https://github.com/mastodon/mastodon/pull/17496), [Gargron](https://github.com/mastodon/mastodon/pull/17722))
- cld3 is very inaccurate on short-form content even with unique alphabets
- Post language can be overriden individually using `language` param
- Otherwise, it defaults to the user's interface language
- Remove support for `OAUTH_REDIRECT_AT_SIGN_IN` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17287))
- Use `OMNIAUTH_ONLY` instead
- Remove Keybase integration ([Gargron](https://github.com/mastodon/mastodon/pull/17045))
- Remove old columns and indexes ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17245), [Gargron](https://github.com/mastodon/mastodon/pull/16409), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/17191))
- Remove shortcodes from newly-created media attachments ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16730), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/16763))
### Deprecated
- `GET /api/v1/trends``GET /api/v1/trends/tags`
- OAuth `follow` scope → `read` and/or `write`
- `text` attribute on `DELETE /api/v1/statuses/:id``GET /api/v1/statuses/:id/source`
### Fixed
- Fix IDN domains not being rendered correctly in a few left-over places ([Gargron](https://github.com/mastodon/mastodon/pull/17848))
- Fix Sanskrit translation not being used in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17820))
- Fix Kurdish languages having the wrong language codes ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17812))
- Fix pghero making database schema suggestions ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17807))
- Fix encoding glitch in the OpenGraph description of a profile page ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17821))
- Fix web manifest not permitting PWA usage from alternate domains ([HolgerHuo](https://github.com/mastodon/mastodon/pull/16714))
- Fix not being able to edit media attachments for scheduled posts ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17690))
- Fix subscribed relay activities being recorded as boosts ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17571))
- Fix streaming API server error messages when JSON parsing fails not specifying the source ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17559))
- Fix browsers autofilling new password field with old password ([mashirozx](https://github.com/mastodon/mastodon/pull/17702))
- Fix text being invisible before fonts load in web UI ([tribela](https://github.com/mastodon/mastodon/pull/16330))
- Fix public profile pages of unconfirmed users being accessible ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17385), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/17457))
- Fix nil error when trying to fetch key for signature verification ([Gargron](https://github.com/mastodon/mastodon/pull/17747))
- Fix null values being included in some indexes ([Gargron](https://github.com/mastodon/mastodon/pull/17711))
- Fix `POST /api/v1/emails/confirmations` not being available after sign-up ([Gargron](https://github.com/mastodon/mastodon/pull/17743))
- Fix rare race condition when reblogged post is deleted ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17693), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/17730))
- Fix being able to add more than 4 hashtags to hashtag column in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/17729))
- Fix data integrity of featured tags ([Gargron](https://github.com/mastodon/mastodon/pull/17712))
- Fix performance of account timelines ([Gargron](https://github.com/mastodon/mastodon/pull/17709))
- Fix returning empty `<p>` tag for blank account `note` in REST API ([Gargron](https://github.com/mastodon/mastodon/pull/17687))
- Fix leak of existence of otherwise inaccessible posts in REST API ([Gargron](https://github.com/mastodon/mastodon/pull/17684))
- Fix not showing loading indicator when searching in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/17655))
- Fix media modal footer's “external link” not being a link ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17561))
- Fix reply button on media modal not giving focus to compose form ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17626))
- Fix some media attachments being converted with too high framerates ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17619))
- Fix sign in token and warning emails failing to send when contact e-mail address is malformed ([helloworldstack](https://github.com/mastodon/mastodon/pull/17589))
- Fix opening the emoji picker scrolling the single-column view to the top ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17579))
- Fix edge case where settings/admin page sidebar would be incorrectly hidden ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17580))
- Fix performance of server-side filtering ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17575))
- Fix privacy policy link not being visible on small screens ([Gargron](https://github.com/mastodon/mastodon/pull/17533))
- Fix duplicate accounts when searching by IP range in admin UI ([Gargron](https://github.com/mastodon/mastodon/pull/17524), [tribela](https://github.com/mastodon/mastodon/pull/17150))
- Fix error when performing a batch action on posts in admin UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17532))
- Fix deletes not being signed in authorized fetch mode ([Gargron](https://github.com/mastodon/mastodon/pull/17484))
- Fix Undo Announce sometimes inlining the originally Announced status ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17516))
- Fix localization of cold-start follow recommendations ([Gargron](https://github.com/mastodon/mastodon/pull/17479), [Gargron](https://github.com/mastodon/mastodon/pull/17486))
- Fix replies collection incorrectly looping ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17462))
- Fix errors when multiple Delete are received for a given actor ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17460))
- Fixed prototype pollution bug and only allow trusted origin ([r0hanSH](https://github.com/mastodon/mastodon/pull/17420))
- Fix text being incorrectly pre-selected in composer textarea on /share ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17339))
- Fix SMTP_ENABLE_STARTTLS_AUTO/SMTP_TLS/SMTP_SSL environment variables don't work ([kgtkr](https://github.com/mastodon/mastodon/pull/17216))
- Fix media upload specific rate limits only being applied to v1 endpoint in REST API ([tribela](https://github.com/mastodon/mastodon/pull/17272))
- Fix media descriptions not being used for client-side filtering ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17206))
- Fix cold-start follow recommendation favouring older accounts due to wrong sorting ([noellabo](https://github.com/mastodon/mastodon/pull/17126))
- Fix not redirect to the right page after authenticating with WebAuthn ([heguro](https://github.com/mastodon/mastodon/pull/17098))
- Fix searching for additional hashtags in hashtag column ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17054))
- Fix color of hashtag column settings inputs ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17058))
- Fix performance of `tootctl statuses remove` ([noellabo](https://github.com/mastodon/mastodon/pull/17052))
- Fix `tootctl accounts cull` not excluding domains on timeouts and certificate issues ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16433))
- Fix 404 error when filtering admin action logs by non-existent target account ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16643))
- Fix error when accessing streaming API without any OAuth scopes ([Brawaru](https://github.com/mastodon/mastodon/pull/16823))
- Fix follow request count not updating when new follow requests arrive over streaming API in web UI ([matildepark](https://github.com/mastodon/mastodon/pull/16652))
- Fix error when unsuspending a local account ([HolgerHuo](https://github.com/mastodon/mastodon/pull/16605))
- Fix crash when a notification contains a not yet processed media attachment in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16573))
- Fix wrong color of download button in audio player in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16572))
- Fix notes for others accounts not being deleted when an account is deleted ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16579))
- Fix error when logging occurrence of unsupported video file ([noellabo](https://github.com/mastodon/mastodon/pull/16581))
- Fix wrong elements in trends widget being hidden on smaller screens in web UI ([tribela](https://github.com/mastodon/mastodon/pull/16570))
- Fix link to about page being displayed in limited federation mode ([weex](https://github.com/mastodon/mastodon/pull/16432))
- Fix styling of boost button in media modal not reflecting ability to boost ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16387))
- Fix OCR failure when erroneous lang data is in cache ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16386))
- Fix downloading media from blocked domains in `tootctl media refresh` ([tribela](https://github.com/mastodon/mastodon/pull/16914))
- Fix login form being displayed on landing page when already logged in ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17348))
- Fix polling for media processing status too frequently in web UI ([tribela](https://github.com/mastodon/mastodon/pull/17271))
- Fix hashtag autocomplete overriding user-typed case ([weex](https://github.com/mastodon/mastodon/pull/16460))
- Fix WebAuthn authentication setup to not prompt for PIN ([truongnmt](https://github.com/mastodon/mastodon/pull/16545))
### Security
- Fix being able to post URLs longer than 4096 characters ([Gargron](https://github.com/mastodon/mastodon/pull/17908))
- Fix being able to bypass e-mail restrictions ([Gargron](https://github.com/mastodon/mastodon/pull/17909))
## [3.4.6] - 2022-02-03
### Fixed
- Fix `mastodon:webpush:generate_vapid_key` task requiring a functional environment ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17338))
- Fix spurious errors when receiving an Add activity for a private post ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17425))
### Security
- Fix error-prone SQL queries ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/15828))
- Fix not compacting incoming signed JSON-LD activities ([puckipedia](https://github.com/mastodon/mastodon/pull/17426), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/17428)) (CVE-2022-24307)
- Fix insufficient sanitization of report comments ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17430))
- Fix stop condition of a Common Table Expression ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17427))
- Disable legacy XSS filtering ([Wonderfall](https://github.com/mastodon/mastodon/pull/17289))
## [3.4.5] - 2022-01-31
### Added
- Add more advanced migration tests ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17393))
- Add github workflow to build Docker images ([unasuke](https://github.com/mastodon/mastodon/pull/16973), [Gargron](https://github.com/mastodon/mastodon/pull/16980), [Gargron](https://github.com/mastodon/mastodon/pull/17000))
### Fixed
- Fix some old migrations failing when skipping releases ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17394))
- Fix migrations script failing in certain edge cases ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17398))
- Fix Docker build ([tribela](https://github.com/mastodon/mastodon/pull/17188))
- Fix Ruby 3.0 dependencies ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16723))
- Fix followers synchronization mechanism ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16510))
## [3.4.4] - 2021-11-26
### Fixed
- Fix error when suspending user with an already blocked canonical email ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17036))
- Fix overflow of long profile fields in admin UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17010))
- Fix confusing error when WebFinger request returns empty document ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16986))
- Fix upload of remote media with OpenStack Swift sometimes failing ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16998))
- Fix logout link not working in Safari ([noellabo](https://github.com/mastodon/mastodon/pull/16574))
- Fix “open” link of media modal not closing modal in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16524))
- Fix replying from modal in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16516))
- Fix `mastodon:setup` command crashing in some circumstances ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16976))
### Security
- Fix filtering DMs from non-followed users ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17042))
- Fix handling of recursive toots in WebUI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17041))
## [3.4.3] - 2021-11-06
### Fixed
- Fix login being broken due to inaccurately applied backport fix in 3.4.2 ([Gargron](https://github.com/mastodon/mastodon/commit/5c47a18c8df3231aa25c6d1f140a71a7fac9cbf9))
## [3.4.2] - 2021-11-06
### Added
- Add `configuration` attribute to `GET /api/v1/instance` ([Gargron](https://github.com/mastodon/mastodon/pull/16485))
### Fixed
- Fix handling of back button with modal windows in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16499))
- Fix pop-in player when author has long username in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16468))
- Fix crash when a status with a playing video gets deleted in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16384))
- Fix crash with Microsoft Translate in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16525))
- Fix PWA not being usable from alternate domains ([HolgerHuo](https://github.com/mastodon/mastodon/pull/16714))
- Fix locale-specific number rounding errors ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16469))
- Fix scheduling a status decreasing status count ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16791))
- Fix user's canonical email address being blocked when user deletes own account ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16503))
- Fix not being able to suspend users that already have their canonical e-mail blocked ([Gargron](https://github.com/mastodon/mastodon/pull/16455))
- Fix anonymous access to outbox not being cached by the reverse proxy ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16458))
- Fix followers synchronization mechanism not working when URI has empty path ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16744))
- Fix serialization of counts in REST API when user hides their network ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16418))
- Fix inefficiencies in auto-linking code ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16506))
- Fix `tootctl self-destruct` not sending delete activities for recently-suspended accounts ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16688))
- Fix suspicious sign-in e-mail text being out of date ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16690))
- Fix some frameworks being unnecessarily loaded ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16725))
- Fix canonical e-mail blocks missing foreign key constraints ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16448))
- Fix inconsistent order on account's statuses page in admin UI ([tribela](https://github.com/mastodon/mastodon/pull/16937))
- Fix media from blocked domains being redownloaded by `tootctl media refresh` ([tribela](https://github.com/mastodon/mastodon/pull/16914))
- Fix `mastodon:setup` generated env-file syntax ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16896))
- Fix link previews being incorrectly generated from earlier links ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16885))
- Fix wrong `to`/`cc` values for remote groups in ActivityPub ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16700))
- Fix mentions with non-ascii TLDs not being processed ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16689))
- Fix authentication failures halfway through a sign-in attempt ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16607), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/16792))
- Fix suspended accounts statuses being merged back into timelines ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16628))
- Fix crash when encountering invalid account fields ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16598))
- Fix invalid blurhash handling for remote activities ([noellabo](https://github.com/mastodon/mastodon/pull/16583))
- Fix newlines being added to account notes when an account moves ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16415), [noellabo](https://github.com/mastodon/mastodon/pull/16576))
- Fix crash when creating an announcement with links ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16941))
- Fix logging out from one browser logging out all other sessions ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16943))
### Security
- Fix user notes not having a length limit ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16942))
- Fix revoking a specific session not working ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16943))
## [3.4.1] - 2021-06-03
### Added
@ -761,7 +327,7 @@ All notable changes to this project will be documented in this file.
- Fix inefficiency when fetching bookmarks ([akihikodaki](https://github.com/mastodon/mastodon/pull/14674))
- Fix inefficiency when fetching favourites ([akihikodaki](https://github.com/mastodon/mastodon/pull/14673))
- Fix inefficiency when fetching media-only account timeline ([akihikodaki](https://github.com/mastodon/mastodon/pull/14675))
- Fix inefficiency when deleting accounts ([Gargron](https://github.com/mastodon/mastodon/pull/15387), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/15409), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/15407), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/15408), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/15402), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/15416), [Gargron](https://github.com/mastodon/mastodon/pull/15421))
- Fix inefficieny when deleting accounts ([Gargron](https://github.com/mastodon/mastodon/pull/15387), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/15409), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/15407), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/15408), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/15402), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/15416), [Gargron](https://github.com/mastodon/mastodon/pull/15421))
- Fix redundant query when processing batch actions on custom emojis ([niwatori24](https://github.com/mastodon/mastodon/pull/14534))
- Fix slow distinct queries where grouped queries are faster ([Gargron](https://github.com/mastodon/mastodon/pull/15287))
- Fix performance on instances list in admin UI ([Gargron](https://github.com/mastodon/mastodon/pull/15282))
@ -848,7 +414,7 @@ All notable changes to this project will be documented in this file.
- Add blurhash to link previews ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13984), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/14143), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/13985), [Sasha-Sorokin](https://github.com/mastodon/mastodon/pull/14267), [Sasha-Sorokin](https://github.com/mastodon/mastodon/pull/14278), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/14126), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/14261), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/14260))
- In web UI, toots cannot be marked as sensitive unless there is media attached
- However, it's possible to do via API or ActivityPub
- Thumbnails of link previews of such posts now use blurhash in web UI
- Thumnails of link previews of such posts now use blurhash in web UI
- The Card entity in REST API has a new `blurhash` attribute
- Add support for `summary` field for media description in ActivityPub ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13763))
- Add hints about incomplete remote content to web UI ([Gargron](https://github.com/mastodon/mastodon/pull/14031), [noellabo](https://github.com/mastodon/mastodon/pull/14195))
@ -871,7 +437,7 @@ All notable changes to this project will be documented in this file.
- The `meta` attribute on the Media Attachment entity in REST API can now have a `colors` attribute which in turn contains three hex colors: `background`, `foreground`, and `accent`
- The background color is chosen from the most dominant color around the edges of the thumbnail
- The foreground and accent colors are chosen from the colors that are the most different from the background color using the CIEDE2000 algorithm
- The most saturated color of the two is designated as the accent color
- The most satured color of the two is designated as the accent color
- The one with the highest W3C contrast is designated as the foreground color
- If there are not enough colors in the thumbnail, new ones are generated using a monochrome pattern
- Add a visibility indicator to toots in web UI ([noellabo](https://github.com/mastodon/mastodon/pull/14123), [highemerly](https://github.com/mastodon/mastodon/pull/14292))
@ -897,7 +463,7 @@ All notable changes to this project will be documented in this file.
- Change boost button to no longer serve as visibility indicator in web UI ([noellabo](https://github.com/mastodon/mastodon/pull/14132), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/14373))
- Change contrast of flash messages ([cchoi12](https://github.com/mastodon/mastodon/pull/13892))
- Change wording from "Hide media" to "Hide image/images" in web UI ([ariasuni](https://github.com/mastodon/mastodon/pull/13834))
- Change appearance of settings pages to be more consistent ([ariasuni](https://github.com/mastodon/mastodon/pull/13938))
- Change appearence of settings pages to be more consistent ([ariasuni](https://github.com/mastodon/mastodon/pull/13938))
- Change "Add media" tooltip to not include long list of formats in web UI ([ariasuni](https://github.com/mastodon/mastodon/pull/13954))
- Change how badly contrasting emoji are rendered in web UI ([leo60228](https://github.com/mastodon/mastodon/pull/13773), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/13772), [mfmfuyu](https://github.com/mastodon/mastodon/pull/14020), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/14015))
- Change structure of unavailable content section on about page ([ariasuni](https://github.com/mastodon/mastodon/pull/13930))
@ -913,14 +479,14 @@ All notable changes to this project will be documented in this file.
- `EMAIL_DOMAIN_WHITELIST``EMAIL_DOMAIN_ALLOWLIST`
- CLI option changed:
- `tootctl domains purge --whitelist-mode``tootctl domains purge --limited-federation-mode`
- Remove some unnecessary database indexes ([lfuelling](https://github.com/mastodon/mastodon/pull/13695), [noellabo](https://github.com/mastodon/mastodon/pull/14259))
- Remove some unnecessary database indices ([lfuelling](https://github.com/mastodon/mastodon/pull/13695), [noellabo](https://github.com/mastodon/mastodon/pull/14259))
- Remove unnecessary Node.js version upper bound ([ykzts](https://github.com/mastodon/mastodon/pull/14139))
### Fixed
- Fix `following` param not working when exact match is found in account search ([noellabo](https://github.com/mastodon/mastodon/pull/14394))
- Fix sometimes occurring duplicate mention notifications ([noellabo](https://github.com/mastodon/mastodon/pull/14378))
- Fix RSS feeds not being cacheable ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/14368))
- Fix sometimes occuring duplicate mention notifications ([noellabo](https://github.com/mastodon/mastodon/pull/14378))
- Fix RSS feeds not being cachable ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/14368))
- Fix lack of locking around processing of Announce activities in ActivityPub ([noellabo](https://github.com/mastodon/mastodon/pull/14365))
- Fix boosted toots from blocked account not being retroactively removed from TL ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/14339))
- Fix large shortened numbers (like 1.2K) using incorrect pluralization ([Sasha-Sorokin](https://github.com/mastodon/mastodon/pull/14061))
@ -932,7 +498,7 @@ All notable changes to this project will be documented in this file.
- Fix new posts pushing down origin of opened dropdown in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/14271), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/14348))
- Fix timeline markers not being saved sometimes ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13887), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/13889), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/14155))
- Fix CSV uploads being rejected ([noellabo](https://github.com/mastodon/mastodon/pull/13835))
- Fix incompatibility with Elasticsearch 7.x ([noellabo](https://github.com/mastodon/mastodon/pull/13828))
- Fix incompatibility with ElasticSearch 7.x ([noellabo](https://github.com/mastodon/mastodon/pull/13828))
- Fix being able to search posts where you're in the target audience but not actively mentioned ([noellabo](https://github.com/mastodon/mastodon/pull/13829))
- Fix non-local posts appearing on local-only hashtag timelines in web UI ([noellabo](https://github.com/mastodon/mastodon/pull/13827))
- Fix `tootctl media remove-orphans` choking on unknown files in storage ([Gargron](https://github.com/mastodon/mastodon/pull/13765))
@ -1047,7 +613,7 @@ All notable changes to this project will be documented in this file.
- Fix poll refresh button not being debounced in web UI ([rasjonell](https://github.com/mastodon/mastodon/pull/13485), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/13490))
- Fix confusing error when failing to add an alias to an unknown account ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13480))
- Fix "Email changed" notification sometimes having wrong e-mail ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13475))
- Fix various issues on the account aliases page ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13452))
- Fix varioues issues on the account aliases page ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13452))
- Fix API footer link in web UI ([bubblineyuri](https://github.com/mastodon/mastodon/pull/13441))
- Fix pagination of following, followers, follow requests, blocks and mutes lists in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13445))
- Fix styling of polls in JS-less fallback on public pages ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13436))
@ -1536,7 +1102,7 @@ All notable changes to this project will be documented in this file.
- Fix URLs appearing twice in errors of ActivityPub::DeliveryWorker ([Gargron](https://github.com/mastodon/mastodon/pull/11231))
- Fix support for HTTP proxies ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/11245))
- Fix HTTP requests to IPv6 hosts ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/11240))
- Fix error in Elasticsearch index import ([mayaeh](https://github.com/mastodon/mastodon/pull/11192))
- Fix error in ElasticSearch index import ([mayaeh](https://github.com/mastodon/mastodon/pull/11192))
- Fix duplicate account error when seeding development database ([ysksn](https://github.com/mastodon/mastodon/pull/11366))
- Fix performance of session clean-up scheduler ([abcang](https://github.com/mastodon/mastodon/pull/11871))
- Fix older migrations not running ([zunda](https://github.com/mastodon/mastodon/pull/11377))
@ -1546,8 +1112,8 @@ All notable changes to this project will be documented in this file.
- Fix muted text color not applying to all text ([trwnh](https://github.com/mastodon/mastodon/pull/11996))
- Fix follower/following lists resetting on back-navigation in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/11986))
- Fix n+1 query when approving multiple follow requests ([abcang](https://github.com/mastodon/mastodon/pull/12004))
- Fix records not being indexed into Elasticsearch sometimes ([Gargron](https://github.com/mastodon/mastodon/pull/12024))
- Fix needlessly indexing unsearchable statuses into Elasticsearch ([Gargron](https://github.com/mastodon/mastodon/pull/12041))
- Fix records not being indexed into ElasticSearch sometimes ([Gargron](https://github.com/mastodon/mastodon/pull/12024))
- Fix needlessly indexing unsearchable statuses into ElasticSearch ([Gargron](https://github.com/mastodon/mastodon/pull/12041))
- Fix new user bootstrapping crashing when to-be-followed accounts are invalid ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/12037))
- Fix featured hashtag URL being interpreted as media or replies tab ([Gargron](https://github.com/mastodon/mastodon/pull/12048))
- Fix account counters being overwritten by parallel writes ([Gargron](https://github.com/mastodon/mastodon/pull/12045))
@ -1837,7 +1403,7 @@ All notable changes to this project will be documented in this file.
- Change Docker image to use Ubuntu with jemalloc ([Sir-Boops](https://github.com/mastodon/mastodon/pull/10100), [BenLubar](https://github.com/mastodon/mastodon/pull/10212))
- Change public pages to be cacheable by proxies ([BenLubar](https://github.com/mastodon/mastodon/pull/9059))
- Change the 410 gone response for suspended accounts to be cacheable by proxies ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/10339))
- Change web UI to not empty timeline of blocked users on block ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/10359))
- Change web UI to not not empty timeline of blocked users on block ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/10359))
- Change JSON serializer to remove unused `@context` values ([Gargron](https://github.com/mastodon/mastodon/pull/10378))
- Change GIFV file size limit to be the same as for other videos ([rinsuki](https://github.com/mastodon/mastodon/pull/9924))
- Change Webpack to not use @babel/preset-env to compile node_modules ([ykzts](https://github.com/mastodon/mastodon/pull/10289))
@ -2014,7 +1580,7 @@ All notable changes to this project will be documented in this file.
- Limit maximum visibility of local silenced users to unlisted ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/9583))
- Change API error message for unconfirmed accounts ([noellabo](https://github.com/mastodon/mastodon/pull/9625))
- Change the icon to "reply-all" when it's a reply to other accounts ([mayaeh](https://github.com/mastodon/mastodon/pull/9378))
- Do not ignore federated reports targeting already-reported accounts ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/9534))
- Do not ignore federated reports targetting already-reported accounts ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/9534))
- Upgrade default Ruby version to 2.6.0 ([Gargron](https://github.com/mastodon/mastodon/pull/9688))
- Change e-mail digest frequency ([Gargron](https://github.com/mastodon/mastodon/pull/9689))
- Change Docker images for Tor support in docker-compose.yml ([Sir-Boops](https://github.com/mastodon/mastodon/pull/9438))

View File

@ -2,10 +2,9 @@ FROM ubuntu:20.04 as build-dep
# Use bash for the shell
SHELL ["/bin/bash", "-c"]
RUN echo 'debconf debconf/frontend select Noninteractive' | debconf-set-selections
# Install Node v16 (LTS)
ENV NODE_VER="16.14.2"
# Install Node v14 (LTS)
ENV NODE_VER="14.17.6"
RUN ARCH= && \
dpkgArch="$(dpkg --print-architecture)" && \
case "${dpkgArch##*-}" in \
@ -19,15 +18,15 @@ RUN ARCH= && \
esac && \
echo "Etc/UTC" > /etc/localtime && \
apt-get update && \
apt-get install -y --no-install-recommends ca-certificates wget python apt-utils && \
apt-get install -y --no-install-recommends ca-certificates wget python && \
cd ~ && \
wget -q https://nodejs.org/download/release/v$NODE_VER/node-v$NODE_VER-linux-$ARCH.tar.gz && \
tar xf node-v$NODE_VER-linux-$ARCH.tar.gz && \
rm node-v$NODE_VER-linux-$ARCH.tar.gz && \
mv node-v$NODE_VER-linux-$ARCH /opt/node
# Install Ruby 3.0
ENV RUBY_VER="3.0.3"
# Install Ruby
ENV RUBY_VER="2.7.4"
RUN apt-get update && \
apt-get install -y --no-install-recommends build-essential \
bison libyaml-dev libgdbm-dev libreadline-dev libjemalloc-dev \
@ -46,19 +45,17 @@ RUN apt-get update && \
ENV PATH="${PATH}:/opt/ruby/bin:/opt/node/bin"
RUN npm install -g npm@latest && \
npm install -g yarn && \
RUN npm install -g yarn && \
gem install bundler && \
apt-get update && \
apt-get install -y --no-install-recommends git libicu-dev libidn11-dev \
libpq-dev shared-mime-info
libpq-dev libprotobuf-dev protobuf-compiler shared-mime-info
COPY Gemfile* package.json yarn.lock /opt/mastodon/
RUN cd /opt/mastodon && \
bundle config set --local deployment 'true' && \
bundle config set --local without 'development test' && \
bundle config set silence_root_warning true && \
bundle config set deployment 'true' && \
bundle config set without 'development test' && \
bundle install -j"$(nproc)" && \
yarn install --pure-lockfile
@ -84,12 +81,11 @@ RUN apt-get update && \
rm -rf /var/lib/apt/lists/*
# Install mastodon runtime deps
RUN echo 'debconf debconf/frontend select Noninteractive' | debconf-set-selections
RUN apt-get update && \
apt-get -y --no-install-recommends install \
libssl1.1 libpq5 imagemagick ffmpeg libjemalloc2 \
libicu66 libidn11 libyaml-0-2 \
file ca-certificates tzdata libreadline8 gcc tini apt-utils && \
libicu66 libprotobuf17 libidn11 libyaml-0-2 \
file ca-certificates tzdata libreadline8 gcc tini && \
ln -s /opt/mastodon /mastodon && \
gem install bundler && \
rm -rf /var/cache && \

View File

@ -1,30 +0,0 @@
## ActivityPub federation in Mastodon
Mastodon largely follows the ActivityPub server-to-server specification but it makes uses of some non-standard extensions, some of which are required for interacting with Mastodon at all.
Supported vocabulary: https://docs.joinmastodon.org/spec/activitypub/
### Required extensions
#### Webfinger
In Mastodon, users are identified by a `username` and `domain` pair (e.g., `Gargron@mastodon.social`).
This is used both for discovery and for unambiguously mentioning users across the fediverse. Furthermore, this is part of Mastodon's database design from its very beginnings.
As a result, Mastodon requires that each ActivityPub actor uniquely maps back to an `acct:` URI that can be resolved via WebFinger.
More information and examples are available at: https://docs.joinmastodon.org/spec/webfinger/
#### HTTP Signatures
In order to authenticate activities, Mastodon relies on HTTP Signatures, signing every `POST` and `GET` request to other ActivityPub implementations on behalf of the user authoring an activity (for `POST` requests) or an actor representing the Mastodon server itself (for most `GET` requests).
Mastodon requires all `POST` requests to be signed, and MAY require `GET` requests to be signed, depending on the configuration of the Mastodon server.
More information on HTTP Signatures, as well as examples, can be found here: https://docs.joinmastodon.org/spec/security/#http
### Optional extensions
- Linked-Data Signatures: https://docs.joinmastodon.org/spec/security/#ld
- Bearcaps: https://docs.joinmastodon.org/spec/bearcaps/
- Followers collection synchronization: https://git.activitypub.dev/ActivityPubDev/Fediverse-Enhancement-Proposals/src/branch/main/feps/fep-8fcf.md

86
Gemfile
View File

@ -1,35 +1,36 @@
# frozen_string_literal: true
source 'https://rubygems.org'
ruby '>= 2.6.0', '< 3.1.0'
ruby '>= 2.5.0', '< 3.1.0'
gem 'pkg-config', '~> 1.4'
gem 'rexml', '~> 3.2'
gem 'puma', '~> 5.6'
gem 'rails', '~> 6.1.6'
gem 'puma', '~> 5.4'
gem 'rails', '~> 6.1.4'
gem 'sprockets', '~> 3.7.2'
gem 'thor', '~> 1.2'
gem 'thor', '~> 1.1'
gem 'rack', '~> 2.2.3'
gem 'hamlit-rails', '~> 0.2'
gem 'pg', '~> 1.3'
gem 'pg', '~> 1.2'
gem 'makara', '~> 0.5'
gem 'pghero', '~> 2.8'
gem 'dotenv-rails', '~> 2.7'
gem 'aws-sdk-s3', '~> 1.114', require: false
gem 'aws-sdk-s3', '~> 1.103', require: false
gem 'fog-core', '<= 2.1.0'
gem 'fog-openstack', '~> 0.3', require: false
gem 'kt-paperclip', '~> 7.1'
gem 'kt-paperclip', '~> 7.0'
gem 'blurhash', '~> 0.1'
gem 'active_model_serializers', '~> 0.10'
gem 'addressable', '~> 2.8'
gem 'bootsnap', '~> 1.11.1', require: false
gem 'bootsnap', '~> 1.9.1', require: false
gem 'browser'
gem 'charlock_holmes', '~> 0.7.7'
gem 'chewy', '~> 7.2'
gem 'iso-639'
gem 'chewy', '~> 5.2'
gem 'cld3', '~> 3.4.2'
gem 'devise', '~> 4.8'
gem 'devise-two-factor', '~> 4.0'
@ -40,14 +41,13 @@ end
gem 'net-ldap', '~> 0.17'
gem 'omniauth-cas', '~> 2.0'
gem 'omniauth-saml', '~> 1.10'
gem 'gitlab-omniauth-openid-connect', '~>0.9.1', require: 'omniauth_openid_connect'
gem 'omniauth', '~> 1.9'
gem 'omniauth-rails_csrf_protection', '~> 0.1'
gem 'color_diff', '~> 0.1'
gem 'discard', '~> 1.2'
gem 'doorkeeper', '~> 5.5'
gem 'ed25519', '~> 1.3'
gem 'ed25519', '~> 1.2'
gem 'fast_blank', '~> 1.0'
gem 'fastimage'
gem 'hiredis', '~> 0.6'
@ -59,54 +59,55 @@ gem 'httplog', '~> 1.5.0'
gem 'idn-ruby', require: 'idn'
gem 'kaminari', '~> 1.2'
gem 'link_header', '~> 0.0'
gem 'mime-types', '~> 3.4.1', require: 'mime/types/columnar'
gem 'nokogiri', '~> 1.13'
gem 'mime-types', '~> 3.3.1', require: 'mime/types/columnar'
gem 'nokogiri', '~> 1.12'
gem 'nsa', '~> 0.2'
gem 'oj', '~> 3.13'
gem 'ox', '~> 2.14'
gem 'parslet'
gem 'parallel', '~> 1.21'
gem 'posix-spawn'
gem 'pundit', '~> 2.2'
gem 'pundit', '~> 2.1'
gem 'premailer-rails'
gem 'rack-attack', '~> 6.6'
gem 'rack-attack', '~> 6.5'
gem 'rack-cors', '~> 1.1', require: 'rack/cors'
gem 'rails-i18n', '~> 6.0'
gem 'rails-settings-cached', '~> 0.6'
gem 'redis', '~> 4.5', require: ['redis', 'redis/connection/hiredis']
gem 'redis', '~> 4.4', require: ['redis', 'redis/connection/hiredis']
gem 'mario-redis-lock', '~> 1.2', require: 'redis_lock'
gem 'rqrcode', '~> 2.1'
gem 'ruby-progressbar', '~> 1.11'
gem 'sanitize', '~> 6.0'
gem 'scenic', '~> 1.6'
gem 'sidekiq', '~> 6.4'
gem 'sidekiq-scheduler', '~> 4.0'
gem 'scenic', '~> 1.5'
gem 'sidekiq', '~> 6.2'
gem 'sidekiq-scheduler', '~> 3.1'
gem 'sidekiq-unique-jobs', '~> 7.1'
gem 'sidekiq-bulk', '~> 0.2.0'
gem 'sidekiq-bulk', '~>0.2.0'
gem 'simple-navigation', '~> 4.3'
gem 'simple_form', '~> 5.1'
gem 'sprockets-rails', '~> 3.4', require: 'sprockets/railtie'
gem 'stoplight', '~> 3.0.0'
gem 'sprockets-rails', '~> 3.2', require: 'sprockets/railtie'
gem 'stoplight', '~> 2.2.1'
gem 'strong_migrations', '~> 0.7'
gem 'tty-prompt', '~> 0.23', require: false
gem 'twitter-text', '~> 3.1.0'
gem 'tzinfo-data', '~> 1.2022'
gem 'tzinfo-data', '~> 1.2021'
gem 'webpacker', '~> 5.4'
gem 'webpush', '~> 0.3'
gem 'webauthn', '~> 3.0.0.alpha1'
gem 'json-ld'
gem 'json-ld-preloaded', '~> 3.2'
gem 'rdf-normalize', '~> 0.5'
gem 'json-ld-preloaded', '~> 3.1'
gem 'rdf-normalize', '~> 0.4'
gem 'redcarpet', '~> 3.5'
group :development, :test do
gem 'fabrication', '~> 2.28'
gem 'fabrication', '~> 2.22'
gem 'fuubar', '~> 2.5'
gem 'i18n-tasks', '~> 1.0', require: false
gem 'i18n-tasks', '~> 0.9', require: false
gem 'pry-byebug', '~> 3.9'
gem 'pry-rails', '~> 0.3'
gem 'rspec-rails', '~> 5.1'
gem 'rspec-rails', '~> 5.0'
end
group :production, :test do
@ -114,32 +115,33 @@ group :production, :test do
end
group :test do
gem 'capybara', '~> 3.37'
gem 'capybara', '~> 3.35'
gem 'climate_control', '~> 0.2'
gem 'faker', '~> 2.21'
gem 'faker', '~> 2.19'
gem 'microformats', '~> 4.2'
gem 'rails-controller-testing', '~> 1.0'
gem 'rspec-sidekiq', '~> 3.1'
gem 'simplecov', '~> 0.21', require: false
gem 'webmock', '~> 3.14'
gem 'rspec_junit_formatter', '~> 0.5'
gem 'parallel_tests', '~> 3.7'
gem 'rspec_junit_formatter', '~> 0.4'
end
group :development do
gem 'active_record_query_trace', '~> 1.8'
gem 'annotate', '~> 3.2'
gem 'annotate', '~> 3.1'
gem 'better_errors', '~> 2.9'
gem 'binding_of_caller', '~> 1.0'
gem 'bullet', '~> 7.0'
gem 'letter_opener', '~> 1.8'
gem 'letter_opener_web', '~> 2.0'
gem 'bullet', '~> 6.1'
gem 'letter_opener', '~> 1.7'
gem 'letter_opener_web', '~> 1.4'
gem 'memory_profiler'
gem 'rubocop', '~> 1.29', require: false
gem 'rubocop-rails', '~> 2.14', require: false
gem 'brakeman', '~> 5.2', require: false
gem 'rubocop', '~> 1.21', require: false
gem 'rubocop-rails', '~> 2.12', require: false
gem 'brakeman', '~> 5.1', require: false
gem 'bundler-audit', '~> 0.9', require: false
gem 'capistrano', '~> 3.17'
gem 'capistrano', '~> 3.16'
gem 'capistrano-rails', '~> 1.6'
gem 'capistrano-rbenv', '~> 2.2'
gem 'capistrano-yarn', '~> 2.0'
@ -148,12 +150,10 @@ group :development do
end
group :production do
gem 'lograge', '~> 0.12'
gem 'lograge', '~> 0.11'
end
gem 'concurrent-ruby', require: false
gem 'connection_pool', require: false
gem 'xorcist', '~> 1.1'
gem 'hcaptcha', '~> 7.1'

File diff suppressed because it is too large Load Diff

View File

@ -1,20 +1,13 @@
# Security Policy
If you believe you've identified a security vulnerability in Mastodon (a bug that allows something to happen that shouldn't be possible), you should submit the report through our [Bug Bounty Program][bug-bounty]. Alternatively, you can reach us at <hello@joinmastodon.org>.
You should *not* report such issues on GitHub or in other public spaces to give us time to publish a fix for the issue without exposing Mastodon's users to increased risk.
## Scope
A "vulnerability in Mastodon" is a vulnerability in the code distributed through our main source code repository on GitHub. Vulnerabilities that are specific to a given installation (e.g. misconfiguration) should be reported to the owner of that installation and not us.
## Supported Versions
| Version | Supported |
| ------- | ------------------ |
| 3.5.x | Yes |
| 3.4.x | Yes |
| 3.3.x | No |
| < 3.3 | No |
| 3.4.x | :white_check_mark: |
| 3.3.x | :white_check_mark: |
| < 3.3 | :x: |
[bug-bounty]: https://app.intigriti.com/programs/mastodon/mastodonio/detail
## Reporting a Vulnerability
hello@joinmastodon.org

14
Vagrantfile vendored
View File

@ -33,9 +33,11 @@ sudo apt-get install \
redis-tools \
postgresql \
postgresql-contrib \
protobuf-compiler \
yarn \
libicu-dev \
libidn11-dev \
libprotobuf-dev \
libreadline-dev \
libpam0g-dev \
-y
@ -43,8 +45,16 @@ sudo apt-get install \
# Install rvm
read RUBY_VERSION < .ruby-version
curl -sSL https://rvm.io/mpapis.asc | gpg --import
curl -sSL https://rvm.io/pkuczynski.asc | gpg --import
gpg_command="gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 7D2BAF1CF37B13E2069D6956105BD0E739499BDB"
$($gpg_command)
if [ $? -ne 0 ];then
echo "GPG command failed, This prevented RVM from installing."
echo "Retrying once..." && $($gpg_command)
if [ $? -ne 0 ];then
echo "GPG failed for the second time, please ensure network connectivity."
echo "Exiting..." && exit 1
fi
fi
curl -sSL https://raw.githubusercontent.com/rvm/rvm/stable/binscripts/rvm-installer | bash -s stable --ruby=$RUBY_VERSION
source /home/vagrant/.rvm/scripts/rvm

View File

@ -95,5 +95,8 @@
"scripts": {
"postdeploy": "bundle exec rails db:migrate && bundle exec rails db:seed"
},
"addons": ["heroku-postgresql", "heroku-redis"]
"addons": [
"heroku-postgresql",
"heroku-redis"
]
}

View File

@ -1,7 +1,7 @@
# frozen_string_literal: true
class AccountsIndex < Chewy::Index
settings index: { refresh_interval: '30s' }, analysis: {
settings index: { refresh_interval: '5m' }, analysis: {
analyzer: {
content: {
tokenizer: 'whitespace',
@ -23,21 +23,21 @@ class AccountsIndex < Chewy::Index
},
}
index_scope ::Account.searchable.includes(:account_stat)
define_type ::Account.searchable.includes(:account_stat), delete_if: ->(account) { account.destroyed? || !account.searchable? } do
root date_detection: false do
field :id, type: 'long'
root date_detection: false do
field :id, type: 'long'
field :display_name, type: 'text', analyzer: 'content' do
field :edge_ngram, type: 'text', analyzer: 'edge_ngram', search_analyzer: 'content'
end
field :display_name, type: 'text', analyzer: 'content' do
field :edge_ngram, type: 'text', analyzer: 'edge_ngram', search_analyzer: 'content'
field :acct, type: 'text', analyzer: 'content', value: ->(account) { [account.username, account.domain].compact.join('@') } do
field :edge_ngram, type: 'text', analyzer: 'edge_ngram', search_analyzer: 'content'
end
field :following_count, type: 'long', value: ->(account) { account.following.local.count }
field :followers_count, type: 'long', value: ->(account) { account.followers.local.count }
field :last_status_at, type: 'date', value: ->(account) { account.last_status_at || account.created_at }
end
field :acct, type: 'text', analyzer: 'content', value: ->(account) { [account.username, account.domain].compact.join('@') } do
field :edge_ngram, type: 'text', analyzer: 'edge_ngram', search_analyzer: 'content'
end
field :following_count, type: 'long', value: ->(account) { account.following_count }
field :followers_count, type: 'long', value: ->(account) { account.followers_count }
field :last_status_at, type: 'date', value: ->(account) { account.last_status_at || account.created_at }
end
end

View File

@ -1,9 +1,7 @@
# frozen_string_literal: true
class StatusesIndex < Chewy::Index
include FormattingHelper
settings index: { refresh_interval: '30s' }, analysis: {
settings index: { refresh_interval: '15m' }, analysis: {
filter: {
english_stop: {
type: 'stop',
@ -33,43 +31,36 @@ class StatusesIndex < Chewy::Index
},
}
# We do not use delete_if option here because it would call a method that we
# expect to be called with crutches without crutches, causing n+1 queries
index_scope ::Status.unscoped.kept.without_reblogs.includes(:media_attachments, :preloadable_poll)
crutch :mentions do |collection|
data = ::Mention.where(status_id: collection.map(&:id)).where(account: Account.local, silent: false).pluck(:status_id, :account_id)
data.each.with_object({}) { |(id, name), result| (result[id] ||= []).push(name) }
end
crutch :favourites do |collection|
data = ::Favourite.where(status_id: collection.map(&:id)).where(account: Account.local).pluck(:status_id, :account_id)
data.each.with_object({}) { |(id, name), result| (result[id] ||= []).push(name) }
end
crutch :reblogs do |collection|
data = ::Status.where(reblog_of_id: collection.map(&:id)).where(account: Account.local).pluck(:reblog_of_id, :account_id)
data.each.with_object({}) { |(id, name), result| (result[id] ||= []).push(name) }
end
crutch :bookmarks do |collection|
data = ::Bookmark.where(status_id: collection.map(&:id)).where(account: Account.local).pluck(:status_id, :account_id)
data.each.with_object({}) { |(id, name), result| (result[id] ||= []).push(name) }
end
crutch :votes do |collection|
data = ::PollVote.joins(:poll).where(poll: { status_id: collection.map(&:id) }).where(account: Account.local).pluck(:status_id, :account_id)
data.each.with_object({}) { |(id, name), result| (result[id] ||= []).push(name) }
end
root date_detection: false do
field :id, type: 'long'
field :account_id, type: 'long'
field :text, type: 'text', value: ->(status) { status.searchable_text } do
field :stemmed, type: 'text', analyzer: 'content'
define_type ::Status.unscoped.kept.without_reblogs.includes(:media_attachments, :preloadable_poll) do
crutch :mentions do |collection|
data = ::Mention.where(status_id: collection.map(&:id)).where(account: Account.local, silent: false).pluck(:status_id, :account_id)
data.each.with_object({}) { |(id, name), result| (result[id] ||= []).push(name) }
end
field :searchable_by, type: 'long', value: ->(status, crutches) { status.searchable_by(crutches) }
crutch :favourites do |collection|
data = ::Favourite.where(status_id: collection.map(&:id)).where(account: Account.local).pluck(:status_id, :account_id)
data.each.with_object({}) { |(id, name), result| (result[id] ||= []).push(name) }
end
crutch :reblogs do |collection|
data = ::Status.where(reblog_of_id: collection.map(&:id)).where(account: Account.local).pluck(:reblog_of_id, :account_id)
data.each.with_object({}) { |(id, name), result| (result[id] ||= []).push(name) }
end
crutch :bookmarks do |collection|
data = ::Bookmark.where(status_id: collection.map(&:id)).where(account: Account.local).pluck(:status_id, :account_id)
data.each.with_object({}) { |(id, name), result| (result[id] ||= []).push(name) }
end
root date_detection: false do
field :id, type: 'long'
field :account_id, type: 'long'
field :text, type: 'text', value: ->(status) { [status.spoiler_text, Formatter.instance.plaintext(status)].concat(status.media_attachments.map(&:description)).concat(status.preloadable_poll ? status.preloadable_poll.options : []).join("\n\n") } do
field :stemmed, type: 'text', analyzer: 'content'
end
field :searchable_by, type: 'long', value: ->(status, crutches) { status.searchable_by(crutches) }
end
end
end

View File

@ -1,7 +1,7 @@
# frozen_string_literal: true
class TagsIndex < Chewy::Index
settings index: { refresh_interval: '30s' }, analysis: {
settings index: { refresh_interval: '15m' }, analysis: {
analyzer: {
content: {
tokenizer: 'keyword',
@ -23,19 +23,15 @@ class TagsIndex < Chewy::Index
},
}
index_scope ::Tag.listable
define_type ::Tag.listable, delete_if: ->(tag) { tag.destroyed? || !tag.listable? } do
root date_detection: false do
field :name, type: 'text', analyzer: 'content' do
field :edge_ngram, type: 'text', analyzer: 'edge_ngram', search_analyzer: 'content'
end
crutch :time_period do
7.days.ago.to_date..0.days.ago.to_date
end
root date_detection: false do
field :name, type: 'text', analyzer: 'content' do
field :edge_ngram, type: 'text', analyzer: 'edge_ngram', search_analyzer: 'content'
field :reviewed, type: 'boolean', value: ->(tag) { tag.reviewed? }
field :usage, type: 'long', value: ->(tag) { tag.history.reduce(0) { |total, day| total + day[:accounts].to_i } }
field :last_status_at, type: 'date', value: ->(tag) { tag.last_status_at || tag.created_at }
end
field :reviewed, type: 'boolean', value: ->(tag) { tag.reviewed? }
field :usage, type: 'long', value: ->(tag, crutches) { tag.history.aggregate(crutches.time_period).accounts }
field :last_status_at, type: 'date', value: ->(tag) { tag.last_status_at || tag.created_at }
end
end

View File

@ -29,7 +29,7 @@ class AccountsController < ApplicationController
return
end
@pinned_statuses = cached_filtered_status_pins if show_pinned_statuses?
@pinned_statuses = cache_collection(@account.pinned_statuses.not_local_only, Status) if show_pinned_statuses?
@statuses = cached_filtered_status_page
@rss_url = rss_url
@ -45,6 +45,7 @@ class AccountsController < ApplicationController
limit = params[:limit].present? ? [params[:limit].to_i, PAGE_SIZE_MAX].min : PAGE_SIZE
@statuses = filtered_statuses.without_reblogs.limit(limit)
@statuses = cache_collection(@statuses, Status)
render xml: RSS::AccountSerializer.render(@account, @statuses, params[:tag])
end
format.json do
@ -64,10 +65,6 @@ class AccountsController < ApplicationController
[replies_requested?, media_requested?, tag_requested?, params[:max_id].present?, params[:min_id].present?].none?
end
def filtered_pinned_statuses
@account.pinned_statuses.not_local_only.where(visibility: [:public, :unlisted])
end
def filtered_statuses
default_statuses.tap do |statuses|
statuses.merge!(hashtag_scope) if tag_requested?
@ -146,13 +143,6 @@ class AccountsController < ApplicationController
request.path.split('.').first.end_with?(Addressable::URI.parse("/tagged/#{params[:tag]}").normalize)
end
def cached_filtered_status_pins
cache_collection(
filtered_pinned_statuses,
Status
)
end
def cached_filtered_status_page
cache_collection_paginated_by_id(
filtered_statuses,

View File

@ -2,8 +2,6 @@
class ActivityPub::BaseController < Api::BaseController
skip_before_action :require_authenticated_user!
skip_before_action :require_not_suspended!
skip_around_action :set_locale
private

View File

@ -21,7 +21,6 @@ class ActivityPub::CollectionsController < ActivityPub::BaseController
case params[:id]
when 'featured'
@items = for_signed_account { cache_collection(@account.pinned_statuses.not_local_only, Status) }
@items = @items.map { |item| item.distributable? ? item : ActivityPub::TagManager.instance.uri_for(item) }
when 'tags'
@items = for_signed_account { @account.featured_tags }
when 'devices'

View File

@ -62,7 +62,7 @@ class ActivityPub::OutboxesController < ActivityPub::BaseController
return unless page_requested?
@statuses = cache_collection_paginated_by_id(
AccountStatusesFilter.new(@account, signed_request_account).results,
@account.statuses.permitted_for(@account, signed_request_account),
Status,
LIMIT,
params_slice(:max_id, :min_id, :since_id)

View File

@ -63,29 +63,15 @@ class ActivityPub::RepliesController < ActivityPub::BaseController
end
def next_page
if only_other_accounts?
# Only consider remote accounts
return nil if @replies.size < DESCENDANTS_LIMIT
only_other_accounts = !(@replies&.last&.account_id == @account.id && @replies.size == DESCENDANTS_LIMIT)
account_status_replies_url(
@account,
@status,
page: true,
min_id: @replies&.last&.id,
only_other_accounts: true
)
else
# For now, we're serving only self-replies, but next page might be other accounts
next_only_other_accounts = @replies&.last&.account_id != @account.id || @replies.size < DESCENDANTS_LIMIT
account_status_replies_url(
@account,
@status,
page: true,
min_id: next_only_other_accounts ? nil : @replies&.last&.id,
only_other_accounts: next_only_other_accounts
)
end
account_status_replies_url(
@account,
@status,
page: true,
min_id: only_other_accounts && !only_other_accounts? ? nil : @replies&.last&.id,
only_other_accounts: only_other_accounts
)
end
def page_params

View File

@ -14,7 +14,7 @@ module Admin
else
@account = @account_moderation_note.target_account
@moderation_notes = @account.targeted_moderation_notes.latest
@warnings = @account.strikes.custom.latest
@warnings = @account.targeted_account_warnings.latest.custom
render template: 'admin/accounts/show'
end

View File

@ -2,24 +2,13 @@
module Admin
class AccountsController < BaseController
before_action :set_account, except: [:index, :batch]
before_action :set_account, except: [:index]
before_action :require_remote_account!, only: [:redownload]
before_action :require_local_account!, only: [:enable, :memorialize, :approve, :reject]
def index
authorize :account, :index?
@accounts = filtered_accounts.page(params[:page])
@form = Form::AccountBatch.new
end
def batch
@form = Form::AccountBatch.new(form_account_batch_params.merge(current_account: current_account, action: action_from_button))
@form.save
rescue ActionController::ParameterMissing
flash[:alert] = I18n.t('admin.accounts.no_account_selected')
ensure
redirect_to admin_accounts_path(filter_params)
end
def show
@ -28,7 +17,7 @@ module Admin
@deletion_request = @account.deletion_request
@account_moderation_note = current_account.account_moderation_notes.new(target_account: @account)
@moderation_notes = @account.targeted_moderation_notes.latest
@warnings = @account.strikes.includes(:target_account, :account, :appeal).latest
@warnings = @account.targeted_account_warnings.latest.custom
@domain_block = DomainBlock.rule_for(@account.domain)
end
@ -49,13 +38,13 @@ module Admin
def approve
authorize @account.user, :approve?
@account.user.approve!
redirect_to admin_accounts_path(status: 'pending'), notice: I18n.t('admin.accounts.approved_msg', username: @account.acct)
redirect_to admin_pending_accounts_path, notice: I18n.t('admin.accounts.approved_msg', username: @account.acct)
end
def reject
authorize @account.user, :reject?
DeleteAccountService.new.call(@account, reserve_email: false, reserve_username: false)
redirect_to admin_accounts_path(status: 'pending'), notice: I18n.t('admin.accounts.rejected_msg', username: @account.acct)
redirect_to admin_pending_accounts_path, notice: I18n.t('admin.accounts.rejected_msg', username: @account.acct)
end
def destroy
@ -117,16 +106,6 @@ module Admin
redirect_to admin_account_path(@account.id), notice: I18n.t('admin.accounts.removed_header_msg', username: @account.acct)
end
def unblock_email
authorize @account, :unblock_email?
CanonicalEmailBlock.where(reference_account: @account).delete_all
log_action :unblock_email, @account
redirect_to admin_account_path(@account.id), notice: I18n.t('admin.accounts.unblocked_email_msg', username: @account.acct)
end
private
def set_account
@ -142,25 +121,11 @@ module Admin
end
def filtered_accounts
AccountFilter.new(filter_params.with_defaults(order: 'recent')).results
AccountFilter.new(filter_params).results
end
def filter_params
params.slice(:page, *AccountFilter::KEYS).permit(:page, *AccountFilter::KEYS)
end
def form_account_batch_params
params.require(:form_account_batch).permit(:action, account_ids: [])
end
def action_from_button
if params[:suspend]
'suspend'
elsif params[:approve]
'approve'
elsif params[:reject]
'reject'
end
params.slice(*AccountFilter::KEYS).permit(*AccountFilter::KEYS)
end
end
end

View File

@ -35,9 +35,6 @@ module Admin
flash[:alert] = I18n.t('admin.accounts.no_account_selected')
rescue Mastodon::NotPermittedError
flash[:alert] = I18n.t('admin.custom_emojis.not_permitted')
rescue ActiveRecord::RecordInvalid => e
error_message = action_from_button == 'copy' ? 'admin.custom_emojis.batch_copy_error' : 'admin.custom_emojis.batch_error'
flash[:alert] = I18n.t(error_message, message: e.message)
ensure
redirect_to admin_custom_emojis_path(filter_params)
end

View File

@ -1,26 +1,56 @@
# frozen_string_literal: true
require 'sidekiq/api'
module Admin
class DashboardController < BaseController
include Redisable
def index
@system_checks = Admin::SystemCheck.perform
@time_period = (29.days.ago.to_date...Time.now.utc.to_date)
@users_count = User.count
@pending_users_count = User.pending.count
@pending_reports_count = Report.unresolved.count
@registrations_week = Redis.current.get("activity:accounts:local:#{current_week}") || 0
@logins_week = Redis.current.pfcount("activity:logins:#{current_week}")
@interactions_week = Redis.current.get("activity:interactions:#{current_week}") || 0
@relay_enabled = Relay.enabled.exists?
@single_user_mode = Rails.configuration.x.single_user_mode
@registrations_enabled = Setting.registrations_mode != 'none'
@deletions_enabled = Setting.open_deletion
@invites_enabled = Setting.min_invite_role == 'user'
@search_enabled = Chewy.enabled?
@version = Mastodon::Version.to_s
@database_version = ActiveRecord::Base.connection.execute('SELECT VERSION()').first['version'].match(/\A(?:PostgreSQL |)([^\s]+).*\z/)[1]
@redis_version = redis_info['redis_version']
@reports_count = Report.unresolved.count
@queue_backlog = Sidekiq::Stats.new.enqueued
@recent_users = User.confirmed.recent.includes(:account).limit(8)
@database_size = ActiveRecord::Base.connection.execute('SELECT pg_database_size(current_database())').first['pg_database_size']
@redis_size = redis_info['used_memory']
@ldap_enabled = ENV['LDAP_ENABLED'] == 'true'
@cas_enabled = ENV['CAS_ENABLED'] == 'true'
@saml_enabled = ENV['SAML_ENABLED'] == 'true'
@pam_enabled = ENV['PAM_ENABLED'] == 'true'
@hidden_service = ENV['ALLOW_ACCESS_TO_HIDDEN_SERVICE'] == 'true'
@trending_hashtags = TrendingTags.get(10, filtered: false)
@pending_tags_count = Tag.pending_review.count
@pending_appeals_count = Appeal.pending.count
@authorized_fetch = authorized_fetch_mode?
@whitelist_enabled = whitelist_mode?
@profile_directory = Setting.profile_directory
@timeline_preview = Setting.timeline_preview
@keybase_integration = Setting.enable_keybase
@trends_enabled = Setting.trends
end
private
def current_week
@current_week ||= Time.now.utc.to_date.cweek
end
def redis_info
@redis_info ||= begin
if redis.is_a?(Redis::Namespace)
redis.redis.info
if Redis.current.is_a?(Redis::Namespace)
Redis.current.redis.info
else
redis.info
Redis.current.info
end
end
end

View File

@ -1,40 +0,0 @@
# frozen_string_literal: true
class Admin::Disputes::AppealsController < Admin::BaseController
before_action :set_appeal, except: :index
def index
authorize :appeal, :index?
@appeals = filtered_appeals.page(params[:page])
end
def approve
authorize @appeal, :approve?
log_action :approve, @appeal
ApproveAppealService.new.call(@appeal, current_account)
redirect_to disputes_strike_path(@appeal.strike)
end
def reject
authorize @appeal, :approve?
log_action :reject, @appeal
@appeal.reject!(current_account)
UserMailer.appeal_rejected(@appeal.account.user, @appeal)
redirect_to disputes_strike_path(@appeal.strike)
end
private
def filtered_appeals
Admin::AppealFilter.new(filter_params.with_defaults(status: 'pending')).results.includes(strike: :account)
end
def filter_params
params.slice(:page, *Admin::AppealFilter::KEYS).permit(:page, *Admin::AppealFilter::KEYS)
end
def set_appeal
@appeal = Appeal.find(params[:id])
end
end

View File

@ -4,17 +4,6 @@ module Admin
class DomainBlocksController < BaseController
before_action :set_domain_block, only: [:show, :destroy, :edit, :update]
def batch
@form = Form::DomainBlockBatch.new(form_domain_block_batch_params.merge(current_account: current_account, action: action_from_button))
@form.save
rescue ActionController::ParameterMissing
flash[:alert] = I18n.t('admin.email_domain_blocks.no_domain_block_selected')
rescue Mastodon::NotPermittedError
flash[:alert] = I18n.t('admin.domain_blocks.created_msg')
else
redirect_to admin_instances_path(limited: '1'), notice: I18n.t('admin.domain_blocks.created_msg')
end
def new
authorize :domain_block, :create?
@domain_block = DomainBlock.new(domain: params[:_domain])
@ -67,6 +56,10 @@ module Admin
end
end
def show
authorize @domain_block, :show?
end
def destroy
authorize @domain_block, :destroy?
UnblockDomainService.new.call(@domain_block)
@ -87,15 +80,5 @@ module Admin
def resource_params
params.require(:domain_block).permit(:domain, :severity, :reject_media, :reject_reports, :private_comment, :public_comment, :obfuscate)
end
def form_domain_block_batch_params
params.require(:form_domain_block_batch).permit(domain_blocks_attributes: [:enabled, :domain, :severity, :reject_media, :reject_reports, :private_comment, :public_comment, :obfuscate])
end
def action_from_button
if params[:save]
'save'
end
end
end
end

View File

@ -6,20 +6,7 @@ module Admin
def index
authorize :email_domain_block, :index?
@email_domain_blocks = EmailDomainBlock.where(parent_id: nil).includes(:children).order(id: :desc).page(params[:page])
@form = Form::EmailDomainBlockBatch.new
end
def batch
@form = Form::EmailDomainBlockBatch.new(form_email_domain_block_batch_params.merge(current_account: current_account, action: action_from_button))
@form.save
rescue ActionController::ParameterMissing
flash[:alert] = I18n.t('admin.email_domain_blocks.no_email_domain_block_selected')
rescue Mastodon::NotPermittedError
flash[:alert] = I18n.t('admin.custom_emojis.not_permitted')
ensure
redirect_to admin_email_domain_blocks_path
end
def new
@ -32,27 +19,41 @@ module Admin
@email_domain_block = EmailDomainBlock.new(resource_params)
if action_from_button == 'save'
EmailDomainBlock.transaction do
@email_domain_block.save!
log_action :create, @email_domain_block
if @email_domain_block.save
log_action :create, @email_domain_block
(@email_domain_block.other_domains || []).uniq.each do |domain|
next if EmailDomainBlock.where(domain: domain).exists?
if @email_domain_block.with_dns_records?
hostnames = []
ips = []
other_email_domain_block = EmailDomainBlock.create!(domain: domain, parent: @email_domain_block)
log_action :create, other_email_domain_block
Resolv::DNS.open do |dns|
dns.timeouts = 5
hostnames = dns.getresources(@email_domain_block.domain, Resolv::DNS::Resource::IN::MX).to_a.map { |e| e.exchange.to_s }
([@email_domain_block.domain] + hostnames).uniq.each do |hostname|
ips.concat(dns.getresources(hostname, Resolv::DNS::Resource::IN::A).to_a.map { |e| e.address.to_s })
ips.concat(dns.getresources(hostname, Resolv::DNS::Resource::IN::AAAA).to_a.map { |e| e.address.to_s })
end
end
(hostnames + ips).each do |hostname|
another_email_domain_block = EmailDomainBlock.new(domain: hostname, parent: @email_domain_block)
log_action :create, another_email_domain_block if another_email_domain_block.save
end
end
redirect_to admin_email_domain_blocks_path, notice: I18n.t('admin.email_domain_blocks.created_msg')
else
set_resolved_records
render :new
end
rescue ActiveRecord::RecordInvalid
set_resolved_records
render :new
end
def destroy
authorize @email_domain_block, :destroy?
@email_domain_block.destroy!
log_action :destroy, @email_domain_block
redirect_to admin_email_domain_blocks_path, notice: I18n.t('admin.email_domain_blocks.destroyed_msg')
end
private
@ -61,27 +62,8 @@ module Admin
@email_domain_block = EmailDomainBlock.find(params[:id])
end
def set_resolved_records
Resolv::DNS.open do |dns|
dns.timeouts = 5
@resolved_records = dns.getresources(@email_domain_block.domain, Resolv::DNS::Resource::IN::MX).to_a
end
end
def resource_params
params.require(:email_domain_block).permit(:domain, other_domains: [])
end
def form_email_domain_block_batch_params
params.require(:form_email_domain_block_batch).permit(email_domain_block_ids: [])
end
def action_from_button
if params[:delete]
'delete'
elsif params[:save]
'save'
end
params.require(:email_domain_block).permit(:domain, :with_dns_records)
end
end
end

View File

@ -1,60 +0,0 @@
# frozen_string_literal: true
require 'csv'
module Admin
class ExportDomainAllowsController < BaseController
include AdminExportControllerConcern
before_action :set_dummy_import!, only: [:new]
ROWS_PROCESSING_LIMIT = 20_000
def new
authorize :domain_allow, :create?
end
def export
authorize :instance, :index?
send_export_file
end
def import
authorize :domain_allow, :create?
begin
@import = Admin::Import.new(import_params)
parse_import_data!(export_headers)
@data.take(ROWS_PROCESSING_LIMIT).each do |row|
domain = row['#domain'].strip
next if DomainAllow.allowed?(domain)
domain_allow = DomainAllow.new(domain: domain)
log_action :create, domain_allow if domain_allow.save
end
flash[:notice] = I18n.t('admin.domain_allows.created_msg')
rescue ActionController::ParameterMissing
flash[:error] = I18n.t('admin.export_domain_allows.no_file')
end
redirect_to admin_instances_path
end
private
def export_filename
'domain_allows.csv'
end
def export_headers
%w(#domain)
end
def export_data
CSV.generate(headers: export_headers, write_headers: true) do |content|
DomainAllow.allowed_domains.each do |instance|
content << [instance.domain]
end
end
end
end
end

View File

@ -1,71 +0,0 @@
# frozen_string_literal: true
require 'csv'
module Admin
class ExportDomainBlocksController < BaseController
include AdminExportControllerConcern
before_action :set_dummy_import!, only: [:new]
ROWS_PROCESSING_LIMIT = 20_000
def new
authorize :domain_block, :create?
end
def export
authorize :instance, :index?
send_export_file
end
def import
authorize :domain_block, :create?
@import = Admin::Import.new(import_params)
parse_import_data!(export_headers)
@global_private_comment = I18n.t('admin.export_domain_blocks.import.private_comment_template', source: @import.data_file_name, date: I18n.l(Time.now.utc))
@form = Form::DomainBlockBatch.new
@domain_blocks = @data.take(ROWS_PROCESSING_LIMIT).filter_map do |row|
domain = row['#domain'].strip
next if DomainBlock.rule_for(domain).present?
domain_block = DomainBlock.new(domain: domain,
severity: row['#severity'].strip,
reject_media: row['#reject_media'].strip,
reject_reports: row['#reject_reports'].strip,
private_comment: @global_private_comment,
public_comment: row['#public_comment']&.strip,
obfuscate: row['#obfuscate'].strip)
domain_block if domain_block.valid?
end
@warning_domains = Instance.where(domain: @domain_blocks.map(&:domain)).where('EXISTS (SELECT 1 FROM follows JOIN accounts ON follows.account_id = accounts.id OR follows.target_account_id = accounts.id WHERE accounts.domain = instances.domain)').pluck(:domain)
rescue ActionController::ParameterMissing
flash.now[:alert] = I18n.t('admin.export_domain_blocks.no_file')
set_dummy_import!
render :new
end
private
def export_filename
'domain_blocks.csv'
end
def export_headers
%w(#domain #severity #reject_media #reject_reports #public_comment #obfuscate)
end
def export_data
CSV.generate(headers: export_headers, write_headers: true) do |content|
DomainBlock.with_user_facing_limitations.each do |instance|
content << [instance.domain, instance.severity, instance.reject_media, instance.reject_reports, instance.public_comment, instance.obfuscate]
end
end
end
end
end

View File

@ -4,26 +4,19 @@ module Admin
class InstancesController < BaseController
before_action :set_instances, only: :index
before_action :set_instance, except: :index
before_action :set_exhausted_deliveries_days, only: :show
def index
authorize :instance, :index?
preload_delivery_failures!
end
def show
authorize :instance, :show?
@time_period = (6.days.ago.to_date...Time.now.utc.to_date)
end
def destroy
authorize :instance, :destroy?
Admin::DomainPurgeWorker.perform_async(@instance.domain)
log_action :destroy, @instance
redirect_to admin_instances_path, notice: I18n.t('admin.instances.destroyed_msg', domain: @instance.domain)
end
def clear_delivery_errors
authorize :delivery, :clear_delivery_errors?
@instance.delivery_failure_tracker.clear_failures!
redirect_to admin_instance_path(@instance.domain)
end
@ -31,9 +24,11 @@ module Admin
def restart_delivery
authorize :delivery, :restart_delivery?
if @instance.unavailable?
last_unavailable_domain = unavailable_domain
if last_unavailable_domain.present?
@instance.delivery_failure_tracker.track_success!
log_action :destroy, @instance.unavailable_domain
log_action :destroy, last_unavailable_domain
end
redirect_to admin_instance_path(@instance.domain)
@ -41,7 +36,8 @@ module Admin
def stop_delivery
authorize :delivery, :stop_delivery?
unavailable_domain = UnavailableDomain.create!(domain: @instance.domain)
UnavailableDomain.create(domain: @instance.domain)
log_action :create, unavailable_domain
redirect_to admin_instance_path(@instance.domain)
end
@ -52,11 +48,12 @@ module Admin
@instance = Instance.find(params[:id])
end
def set_instances
@instances = filtered_instances.page(params[:page])
def set_exhausted_deliveries_days
@exhausted_deliveries_days = @instance.delivery_failure_tracker.exhausted_deliveries_days
end
def preload_delivery_failures!
def set_instances
@instances = filtered_instances.page(params[:page])
warning_domains_map = DeliveryFailureTracker.warning_domains_map
@instances.each do |instance|
@ -64,6 +61,10 @@ module Admin
end
end
def unavailable_domain
UnavailableDomain.find_by(domain: @instance.domain)
end
def filtered_instances
InstanceFilter.new(whitelist_mode? ? { allowed: true } : filter_params).results
end

View File

@ -0,0 +1,52 @@
# frozen_string_literal: true
module Admin
class PendingAccountsController < BaseController
before_action :set_accounts, only: :index
def index
@form = Form::AccountBatch.new
end
def batch
@form = Form::AccountBatch.new(form_account_batch_params.merge(current_account: current_account, action: action_from_button))
@form.save
rescue ActionController::ParameterMissing
flash[:alert] = I18n.t('admin.accounts.no_account_selected')
ensure
redirect_to admin_pending_accounts_path(current_params)
end
def approve_all
Form::AccountBatch.new(current_account: current_account, account_ids: User.pending.pluck(:account_id), action: 'approve').save
redirect_to admin_pending_accounts_path(current_params)
end
def reject_all
Form::AccountBatch.new(current_account: current_account, account_ids: User.pending.pluck(:account_id), action: 'reject').save
redirect_to admin_pending_accounts_path(current_params)
end
private
def set_accounts
@accounts = Account.joins(:user).merge(User.pending.recent).includes(user: :invite_request).page(params[:page])
end
def form_account_batch_params
params.require(:form_account_batch).permit(:action, account_ids: [])
end
def action_from_button
if params[:approve]
'approve'
elsif params[:reject]
'reject'
end
end
def current_params
params.slice(:page).permit(:page)
end
end
end

View File

@ -9,8 +9,7 @@ module Admin
def index
authorize :account, :index?
@accounts = RelationshipFilter.new(@account, filter_params).results.includes(:account_stat, user: [:ips, :invite_request]).page(params[:page]).per(PER_PAGE)
@form = Form::AccountBatch.new
@accounts = RelationshipFilter.new(@account, filter_params).results.page(params[:page]).per(PER_PAGE)
end
private

View File

@ -14,17 +14,20 @@ module Admin
if params[:create_and_resolve]
@report.resolve!(current_account)
log_action :resolve, @report
elsif params[:create_and_unresolve]
redirect_to admin_reports_path, notice: I18n.t('admin.reports.resolved_msg')
return
end
if params[:create_and_unresolve]
@report.unresolve!
log_action :reopen, @report
end
redirect_to after_create_redirect_path, notice: I18n.t('admin.report_notes.created_msg')
redirect_to admin_report_path(@report), notice: I18n.t('admin.report_notes.created_msg')
else
@report_notes = @report.notes.includes(:account).order(id: :desc)
@action_logs = @report.history.includes(:target)
@form = Admin::StatusBatchAction.new
@statuses = @report.statuses.with_includes
@report_notes = (@report.notes.latest + @report.history + @report.target_account.targeted_account_warnings.latest.custom).sort_by(&:created_at)
@form = Form::StatusBatch.new
render template: 'admin/reports/show'
end
@ -38,14 +41,6 @@ module Admin
private
def after_create_redirect_path
if params[:create_and_resolve]
admin_reports_path
else
admin_report_path(@report)
end
end
def resource_params
params.require(:report_note).permit(
:content,

View File

@ -0,0 +1,44 @@
# frozen_string_literal: true
module Admin
class ReportedStatusesController < BaseController
before_action :set_report
def create
authorize :status, :update?
@form = Form::StatusBatch.new(form_status_batch_params.merge(current_account: current_account, action: action_from_button))
flash[:alert] = I18n.t('admin.statuses.failed_to_execute') unless @form.save
redirect_to admin_report_path(@report)
rescue ActionController::ParameterMissing
flash[:alert] = I18n.t('admin.statuses.no_status_selected')
redirect_to admin_report_path(@report)
end
private
def status_params
params.require(:status).permit(:sensitive)
end
def form_status_batch_params
params.require(:form_status_batch).permit(status_ids: [])
end
def action_from_button
if params[:nsfw_on]
'nsfw_on'
elsif params[:nsfw_off]
'nsfw_off'
elsif params[:delete]
'delete'
end
end
def set_report
@report = Report.find(params[:report_id])
end
end
end

View File

@ -1,52 +0,0 @@
# frozen_string_literal: true
class Admin::Reports::ActionsController < Admin::BaseController
before_action :set_report
def create
authorize @report, :show?
case action_from_button
when 'delete', 'mark_as_sensitive'
status_batch_action = Admin::StatusBatchAction.new(
type: action_from_button,
status_ids: @report.status_ids,
current_account: current_account,
report_id: @report.id,
send_email_notification: !@report.spam?
)
status_batch_action.save!
when 'silence', 'suspend'
account_action = Admin::AccountAction.new(
type: action_from_button,
report_id: @report.id,
target_account: @report.target_account,
current_account: current_account,
send_email_notification: !@report.spam?
)
account_action.save!
end
redirect_to admin_reports_path
end
private
def set_report
@report = Report.find(params[:report_id])
end
def action_from_button
if params[:delete]
'delete'
elsif params[:mark_as_sensitive]
'mark_as_sensitive'
elsif params[:silence]
'silence'
elsif params[:suspend]
'suspend'
end
end
end

View File

@ -13,10 +13,8 @@ module Admin
authorize @report, :show?
@report_note = @report.notes.new
@report_notes = @report.notes.includes(:account).order(id: :desc)
@action_logs = @report.history.includes(:target)
@form = Admin::StatusBatchAction.new
@statuses = @report.statuses.with_includes
@report_notes = (@report.notes.latest + @report.history + @report.target_account.targeted_account_warnings.latest.custom).sort_by(&:created_at)
@form = Form::StatusBatch.new
end
def assign_to_self

View File

@ -0,0 +1,27 @@
# frozen_string_literal: true
module Admin
class SignInTokenAuthenticationsController < BaseController
before_action :set_target_user
def create
authorize @user, :enable_sign_in_token_auth?
@user.update(skip_sign_in_token: false)
log_action :enable_sign_in_token_auth, @user
redirect_to admin_account_path(@user.account_id)
end
def destroy
authorize @user, :disable_sign_in_token_auth?
@user.update(skip_sign_in_token: true)
log_action :disable_sign_in_token_auth, @user
redirect_to admin_account_path(@user.account_id)
end
private
def set_target_user
@user = User.find(params[:user_id])
end
end
end

View File

@ -2,62 +2,71 @@
module Admin
class StatusesController < BaseController
helper_method :current_params
before_action :set_account
before_action :set_statuses
PER_PAGE = 20
def index
authorize :status, :index?
@status_batch_action = Admin::StatusBatchAction.new
@statuses = @account.statuses.where(visibility: [:public, :unlisted])
if params[:media]
@statuses.merge!(Status.joins(:media_attachments).merge(@account.media_attachments.reorder(nil)).group(:id))
end
@statuses = @statuses.preload(:media_attachments, :mentions).page(params[:page]).per(PER_PAGE)
@form = Form::StatusBatch.new
end
def batch
@status_batch_action = Admin::StatusBatchAction.new(admin_status_batch_action_params.merge(current_account: current_account, report_id: params[:report_id], type: action_from_button))
@status_batch_action.save!
def show
authorize :status, :index?
@statuses = @account.statuses.where(id: params[:id])
authorize @statuses.first, :show?
@form = Form::StatusBatch.new
end
def create
authorize :status, :update?
@form = Form::StatusBatch.new(form_status_batch_params.merge(current_account: current_account, action: action_from_button))
flash[:alert] = I18n.t('admin.statuses.failed_to_execute') unless @form.save
redirect_to admin_account_statuses_path(@account.id, current_params)
rescue ActionController::ParameterMissing
flash[:alert] = I18n.t('admin.statuses.no_status_selected')
ensure
redirect_to after_create_redirect_path
redirect_to admin_account_statuses_path(@account.id, current_params)
end
private
def admin_status_batch_action_params
params.require(:admin_status_batch_action).permit(status_ids: [])
end
def after_create_redirect_path
report_id = @status_batch_action&.report_id || params[:report_id]
if report_id.present?
admin_report_path(report_id)
else
admin_account_statuses_path(params[:account_id], current_params)
end
def form_status_batch_params
params.require(:form_status_batch).permit(:action, status_ids: [])
end
def set_account
@account = Account.find(params[:account_id])
end
def set_statuses
@statuses = Admin::StatusFilter.new(@account, filter_params).results.preload(:application, :preloadable_poll, :media_attachments, active_mentions: :account, reblog: [:account, :application, :preloadable_poll, :media_attachments, active_mentions: :account]).page(params[:page]).per(PER_PAGE)
end
def filter_params
params.slice(*Admin::StatusFilter::KEYS).permit(*Admin::StatusFilter::KEYS)
end
def current_params
params.slice(:media, :page).permit(:media, :page)
page = (params[:page] || 1).to_i
{
media: params[:media],
page: page > 1 && page,
}.select { |_, value| value.present? }
end
def action_from_button
if params[:report]
'report'
elsif params[:remove_from_report]
'remove_from_report'
if params[:nsfw_on]
'nsfw_on'
elsif params[:nsfw_off]
'nsfw_off'
elsif params[:delete]
'delete'
end

View File

@ -2,12 +2,38 @@
module Admin
class TagsController < BaseController
before_action :set_tag
before_action :set_tag, except: [:index, :batch, :approve_all, :reject_all]
before_action :set_usage_by_domain, except: [:index, :batch, :approve_all, :reject_all]
before_action :set_counters, except: [:index, :batch, :approve_all, :reject_all]
def index
authorize :tag, :index?
@tags = filtered_tags.page(params[:page])
@form = Form::TagBatch.new
end
def batch
@form = Form::TagBatch.new(form_tag_batch_params.merge(current_account: current_account, action: action_from_button))
@form.save
rescue ActionController::ParameterMissing
flash[:alert] = I18n.t('admin.accounts.no_account_selected')
ensure
redirect_to admin_tags_path(filter_params)
end
def approve_all
Form::TagBatch.new(current_account: current_account, tag_ids: Tag.pending_review.pluck(:id), action: 'approve').save
redirect_to admin_tags_path(filter_params)
end
def reject_all
Form::TagBatch.new(current_account: current_account, tag_ids: Tag.pending_review.pluck(:id), action: 'reject').save
redirect_to admin_tags_path(filter_params)
end
def show
authorize @tag, :show?
@time_period = (6.days.ago.to_date...Time.now.utc.to_date)
end
def update
@ -26,8 +52,52 @@ module Admin
@tag = Tag.find(params[:id])
end
def set_usage_by_domain
@usage_by_domain = @tag.statuses
.with_public_visibility
.excluding_silenced_accounts
.where(Status.arel_table[:id].gteq(Mastodon::Snowflake.id_at(Time.now.utc.beginning_of_day)))
.joins(:account)
.group('accounts.domain')
.reorder(statuses_count: :desc)
.pluck(Arel.sql('accounts.domain, count(*) AS statuses_count'))
end
def set_counters
@accounts_today = @tag.history.first[:accounts]
@accounts_week = Redis.current.pfcount(*current_week_days.map { |day| "activity:tags:#{@tag.id}:#{day}:accounts" })
end
def filtered_tags
TagFilter.new(filter_params).results
end
def filter_params
params.slice(:page, *TagFilter::KEYS).permit(:page, *TagFilter::KEYS)
end
def tag_params
params.require(:tag).permit(:name, :trendable, :usable, :listable)
end
def current_week_days
now = Time.now.utc.beginning_of_day.to_date
(Date.commercial(now.cwyear, now.cweek)..now).map do |date|
date.to_time(:utc).beginning_of_day.to_i
end
end
def form_tag_batch_params
params.require(:form_tag_batch).permit(:action, tag_ids: [])
end
def action_from_button
if params[:approve]
'approve'
elsif params[:reject]
'reject'
end
end
end
end

View File

@ -1,41 +0,0 @@
# frozen_string_literal: true
class Admin::Trends::Links::PreviewCardProvidersController < Admin::BaseController
def index
authorize :preview_card_provider, :index?
@preview_card_providers = filtered_preview_card_providers.page(params[:page])
@form = Trends::PreviewCardProviderBatch.new
end
def batch
@form = Trends::PreviewCardProviderBatch.new(trends_preview_card_provider_batch_params.merge(current_account: current_account, action: action_from_button))
@form.save
rescue ActionController::ParameterMissing
flash[:alert] = I18n.t('admin.accounts.no_account_selected')
ensure
redirect_to admin_trends_links_preview_card_providers_path(filter_params)
end
private
def filtered_preview_card_providers
Trends::PreviewCardProviderFilter.new(filter_params).results
end
def filter_params
params.slice(:page, *Trends::PreviewCardProviderFilter::KEYS).permit(:page, *Trends::PreviewCardProviderFilter::KEYS)
end
def trends_preview_card_provider_batch_params
params.require(:trends_preview_card_provider_batch).permit(:action, preview_card_provider_ids: [])
end
def action_from_button
if params[:approve]
'approve'
elsif params[:reject]
'reject'
end
end
end

View File

@ -1,45 +0,0 @@
# frozen_string_literal: true
class Admin::Trends::LinksController < Admin::BaseController
def index
authorize :preview_card, :index?
@preview_cards = filtered_preview_cards.page(params[:page])
@form = Trends::PreviewCardBatch.new
end
def batch
@form = Trends::PreviewCardBatch.new(trends_preview_card_batch_params.merge(current_account: current_account, action: action_from_button))
@form.save
rescue ActionController::ParameterMissing
flash[:alert] = I18n.t('admin.accounts.no_account_selected')
ensure
redirect_to admin_trends_links_path(filter_params)
end
private
def filtered_preview_cards
Trends::PreviewCardFilter.new(filter_params.with_defaults(trending: 'all')).results
end
def filter_params
params.slice(:page, *Trends::PreviewCardFilter::KEYS).permit(:page, *Trends::PreviewCardFilter::KEYS)
end
def trends_preview_card_batch_params
params.require(:trends_preview_card_batch).permit(:action, preview_card_ids: [])
end
def action_from_button
if params[:approve]
'approve'
elsif params[:approve_providers]
'approve_providers'
elsif params[:reject]
'reject'
elsif params[:reject_providers]
'reject_providers'
end
end
end

View File

@ -1,45 +0,0 @@
# frozen_string_literal: true
class Admin::Trends::StatusesController < Admin::BaseController
def index
authorize :status, :index?
@statuses = filtered_statuses.page(params[:page])
@form = Trends::StatusBatch.new
end
def batch
@form = Trends::StatusBatch.new(trends_status_batch_params.merge(current_account: current_account, action: action_from_button))
@form.save
rescue ActionController::ParameterMissing
flash[:alert] = I18n.t('admin.accounts.no_account_selected')
ensure
redirect_to admin_trends_statuses_path(filter_params)
end
private
def filtered_statuses
Trends::StatusFilter.new(filter_params.with_defaults(trending: 'all')).results.includes(:account, :media_attachments, :active_mentions)
end
def filter_params
params.slice(:page, *Trends::StatusFilter::KEYS).permit(:page, *Trends::StatusFilter::KEYS)
end
def trends_status_batch_params
params.require(:trends_status_batch).permit(:action, status_ids: [])
end
def action_from_button
if params[:approve]
'approve'
elsif params[:approve_accounts]
'approve_accounts'
elsif params[:reject]
'reject'
elsif params[:reject_accounts]
'reject_accounts'
end
end
end

View File

@ -1,41 +0,0 @@
# frozen_string_literal: true
class Admin::Trends::TagsController < Admin::BaseController
def index
authorize :tag, :index?
@tags = filtered_tags.page(params[:page])
@form = Trends::TagBatch.new
end
def batch
@form = Trends::TagBatch.new(trends_tag_batch_params.merge(current_account: current_account, action: action_from_button))
@form.save
rescue ActionController::ParameterMissing
flash[:alert] = I18n.t('admin.accounts.no_account_selected')
ensure
redirect_to admin_trends_tags_path(filter_params)
end
private
def filtered_tags
Trends::TagFilter.new(filter_params).results
end
def filter_params
params.slice(:page, *Trends::TagFilter::KEYS).permit(:page, *Trends::TagFilter::KEYS)
end
def trends_tag_batch_params
params.require(:trends_tag_batch).permit(:action, tag_ids: [])
end
def action_from_button
if params[:approve]
'approve'
elsif params[:reject]
'reject'
end
end
end

View File

@ -5,17 +5,17 @@ class Api::BaseController < ApplicationController
DEFAULT_ACCOUNTS_LIMIT = 40
include RateLimitHeaders
include AccessTokenTrackingConcern
skip_before_action :store_current_location
skip_before_action :require_functional!, unless: :whitelist_mode?
before_action :require_authenticated_user!, if: :disallow_unauthenticated_api_access?
before_action :require_not_suspended!
before_action :set_cache_headers
protect_from_forgery with: :null_session
skip_around_action :set_locale
rescue_from ActiveRecord::RecordInvalid, Mastodon::ValidationError do |e|
render json: { error: e.to_s }, status: 422
end
@ -98,10 +98,6 @@ class Api::BaseController < ApplicationController
render json: { error: 'This method requires an authenticated user' }, status: 401 unless current_user
end
def require_not_suspended!
render json: { error: 'Your login is currently disabled' }, status: 403 if current_user&.account&.suspended?
end
def require_user!
if !current_user
render json: { error: 'This method requires an authenticated user' }, status: 422

View File

@ -0,0 +1,23 @@
# frozen_string_literal: true
class Api::ProofsController < Api::BaseController
include AccountOwnedConcern
skip_before_action :require_authenticated_user!
before_action :set_provider
def index
render json: @account, serializer: @provider.serializer_class
end
private
def set_provider
@provider = ProofProvider.find(params[:provider]) || raise(ActiveRecord::RecordNotFound)
end
def username_param
params[:username]
end
end

View File

@ -1,25 +0,0 @@
# frozen_string_literal: true
class Api::V1::Accounts::FamiliarFollowersController < Api::BaseController
before_action -> { doorkeeper_authorize! :read, :'read:follows' }
before_action :require_user!
before_action :set_accounts
def index
render json: familiar_followers.accounts, each_serializer: REST::FamiliarFollowersSerializer
end
private
def set_accounts
@accounts = Account.without_suspended.where(id: account_ids).select('id, hide_collections').index_by(&:id).values_at(*account_ids).compact
end
def familiar_followers
FamiliarFollowersPresenter.new(@accounts, current_user.account_id)
end
def account_ids
Array(params[:id]).map(&:to_i)
end
end

View File

@ -5,7 +5,8 @@ class Api::V1::Accounts::IdentityProofsController < Api::BaseController
before_action :set_account
def index
render json: []
@proofs = @account.suspended? ? [] : @account.identity_proofs.active
render json: @proofs, each_serializer: REST::IdentityProofSerializer
end
private

View File

@ -12,7 +12,5 @@ class Api::V1::Accounts::LookupController < Api::BaseController
def set_account
@account = ResolveAccountService.new.call(params[:acct], skip_webfinger: true) || raise(ActiveRecord::RecordNotFound)
rescue Addressable::URI::InvalidURIError
raise(ActiveRecord::RecordNotFound)
end
end

View File

@ -22,16 +22,55 @@ class Api::V1::Accounts::StatusesController < Api::BaseController
end
def cached_account_statuses
statuses = truthy_param?(:pinned) ? pinned_scope : permitted_account_statuses
statuses.merge!(only_media_scope) if truthy_param?(:only_media)
statuses.merge!(no_replies_scope) if truthy_param?(:exclude_replies)
statuses.merge!(no_reblogs_scope) if truthy_param?(:exclude_reblogs)
statuses.merge!(hashtag_scope) if params[:tagged].present?
cache_collection_paginated_by_id(
AccountStatusesFilter.new(@account, current_account, params).results,
statuses,
Status,
limit_param(DEFAULT_STATUSES_LIMIT),
params_slice(:max_id, :since_id, :min_id)
)
end
def permitted_account_statuses
@account.statuses.permitted_for(@account, current_account)
end
def only_media_scope
Status.joins(:media_attachments).merge(@account.media_attachments.reorder(nil)).group(:id)
end
def pinned_scope
return Status.none if @account.blocking?(current_account)
@account.pinned_statuses
end
def no_replies_scope
Status.without_replies
end
def no_reblogs_scope
Status.without_reblogs
end
def hashtag_scope
tag = Tag.find_normalized(params[:tagged])
if tag
Status.tagged_with(tag.id)
else
Status.none
end
end
def pagination_params(core_params)
params.slice(:limit, *AccountStatusesFilter::KEYS).permit(:limit, *AccountStatusesFilter::KEYS).merge(core_params)
params.slice(:limit, :only_media, :exclude_replies).permit(:limit, :only_media, :exclude_replies).merge(core_params)
end
def insert_pagination_headers

View File

@ -1,16 +1,14 @@
# frozen_string_literal: true
class Api::V1::AccountsController < Api::BaseController
before_action -> { authorize_if_got_token! :read, :'read:accounts' }, except: [:create, :follow, :unfollow, :remove_from_followers, :block, :unblock, :mute, :unmute]
before_action -> { doorkeeper_authorize! :follow, :write, :'write:follows' }, only: [:follow, :unfollow, :remove_from_followers]
before_action -> { doorkeeper_authorize! :follow, :write, :'write:mutes' }, only: [:mute, :unmute]
before_action -> { doorkeeper_authorize! :follow, :write, :'write:blocks' }, only: [:block, :unblock]
before_action -> { authorize_if_got_token! :read, :'read:accounts' }, except: [:create, :follow, :unfollow, :block, :unblock, :mute, :unmute]
before_action -> { doorkeeper_authorize! :follow, :'write:follows' }, only: [:follow, :unfollow]
before_action -> { doorkeeper_authorize! :follow, :'write:mutes' }, only: [:mute, :unmute]
before_action -> { doorkeeper_authorize! :follow, :'write:blocks' }, only: [:block, :unblock]
before_action -> { doorkeeper_authorize! :write, :'write:accounts' }, only: [:create]
before_action :require_user!, except: [:show, :create]
before_action :set_account, except: [:create]
before_action :check_account_approval, except: [:create]
before_action :check_account_confirmation, except: [:create]
before_action :check_enabled_registrations, only: [:create]
skip_before_action :require_authenticated_user!, only: :create
@ -55,11 +53,6 @@ class Api::V1::AccountsController < Api::BaseController
render json: @account, serializer: REST::RelationshipSerializer, relationships: relationships
end
def remove_from_followers
RemoveFromFollowersService.new.call(current_user.account, @account)
render json: @account, serializer: REST::RelationshipSerializer, relationships: relationships
end
def unblock
UnblockService.new.call(current_user.account, @account)
render json: @account, serializer: REST::RelationshipSerializer, relationships: relationships
@ -76,14 +69,6 @@ class Api::V1::AccountsController < Api::BaseController
@account = Account.find(params[:id])
end
def check_account_approval
raise(ActiveRecord::RecordNotFound) if @account.local? && @account.user_pending?
end
def check_account_confirmation
raise(ActiveRecord::RecordNotFound) if @account.local? && !@account.user_confirmed?
end
def relationships(**options)
AccountRelationshipsPresenter.new([@account.id], current_user.account_id, **options)
end
@ -93,14 +78,10 @@ class Api::V1::AccountsController < Api::BaseController
end
def check_enabled_registrations
forbidden if single_user_mode? || omniauth_only? || !allowed_registrations?
forbidden if single_user_mode? || !allowed_registrations?
end
def allowed_registrations?
Setting.registrations_mode != 'none'
end
def omniauth_only?
ENV['OMNIAUTH_ONLY'] == 'true'
end
end

View File

@ -1,7 +1,7 @@
# frozen_string_literal: true
class Api::V1::Admin::AccountActionsController < Api::BaseController
before_action -> { authorize_if_got_token! :'admin:write', :'admin:write:accounts' }
before_action -> { doorkeeper_authorize! :'admin:write', :'admin:write:accounts' }
before_action :require_staff!
before_action :set_account

View File

@ -6,8 +6,8 @@ class Api::V1::Admin::AccountsController < Api::BaseController
LIMIT = 100
before_action -> { authorize_if_got_token! :'admin:read', :'admin:read:accounts' }, only: [:index, :show]
before_action -> { authorize_if_got_token! :'admin:write', :'admin:write:accounts' }, except: [:index, :show]
before_action -> { doorkeeper_authorize! :'admin:read', :'admin:read:accounts' }, only: [:index, :show]
before_action -> { doorkeeper_authorize! :'admin:write', :'admin:write:accounts' }, except: [:index, :show]
before_action :require_staff!
before_action :set_accounts, only: :index
before_action :set_account, except: :index
@ -65,9 +65,8 @@ class Api::V1::Admin::AccountsController < Api::BaseController
def destroy
authorize @account, :destroy?
json = render_to_body json: @account, serializer: REST::Admin::AccountSerializer
Admin::AccountDeletionWorker.perform_async(@account.id)
render json: json
render json: @account, serializer: REST::Admin::AccountSerializer
end
def unsensitive
@ -95,7 +94,7 @@ class Api::V1::Admin::AccountsController < Api::BaseController
private
def set_accounts
@accounts = filtered_accounts.order(id: :desc).includes(user: [:invite_request, :invite, :ips]).to_a_paginated_by_id(limit_param(LIMIT), params_slice(:max_id, :since_id, :min_id))
@accounts = filtered_accounts.order(id: :desc).includes(user: [:invite_request, :invite]).to_a_paginated_by_id(limit_param(LIMIT), params_slice(:max_id, :since_id, :min_id))
end
def set_account
@ -103,27 +102,13 @@ class Api::V1::Admin::AccountsController < Api::BaseController
end
def filtered_accounts
AccountFilter.new(translated_filter_params).results
AccountFilter.new(filter_params).results
end
def filter_params
params.permit(*FILTER_PARAMS)
end
def translated_filter_params
translated_params = { origin: 'local', status: 'active' }.merge(filter_params.slice(*AccountFilter::KEYS))
translated_params[:origin] = 'remote' if params[:remote].present?
%i(active pending disabled silenced suspended).each do |status|
translated_params[:status] = status.to_s if params[status].present?
end
translated_params[:permissions] = 'staff' if params[:staff].present?
translated_params
end
def insert_pagination_headers
set_pagination_headers(next_path, prev_path)
end

View File

@ -1,23 +0,0 @@
# frozen_string_literal: true
class Api::V1::Admin::DimensionsController < Api::BaseController
before_action -> { authorize_if_got_token! :'admin:read' }
before_action :require_staff!
before_action :set_dimensions
def create
render json: @dimensions, each_serializer: REST::Admin::DimensionSerializer
end
private
def set_dimensions
@dimensions = Admin::Metrics::Dimension.retrieve(
params[:keys],
params[:start_at],
params[:end_at],
params[:limit],
params
)
end
end

View File

@ -1,22 +0,0 @@
# frozen_string_literal: true
class Api::V1::Admin::MeasuresController < Api::BaseController
before_action -> { authorize_if_got_token! :'admin:read' }
before_action :require_staff!
before_action :set_measures
def create
render json: @measures, each_serializer: REST::Admin::MeasureSerializer
end
private
def set_measures
@measures = Admin::Metrics::Measure.retrieve(
params[:keys],
params[:start_at],
params[:end_at],
params
)
end
end

View File

@ -6,8 +6,8 @@ class Api::V1::Admin::ReportsController < Api::BaseController
LIMIT = 100
before_action -> { authorize_if_got_token! :'admin:read', :'admin:read:reports' }, only: [:index, :show]
before_action -> { authorize_if_got_token! :'admin:write', :'admin:write:reports' }, except: [:index, :show]
before_action -> { doorkeeper_authorize! :'admin:read', :'admin:read:reports' }, only: [:index, :show]
before_action -> { doorkeeper_authorize! :'admin:write', :'admin:write:reports' }, except: [:index, :show]
before_action :require_staff!
before_action :set_reports, only: :index
before_action :set_report, except: :index
@ -32,12 +32,6 @@ class Api::V1::Admin::ReportsController < Api::BaseController
render json: @report, serializer: REST::Admin::ReportSerializer
end
def update
authorize @report, :update?
@report.update!(report_params)
render json: @report, serializer: REST::Admin::ReportSerializer
end
def assign_to_self
authorize @report, :update?
@report.update!(assigned_account_id: current_account.id)
@ -80,10 +74,6 @@ class Api::V1::Admin::ReportsController < Api::BaseController
ReportFilter.new(filter_params).results
end
def report_params
params.permit(:category, rule_ids: [])
end
def filter_params
params.permit(*FILTER_PARAMS)
end

View File

@ -1,21 +0,0 @@
# frozen_string_literal: true
class Api::V1::Admin::RetentionController < Api::BaseController
before_action -> { authorize_if_got_token! :'admin:read' }
before_action :require_staff!
before_action :set_cohorts
def create
render json: @cohorts, each_serializer: REST::Admin::CohortSerializer
end
private
def set_cohorts
@cohorts = Admin::Metrics::Retention.new(
params[:start_at],
params[:end_at],
params[:frequency]
).cohorts
end
end

View File

@ -1,17 +0,0 @@
# frozen_string_literal: true
class Api::V1::Admin::Trends::LinksController < Api::BaseController
before_action -> { authorize_if_got_token! :'admin:read' }
before_action :require_staff!
before_action :set_links
def index
render json: @links, each_serializer: REST::Trends::LinkSerializer
end
private
def set_links
@links = Trends.links.query.limit(limit_param(10))
end
end

View File

@ -1,17 +0,0 @@
# frozen_string_literal: true
class Api::V1::Admin::Trends::StatusesController < Api::BaseController
before_action -> { authorize_if_got_token! :'admin:read' }
before_action :require_staff!
before_action :set_statuses
def index
render json: @statuses, each_serializer: REST::StatusSerializer
end
private
def set_statuses
@statuses = cache_collection(Trends.statuses.query.limit(limit_param(DEFAULT_STATUSES_LIMIT)), Status)
end
end

View File

@ -1,17 +0,0 @@
# frozen_string_literal: true
class Api::V1::Admin::Trends::TagsController < Api::BaseController
before_action -> { authorize_if_got_token! :'admin:read' }
before_action :require_staff!
before_action :set_tags
def index
render json: @tags, each_serializer: REST::Admin::TagSerializer
end
private
def set_tags
@tags = Trends.tags.query.limit(limit_param(10))
end
end

View File

@ -1,7 +1,7 @@
# frozen_string_literal: true
class Api::V1::BlocksController < Api::BaseController
before_action -> { doorkeeper_authorize! :follow, :read, :'read:blocks' }
before_action -> { doorkeeper_authorize! :follow, :'read:blocks' }
before_action :require_user!
after_action :insert_pagination_headers

View File

@ -21,7 +21,7 @@ class Api::V1::BookmarksController < Api::BaseController
end
def results
@_results ||= account_bookmarks.joins(:status).eager_load(:status).to_a_paginated_by_id(
@_results ||= account_bookmarks.eager_load(:status).to_a_paginated_by_id(
limit_param(DEFAULT_STATUSES_LIMIT),
params_slice(:max_id, :since_id, :min_id)
)

View File

@ -3,8 +3,8 @@
class Api::V1::DomainBlocksController < Api::BaseController
BLOCK_LIMIT = 100
before_action -> { doorkeeper_authorize! :follow, :read, :'read:blocks' }, only: :show
before_action -> { doorkeeper_authorize! :follow, :write, :'write:blocks' }, except: :show
before_action -> { doorkeeper_authorize! :follow, :'read:blocks' }, only: :show
before_action -> { doorkeeper_authorize! :follow, :'write:blocks' }, except: :show
before_action :require_user!
after_action :insert_pagination_headers, only: :show

View File

@ -1,7 +1,7 @@
# frozen_string_literal: true
class Api::V1::Emails::ConfirmationsController < Api::BaseController
before_action -> { doorkeeper_authorize! :write, :'write:accounts' }
before_action :doorkeeper_authorize!
before_action :require_user_owned_by_application!
before_action :require_user_not_confirmed!
@ -19,6 +19,6 @@ class Api::V1::Emails::ConfirmationsController < Api::BaseController
end
def require_user_not_confirmed!
render json: { error: 'This method is only available while the e-mail is awaiting confirmation' }, status: :forbidden unless !current_user.confirmed? || current_user.unconfirmed_email.present?
render json: { error: 'This method is only available while the e-mail is awaiting confirmation' }, status: :forbidden if current_user.confirmed? || current_user.unconfirmed_email.blank?
end
end

View File

@ -21,7 +21,7 @@ class Api::V1::FavouritesController < Api::BaseController
end
def results
@_results ||= account_favourites.joins(:status).eager_load(:status).to_a_paginated_by_id(
@_results ||= account_favourites.eager_load(:status).to_a_paginated_by_id(
limit_param(DEFAULT_STATUSES_LIMIT),
params_slice(:max_id, :since_id, :min_id)
)

View File

@ -1,8 +1,8 @@
# frozen_string_literal: true
class Api::V1::FollowRequestsController < Api::BaseController
before_action -> { doorkeeper_authorize! :follow, :read, :'read:follows' }, only: :index
before_action -> { doorkeeper_authorize! :follow, :write, :'write:follows' }, except: :index
before_action -> { doorkeeper_authorize! :follow, :'read:follows' }, only: :index
before_action -> { doorkeeper_authorize! :follow, :'write:follows' }, except: :index
before_action :require_user!
after_action :insert_pagination_headers, only: :index
@ -13,7 +13,7 @@ class Api::V1::FollowRequestsController < Api::BaseController
def authorize
AuthorizeFollowService.new.call(account, current_account)
LocalNotificationWorker.perform_async(current_account.id, Follow.find_by(account: account, target_account: current_account).id, 'Follow', 'follow')
NotifyService.new.call(current_account, :follow, Follow.find_by(account: account, target_account: current_account))
render json: account, serializer: REST::RelationshipSerializer, relationships: relationships
end

View File

@ -14,21 +14,22 @@ class Api::V1::Instances::ActivityController < Api::BaseController
private
def activity
statuses_tracker = ActivityTracker.new('activity:statuses:local', :basic)
logins_tracker = ActivityTracker.new('activity:logins', :unique)
registrations_tracker = ActivityTracker.new('activity:accounts:local', :basic)
weeks = []
(0...12).map do |i|
start_of_week = i.weeks.ago
end_of_week = start_of_week + 6.days
12.times do |i|
day = i.weeks.ago.to_date
week_id = day.cweek
week = Date.commercial(day.cwyear, week_id)
{
week: start_of_week.to_i.to_s,
statuses: statuses_tracker.sum(start_of_week, end_of_week).to_s,
logins: logins_tracker.sum(start_of_week, end_of_week).to_s,
registrations: registrations_tracker.sum(start_of_week, end_of_week).to_s,
weeks << {
week: week.to_time.to_i.to_s,
statuses: Redis.current.get("activity:statuses:local:#{week_id}") || '0',
logins: Redis.current.pfcount("activity:logins:#{week_id}").to_s,
registrations: Redis.current.get("activity:accounts:local:#{week_id}") || '0',
}
end
weeks
end
def require_enabled_api!

View File

@ -20,7 +20,7 @@ class Api::V1::MediaController < Api::BaseController
end
def update
@media_attachment.update!(updateable_media_attachment_params)
@media_attachment.update!(media_attachment_params)
render json: @media_attachment, serializer: REST::MediaAttachmentSerializer, status: status_code_for_media_attachment
end
@ -31,7 +31,7 @@ class Api::V1::MediaController < Api::BaseController
end
def set_media_attachment
@media_attachment = current_account.media_attachments.where(status_id: nil).find(params[:id])
@media_attachment = current_account.media_attachments.unattached.find(params[:id])
end
def check_processing
@ -42,10 +42,6 @@ class Api::V1::MediaController < Api::BaseController
params.permit(:file, :thumbnail, :description, :focus)
end
def updateable_media_attachment_params
params.permit(:thumbnail, :description, :focus)
end
def file_type_error
{ error: 'File type of uploaded media could not be verified' }
end

View File

@ -1,7 +1,7 @@
# frozen_string_literal: true
class Api::V1::MutesController < Api::BaseController
before_action -> { doorkeeper_authorize! :follow, :read, :'read:mutes' }
before_action -> { doorkeeper_authorize! :follow, :'read:mutes' }
before_action :require_user!
after_action :insert_pagination_headers

View File

@ -1,8 +1,8 @@
# frozen_string_literal: true
class Api::V1::NotificationsController < Api::BaseController
before_action -> { doorkeeper_authorize! :read, :'read:notifications' }, except: [:clear, :dismiss, :destroy, :destroy_multiple]
before_action -> { doorkeeper_authorize! :write, :'write:notifications' }, only: [:clear, :dismiss, :destroy, :destroy_multiple]
before_action -> { doorkeeper_authorize! :read, :'read:notifications' }, except: [:clear, :dismiss]
before_action -> { doorkeeper_authorize! :write, :'write:notifications' }, only: [:clear, :dismiss]
before_action :require_user!
after_action :insert_pagination_headers, only: :index
@ -44,18 +44,13 @@ class Api::V1::NotificationsController < Api::BaseController
limit_param(DEFAULT_NOTIFICATIONS_LIMIT),
params_slice(:max_id, :since_id, :min_id)
)
Notification.preload_cache_collection_target_statuses(notifications) do |target_statuses|
cache_collection(target_statuses, Status)
end
end
def browserable_account_notifications
current_account.notifications.without_suspended.browserable(
types: Array(browserable_params[:types]),
exclude_types: Array(browserable_params[:exclude_types]),
from_account_id: browserable_params[:account_id]
)
current_account.notifications.without_suspended.browserable(exclude_types, from_account)
end
def target_statuses_from_notifications
@ -86,11 +81,17 @@ class Api::V1::NotificationsController < Api::BaseController
@notifications.first.id
end
def browserable_params
params.permit(:account_id, types: [], exclude_types: [])
def exclude_types
val = params.permit(exclude_types: [])[:exclude_types] || []
val = [val] unless val.is_a?(Enumerable)
val
end
def from_account
params[:account_id]
end
def pagination_params(core_params)
params.slice(:limit, :account_id, :types, :exclude_types).permit(:limit, :account_id, types: [], exclude_types: []).merge(core_params)
params.slice(:limit, :exclude_types).permit(:limit, exclude_types: []).merge(core_params)
end
end

View File

@ -10,7 +10,9 @@ class Api::V1::ReportsController < Api::BaseController
@report = ReportService.new.call(
current_account,
reported_account,
report_params
status_ids: reported_status_ids,
comment: report_params[:comment],
forward: report_params[:forward]
)
render json: @report, serializer: REST::ReportSerializer
@ -18,11 +20,19 @@ class Api::V1::ReportsController < Api::BaseController
private
def reported_status_ids
reported_account.statuses.with_discarded.find(status_ids).pluck(:id)
end
def status_ids
Array(report_params[:status_ids])
end
def reported_account
Account.find(report_params[:account_id])
end
def report_params
params.permit(:account_id, :comment, :category, :forward, status_ids: [], rule_ids: [])
params.permit(:account_id, :comment, :forward, status_ids: [])
end
end

View File

@ -1,21 +0,0 @@
# frozen_string_literal: true
class Api::V1::Statuses::HistoriesController < Api::BaseController
include Authorization
before_action -> { authorize_if_got_token! :read, :'read:statuses' }
before_action :set_status
def show
render json: @status.edits.includes(:account, status: [:account]), each_serializer: REST::StatusEditSerializer
end
private
def set_status
@status = Status.find(params[:status_id])
authorize @status, :show?
rescue Mastodon::NotPermittedError
not_found
end
end

View File

@ -1,21 +0,0 @@
# frozen_string_literal: true
class Api::V1::Statuses::SourcesController < Api::BaseController
include Authorization
before_action -> { doorkeeper_authorize! :read, :'read:statuses' }
before_action :set_status
def show
render json: @status, serializer: REST::StatusSourceSerializer
end
private
def set_status
@status = Status.find(params[:status_id])
authorize @status, :show?
rescue Mastodon::NotPermittedError
not_found
end
end

View File

@ -3,14 +3,13 @@
class Api::V1::StatusesController < Api::BaseController
include Authorization
before_action -> { authorize_if_got_token! :read, :'read:statuses' }, except: [:create, :update, :destroy]
before_action -> { doorkeeper_authorize! :write, :'write:statuses' }, only: [:create, :update, :destroy]
before_action -> { authorize_if_got_token! :read, :'read:statuses' }, except: [:create, :destroy]
before_action -> { doorkeeper_authorize! :write, :'write:statuses' }, only: [:create, :destroy]
before_action :require_user!, except: [:show, :context]
before_action :set_status, only: [:show, :context]
before_action :set_thread, only: [:create]
override_rate_limit_headers :create, family: :statuses
override_rate_limit_headers :update, family: :statuses
# This API was originally unlimited, pagination cannot be introduced without
# breaking backwards-compatibility. Arbitrarily high number to cover most
@ -36,55 +35,32 @@ class Api::V1::StatusesController < Api::BaseController
end
def create
@status = PostStatusService.new.call(
current_user.account,
text: status_params[:status],
thread: @thread,
media_ids: status_params[:media_ids],
sensitive: status_params[:sensitive],
spoiler_text: status_params[:spoiler_text],
visibility: status_params[:visibility],
language: status_params[:language],
scheduled_at: status_params[:scheduled_at],
application: doorkeeper_token.application,
poll: status_params[:poll],
content_type: status_params[:content_type],
idempotency: request.headers['Idempotency-Key'],
with_rate_limit: true
)
@status = PostStatusService.new.call(current_user.account,
text: status_params[:status],
thread: @thread,
media_ids: status_params[:media_ids],
sensitive: status_params[:sensitive],
spoiler_text: status_params[:spoiler_text],
visibility: status_params[:visibility],
scheduled_at: status_params[:scheduled_at],
application: doorkeeper_token.application,
poll: status_params[:poll],
content_type: status_params[:content_type],
idempotency: request.headers['Idempotency-Key'],
with_rate_limit: true)
render json: @status, serializer: @status.is_a?(ScheduledStatus) ? REST::ScheduledStatusSerializer : REST::StatusSerializer
end
def update
@status = Status.where(account: current_account).find(params[:id])
authorize @status, :update?
UpdateStatusService.new.call(
@status,
current_account.id,
text: status_params[:status],
media_ids: status_params[:media_ids],
sensitive: status_params[:sensitive],
spoiler_text: status_params[:spoiler_text],
poll: status_params[:poll],
content_type: status_params[:content_type]
)
render json: @status, serializer: REST::StatusSerializer
end
def destroy
@status = Status.where(account: current_account).find(params[:id])
@status = Status.where(account_id: current_user.account).find(params[:id])
authorize @status, :destroy?
@status.discard
RemovalWorker.perform_async(@status.id, redraft: true)
@status.account.statuses_count = @status.account.statuses_count - 1
json = render_to_body json: @status, serializer: REST::StatusSerializer, source_requested: true
RemovalWorker.perform_async(@status.id, { 'redraft' => true })
render json: json
render json: @status, serializer: REST::StatusSerializer, source_requested: true
end
private
@ -97,9 +73,8 @@ class Api::V1::StatusesController < Api::BaseController
end
def set_thread
@thread = Status.find(status_params[:in_reply_to_id]) if status_params[:in_reply_to_id].present?
authorize(@thread, :show?) if @thread.present?
rescue ActiveRecord::RecordNotFound, Mastodon::NotPermittedError
@thread = status_params[:in_reply_to_id].blank? ? nil : Status.find(status_params[:in_reply_to_id])
rescue ActiveRecord::RecordNotFound
render json: { error: I18n.t('statuses.errors.in_reply_not_found') }, status: 404
end
@ -110,7 +85,6 @@ class Api::V1::StatusesController < Api::BaseController
:sensitive,
:spoiler_text,
:visibility,
:language,
:scheduled_at,
:content_type,
media_ids: [],

View File

@ -1,53 +0,0 @@
# frozen_string_literal: true
class Api::V1::Trends::LinksController < Api::BaseController
before_action :set_links
after_action :insert_pagination_headers
DEFAULT_LINKS_LIMIT = 10
def index
render json: @links, each_serializer: REST::Trends::LinkSerializer
end
private
def set_links
@links = begin
if Setting.trends
links_from_trends
else
[]
end
end
end
def links_from_trends
Trends.links.query.allowed.in_locale(content_locale).offset(offset_param).limit(limit_param(DEFAULT_LINKS_LIMIT))
end
def insert_pagination_headers
set_pagination_headers(next_path, prev_path)
end
def pagination_params(core_params)
params.slice(:limit).permit(:limit).merge(core_params)
end
def next_path
api_v1_trends_links_url pagination_params(offset: offset_param + limit_param(DEFAULT_LINKS_LIMIT)) if records_continue?
end
def prev_path
api_v1_trends_links_url pagination_params(offset: offset_param - limit_param(DEFAULT_LINKS_LIMIT)) if offset_param > limit_param(DEFAULT_LINKS_LIMIT)
end
def records_continue?
@links.size == limit_param(DEFAULT_LINKS_LIMIT)
end
def offset_param
params[:offset].to_i
end
end

View File

@ -1,53 +0,0 @@
# frozen_string_literal: true
class Api::V1::Trends::StatusesController < Api::BaseController
before_action :set_statuses
after_action :insert_pagination_headers
def index
render json: @statuses, each_serializer: REST::StatusSerializer
end
private
def set_statuses
@statuses = begin
if Setting.trends
cache_collection(statuses_from_trends, Status)
else
[]
end
end
end
def statuses_from_trends
scope = Trends.statuses.query.allowed.in_locale(content_locale)
scope = scope.filtered_for(current_account) if user_signed_in?
scope.offset(offset_param).limit(limit_param(DEFAULT_STATUSES_LIMIT))
end
def insert_pagination_headers
set_pagination_headers(next_path, prev_path)
end
def pagination_params(core_params)
params.slice(:limit).permit(:limit).merge(core_params)
end
def next_path
api_v1_trends_statuses_url pagination_params(offset: offset_param + limit_param(DEFAULT_STATUSES_LIMIT)) if records_continue?
end
def prev_path
api_v1_trends_statuses_url pagination_params(offset: offset_param - limit_param(DEFAULT_STATUSES_LIMIT)) if offset_param > limit_param(DEFAULT_STATUSES_LIMIT)
end
def offset_param
params[:offset].to_i
end
def records_continue?
@statuses.size == limit_param(DEFAULT_STATUSES_LIMIT)
end
end

View File

@ -1,49 +0,0 @@
# frozen_string_literal: true
class Api::V1::Trends::TagsController < Api::BaseController
before_action :set_tags
after_action :insert_pagination_headers
DEFAULT_TAGS_LIMIT = 10
def index
render json: @tags, each_serializer: REST::TagSerializer
end
private
def set_tags
@tags = begin
if Setting.trends
Trends.tags.query.allowed.offset(offset_param).limit(limit_param(DEFAULT_TAGS_LIMIT))
else
[]
end
end
end
def insert_pagination_headers
set_pagination_headers(next_path, prev_path)
end
def pagination_params(core_params)
params.slice(:limit).permit(:limit).merge(core_params)
end
def next_path
api_v1_trends_tags_url pagination_params(offset: offset_param + limit_param(DEFAULT_TAGS_LIMIT)) if records_continue?
end
def prev_path
api_v1_trends_tags_url pagination_params(offset: offset_param - limit_param(DEFAULT_TAGS_LIMIT)) if offset_param > limit_param(DEFAULT_TAGS_LIMIT)
end
def offset_param
params[:offset].to_i
end
def records_continue?
@tags.size == limit_param(DEFAULT_TAGS_LIMIT)
end
end

View File

@ -0,0 +1,15 @@
# frozen_string_literal: true
class Api::V1::TrendsController < Api::BaseController
before_action :set_tags
def index
render json: @tags, each_serializer: REST::TagSerializer
end
private
def set_tags
@tags = TrendingTags.get(limit_param(10))
end
end

View File

@ -1,31 +0,0 @@
# frozen_string_literal: true
class Api::V2::Admin::AccountsController < Api::V1::Admin::AccountsController
FILTER_PARAMS = %i(
origin
status
permissions
username
by_domain
display_name
email
ip
invited_by
).freeze
PAGINATION_PARAMS = (%i(limit) + FILTER_PARAMS).freeze
private
def filtered_accounts
AccountFilter.new(filter_params).results
end
def filter_params
params.permit(*FILTER_PARAMS)
end
def pagination_params(core_params)
params.slice(*PAGINATION_PARAMS).permit(*PAGINATION_PARAMS).merge(core_params)
end
end

View File

@ -11,10 +11,6 @@ class Api::V2::SearchController < Api::BaseController
def index
@search = Search.new(search_results)
render json: @search, serializer: REST::SearchSerializer
rescue Mastodon::SyntaxError
unprocessable_entity
rescue ActiveRecord::RecordNotFound
not_found
end
private

Some files were not shown because too many files have changed in this diff Show More