0
0
Fork 0

Merge remote-tracking branch 'upstream/main'

This commit is contained in:
ASTRO:? 2025-03-14 20:25:34 +09:00
commit d564483d30
No known key found for this signature in database
GPG key ID: 2938B9B314D8EF8B
1796 changed files with 48111 additions and 29322 deletions

59
.annotaterb.yml Normal file
View file

@ -0,0 +1,59 @@
---
:position: before
:position_in_additional_file_patterns: before
:position_in_class: before
:position_in_factory: before
:position_in_fixture: before
:position_in_routes: before
:position_in_serializer: before
:position_in_test: before
:classified_sort: true
:exclude_controllers: true
:exclude_factories: true
:exclude_fixtures: true
:exclude_helpers: true
:exclude_scaffolds: true
:exclude_serializers: true
:exclude_sti_subclasses: true
:exclude_tests: true
:force: false
:format_markdown: false
:format_rdoc: false
:format_yard: false
:frozen: false
:ignore_model_sub_dir: false
:ignore_unknown_models: false
:include_version: false
:show_complete_foreign_keys: false
:show_foreign_keys: false
:show_indexes: false
:simple_indexes: false
:sort: false
:timestamp: false
:trace: false
:with_comment: true
:with_column_comments: true
:with_table_comments: true
:active_admin: false
:command:
:debug: false
:hide_default_column_types: ''
:hide_limit_column_types: 'integer,boolean'
:ignore_columns:
:ignore_routes:
:models: true
:routes: false
:skip_on_db_migrate: false
:target_action: :do_annotations
:wrapper:
:wrapper_close:
:wrapper_open:
:classes_default_to_s: []
:additional_file_patterns: []
:model_dir:
- app/models
:require: []
:root_dir:
- ''
:show_check_constraints: false

View file

@ -1,10 +1,6 @@
[production]
defaults defaults
> 0.2% > 0.2%
firefox >= 78 firefox >= 78
ios >= 15.6 ios >= 15.6
not dead not dead
not OperaMini all not OperaMini all
[development]
supports es6-module

View file

@ -10,6 +10,7 @@ services:
RAILS_ENV: development RAILS_ENV: development
NODE_ENV: development NODE_ENV: development
BIND: 0.0.0.0 BIND: 0.0.0.0
BOOTSNAP_CACHE_DIR: /tmp
REDIS_HOST: redis REDIS_HOST: redis
REDIS_PORT: '6379' REDIS_PORT: '6379'
DB_HOST: db DB_HOST: db
@ -69,7 +70,7 @@ services:
hard: -1 hard: -1
libretranslate: libretranslate:
image: libretranslate/libretranslate:v1.6.1 image: libretranslate/libretranslate:v1.6.2
restart: unless-stopped restart: unless-stopped
volumes: volumes:
- lt-data:/home/libretranslate/.local - lt-data:/home/libretranslate/.local

View file

@ -20,3 +20,9 @@ postgres14
redis redis
elasticsearch elasticsearch
chart chart
.yarn/
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/sdks
!.yarn/versions

View file

@ -50,7 +50,7 @@ OTP_SECRET=
# Must be available (and set to same values) for all server processes # Must be available (and set to same values) for all server processes
# These are private/secret values, do not share outside hosting environment # These are private/secret values, do not share outside hosting environment
# Use `bin/rails db:encryption:init` to generate fresh secrets # Use `bin/rails db:encryption:init` to generate fresh secrets
# Do not change these secrets once in use, as this would cause data loss and other issues # Do NOT change these secrets once in use, as this would cause data loss and other issues
# ------------------ # ------------------
# ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY= # ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY=
# ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT= # ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT=
@ -86,3 +86,24 @@ S3_ALIAS_HOST=files.example.com
# ----------------------- # -----------------------
IP_RETENTION_PERIOD=31556952 IP_RETENTION_PERIOD=31556952
SESSION_RETENTION_PERIOD=31556952 SESSION_RETENTION_PERIOD=31556952
# Fetch All Replies Behavior
# --------------------------
# When a user expands a post (DetailedStatus view), fetch all of its replies
# (default: false)
FETCH_REPLIES_ENABLED=false
# Period to wait between fetching replies (in minutes)
FETCH_REPLIES_COOLDOWN_MINUTES=15
# Period to wait after a post is first created before fetching its replies (in minutes)
FETCH_REPLIES_INITIAL_WAIT_MINUTES=5
# Max number of replies to fetch - total, recursively through a whole reply tree
FETCH_REPLIES_MAX_GLOBAL=1000
# Max number of replies to fetch - for a single post
FETCH_REPLIES_MAX_SINGLE=500
# Max number of replies Collection pages to fetch - total
FETCH_REPLIES_MAX_PAGES=500

View file

@ -109,7 +109,7 @@ module.exports = defineConfig({
'react/jsx-equals-spacing': 'error', 'react/jsx-equals-spacing': 'error',
'react/jsx-no-bind': 'error', 'react/jsx-no-bind': 'error',
'react/jsx-no-useless-fragment': 'error', 'react/jsx-no-useless-fragment': 'error',
'react/jsx-no-target-blank': 'off', 'react/jsx-no-target-blank': ['error', { allowReferrer: true }],
'react/jsx-tag-spacing': 'error', 'react/jsx-tag-spacing': 'error',
'react/jsx-uses-react': 'off', // not needed with new JSX transform 'react/jsx-uses-react': 'off', // not needed with new JSX transform
'react/jsx-wrap-multilines': 'error', 'react/jsx-wrap-multilines': 'error',

View file

@ -60,7 +60,7 @@ body:
Any additional technical details you may have, like logs or error traces Any additional technical details you may have, like logs or error traces
value: | value: |
If this is happening on your own Mastodon server, please fill out those: If this is happening on your own Mastodon server, please fill out those:
- Ruby version: (from `ruby --version`, eg. v3.3.5) - Ruby version: (from `ruby --version`, eg. v3.4.1)
- Node.js version: (from `node --version`, eg. v20.18.0) - Node.js version: (from `node --version`, eg. v20.18.0)
validations: validations:
required: false required: false

View file

@ -61,7 +61,7 @@ body:
value: | value: |
Please at least include those informations: Please at least include those informations:
- Operating system: (eg. Ubuntu 22.04) - Operating system: (eg. Ubuntu 22.04)
- Ruby version: (from `ruby --version`, eg. v3.3.5) - Ruby version: (from `ruby --version`, eg. v3.4.1)
- Node.js version: (from `node --version`, eg. v20.18.0) - Node.js version: (from `node --version`, eg. v20.18.0)
validations: validations:
required: false required: false

View file

@ -1,14 +1,9 @@
on: on:
workflow_call: workflow_call:
inputs: inputs:
platforms:
required: true
type: string
cache: cache:
type: boolean type: boolean
default: true default: true
use_native_arm64_builder:
type: boolean
push_to_images: push_to_images:
type: string type: string
version_prerelease: version_prerelease:
@ -24,42 +19,36 @@ on:
file_to_build: file_to_build:
type: string type: string
# This builds multiple images with one runner each, allowing us to build for multiple architectures
# using Github's runners.
# The two-step process is adapted form:
# https://docs.docker.com/build/ci/github-actions/multi-platform/#distribute-build-across-multiple-runners
jobs: jobs:
# Build each (amd64 and arm64) image separately
build-image: build-image:
runs-on: ubuntu-latest runs-on: ${{ startsWith(matrix.platform, 'linux/arm') && 'ubuntu-24.04-arm' || 'ubuntu-24.04' }}
strategy:
fail-fast: false
matrix:
platform:
- linux/amd64
- linux/arm64
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: docker/setup-qemu-action@v3 - name: Prepare
if: contains(inputs.platforms, 'linux/arm64') && !inputs.use_native_arm64_builder env:
PUSH_TO_IMAGES: ${{ inputs.push_to_images }}
run: |
platform=${{ matrix.platform }}
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
# Transform multi-line variable into comma-separated variable
image_names=${PUSH_TO_IMAGES//$'\n'/,}
echo "IMAGE_NAMES=${image_names%,}" >> $GITHUB_ENV
- uses: docker/setup-buildx-action@v3 - uses: docker/setup-buildx-action@v3
id: buildx id: buildx
if: ${{ !(inputs.use_native_arm64_builder && contains(inputs.platforms, 'linux/arm64')) }}
- name: Start a local Docker Builder
if: inputs.use_native_arm64_builder && contains(inputs.platforms, 'linux/arm64')
run: |
docker run --rm -d --name buildkitd -p 1234:1234 --privileged moby/buildkit:latest --addr tcp://0.0.0.0:1234
- uses: docker/setup-buildx-action@v3
id: buildx-native
if: inputs.use_native_arm64_builder && contains(inputs.platforms, 'linux/arm64')
with:
driver: remote
endpoint: tcp://localhost:1234
platforms: linux/amd64
append: |
- endpoint: tcp://${{ vars.DOCKER_BUILDER_HETZNER_ARM64_01_HOST }}:13865
platforms: linux/arm64
name: mastodon-docker-builder-arm64-01
driver-opts:
- servername=mastodon-docker-builder-arm64-01
env:
BUILDER_NODE_1_AUTH_TLS_CACERT: ${{ secrets.DOCKER_BUILDER_HETZNER_ARM64_01_CACERT }}
BUILDER_NODE_1_AUTH_TLS_CERT: ${{ secrets.DOCKER_BUILDER_HETZNER_ARM64_01_CERT }}
BUILDER_NODE_1_AUTH_TLS_KEY: ${{ secrets.DOCKER_BUILDER_HETZNER_ARM64_01_KEY }}
- name: Log in to Docker Hub - name: Log in to Docker Hub
if: contains(inputs.push_to_images, 'tootsuite') if: contains(inputs.push_to_images, 'tootsuite')
@ -76,8 +65,91 @@ jobs:
username: ${{ github.actor }} username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }} password: ${{ secrets.GITHUB_TOKEN }}
- uses: docker/metadata-action@v5 - name: Docker meta
id: meta id: meta
uses: docker/metadata-action@v5
if: ${{ inputs.push_to_images != '' }}
with:
images: ${{ inputs.push_to_images }}
flavor: ${{ inputs.flavor }}
labels: ${{ inputs.labels }}
- name: Build and push by digest
id: build
uses: docker/build-push-action@v6
with:
context: .
file: ${{ inputs.file_to_build }}
build-args: |
MASTODON_VERSION_PRERELEASE=${{ inputs.version_prerelease }}
MASTODON_VERSION_METADATA=${{ inputs.version_metadata }}
SOURCE_COMMIT=${{ github.sha }}
platforms: ${{ matrix.platform }}
provenance: false
push: ${{ inputs.push_to_images != '' }}
cache-from: ${{ inputs.cache && 'type=gha' || '' }}
cache-to: ${{ inputs.cache && 'type=gha,mode=max' || '' }}
outputs: type=image,"name=${{ env.IMAGE_NAMES }}",push-by-digest=true,name-canonical=true,push=${{ inputs.push_to_images != '' }}
- name: Export digest
if: ${{ inputs.push_to_images != '' }}
run: |
mkdir -p "${{ runner.temp }}/digests"
digest="${{ steps.build.outputs.digest }}"
touch "${{ runner.temp }}/digests/${digest#sha256:}"
- name: Upload digest
if: ${{ inputs.push_to_images != '' }}
uses: actions/upload-artifact@v4
with:
# `hashFiles` is used to disambiguate between streaming and non-streaming images
name: digests-${{ hashFiles(inputs.file_to_build) }}-${{ env.PLATFORM_PAIR }}
path: ${{ runner.temp }}/digests/*
if-no-files-found: error
retention-days: 1
# Then merge the docker images into a single one
merge-images:
if: ${{ inputs.push_to_images != '' }}
runs-on: ubuntu-24.04
needs:
- build-image
env:
PUSH_TO_IMAGES: ${{ inputs.push_to_images }}
steps:
- uses: actions/checkout@v4
- name: Download digests
uses: actions/download-artifact@v4
with:
path: ${{ runner.temp }}/digests
# `hashFiles` is used to disambiguate between streaming and non-streaming images
pattern: digests-${{ hashFiles(inputs.file_to_build) }}-*
merge-multiple: true
- name: Log in to Docker Hub
if: contains(inputs.push_to_images, 'tootsuite')
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Log in to the GitHub Container registry
if: contains(inputs.push_to_images, 'ghcr.io')
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Docker meta
id: meta
uses: docker/metadata-action@v5
if: ${{ inputs.push_to_images != '' }} if: ${{ inputs.push_to_images != '' }}
with: with:
images: ${{ inputs.push_to_images }} images: ${{ inputs.push_to_images }}
@ -85,18 +157,14 @@ jobs:
tags: ${{ inputs.tags }} tags: ${{ inputs.tags }}
labels: ${{ inputs.labels }} labels: ${{ inputs.labels }}
- uses: docker/build-push-action@v6 - name: Create manifest list and push
with: working-directory: ${{ runner.temp }}/digests
context: . run: |
file: ${{ inputs.file_to_build }} echo "$PUSH_TO_IMAGES" | xargs -I{} \
build-args: | docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
MASTODON_VERSION_PRERELEASE=${{ inputs.version_prerelease }} $(printf '{}@sha256:%s ' *)
MASTODON_VERSION_METADATA=${{ inputs.version_metadata }}
platforms: ${{ inputs.platforms }} - name: Inspect image
provenance: false run: |
builder: ${{ steps.buildx.outputs.name || steps.buildx-native.outputs.name }} echo "$PUSH_TO_IMAGES" | xargs -i{} \
push: ${{ inputs.push_to_images != '' }} docker buildx imagetools inspect {}:${{ steps.meta.outputs.version }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: ${{ inputs.cache && 'type=gha' || '' }}
cache-to: ${{ inputs.cache && 'type=gha,mode=max' || '' }}

View file

@ -26,8 +26,6 @@ jobs:
uses: ./.github/workflows/build-container-image.yml uses: ./.github/workflows/build-container-image.yml
with: with:
file_to_build: Dockerfile file_to_build: Dockerfile
platforms: linux/amd64,linux/arm64
use_native_arm64_builder: true
cache: false cache: false
push_to_images: | push_to_images: |
tootsuite/mastodon tootsuite/mastodon
@ -48,8 +46,6 @@ jobs:
uses: ./.github/workflows/build-container-image.yml uses: ./.github/workflows/build-container-image.yml
with: with:
file_to_build: streaming/Dockerfile file_to_build: streaming/Dockerfile
platforms: linux/amd64,linux/arm64
use_native_arm64_builder: true
cache: false cache: false
push_to_images: | push_to_images: |
tootsuite/mastodon-streaming tootsuite/mastodon-streaming

View file

@ -32,8 +32,6 @@ jobs:
uses: ./.github/workflows/build-container-image.yml uses: ./.github/workflows/build-container-image.yml
with: with:
file_to_build: Dockerfile file_to_build: Dockerfile
platforms: linux/amd64,linux/arm64
use_native_arm64_builder: true
push_to_images: | push_to_images: |
ghcr.io/mastodon/mastodon ghcr.io/mastodon/mastodon
version_metadata: ${{ needs.compute-suffix.outputs.metadata }} version_metadata: ${{ needs.compute-suffix.outputs.metadata }}
@ -49,8 +47,6 @@ jobs:
uses: ./.github/workflows/build-container-image.yml uses: ./.github/workflows/build-container-image.yml
with: with:
file_to_build: streaming/Dockerfile file_to_build: streaming/Dockerfile
platforms: linux/amd64,linux/arm64
use_native_arm64_builder: true
push_to_images: | push_to_images: |
ghcr.io/mastodon/mastodon-streaming ghcr.io/mastodon/mastodon-streaming
version_metadata: ${{ needs.compute-suffix.outputs.metadata }} version_metadata: ${{ needs.compute-suffix.outputs.metadata }}

View file

@ -13,8 +13,6 @@ jobs:
uses: ./.github/workflows/build-container-image.yml uses: ./.github/workflows/build-container-image.yml
with: with:
file_to_build: Dockerfile file_to_build: Dockerfile
platforms: linux/amd64,linux/arm64
use_native_arm64_builder: true
push_to_images: | push_to_images: |
tootsuite/mastodon tootsuite/mastodon
ghcr.io/mastodon/mastodon ghcr.io/mastodon/mastodon
@ -34,8 +32,6 @@ jobs:
uses: ./.github/workflows/build-container-image.yml uses: ./.github/workflows/build-container-image.yml
with: with:
file_to_build: streaming/Dockerfile file_to_build: streaming/Dockerfile
platforms: linux/amd64,linux/arm64
use_native_arm64_builder: true
push_to_images: | push_to_images: |
tootsuite/mastodon-streaming tootsuite/mastodon-streaming
ghcr.io/mastodon/mastodon-streaming ghcr.io/mastodon/mastodon-streaming

View file

@ -24,8 +24,6 @@ jobs:
uses: ./.github/workflows/build-container-image.yml uses: ./.github/workflows/build-container-image.yml
with: with:
file_to_build: Dockerfile file_to_build: Dockerfile
platforms: linux/amd64,linux/arm64
use_native_arm64_builder: true
cache: false cache: false
push_to_images: | push_to_images: |
tootsuite/mastodon tootsuite/mastodon
@ -46,8 +44,6 @@ jobs:
uses: ./.github/workflows/build-container-image.yml uses: ./.github/workflows/build-container-image.yml
with: with:
file_to_build: streaming/Dockerfile file_to_build: streaming/Dockerfile
platforms: linux/amd64,linux/arm64
use_native_arm64_builder: true
cache: false cache: false
push_to_images: | push_to_images: |
tootsuite/mastodon-streaming tootsuite/mastodon-streaming

View file

@ -36,4 +36,4 @@ jobs:
bundler-cache: true bundler-cache: true
- name: Run bundler-audit - name: Run bundler-audit
run: bundle exec bundler-audit check --update run: bin/bundler-audit check --update

View file

@ -18,7 +18,7 @@ permissions:
jobs: jobs:
check-i18n: check-i18n:
runs-on: ubuntu-24.04 runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
@ -35,18 +35,18 @@ jobs:
git diff --exit-code git diff --exit-code
- name: Check locale file normalization - name: Check locale file normalization
run: bundle exec i18n-tasks check-normalized run: bin/i18n-tasks check-normalized
- name: Check for unused strings - name: Check for unused strings
run: bundle exec i18n-tasks unused run: bin/i18n-tasks unused
- name: Check for missing strings in English YML - name: Check for missing strings in English YML
run: | run: |
bundle exec i18n-tasks add-missing -l en bin/i18n-tasks add-missing -l en
git diff --exit-code git diff --exit-code
- name: Check for wrong string interpolations - name: Check for wrong string interpolations
run: bundle exec i18n-tasks check-consistent-interpolations run: bin/i18n-tasks check-consistent-interpolations
- name: Check that all required locale files exist - name: Check that all required locale files exist
run: bundle exec rake repo:check_locales_files run: bin/rake repo:check_locales_files

View file

@ -46,11 +46,11 @@ jobs:
uses: ./.github/actions/setup-ruby uses: ./.github/actions/setup-ruby
- name: Run i18n normalize task - name: Run i18n normalize task
run: bundle exec i18n-tasks normalize run: bin/i18n-tasks normalize
# Create or update the pull request # Create or update the pull request
- name: Create Pull Request - name: Create Pull Request
uses: peter-evans/create-pull-request@v7.0.5 uses: peter-evans/create-pull-request@v7.0.6
with: with:
commit-message: 'New Crowdin translations' commit-message: 'New Crowdin translations'
title: 'New Crowdin Translations for ${{ github.base_ref || github.ref_name }} (automated)' title: 'New Crowdin Translations for ${{ github.base_ref || github.ref_name }} (automated)'

View file

@ -48,11 +48,11 @@ jobs:
uses: ./.github/actions/setup-ruby uses: ./.github/actions/setup-ruby
- name: Run i18n normalize task - name: Run i18n normalize task
run: bundle exec i18n-tasks normalize run: bin/i18n-tasks normalize
# Create or update the pull request # Create or update the pull request
- name: Create Pull Request - name: Create Pull Request
uses: peter-evans/create-pull-request@v7.0.5 uses: peter-evans/create-pull-request@v7
with: with:
commit-message: 'New Crowdin translations' commit-message: 'New Crowdin translations'
title: 'New Crowdin Translations (automated)' title: 'New Crowdin Translations (automated)'

View file

@ -40,4 +40,4 @@ jobs:
uses: ./.github/actions/setup-javascript uses: ./.github/actions/setup-javascript
- name: Stylelint - name: Stylelint
run: yarn lint:css -f github run: yarn lint:css --custom-formatter @csstools/stylelint-formatter-github

View file

@ -43,4 +43,4 @@ jobs:
- name: Run haml-lint - name: Run haml-lint
run: | run: |
echo "::add-matcher::.github/workflows/haml-lint-problem-matcher.json" echo "::add-matcher::.github/workflows/haml-lint-problem-matcher.json"
bundle exec haml-lint --reporter github bin/haml-lint --reporter github

View file

@ -9,6 +9,7 @@ on:
- 'Gemfile*' - 'Gemfile*'
- '.rubocop*.yml' - '.rubocop*.yml'
- '.ruby-version' - '.ruby-version'
- 'bin/rubocop'
- 'config/brakeman.ignore' - 'config/brakeman.ignore'
- '**/*.rb' - '**/*.rb'
- '**/*.rake' - '**/*.rake'
@ -19,6 +20,7 @@ on:
- 'Gemfile*' - 'Gemfile*'
- '.rubocop*.yml' - '.rubocop*.yml'
- '.ruby-version' - '.ruby-version'
- 'bin/rubocop'
- 'config/brakeman.ignore' - 'config/brakeman.ignore'
- '**/*.rb' - '**/*.rb'
- '**/*.rake' - '**/*.rake'

View file

@ -8,6 +8,7 @@ on:
- .github/workflows/test-image-build.yml - .github/workflows/test-image-build.yml
- Dockerfile - Dockerfile
- streaming/Dockerfile - streaming/Dockerfile
- .dockerignore
permissions: permissions:
contents: read contents: read
@ -20,7 +21,6 @@ jobs:
uses: ./.github/workflows/build-container-image.yml uses: ./.github/workflows/build-container-image.yml
with: with:
file_to_build: Dockerfile file_to_build: Dockerfile
platforms: linux/amd64 # Testing only on native platform so it is performant
cache: true cache: true
build-image-streaming: build-image-streaming:
@ -31,5 +31,4 @@ jobs:
uses: ./.github/workflows/build-container-image.yml uses: ./.github/workflows/build-container-image.yml
with: with:
file_to_build: streaming/Dockerfile file_to_build: streaming/Dockerfile
platforms: linux/amd64 # Testing only on native platform so it is performant
cache: true cache: true

View file

@ -12,6 +12,7 @@ on:
- '**/*.rb' - '**/*.rb'
- '.github/workflows/test-migrations.yml' - '.github/workflows/test-migrations.yml'
- 'lib/tasks/tests.rake' - 'lib/tasks/tests.rake'
- 'lib/tasks/db.rake'
pull_request: pull_request:
paths: paths:
@ -63,7 +64,6 @@ jobs:
DB_HOST: localhost DB_HOST: localhost
DB_USER: postgres DB_USER: postgres
DB_PASS: postgres DB_PASS: postgres
DISABLE_SIMPLECOV: true
RAILS_ENV: test RAILS_ENV: test
BUNDLE_CLEAN: true BUNDLE_CLEAN: true
BUNDLE_FROZEN: true BUNDLE_FROZEN: true
@ -77,6 +77,18 @@ jobs:
- name: Set up Ruby environment - name: Set up Ruby environment
uses: ./.github/actions/setup-ruby uses: ./.github/actions/setup-ruby
- name: Ensure no errors with `db:prepare`
run: |
bin/rails db:drop
bin/rails db:prepare
bin/rails db:migrate
- name: Ensure no errors with `db:prepare` and SKIP_POST_DEPLOYMENT_MIGRATIONS
run: |
bin/rails db:drop
SKIP_POST_DEPLOYMENT_MIGRATIONS=true bin/rails db:prepare
bin/rails db:migrate
- name: Test "one step migration" flow - name: Test "one step migration" flow
run: | run: |
bin/rails db:drop bin/rails db:drop
@ -90,6 +102,11 @@ jobs:
bin/rails db:drop bin/rails db:drop
bin/rails db:create bin/rails db:create
SKIP_POST_DEPLOYMENT_MIGRATIONS=true bin/rails tests:migrations:prepare_database SKIP_POST_DEPLOYMENT_MIGRATIONS=true bin/rails tests:migrations:prepare_database
# Migrate up to v4.2.0 breakpoint
bin/rails db:migrate VERSION=20230907150100
# Migrate the rest
SKIP_POST_DEPLOYMENT_MIGRATIONS=true bin/rails db:migrate SKIP_POST_DEPLOYMENT_MIGRATIONS=true bin/rails db:migrate
bin/rails db:migrate bin/rails db:migrate
bin/rails tests:migrations:check_database bin/rails tests:migrations:check_database

View file

@ -107,7 +107,7 @@ jobs:
DB_HOST: localhost DB_HOST: localhost
DB_USER: postgres DB_USER: postgres
DB_PASS: postgres DB_PASS: postgres
DISABLE_SIMPLECOV: ${{ matrix.ruby-version != '.ruby-version' }} COVERAGE: ${{ matrix.ruby-version == '.ruby-version' }}
RAILS_ENV: test RAILS_ENV: test
ALLOW_NOPAM: true ALLOW_NOPAM: true
PAM_ENABLED: true PAM_ENABLED: true
@ -124,8 +124,8 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
ruby-version: ruby-version:
- '3.1'
- '3.2' - '3.2'
- '3.3'
- '.ruby-version' - '.ruby-version'
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
@ -167,7 +167,7 @@ jobs:
- name: Upload coverage reports to Codecov - name: Upload coverage reports to Codecov
if: matrix.ruby-version == '.ruby-version' if: matrix.ruby-version == '.ruby-version'
uses: codecov/codecov-action@v4 uses: codecov/codecov-action@v5
with: with:
files: coverage/lcov/*.lcov files: coverage/lcov/*.lcov
env: env:
@ -175,7 +175,7 @@ jobs:
test-libvips: test-libvips:
name: Libvips tests name: Libvips tests
runs-on: ubuntu-24.04 runs-on: ubuntu-latest
needs: needs:
- build - build
@ -208,7 +208,7 @@ jobs:
DB_HOST: localhost DB_HOST: localhost
DB_USER: postgres DB_USER: postgres
DB_PASS: postgres DB_PASS: postgres
DISABLE_SIMPLECOV: ${{ matrix.ruby-version != '.ruby-version' }} COVERAGE: ${{ matrix.ruby-version == '.ruby-version' }}
RAILS_ENV: test RAILS_ENV: test
ALLOW_NOPAM: true ALLOW_NOPAM: true
PAM_ENABLED: true PAM_ENABLED: true
@ -226,8 +226,8 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
ruby-version: ruby-version:
- '3.1'
- '3.2' - '3.2'
- '3.3'
- '.ruby-version' - '.ruby-version'
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
@ -254,7 +254,7 @@ jobs:
- name: Upload coverage reports to Codecov - name: Upload coverage reports to Codecov
if: matrix.ruby-version == '.ruby-version' if: matrix.ruby-version == '.ruby-version'
uses: codecov/codecov-action@v4 uses: codecov/codecov-action@v5
with: with:
files: coverage/lcov/mastodon.lcov files: coverage/lcov/mastodon.lcov
env: env:
@ -295,7 +295,6 @@ jobs:
DB_HOST: localhost DB_HOST: localhost
DB_USER: postgres DB_USER: postgres
DB_PASS: postgres DB_PASS: postgres
DISABLE_SIMPLECOV: true
RAILS_ENV: test RAILS_ENV: test
BUNDLE_WITH: test BUNDLE_WITH: test
LOCAL_DOMAIN: localhost:3000 LOCAL_DOMAIN: localhost:3000
@ -305,8 +304,8 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
ruby-version: ruby-version:
- '3.1'
- '3.2' - '3.2'
- '3.3'
- '.ruby-version' - '.ruby-version'
steps: steps:
@ -411,7 +410,6 @@ jobs:
DB_HOST: localhost DB_HOST: localhost
DB_USER: postgres DB_USER: postgres
DB_PASS: postgres DB_PASS: postgres
DISABLE_SIMPLECOV: true
RAILS_ENV: test RAILS_ENV: test
BUNDLE_WITH: test BUNDLE_WITH: test
ES_ENABLED: true ES_ENABLED: true
@ -422,8 +420,8 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
ruby-version: ruby-version:
- '3.1'
- '3.2' - '3.2'
- '3.3'
- '.ruby-version' - '.ruby-version'
search-image: search-image:
- docker.elastic.co/elasticsearch/elasticsearch:7.17.13 - docker.elastic.co/elasticsearch/elasticsearch:7.17.13

2
.nvmrc
View file

@ -1 +1 @@
20.18 22.14

View file

@ -63,6 +63,7 @@ docker-compose.override.yml
# Ignore emoji map file # Ignore emoji map file
/app/javascript/mastodon/features/emoji/emoji_map.json /app/javascript/mastodon/features/emoji/emoji_map.json
/app/javascript/mastodon/features/emoji/emoji_sheet.json
# Ignore locale files # Ignore locale files
/app/javascript/mastodon/locales/*.json /app/javascript/mastodon/locales/*.json

View file

@ -8,7 +8,7 @@ AllCops:
- lib/mastodon/migration_helpers.rb - lib/mastodon/migration_helpers.rb
ExtraDetails: true ExtraDetails: true
NewCops: enable NewCops: enable
TargetRubyVersion: 3.1 # Oldest supported ruby version TargetRubyVersion: 3.2 # Oldest supported ruby version
inherit_from: inherit_from:
- .rubocop/layout.yml - .rubocop/layout.yml
@ -18,6 +18,7 @@ inherit_from:
- .rubocop/rspec_rails.yml - .rubocop/rspec_rails.yml
- .rubocop/rspec.yml - .rubocop/rspec.yml
- .rubocop/style.yml - .rubocop/style.yml
- .rubocop/i18n.yml
- .rubocop/custom.yml - .rubocop/custom.yml
- .rubocop_todo.yml - .rubocop_todo.yml
- .rubocop/strict.yml - .rubocop/strict.yml
@ -26,9 +27,10 @@ inherit_mode:
merge: merge:
- Exclude - Exclude
require: plugins:
- rubocop-capybara
- rubocop-i18n
- rubocop-performance
- rubocop-rails - rubocop-rails
- rubocop-rspec - rubocop-rspec
- rubocop-rspec_rails - rubocop-rspec_rails
- rubocop-performance
- rubocop-capybara

12
.rubocop/i18n.yml Normal file
View file

@ -0,0 +1,12 @@
I18n/RailsI18n:
Enabled: true
Exclude:
- 'config/**/*'
- 'db/**/*'
- 'lib/**/*'
- 'spec/**/*'
I18n/GetText:
Enabled: false
I18n/RailsI18n/DecorateStringFormattingUsingInterpolation:
Enabled: false

View file

@ -2,6 +2,9 @@
Rails/BulkChangeTable: Rails/BulkChangeTable:
Enabled: false # Conflicts with strong_migrations features Enabled: false # Conflicts with strong_migrations features
Rails/Delegate:
Enabled: false
Rails/FilePath: Rails/FilePath:
EnforcedStyle: arguments EnforcedStyle: arguments

View file

@ -1,4 +1,7 @@
--- ---
Style/ArrayIntersect:
Enabled: false
Style/ClassAndModuleChildren: Style/ClassAndModuleChildren:
Enabled: false Enabled: false
@ -19,6 +22,13 @@ Style/HashSyntax:
EnforcedShorthandSyntax: either EnforcedShorthandSyntax: either
EnforcedStyle: ruby19_no_mixed_keys EnforcedStyle: ruby19_no_mixed_keys
Style/IfUnlessModifier:
Exclude:
- '**/*.haml'
Style/KeywordArgumentsMerging:
Enabled: false
Style/NumericLiterals: Style/NumericLiterals:
AllowedPatterns: AllowedPatterns:
- \d{4}_\d{2}_\d{2}_\d{6} - \d{4}_\d{2}_\d{2}_\d{6}
@ -37,6 +47,9 @@ Style/RedundantFetchBlock:
Style/RescueStandardError: Style/RescueStandardError:
EnforcedStyle: implicit EnforcedStyle: implicit
Style/SafeNavigationChainLength:
Enabled: false
Style/SymbolArray: Style/SymbolArray:
Enabled: false Enabled: false
@ -45,3 +58,6 @@ Style/TrailingCommaInArrayLiteral:
Style/TrailingCommaInHashLiteral: Style/TrailingCommaInHashLiteral:
EnforcedStyleForMultiline: comma EnforcedStyleForMultiline: comma
Style/WordArray:
MinSize: 3 # Override default of 2

View file

@ -1,6 +1,6 @@
# This configuration was generated by # This configuration was generated by
# `rubocop --auto-gen-config --auto-gen-only-exclude --no-offense-counts --no-auto-gen-timestamp` # `rubocop --auto-gen-config --auto-gen-only-exclude --no-offense-counts --no-auto-gen-timestamp`
# using RuboCop version 1.66.1. # using RuboCop version 1.73.2.
# The point is for the user to remove these configuration records # The point is for the user to remove these configuration records
# one by one as the offenses are removed from the code base. # one by one as the offenses are removed from the code base.
# Note that changes in the inspected code, or installation of new # Note that changes in the inspected code, or installation of new
@ -8,7 +8,7 @@
Lint/NonLocalExitFromIterator: Lint/NonLocalExitFromIterator:
Exclude: Exclude:
- 'app/helpers/jsonld_helper.rb' - 'app/helpers/json_ld_helper.rb'
# Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes. # Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes.
Metrics/AbcSize: Metrics/AbcSize:
@ -35,7 +35,6 @@ Rails/OutputSafety:
# Configuration parameters: AllowedVars. # Configuration parameters: AllowedVars.
Style/FetchEnvVar: Style/FetchEnvVar:
Exclude: Exclude:
- 'app/lib/translation_service.rb'
- 'config/environments/production.rb' - 'config/environments/production.rb'
- 'config/initializers/2_limited_federation_mode.rb' - 'config/initializers/2_limited_federation_mode.rb'
- 'config/initializers/3_omniauth.rb' - 'config/initializers/3_omniauth.rb'
@ -70,20 +69,11 @@ Style/MapToHash:
Exclude: Exclude:
- 'app/models/status.rb' - 'app/models/status.rb'
# This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: EnforcedStyle.
# SupportedStyles: literals, strict
Style/MutableConstant:
Exclude:
- 'app/models/tag.rb'
- 'app/services/delete_account_service.rb'
- 'lib/mastodon/migration_warning.rb'
# Configuration parameters: AllowedMethods. # Configuration parameters: AllowedMethods.
# AllowedMethods: respond_to_missing? # AllowedMethods: respond_to_missing?
Style/OptionalBooleanParameter: Style/OptionalBooleanParameter:
Exclude: Exclude:
- 'app/helpers/jsonld_helper.rb' - 'app/helpers/json_ld_helper.rb'
- 'app/lib/admin/system_check/message.rb' - 'app/lib/admin/system_check/message.rb'
- 'app/lib/request.rb' - 'app/lib/request.rb'
- 'app/lib/webfinger.rb' - 'app/lib/webfinger.rb'
@ -104,10 +94,3 @@ Style/RedundantConstantBase:
Exclude: Exclude:
- 'config/environments/production.rb' - 'config/environments/production.rb'
- 'config/initializers/sidekiq.rb' - 'config/initializers/sidekiq.rb'
# This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: WordRegex.
# SupportedStyles: percent, brackets
Style/WordArray:
EnforcedStyle: percent
MinSize: 3

View file

@ -1 +1 @@
3.3.5 3.4.2

View file

@ -2,6 +2,167 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
## [4.3.6] - 2025-03-13
### Security
- Update dependency `omniauth-saml`
- Update dependency `rack`
### Fixed
- Fix Stoplight errors when using `REDIS_NAMESPACE` (#34126 by @ClearlyClaire)
## [4.3.5] - 2025-03-10
### Changed
- Change hashtag suggestion to prefer personal history capitalization (#34070 by @ClearlyClaire)
### Fixed
- Fix processing errors for some HEIF images from iOS 18 (#34086 by @renchap)
- Fix streaming server not filtering unknown-language posts from public timelines (#33774 by @ClearlyClaire)
- Fix preview cards under Content Warnings not being shown in detailed statuses (#34068 by @ClearlyClaire)
- Fix username and display name being hidden on narrow screens in moderation interface (#33064 by @ClearlyClaire)
## [4.3.4] - 2025-02-27
### Security
- Update dependencies
- Change HTML sanitization to remove unusable and unused `embed` tag (#34021 by @ClearlyClaire, [GHSA-mq2m-hr29-8gqf](https://github.com/mastodon/mastodon/security/advisories/GHSA-mq2m-hr29-8gqf))
- Fix rate-limit on sign-up email verification ([GHSA-v39f-c9jj-8w7h](https://github.com/mastodon/mastodon/security/advisories/GHSA-v39f-c9jj-8w7h))
- Fix improper disclosure of domain blocks to unverified users ([GHSA-94h4-fj37-c825](https://github.com/mastodon/mastodon/security/advisories/GHSA-94h4-fj37-c825))
### Changed
- Change preview cards to be shown when Content Warnings are expanded (#33827 by @ClearlyClaire)
- Change warnings against changing encryption secrets to be even more noticeable (#33631 by @ClearlyClaire)
- Change `mastodon:setup` to prevent overwriting already-configured servers (#33603, #33616, and #33684 by @ClearlyClaire and @mjankowski)
- Change notifications from moderators to not be filtered (#32974 and #33654 by @ClearlyClaire and @mjankowski)
### Fixed
- Fix `GET /api/v2/notifications/:id` and `POST /api/v2/notifications/:id/dismiss` for ungrouped notifications (#33990 by @ClearlyClaire)
- Fix issue with some versions of libvips on some systems (#33853 by @kleisauke)
- Fix handling of duplicate mentions in incoming status `Update` (#33911 by @ClearlyClaire)
- Fix inefficiencies in timeline generation (#33839 and #33842 by @ClearlyClaire)
- Fix emoji rewrite adding unnecessary curft to the DOM for most emoji (#33818 by @ClearlyClaire)
- Fix `tootctl feeds build` not building list timelines (#33783 by @ClearlyClaire)
- Fix flaky test in `/api/v2/notifications` tests (#33773 by @ClearlyClaire)
- Fix incorrect signature after HTTP redirect (#33757 and #33769 by @ClearlyClaire)
- Fix polls not being validated on edition (#33755 by @ClearlyClaire)
- Fix media preview height in compose form when 3 or more images are attached (#33571 by @ClearlyClaire)
- Fix preview card sizing in “Author attribution” in profile settings (#33482 by @ClearlyClaire)
- Fix processing of incoming notifications for unfilterable types (#33429 by @ClearlyClaire)
- Fix featured tags for remote accounts not being kept up to date (#33372, #33406, and #33425 by @ClearlyClaire and @mjankowski)
- Fix notification polling showing a loading bar in web UI (#32960 by @Gargron)
- Fix accounts table long display name (#29316 by @WebCoder49)
- Fix exclusive lists interfering with notifications (#28162 by @ShadowJonathan)
## [4.3.3] - 2025-01-16
### Security
- Fix insufficient validation of account URIs ([GHSA-5wxh-3p65-r4g6](https://github.com/mastodon/mastodon/security/advisories/GHSA-5wxh-3p65-r4g6))
- Update dependencies
### Fixed
- Fix `libyaml` missing from `Dockerfile` build stage (#33591 by @vmstan)
- Fix incorrect notification settings migration for non-followers (#33348 by @ClearlyClaire)
- Fix down clause for notification policy v2 migrations (#33340 by @jesseplusplus)
- Fix error decrementing status count when `FeaturedTags#last_status_at` is `nil` (#33320 by @ClearlyClaire)
- Fix last paginated notification group only including data on a single notification (#33271 by @ClearlyClaire)
- Fix processing of mentions for post edits with an existing corresponding silent mention (#33227 by @ClearlyClaire)
- Fix deletion of unconfirmed users with Webauthn set (#33186 by @ClearlyClaire)
- Fix empty authors preview card serialization (#33151, #33466 by @mjankowski and @ClearlyClaire)
## [4.3.2] - 2024-12-03
### Added
- Add `tootctl feeds vacuum` (#33065 by @ClearlyClaire)
- Add error message when user tries to follow their own account (#31910 by @lenikadali)
- Add client_secret_expires_at to OAuth Applications (#30317 by @ThisIsMissEm)
### Changed
- Change design of Content Warnings and filters (#32543 by @ClearlyClaire)
### Fixed
- Fix processing incoming post edits with mentions to unresolvable accounts (#33129 by @ClearlyClaire)
- Fix error when including multiple instances of `embed.js` (#33107 by @YKWeyer)
- Fix inactive users' timelines being backfilled on follow and unsuspend (#33094 by @ClearlyClaire)
- Fix direct inbox delivery pushing posts into inactive followers' timelines (#33067 by @ClearlyClaire)
- Fix `TagFollow` records not being correctly handled in account operations (#33063 by @ClearlyClaire)
- Fix pushing hashtag-followed posts to feeds of inactive users (#33018 by @Gargron)
- Fix duplicate notifications in notification groups when using slow mode (#33014 by @ClearlyClaire)
- Fix posts made in the future being allowed to trend (#32996 by @ClearlyClaire)
- Fix uploading higher-than-wide GIF profile picture with libvips enabled (#32911 by @ClearlyClaire)
- Fix domain attribution field having autocorrect and autocapitalize enabled (#32903 by @ClearlyClaire)
- Fix titles being escaped twice (#32889 by @ClearlyClaire)
- Fix list creation limit check (#32869 by @ClearlyClaire)
- Fix error in `tootctl email_domain_blocks` when supplying `--with-dns-records` (#32863 by @mjankowski)
- Fix `min_id` and `max_id` causing error in search API (#32857 by @Gargron)
- Fix inefficiencies when processing removal of posts that use featured tags (#32787 by @ClearlyClaire)
- Fix alt-text pop-in not using the translated description (#32766 by @ClearlyClaire)
- Fix preview cards with long titles erroneously causing layout changes (#32678 by @ClearlyClaire)
- Fix embed modal layout on mobile (#32641 by @DismalShadowX)
- Fix and improve batch attachment deletion handling when using OpenStack Swift (#32637 by @hugogameiro)
- Fix blocks not being applied on link timeline (#32625 by @tribela)
- Fix follow counters being incorrectly changed (#32622 by @oneiros)
- Fix 'unknown' media attachment type rendering (#32613 and #32713 by @ThisIsMissEm and @renatolond)
- Fix tl language native name (#32606 by @seav)
### Security
- Update dependencies
## [4.3.1] - 2024-10-21
### Added
- Add more explicit explanations about author attribution and `fediverse:creator` (#32383 by @ClearlyClaire)
- Add ability to group follow notifications in WebUI, can be disabled in the column settings (#32520 by @renchap)
- Add back a 6 hours mute duration option (#32522 by @renchap)
- Add note about not changing ActiveRecord encryption secrets once they are set (#32413, #32476, #32512, and #32537 by @ClearlyClaire and @mjankowski)
### Changed
- Change translation feature to translate to selected regional variant (e.g. pt-BR) if available (#32428 by @c960657)
### Removed
- Remove ability to get embed code for remote posts (#32578 by @ClearlyClaire)\
Getting the embed code is only reliable for local posts.\
It never worked for non-Mastodon servers, and stopped working correctly with the changes made in 4.3.0.\
We have therefore decided to remove the menu entry while we investigate solutions.
### Fixed
- Fix follow recommendation moderation page default language when using regional variant (#32580 by @ClearlyClaire)
- Fix column-settings spacing in local timeline in advanced view (#32567 by @lindwurm)
- Fix broken i18n in text welcome mailer tags area (#32571 by @mjankowski)
- Fix missing or incorrect cache-control headers for Streaming server (#32551 by @ThisIsMissEm)
- Fix only the first paragraph being displayed in some notifications (#32348 by @ClearlyClaire)
- Fix reblog icons on account media view (#32506 by @tribela)
- Fix Content-Security-Policy not allowing OpenStack SWIFT object storage URI (#32439 by @kenkiku1021)
- Fix back arrow pointing to the incorrect direction in RTL languages (#32485 by @renchap)
- Fix streaming server using `REDIS_USERNAME` instead of `REDIS_USER` (#32493 by @ThisIsMissEm)
- Fix follow recommendation carrousel scrolling on RTL layouts (#32462 and #32505 by @ClearlyClaire)
- Fix follow recommendation suppressions not applying immediately (#32392 by @ClearlyClaire)
- Fix language of push notifications (#32415 by @ClearlyClaire)
- Fix mute duration not being shown in list of muted accounts in web UI (#32388 by @ClearlyClaire)
- Fix “Mark every notification as read” not updating the read marker if scrolled down (#32385 by @ClearlyClaire)
- Fix “Mention” appearing for otherwise filtered posts (#32356 by @ClearlyClaire)
- Fix notification requests from suspended accounts still being listed (#32354 by @ClearlyClaire)
- Fix list edition modal styling (#32358 and #32367 by @ClearlyClaire and @vmstan)
- Fix 4 columns barely not fitting on 1920px screen (#32361 by @ClearlyClaire)
- Fix icon alignment in applications list (#32293 by @mjankowski)
## [4.3.0] - 2024-10-08 ## [4.3.0] - 2024-10-08
The following changelog entries focus on changes visible to users, administrators, client developers or federated software developers, but there has also been a lot of code modernization, refactoring, and tooling work, in particular by @mjankowski. The following changelog entries focus on changes visible to users, administrators, client developers or federated software developers, but there has also been a lot of code modernization, refactoring, and tooling work, in particular by @mjankowski.
@ -26,7 +187,7 @@ The following changelog entries focus on changes visible to users, administrator
- `GET /api/v2/notifications`: https://docs.joinmastodon.org/methods/grouped_notifications/#get-grouped - `GET /api/v2/notifications`: https://docs.joinmastodon.org/methods/grouped_notifications/#get-grouped
- `GET /api/v2/notifications/:group_key`: https://docs.joinmastodon.org/methods/grouped_notifications/#get-notification-group - `GET /api/v2/notifications/:group_key`: https://docs.joinmastodon.org/methods/grouped_notifications/#get-notification-group
- `GET /api/v2/notifications/:group_key/accounts`: https://docs.joinmastodon.org/methods/grouped_notifications/#get-group-accounts - `GET /api/v2/notifications/:group_key/accounts`: https://docs.joinmastodon.org/methods/grouped_notifications/#get-group-accounts
- `POST /api/v2/notifications/:group_key/dimsiss`: https://docs.joinmastodon.org/methods/grouped_notifications/#dismiss-group - `POST /api/v2/notifications/:group_key/dismiss`: https://docs.joinmastodon.org/methods/grouped_notifications/#dismiss-group
- `GET /api/v2/notifications/:unread_count`: https://docs.joinmastodon.org/methods/grouped_notifications/#unread-group-count - `GET /api/v2/notifications/:unread_count`: https://docs.joinmastodon.org/methods/grouped_notifications/#unread-group-count
- **Add notification policies, filtered notifications and notification requests** (#29366, #29529, #29433, #29565, #29567, #29572, #29575, #29588, #29646, #29652, #29658, #29666, #29693, #29699, #29737, #29706, #29570, #29752, #29810, #29826, #30114, #30251, #30559, #29868, #31008, #31011, #30996, #31149, #31220, #31222, #31225, #31242, #31262, #31250, #31273, #31310, #31316, #31322, #31329, #31324, #31331, #31343, #31342, #31309, #31358, #31378, #31406, #31256, #31456, #31419, #31457, #31508, #31540, #31541, #31723, #32062 and #32281 by @ClearlyClaire, @Gargron, @TheEssem, @mgmn, @oneiros, and @renchap)\ - **Add notification policies, filtered notifications and notification requests** (#29366, #29529, #29433, #29565, #29567, #29572, #29575, #29588, #29646, #29652, #29658, #29666, #29693, #29699, #29737, #29706, #29570, #29752, #29810, #29826, #30114, #30251, #30559, #29868, #31008, #31011, #30996, #31149, #31220, #31222, #31225, #31242, #31262, #31250, #31273, #31310, #31316, #31322, #31329, #31324, #31331, #31343, #31342, #31309, #31358, #31378, #31406, #31256, #31456, #31419, #31457, #31508, #31540, #31541, #31723, #32062 and #32281 by @ClearlyClaire, @Gargron, @TheEssem, @mgmn, @oneiros, and @renchap)\
The old “Block notifications from non-followers”, “Block notifications from people you don't follow” and “Block direct messages from people you don't follow” notification settings have been replaced by a new set of settings found directly in the notification column.\ The old “Block notifications from non-followers”, “Block notifications from people you don't follow” and “Block direct messages from people you don't follow” notification settings have been replaced by a new set of settings found directly in the notification column.\
@ -51,7 +212,7 @@ The following changelog entries focus on changes visible to users, administrator
- **Add notifications of severed relationships** (#27511, #29665, #29668, #29670, #29700, #29714, #29712, and #29731 by @ClearlyClaire and @Gargron)\ - **Add notifications of severed relationships** (#27511, #29665, #29668, #29670, #29700, #29714, #29712, and #29731 by @ClearlyClaire and @Gargron)\
Notify local users when they lose relationships as a result of a local moderator blocking a remote account or server, allowing the affected user to retrieve the list of broken relationships.\ Notify local users when they lose relationships as a result of a local moderator blocking a remote account or server, allowing the affected user to retrieve the list of broken relationships.\
Note that this does not notify remote users.\ Note that this does not notify remote users.\
This adds the `severed_relationships` notification type to the REST API and streaming, with a new [`relationship_severance_event` attribute](https://docs.joinmastodon.org/entities/Notification/#relationship_severance_event). This adds the `severed_relationships` notification type to the REST API and streaming, with a new [`event` attribute](https://docs.joinmastodon.org/entities/Notification/#relationship_severance_event).
- **Add hover cards in web UI** (#30754, #30864, #30850, #30879, #30928, #30949, #30948, #30931, and #31300 by @ClearlyClaire, @Gargron, and @renchap)\ - **Add hover cards in web UI** (#30754, #30864, #30850, #30879, #30928, #30949, #30948, #30931, and #31300 by @ClearlyClaire, @Gargron, and @renchap)\
Hovering over an avatar or username will now display a hover card with the first two lines of the user's description and their first two profile fields.\ Hovering over an avatar or username will now display a hover card with the first two lines of the user's description and their first two profile fields.\
This can be disabled in the “Animations and accessibility” section of the preferences. This can be disabled in the “Animations and accessibility” section of the preferences.
@ -357,7 +518,7 @@ The following changelog entries focus on changes visible to users, administrator
- Fix empty environment variables not using default nil value (#27400 by @renchap) - Fix empty environment variables not using default nil value (#27400 by @renchap)
- Fix language sorting in settings (#27158 by @gunchleoc) - Fix language sorting in settings (#27158 by @gunchleoc)
## |4.2.11] - 2024-08-16 ## [4.2.11] - 2024-08-16
### Added ### Added

View file

@ -9,30 +9,51 @@ You can contribute in the following ways:
- Contributing code to Mastodon by fixing bugs or implementing features - Contributing code to Mastodon by fixing bugs or implementing features
- Improving the documentation - Improving the documentation
If your contributions are accepted into Mastodon, you can request to be paid through [our OpenCollective](https://opencollective.com/mastodon).
Please review the org-level [contribution guidelines] for high-level acceptance Please review the org-level [contribution guidelines] for high-level acceptance
criteria guidance. criteria guidance and the [DEVELOPMENT] guide for environment-specific details.
[contribution guidelines]: https://github.com/mastodon/.github/blob/main/CONTRIBUTING.md
## API Changes and Additions ## API Changes and Additions
Please note that any changes or additions made to the API should have an accompanying pull request on [our documentation repository](https://github.com/mastodon/documentation). Any changes or additions made to the API should have an accompanying pull
request on our [documentation repository].
## Bug reports ## Bug Reports
Bug reports and feature suggestions must use descriptive and concise titles and be submitted to [GitHub Issues](https://github.com/mastodon/mastodon/issues). Please use the search function to make sure that you are not submitting duplicates, and that a similar report or request has not already been resolved or rejected. Bug reports and feature suggestions must use descriptive and concise titles and
be submitted to [GitHub Issues]. Please use the search function to make sure
there are not duplicate bug reports or feature requests.
## Translations ## Translations
You can submit translations via [Crowdin](https://crowdin.com/project/mastodon). They are periodically merged into the codebase. Translations are community contributed via [Crowdin]. They are periodically
reviewed and merged into the codebase.
[![Crowdin](https://d322cqt584bo4o.cloudfront.net/mastodon/localized.svg)](https://crowdin.com/project/mastodon) [![Crowdin](https://d322cqt584bo4o.cloudfront.net/mastodon/localized.svg)](https://crowdin.com/project/mastodon)
## Pull requests ## Pull Requests
**Please use clean, concise titles for your pull requests.** Unless the pull request is about refactoring code, updating dependencies or other internal tasks, assume that the person reading the pull request title is not a programmer or Mastodon developer, but instead a Mastodon user or server administrator, and **try to describe your change or fix from their perspective**. We use commit squashing, so the final commit in the main branch will carry the title of the pull request, and commits from the main branch are fed into the changelog. The changelog is separated into [keepachangelog.com categories](https://keepachangelog.com/en/1.0.0/), and while that spec does not prescribe how the entries ought to be named, for easier sorting, start your pull request titles using one of the verbs "Add", "Change", "Deprecate", "Remove", or "Fix" (present tense). ### Size and Scope
Our time is limited and PRs making large, unsolicited changes are unlikely to
get a response. Changes which link to an existing confirmed issue, or which come
from a "help wanted" issue or other request are more likely to be reviewed.
The smaller and more narrowly focused the changes in a PR are, the easier they
are to review and potentially merge. If the change only makes sense in some
larger context of future ongoing work, note that in the description, but still
aim to keep each distinct PR to a "smallest viable change" chunk of work.
### Description of Changes
Unless the Pull Request is about refactoring code, updating dependencies or
other internal tasks, assume that the audience are not developers, but a
Mastodon user or server admin, and try to describe it from their perspective.
The final commit in the main branch will carry the title from the PR. The main
branch is then fed into the changelog and ultimately into release notes. We try
to follow the [keepachangelog] spec, and while that does not prescribe how
exactly the entries ought to be named, starting titles using one of the verbs
"Add", "Change", "Deprecate", "Remove", or "Fix" (present tense) is helpful.
Example: Example:
@ -40,16 +61,25 @@ Example:
| ------------------------------------ | ------------------------------------------------------------- | | ------------------------------------ | ------------------------------------------------------------- |
| Fixed NoMethodError in RemovalWorker | Fix nil error when removing statuses caused by race condition | | Fixed NoMethodError in RemovalWorker | Fix nil error when removing statuses caused by race condition |
It is not always possible to phrase every change in such a manner, but it is desired. ### Technical Requirements
**The smaller the set of changes in the pull request is, the quicker it can be reviewed and merged.** Splitting tasks into multiple smaller pull requests is often preferable. Pull requests that do not pass automated checks on CI may not be reviewed. In
particular, please keep in mind:
**Pull requests that do not pass automated checks may not be reviewed**. In particular, you need to keep in mind:
- Unit and integration tests (rspec, jest) - Unit and integration tests (rspec, jest)
- Code style rules (rubocop, eslint) - Code style rules (rubocop, eslint)
- Normalization of locale files (i18n-tasks) - Normalization of locale files (i18n-tasks)
- Relevant accessibility or performance concerns
## Documentation ## Documentation
The [Mastodon documentation](https://docs.joinmastodon.org) is a statically generated site. You can [submit merge requests to mastodon/documentation](https://github.com/mastodon/documentation). The [Mastodon documentation] is a statically generated site that contains guides
and API docs. Improvements are made via PRs to the [documentation repository].
[contribution guidelines]: https://github.com/mastodon/.github/blob/main/CONTRIBUTING.md
[Crowdin]: https://crowdin.com/project/mastodon
[DEVELOPMENT]: docs/DEVELOPMENT.md
[documentation repository]: https://github.com/mastodon/documentation
[GitHub Issues]: https://github.com/mastodon/mastodon/issues
[keepachangelog]: https://keepachangelog.com/en/1.0.0/
[Mastodon documentation]: https://docs.joinmastodon.org

View file

@ -1,7 +1,7 @@
# syntax=docker/dockerfile:1.10 # syntax=docker/dockerfile:1.12
# This file is designed for production server deployment, not local development work # This file is designed for production server deployment, not local development work
# For a containerized local dev environment, see: https://github.com/mastodon/mastodon/blob/main/README.md#docker # For a containerized local dev environment, see: https://github.com/mastodon/mastodon/blob/main/docs/DEVELOPMENT.md#docker
# Please see https://docs.docker.com/engine/reference/builder for information about # Please see https://docs.docker.com/engine/reference/builder for information about
# the extended buildx capabilities used in this file. # the extended buildx capabilities used in this file.
@ -9,19 +9,20 @@
# See: https://docs.docker.com/build/building/multi-platform/ # See: https://docs.docker.com/build/building/multi-platform/
ARG TARGETPLATFORM=${TARGETPLATFORM} ARG TARGETPLATFORM=${TARGETPLATFORM}
ARG BUILDPLATFORM=${BUILDPLATFORM} ARG BUILDPLATFORM=${BUILDPLATFORM}
ARG BASE_REGISTRY="docker.io"
# Ruby image to use for base image, change with [--build-arg RUBY_VERSION="3.3.x"] # Ruby image to use for base image, change with [--build-arg RUBY_VERSION="3.4.x"]
# renovate: datasource=docker depName=docker.io/ruby # renovate: datasource=docker depName=docker.io/ruby
ARG RUBY_VERSION="3.3.5" ARG RUBY_VERSION="3.4.2"
# # Node version to use in base image, change with [--build-arg NODE_MAJOR_VERSION="20"] # # Node.js version to use in base image, change with [--build-arg NODE_MAJOR_VERSION="20"]
# renovate: datasource=node-version depName=node # renovate: datasource=node-version depName=node
ARG NODE_MAJOR_VERSION="20" ARG NODE_MAJOR_VERSION="22"
# Debian image to use for base image, change with [--build-arg DEBIAN_VERSION="bookworm"] # Debian image to use for base image, change with [--build-arg DEBIAN_VERSION="bookworm"]
ARG DEBIAN_VERSION="bookworm" ARG DEBIAN_VERSION="bookworm"
# Node image to use for base image based on combined variables (ex: 20-bookworm-slim) # Node.js image to use for base image based on combined variables (ex: 20-bookworm-slim)
FROM docker.io/node:${NODE_MAJOR_VERSION}-${DEBIAN_VERSION}-slim AS node FROM ${BASE_REGISTRY}/node:${NODE_MAJOR_VERSION}-${DEBIAN_VERSION}-slim AS node
# Ruby image to use for base image based on combined variables (ex: 3.3.x-slim-bookworm) # Ruby image to use for base image based on combined variables (ex: 3.4.x-slim-bookworm)
FROM docker.io/ruby:${RUBY_VERSION}-slim-${DEBIAN_VERSION} AS ruby FROM ${BASE_REGISTRY}/ruby:${RUBY_VERSION}-slim-${DEBIAN_VERSION} AS ruby
# Resulting version string is vX.X.X-MASTODON_VERSION_PRERELEASE+MASTODON_VERSION_METADATA # Resulting version string is vX.X.X-MASTODON_VERSION_PRERELEASE+MASTODON_VERSION_METADATA
# Example: v4.3.0-nightly.2023.11.09+pr-123456 # Example: v4.3.0-nightly.2023.11.09+pr-123456
@ -29,6 +30,8 @@ FROM docker.io/ruby:${RUBY_VERSION}-slim-${DEBIAN_VERSION} AS ruby
ARG MASTODON_VERSION_PRERELEASE="" ARG MASTODON_VERSION_PRERELEASE=""
# Append build metadata or fork information to version.rb [--build-arg MASTODON_VERSION_METADATA="pr-123456"] # Append build metadata or fork information to version.rb [--build-arg MASTODON_VERSION_METADATA="pr-123456"]
ARG MASTODON_VERSION_METADATA="" ARG MASTODON_VERSION_METADATA=""
# Will be available as Mastodon::Version.source_commit
ARG SOURCE_COMMIT=""
# Allow Ruby on Rails to serve static files # Allow Ruby on Rails to serve static files
# See: https://docs.joinmastodon.org/admin/config/#rails_serve_static_files # See: https://docs.joinmastodon.org/admin/config/#rails_serve_static_files
@ -48,6 +51,7 @@ ENV \
# Apply Mastodon version information # Apply Mastodon version information
MASTODON_VERSION_PRERELEASE="${MASTODON_VERSION_PRERELEASE}" \ MASTODON_VERSION_PRERELEASE="${MASTODON_VERSION_PRERELEASE}" \
MASTODON_VERSION_METADATA="${MASTODON_VERSION_METADATA}" \ MASTODON_VERSION_METADATA="${MASTODON_VERSION_METADATA}" \
SOURCE_COMMIT="${SOURCE_COMMIT}" \
# Apply Mastodon static files and YJIT options # Apply Mastodon static files and YJIT options
RAILS_SERVE_STATIC_FILES=${RAILS_SERVE_STATIC_FILES} \ RAILS_SERVE_STATIC_FILES=${RAILS_SERVE_STATIC_FILES} \
RUBY_YJIT_ENABLE=${RUBY_YJIT_ENABLE} \ RUBY_YJIT_ENABLE=${RUBY_YJIT_ENABLE} \
@ -57,7 +61,7 @@ ENV \
ENV \ ENV \
# Configure the IP to bind Mastodon to when serving traffic # Configure the IP to bind Mastodon to when serving traffic
BIND="0.0.0.0" \ BIND="0.0.0.0" \
# Use production settings for Yarn, Node and related nodejs based tools # Use production settings for Yarn, Node.js and related tools
NODE_ENV="production" \ NODE_ENV="production" \
# Use production settings for Ruby on Rails # Use production settings for Ruby on Rails
RAILS_ENV="production" \ RAILS_ENV="production" \
@ -92,6 +96,9 @@ RUN \
# Set /opt/mastodon as working directory # Set /opt/mastodon as working directory
WORKDIR /opt/mastodon WORKDIR /opt/mastodon
# Add backport repository for some specific packages where we need the latest version
RUN echo 'deb http://deb.debian.org/debian bookworm-backports main' >> /etc/apt/sources.list
# hadolint ignore=DL3008,DL3005 # hadolint ignore=DL3008,DL3005
RUN \ RUN \
# Mount Apt cache and lib directories from Docker buildx caches # Mount Apt cache and lib directories from Docker buildx caches
@ -121,13 +128,6 @@ RUN \
# Create temporary build layer from base image # Create temporary build layer from base image
FROM ruby AS build FROM ruby AS build
# Copy Node package configuration files into working directory
COPY package.json yarn.lock .yarnrc.yml /opt/mastodon/
COPY .yarn /opt/mastodon/.yarn
COPY --from=node /usr/local/bin /usr/local/bin
COPY --from=node /usr/local/lib /usr/local/lib
ARG TARGETPLATFORM ARG TARGETPLATFORM
# hadolint ignore=DL3008 # hadolint ignore=DL3008
@ -150,6 +150,7 @@ RUN \
libpq-dev \ libpq-dev \
libssl-dev \ libssl-dev \
libtool \ libtool \
libyaml-dev \
meson \ meson \
nasm \ nasm \
pkg-config \ pkg-config \
@ -160,7 +161,7 @@ RUN \
libexif-dev \ libexif-dev \
libexpat1-dev \ libexpat1-dev \
libgirepository1.0-dev \ libgirepository1.0-dev \
libheif-dev \ libheif-dev/bookworm-backports \
libimagequant-dev \ libimagequant-dev \
libjpeg62-turbo-dev \ libjpeg62-turbo-dev \
liblcms2-dev \ liblcms2-dev \
@ -180,18 +181,12 @@ RUN \
libx265-dev \ libx265-dev \
; ;
RUN \
# Configure Corepack
rm /usr/local/bin/yarn*; \
corepack enable; \
corepack prepare --activate;
# Create temporary libvips specific build layer from build layer # Create temporary libvips specific build layer from build layer
FROM build AS libvips FROM build AS libvips
# libvips version to compile, change with [--build-arg VIPS_VERSION="8.15.2"] # libvips version to compile, change with [--build-arg VIPS_VERSION="8.15.2"]
# renovate: datasource=github-releases depName=libvips packageName=libvips/libvips # renovate: datasource=github-releases depName=libvips packageName=libvips/libvips
ARG VIPS_VERSION=8.15.5 ARG VIPS_VERSION=8.16.1
# libvips download URL, change with [--build-arg VIPS_URL="https://github.com/libvips/libvips/releases/download"] # libvips download URL, change with [--build-arg VIPS_URL="https://github.com/libvips/libvips/releases/download"]
ARG VIPS_URL=https://github.com/libvips/libvips/releases/download ARG VIPS_URL=https://github.com/libvips/libvips/releases/download
@ -276,38 +271,37 @@ RUN \
# Download and install required Gems # Download and install required Gems
bundle install -j"$(nproc)"; bundle install -j"$(nproc)";
# Create temporary node specific build layer from build layer # Create temporary assets build layer from build layer
FROM build AS yarn FROM build AS precompiler
ARG TARGETPLATFORM ARG TARGETPLATFORM
# Copy Node package configuration files into working directory # Copy Mastodon sources into layer
COPY package.json yarn.lock .yarnrc.yml /opt/mastodon/ COPY . /opt/mastodon/
COPY streaming/package.json /opt/mastodon/streaming/
COPY .yarn /opt/mastodon/.yarn # Copy Node.js binaries/libraries into layer
COPY --from=node /usr/local/bin /usr/local/bin
COPY --from=node /usr/local/lib /usr/local/lib
RUN \
# Configure Corepack
rm /usr/local/bin/yarn*; \
corepack enable; \
corepack prepare --activate;
# hadolint ignore=DL3008 # hadolint ignore=DL3008
RUN \ RUN \
--mount=type=cache,id=corepack-cache-${TARGETPLATFORM},target=/usr/local/share/.cache/corepack,sharing=locked \ --mount=type=cache,id=corepack-cache-${TARGETPLATFORM},target=/usr/local/share/.cache/corepack,sharing=locked \
--mount=type=cache,id=yarn-cache-${TARGETPLATFORM},target=/usr/local/share/.cache/yarn,sharing=locked \ --mount=type=cache,id=yarn-cache-${TARGETPLATFORM},target=/usr/local/share/.cache/yarn,sharing=locked \
# Install Node packages # Install Node.js packages
yarn workspaces focus --production @mastodon/mastodon; yarn workspaces focus --production @mastodon/mastodon;
# Create temporary assets build layer from build layer # Copy libvips components into layer for precompiler
FROM build AS precompiler
# Copy Mastodon sources into precompiler layer
COPY . /opt/mastodon/
# Copy bundler and node packages from build layer to container
COPY --from=yarn /opt/mastodon /opt/mastodon/
COPY --from=bundler /opt/mastodon /opt/mastodon/
COPY --from=bundler /usr/local/bundle/ /usr/local/bundle/
# Copy libvips components to layer for precompiler
COPY --from=libvips /usr/local/libvips/bin /usr/local/bin COPY --from=libvips /usr/local/libvips/bin /usr/local/bin
COPY --from=libvips /usr/local/libvips/lib /usr/local/lib COPY --from=libvips /usr/local/libvips/lib /usr/local/lib
# Copy bundler packages into layer for precompiler
ARG TARGETPLATFORM COPY --from=bundler /opt/mastodon /opt/mastodon/
COPY --from=bundler /usr/local/bundle/ /usr/local/bundle/
RUN \ RUN \
ldconfig; \ ldconfig; \
@ -343,7 +337,7 @@ RUN \
# libvips components # libvips components
libcgif0 \ libcgif0 \
libexif12 \ libexif12 \
libheif1 \ libheif1/bookworm-backports \
libimagequant0 \ libimagequant0 \
libjpeg62-turbo \ libjpeg62-turbo \
liblcms2-2 \ liblcms2-2 \

72
Gemfile
View file

@ -1,12 +1,12 @@
# frozen_string_literal: true # frozen_string_literal: true
source 'https://rubygems.org' source 'https://rubygems.org'
ruby '>= 3.1.0' ruby '>= 3.2.0', '< 3.5.0'
gem 'propshaft' gem 'propshaft'
gem 'puma', '~> 6.3' gem 'puma', '~> 6.3'
gem 'rack', '~> 2.2.7' gem 'rack', '~> 2.2.7'
gem 'rails', '~> 7.1.1' gem 'rails', '~> 8.0'
gem 'thor', '~> 1.2' gem 'thor', '~> 1.2'
gem 'dotenv' gem 'dotenv'
@ -16,16 +16,16 @@ gem 'pghero'
gem 'aws-sdk-s3', '~> 1.123', require: false gem 'aws-sdk-s3', '~> 1.123', require: false
gem 'blurhash', '~> 0.1' gem 'blurhash', '~> 0.1'
gem 'fog-core', '<= 2.5.0' gem 'fog-core', '<= 2.6.0'
gem 'fog-openstack', '~> 1.0', require: false gem 'fog-openstack', '~> 1.0', require: false
gem 'jd-paperclip-azure', '~> 3.0', require: false
gem 'kt-paperclip', '~> 7.2' gem 'kt-paperclip', '~> 7.2'
gem 'md-paperclip-azure', '~> 2.2', require: false
gem 'ruby-vips', '~> 2.2', require: false gem 'ruby-vips', '~> 2.2', require: false
gem 'active_model_serializers', '~> 0.10' gem 'active_model_serializers', '~> 0.10'
gem 'addressable', '~> 2.8' gem 'addressable', '~> 2.8'
gem 'bootsnap', '~> 1.18.0', require: false gem 'bootsnap', '~> 1.18.0', require: false
gem 'browser', '< 6' # https://github.com/fnando/browser/issues/543 gem 'browser'
gem 'charlock_holmes', '~> 0.7.7' gem 'charlock_holmes', '~> 0.7.7'
gem 'chewy', '~> 7.3' gem 'chewy', '~> 7.3'
gem 'devise', '~> 4.9' gem 'devise', '~> 4.9'
@ -39,7 +39,7 @@ gem 'net-ldap', '~> 0.18'
gem 'omniauth', '~> 2.0' gem 'omniauth', '~> 2.0'
gem 'omniauth-cas', '~> 3.0.0.beta.1' gem 'omniauth-cas', '~> 3.0.0.beta.1'
gem 'omniauth_openid_connect', '~> 0.6.1' gem 'omniauth_openid_connect', '~> 0.8.0'
gem 'omniauth-rails_csrf_protection', '~> 1.0' gem 'omniauth-rails_csrf_protection', '~> 1.0'
gem 'omniauth-saml', '~> 2.0' gem 'omniauth-saml', '~> 2.0'
@ -47,13 +47,14 @@ gem 'color_diff', '~> 0.1'
gem 'csv', '~> 3.2' gem 'csv', '~> 3.2'
gem 'discard', '~> 1.2' gem 'discard', '~> 1.2'
gem 'doorkeeper', '~> 5.6' gem 'doorkeeper', '~> 5.6'
gem 'faraday-httpclient'
gem 'fast_blank', '~> 1.0' gem 'fast_blank', '~> 1.0'
gem 'fastimage' gem 'fastimage'
gem 'hiredis', '~> 0.6' gem 'hiredis', '~> 0.6'
gem 'htmlentities', '~> 4.3' gem 'htmlentities', '~> 4.3'
gem 'http', '~> 5.2.0' gem 'http', '~> 5.2.0'
gem 'http_accept_language', '~> 2.1' gem 'http_accept_language', '~> 2.1'
gem 'httplog', '~> 1.7.0' gem 'httplog', '~> 1.7.0', require: false
gem 'i18n' gem 'i18n'
gem 'idn-ruby', require: 'idn' gem 'idn-ruby', require: 'idn'
gem 'inline_svg' gem 'inline_svg'
@ -62,6 +63,7 @@ gem 'kaminari', '~> 1.2'
gem 'link_header', '~> 0.0' gem 'link_header', '~> 0.0'
gem 'mario-redis-lock', '~> 1.2', require: 'redis_lock' gem 'mario-redis-lock', '~> 1.2', require: 'redis_lock'
gem 'mime-types', '~> 3.6.0', require: 'mime/types/columnar' gem 'mime-types', '~> 3.6.0', require: 'mime/types/columnar'
gem 'mutex_m'
gem 'nokogiri', '~> 1.15' gem 'nokogiri', '~> 1.15'
gem 'oj', '~> 3.14' gem 'oj', '~> 3.14'
gem 'ox', '~> 2.14' gem 'ox', '~> 2.14'
@ -71,13 +73,13 @@ gem 'public_suffix', '~> 6.0'
gem 'pundit', '~> 2.3' gem 'pundit', '~> 2.3'
gem 'rack-attack', '~> 6.6' gem 'rack-attack', '~> 6.6'
gem 'rack-cors', '~> 2.0', require: 'rack/cors' gem 'rack-cors', '~> 2.0', require: 'rack/cors'
gem 'rails-i18n', '~> 7.0' gem 'rails-i18n', '~> 8.0'
gem 'redcarpet', '~> 3.6' gem 'redcarpet', '~> 3.6'
gem 'redis', '~> 4.5', require: ['redis', 'redis/connection/hiredis'] gem 'redis', '~> 4.5', require: ['redis', 'redis/connection/hiredis']
gem 'redis-namespace', '~> 1.10' gem 'redis-namespace', '~> 1.10'
gem 'rqrcode', '~> 2.2' gem 'rqrcode', '~> 2.2'
gem 'ruby-progressbar', '~> 1.13' gem 'ruby-progressbar', '~> 1.13'
gem 'sanitize', '~> 6.0' gem 'sanitize', '~> 7.0'
gem 'scenic', '~> 1.7' gem 'scenic', '~> 1.7'
gem 'sidekiq', '~> 6.5' gem 'sidekiq', '~> 6.5'
gem 'sidekiq-bulk', '~> 0.2.0' gem 'sidekiq-bulk', '~> 0.2.0'
@ -92,29 +94,31 @@ gem 'twitter-text', '~> 3.1.0'
gem 'tzinfo-data', '~> 1.2023' gem 'tzinfo-data', '~> 1.2023'
gem 'webauthn', '~> 3.0' gem 'webauthn', '~> 3.0'
gem 'webpacker', '~> 5.4' gem 'webpacker', '~> 5.4'
gem 'webpush', github: 'ClearlyClaire/webpush', ref: 'f14a4d52e201128b1b00245d11b6de80d6cfdcd9' gem 'webpush', github: 'mastodon/webpush', ref: '9631ac63045cfabddacc69fc06e919b4c13eb913'
gem 'json-ld' gem 'json-ld'
gem 'json-ld-preloaded', '~> 3.2' gem 'json-ld-preloaded', '~> 3.2'
gem 'rdf-normalize', '~> 0.5' gem 'rdf-normalize', '~> 0.5'
gem 'opentelemetry-api', '~> 1.4.0' gem 'prometheus_exporter', '~> 2.2', require: false
gem 'opentelemetry-api', '~> 1.5.0'
group :opentelemetry do group :opentelemetry do
gem 'opentelemetry-exporter-otlp', '~> 0.29.0', require: false gem 'opentelemetry-exporter-otlp', '~> 0.30.0', require: false
gem 'opentelemetry-instrumentation-active_job', '~> 0.7.1', require: false gem 'opentelemetry-instrumentation-active_job', '~> 0.8.0', require: false
gem 'opentelemetry-instrumentation-active_model_serializers', '~> 0.20.1', require: false gem 'opentelemetry-instrumentation-active_model_serializers', '~> 0.22.0', require: false
gem 'opentelemetry-instrumentation-concurrent_ruby', '~> 0.21.2', require: false gem 'opentelemetry-instrumentation-concurrent_ruby', '~> 0.22.0', require: false
gem 'opentelemetry-instrumentation-excon', '~> 0.22.0', require: false gem 'opentelemetry-instrumentation-excon', '~> 0.23.0', require: false
gem 'opentelemetry-instrumentation-faraday', '~> 0.24.1', require: false gem 'opentelemetry-instrumentation-faraday', '~> 0.26.0', require: false
gem 'opentelemetry-instrumentation-http', '~> 0.23.2', require: false gem 'opentelemetry-instrumentation-http', '~> 0.24.0', require: false
gem 'opentelemetry-instrumentation-http_client', '~> 0.22.3', require: false gem 'opentelemetry-instrumentation-http_client', '~> 0.23.0', require: false
gem 'opentelemetry-instrumentation-net_http', '~> 0.22.4', require: false gem 'opentelemetry-instrumentation-net_http', '~> 0.23.0', require: false
gem 'opentelemetry-instrumentation-pg', '~> 0.29.0', require: false gem 'opentelemetry-instrumentation-pg', '~> 0.30.0', require: false
gem 'opentelemetry-instrumentation-rack', '~> 0.25.0', require: false gem 'opentelemetry-instrumentation-rack', '~> 0.26.0', require: false
gem 'opentelemetry-instrumentation-rails', '~> 0.32.0', require: false gem 'opentelemetry-instrumentation-rails', '~> 0.36.0', require: false
gem 'opentelemetry-instrumentation-redis', '~> 0.25.3', require: false gem 'opentelemetry-instrumentation-redis', '~> 0.26.0', require: false
gem 'opentelemetry-instrumentation-sidekiq', '~> 0.25.2', require: false gem 'opentelemetry-instrumentation-sidekiq', '~> 0.26.0', require: false
gem 'opentelemetry-sdk', '~> 1.4', require: false gem 'opentelemetry-sdk', '~> 1.4', require: false
end end
@ -123,7 +127,7 @@ group :test do
gem 'flatware-rspec' gem 'flatware-rspec'
# Adds RSpec Error/Warning annotations to GitHub PRs on the Files tab # Adds RSpec Error/Warning annotations to GitHub PRs on the Files tab
gem 'rspec-github', '~> 2.4', require: false gem 'rspec-github', '~> 3.0', require: false
# RSpec helpers for email specs # RSpec helpers for email specs
gem 'email_spec' gem 'email_spec'
@ -141,9 +145,6 @@ group :test do
# Used to mock environment variables # Used to mock environment variables
gem 'climate_control' gem 'climate_control'
# Add back helpers functions removed in Rails 5.1
gem 'rails-controller-testing', '~> 1.0'
# Validate schemas in specs # Validate schemas in specs
gem 'json-schema', '~> 5.0' gem 'json-schema', '~> 5.0'
@ -152,7 +153,7 @@ group :test do
gem 'shoulda-matchers' gem 'shoulda-matchers'
# Coverage formatter for RSpec test if DISABLE_SIMPLECOV is false # Coverage formatter for RSpec
gem 'simplecov', '~> 0.22', require: false gem 'simplecov', '~> 0.22', require: false
gem 'simplecov-lcov', '~> 0.8', require: false gem 'simplecov-lcov', '~> 0.8', require: false
@ -164,13 +165,14 @@ group :development do
# Code linting CLI and plugins # Code linting CLI and plugins
gem 'rubocop', require: false gem 'rubocop', require: false
gem 'rubocop-capybara', require: false gem 'rubocop-capybara', require: false
gem 'rubocop-i18n', require: false
gem 'rubocop-performance', require: false gem 'rubocop-performance', require: false
gem 'rubocop-rails', require: false gem 'rubocop-rails', require: false
gem 'rubocop-rspec', require: false gem 'rubocop-rspec', require: false
gem 'rubocop-rspec_rails', require: false gem 'rubocop-rspec_rails', require: false
# Annotates modules with schema # Annotates modules with schema
gem 'annotate', '~> 3.2' gem 'annotaterb', '~> 4.13', require: false
# Enhanced error message pages for development # Enhanced error message pages for development
gem 'better_errors', '~> 2.9' gem 'better_errors', '~> 2.9'
@ -181,7 +183,7 @@ group :development do
gem 'letter_opener_web', '~> 3.0' gem 'letter_opener_web', '~> 3.0'
# Security analysis CLI tools # Security analysis CLI tools
gem 'brakeman', '~> 6.0', require: false gem 'brakeman', '~> 7.0', require: false
gem 'bundler-audit', '~> 0.9', require: false gem 'bundler-audit', '~> 0.9', require: false
# Linter CLI for HAML files # Linter CLI for HAML files
@ -193,7 +195,7 @@ end
group :development, :test do group :development, :test do
# Interactive Debugging tools # Interactive Debugging tools
gem 'debug', '~> 1.8' gem 'debug', '~> 1.8', require: false
# Generate fake data values # Generate fake data values
gem 'faker', '~> 3.2' gem 'faker', '~> 3.2'
@ -205,7 +207,7 @@ group :development, :test do
gem 'memory_profiler', require: false gem 'memory_profiler', require: false
gem 'ruby-prof', require: false gem 'ruby-prof', require: false
gem 'stackprof', require: false gem 'stackprof', require: false
gem 'test-prof' gem 'test-prof', require: false
# RSpec runner for rails # RSpec runner for rails
gem 'rspec-rails', '~> 7.0' gem 'rspec-rails', '~> 7.0'
@ -220,7 +222,7 @@ gem 'concurrent-ruby', require: false
gem 'connection_pool', require: false gem 'connection_pool', require: false
gem 'xorcist', '~> 1.1' gem 'xorcist', '~> 1.1'
gem 'net-http', '~> 0.4.0' gem 'net-http', '~> 0.6.0'
gem 'rubyzip', '~> 2.3' gem 'rubyzip', '~> 2.3'
gem 'hcaptcha', '~> 7.1' gem 'hcaptcha', '~> 7.1'

File diff suppressed because it is too large Load diff

149
README.md
View file

@ -1,24 +1,24 @@
<h1><picture> > [!NOTE]
<source media="(prefers-color-scheme: dark)" srcset="./lib/assets/wordmark.dark.png?raw=true"> > Want to learn more about Mastodon?
<source media="(prefers-color-scheme: light)" srcset="./lib/assets/wordmark.light.png?raw=true"> > Click below to find out more in a video.
<img alt="Mastodon" src="./lib/assets/wordmark.light.png?raw=true" height="34">
</picture></h1>
[![GitHub release](https://img.shields.io/github/release/mastodon/mastodon.svg)][releases] <p align="center">
[![Ruby Testing](https://github.com/mastodon/mastodon/actions/workflows/test-ruby.yml/badge.svg)](https://github.com/mastodon/mastodon/actions/workflows/test-ruby.yml) <a style="text-decoration:none" href="https://www.youtube.com/watch?v=IPSbNdBmWKE">
[![Crowdin](https://d322cqt584bo4o.cloudfront.net/mastodon/localized.svg)][crowdin] <img alt="Mastodon hero image" src="https://github.com/user-attachments/assets/ef53f5e9-c0d8-484d-9f53-00efdebb92c3" />
</a>
</p>
[releases]: https://github.com/mastodon/mastodon/releases <p align="center">
[crowdin]: https://crowdin.com/project/mastodon <a style="text-decoration:none" href="https://github.com/mastodon/mastodon/releases">
<img src="https://img.shields.io/github/release/mastodon/mastodon.svg" alt="Release" /></a>
<a style="text-decoration:none" href="https://github.com/mastodon/mastodon/actions/workflows/test-ruby.yml">
<img src="https://github.com/mastodon/mastodon/actions/workflows/test-ruby.yml/badge.svg" alt="Ruby Testing" /></a>
<a style="text-decoration:none" href="https://crowdin.com/project/mastodon">
<img src="https://d322cqt584bo4o.cloudfront.net/mastodon/localized.svg" alt="Crowdin" /></a>
</p>
Mastodon is a **free, open-source social network server** based on ActivityPub where users can follow friends and discover new ones. On Mastodon, users can publish anything they want: links, pictures, text, and video. All Mastodon servers are interoperable as a federated network (users on one server can seamlessly communicate with users from another one, including non-Mastodon software that implements ActivityPub!) Mastodon is a **free, open-source social network server** based on ActivityPub where users can follow friends and discover new ones. On Mastodon, users can publish anything they want: links, pictures, text, and video. All Mastodon servers are interoperable as a federated network (users on one server can seamlessly communicate with users from another one, including non-Mastodon software that implements ActivityPub!)
Click below to **learn more** in a video:
[![Screenshot](https://blog.joinmastodon.org/2018/06/why-activitypub-is-the-future/ezgif-2-60f1b00403.gif)][youtube_demo]
[youtube_demo]: https://www.youtube.com/watch?v=IPSbNdBmWKE
## Navigation ## Navigation
- [Project homepage 🐘](https://joinmastodon.org) - [Project homepage 🐘](https://joinmastodon.org)
@ -37,25 +37,15 @@ Click below to **learn more** in a video:
<img src="/app/javascript/images/elephant_ui_working.svg?raw=true" align="right" width="30%" /> <img src="/app/javascript/images/elephant_ui_working.svg?raw=true" align="right" width="30%" />
### No vendor lock-in: Fully interoperable with any conforming platform **No vendor lock-in: Fully interoperable with any conforming platform** - It doesn't have to be Mastodon; whatever implements ActivityPub is part of the social network! [Learn more](https://blog.joinmastodon.org/2018/06/why-activitypub-is-the-future/)
It doesn't have to be Mastodon; whatever implements ActivityPub is part of the social network! [Learn more](https://blog.joinmastodon.org/2018/06/why-activitypub-is-the-future/) **Real-time, chronological timeline updates** - updates of people you're following appear in real-time in the UI via WebSockets. There's a firehose view as well!
### Real-time, chronological timeline updates **Media attachments like images and short videos** - upload and view images and WebM/MP4 videos attached to the updates. Videos with no audio track are treated like GIFs; normal videos loop continuously!
Updates of people you're following appear in real-time in the UI via WebSockets. There's a firehose view as well! **Safety and moderation tools** - Mastodon includes private posts, locked accounts, phrase filtering, muting, blocking, and all sorts of other features, along with a reporting and moderation system. [Learn more](https://blog.joinmastodon.org/2018/07/cage-the-mastodon/)
### Media attachments like images and short videos **OAuth2 and a straightforward REST API** - Mastodon acts as an OAuth2 provider, so 3rd party apps can use the REST and Streaming APIs. This results in a rich app ecosystem with a lot of choices!
Upload and view images and WebM/MP4 videos attached to the updates. Videos with no audio track are treated like GIFs; normal videos loop continuously!
### Safety and moderation tools
Mastodon includes private posts, locked accounts, phrase filtering, muting, blocking, and all sorts of other features, along with a reporting and moderation system. [Learn more](https://blog.joinmastodon.org/2018/07/cage-the-mastodon/)
### OAuth2 and a straightforward REST API
Mastodon acts as an OAuth2 provider, so 3rd party apps can use the REST and Streaming APIs. This results in a rich app ecosystem with a lot of choices!
## Deployment ## Deployment
@ -69,90 +59,45 @@ Mastodon acts as an OAuth2 provider, so 3rd party apps can use the REST and Stre
- **PostgreSQL** 12+ - **PostgreSQL** 12+
- **Redis** 4+ - **Redis** 4+
- **Ruby** 3.1+ - **Ruby** 3.2+
- **Node.js** 18+ - **Node.js** 18+
The repository includes deployment configurations for **Docker and docker-compose** as well as specific platforms like **Heroku**, and **Scalingo**. For Helm charts, reference the [mastodon/chart repository](https://github.com/mastodon/chart). The [**standalone** installation guide](https://docs.joinmastodon.org/admin/install/) is available in the documentation. The repository includes deployment configurations for **Docker and docker-compose** as well as specific platforms like **Heroku**, and **Scalingo**. For Helm charts, reference the [mastodon/chart repository](https://github.com/mastodon/chart). The [**standalone** installation guide](https://docs.joinmastodon.org/admin/install/) is available in the documentation.
## Development
### Vagrant
A **Vagrant** configuration is included for development purposes. To use it, complete the following steps:
- Install Vagrant and Virtualbox
- Install the `vagrant-hostsupdater` plugin: `vagrant plugin install vagrant-hostsupdater`
- Run `vagrant up`
- Run `vagrant ssh -c "cd /vagrant && bin/dev"`
- Open `http://mastodon.local` in your browser
### macOS
To set up **macOS** for native development, complete the following steps:
- Install [Homebrew] and run `brew install postgresql@14 redis imagemagick
libidn nvm` to install the required project dependencies
- Use a Ruby version manager to activate the ruby in `.ruby-version` and run
`nvm use` to activate the node version from `.nvmrc`
- Run the `bin/setup` script, which will install the required ruby gems and node
packages and prepare the database for local development
- Finally, run the `bin/dev` script which will launch services via `overmind`
(if installed) or `foreman`
### Docker
For production hosting and deployment with **Docker**, use the `Dockerfile` and
`docker-compose.yml` in the project root directory.
For local development, install and launch [Docker], and run:
```shell
docker compose -f .devcontainer/compose.yaml up -d
docker compose -f .devcontainer/compose.yaml exec app bin/setup
docker compose -f .devcontainer/compose.yaml exec app bin/dev
```
### Dev Containers
Within IDEs that support the [Development Containers] specification, start the
"Mastodon on local machine" container from the editor. The necessary `docker
compose` commands to build and setup the container should run automatically. For
**Visual Studio Code** this requires installing the [Dev Container extension].
### GitHub Codespaces
[GitHub Codespaces] provides a web-based version of VS Code and a cloud hosted
development environment configured with the software needed for this project.
[![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)][codespace]
- Click the button to create a new codespace, and confirm the options
- Wait for the environment to build (takes a few minutes)
- When the editor is ready, run `bin/dev` in the terminal
- Wait for an _Open in Browser_ prompt. This will open Mastodon
- On the _Ports_ tab "stream" setting change _Port visibility_ → _Public_
## Contributing ## Contributing
Mastodon is **free, open-source software** licensed under **AGPLv3**. Mastodon is **free, open-source software** licensed under **AGPLv3**.
You can open issues for bugs you've found or features you think are missing. You can also submit pull requests to this repository or submit translations using Crowdin. To get started, take a look at [CONTRIBUTING.md](CONTRIBUTING.md). If your contributions are accepted into Mastodon, you can request to be paid through [our OpenCollective](https://opencollective.com/mastodon). You can open issues for bugs you've found or features you think are missing. You
can also submit pull requests to this repository or translations via Crowdin. To
get started, look at the [CONTRIBUTING] and [DEVELOPMENT] guides. For changes
accepted into Mastodon, you can request to be paid through our [OpenCollective].
**IRC channel**: #mastodon on irc.libera.chat **IRC channel**: #mastodon on [`irc.libera.chat`](https://libera.chat)
## License ## License
Copyright (C) 2016-2024 Eugen Rochko & other Mastodon contributors (see [AUTHORS.md](AUTHORS.md)) Copyright (c) 2016-2024 Eugen Rochko (+ [`mastodon authors`](AUTHORS.md))
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Licensed under GNU Affero General Public License as stated in the [LICENSE](LICENSE):
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. ```
Copyright (c) 2016-2024 Eugen Rochko & other Mastodon contributors
You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>. This program is free software: you can redistribute it and/or modify it under
the terms of the GNU Affero General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option) any
later version.
[codespace]: https://codespaces.new/mastodon/mastodon?quickstart=1&devcontainer_path=.devcontainer%2Fcodespaces%2Fdevcontainer.json This program is distributed in the hope that it will be useful, but WITHOUT
[Dev Container extension]: https://containers.dev/supporting#dev-containers ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
[Development Containers]: https://containers.dev/supporting FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
[Docker]: https://docs.docker.com details.
[GitHub Codespaces]: https://docs.github.com/en/codespaces
[Homebrew]: https://brew.sh You should have received a copy of the GNU Affero General Public License along
with this program. If not, see https://www.gnu.org/licenses/
```
[CONTRIBUTING]: CONTRIBUTING.md
[DEVELOPMENT]: docs/DEVELOPMENT.md
[OpenCollective]: https://opencollective.com/mastodon

View file

@ -3,6 +3,6 @@
# Add your own tasks in files placed in lib/tasks ending in .rake, # Add your own tasks in files placed in lib/tasks ending in .rake,
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
require File.expand_path('config/application', __dir__) require_relative 'config/application'
Rails.application.load_tasks Rails.application.load_tasks

2
Vagrantfile vendored
View file

@ -174,7 +174,7 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
if config.vm.networks.any? { |type, options| type == :private_network } if config.vm.networks.any? { |type, options| type == :private_network }
config.vm.synced_folder ".", "/vagrant", type: "nfs", mount_options: ['rw', 'actimeo=1'] config.vm.synced_folder ".", "/vagrant", type: "nfs", mount_options: ['rw', 'actimeo=1']
else else
config.vm.synced_folder ".", "/vagrant" config.vm.synced_folder ".", "/vagrant", type: "rsync", create: true, rsync__args: ["--verbose", "--archive", "--delete", "-z"]
end end
# Otherwise, you can access the site at http://localhost:3000 and http://localhost:4000 , http://localhost:8080 # Otherwise, you can access the site at http://localhost:3000 and http://localhost:4000 , http://localhost:8080

View file

@ -49,7 +49,7 @@ class ActivityPub::CollectionsController < ActivityPub::BaseController
def collection_presenter def collection_presenter
ActivityPub::CollectionPresenter.new( ActivityPub::CollectionPresenter.new(
id: account_collection_url(@account, params[:id]), id: ActivityPub::TagManager.instance.collection_uri_for(@account, params[:id]),
type: @type, type: @type,
size: @size, size: @size,
items: @items items: @items

View file

@ -41,12 +41,8 @@ class ActivityPub::OutboxesController < ActivityPub::BaseController
end end
end end
def outbox_url(**kwargs) def outbox_url(...)
if params[:account_username].present? ActivityPub::TagManager.instance.outbox_uri_for(@account, ...)
account_outbox_url(@account, **kwargs)
else
instance_actor_outbox_url(**kwargs)
end
end end
def next_page def next_page

View file

@ -34,7 +34,8 @@ module Admin
end end
def resource_params def resource_params
params.require(:admin_account_action).permit(:type, :report_id, :warning_preset_id, :text, :send_email_notification, :include_statuses) params
.expect(admin_account_action: [:type, :report_id, :warning_preset_id, :text, :send_email_notification, :include_statuses])
end end
end end
end end

View file

@ -29,10 +29,8 @@ module Admin
private private
def resource_params def resource_params
params.require(:account_moderation_note).permit( params
:content, .expect(account_moderation_note: [:content, :target_account_id])
:target_account_id
)
end end
def set_account_moderation_note def set_account_moderation_note

View file

@ -158,7 +158,8 @@ module Admin
end end
def form_account_batch_params def form_account_batch_params
params.require(:form_account_batch).permit(:action, account_ids: []) params
.expect(form_account_batch: [:action, account_ids: []])
end end
def action_from_button def action_from_button

View file

@ -0,0 +1,18 @@
# frozen_string_literal: true
class Admin::Announcements::DistributionsController < Admin::BaseController
before_action :set_announcement
def create
authorize @announcement, :distribute?
@announcement.touch(:notification_sent_at)
Admin::DistributeAnnouncementNotificationWorker.perform_async(@announcement.id)
redirect_to admin_announcements_path
end
private
def set_announcement
@announcement = Announcement.find(params[:announcement_id])
end
end

View file

@ -0,0 +1,16 @@
# frozen_string_literal: true
class Admin::Announcements::PreviewsController < Admin::BaseController
before_action :set_announcement
def show
authorize @announcement, :distribute?
@user_count = @announcement.scope_for_notification.count
end
private
def set_announcement
@announcement = Announcement.find(params[:announcement_id])
end
end

View file

@ -0,0 +1,17 @@
# frozen_string_literal: true
class Admin::Announcements::TestsController < Admin::BaseController
before_action :set_announcement
def create
authorize @announcement, :distribute?
UserMailer.announcement_published(current_user, @announcement).deliver_later!
redirect_to admin_announcements_path
end
private
def set_announcement
@announcement = Announcement.find(params[:announcement_id])
end
end

View file

@ -84,6 +84,7 @@ class Admin::AnnouncementsController < Admin::BaseController
end end
def resource_params def resource_params
params.require(:announcement).permit(:text, :scheduled_at, :starts_at, :ends_at, :all_day) params
.expect(announcement: [:text, :scheduled_at, :starts_at, :ends_at, :all_day])
end end
end end

View file

@ -7,14 +7,14 @@ module Admin
layout 'admin' layout 'admin'
before_action :set_cache_headers before_action :set_referrer_policy_header
after_action :verify_authorized after_action :verify_authorized
private private
def set_cache_headers def set_referrer_policy_header
response.cache_control.replace(private: true, no_store: true) response.headers['Referrer-Policy'] = 'same-origin'
end end
def set_user def set_user

View file

@ -41,9 +41,8 @@ module Admin
end end
def resource_params def resource_params
params.require(:user).permit( params
:unconfirmed_email .expect(user: [:unconfirmed_email])
)
end end
end end
end end

View file

@ -44,7 +44,8 @@ module Admin
private private
def resource_params def resource_params
params.require(:custom_emoji).permit(:shortcode, :image, :visible_in_picker) params
.expect(custom_emoji: [:shortcode, :image, :visible_in_picker])
end end
def filtered_custom_emojis def filtered_custom_emojis
@ -74,7 +75,8 @@ module Admin
end end
def form_custom_emoji_batch_params def form_custom_emoji_batch_params
params.require(:form_custom_emoji_batch).permit(:action, :category_id, :category_name, custom_emoji_ids: []) params
.expect(form_custom_emoji_batch: [:action, :category_id, :category_name, custom_emoji_ids: []])
end end
end end
end end

View file

@ -37,6 +37,7 @@ class Admin::DomainAllowsController < Admin::BaseController
end end
def resource_params def resource_params
params.require(:domain_allow).permit(:domain) params
.expect(domain_allow: [:domain])
end end
end end

View file

@ -25,7 +25,9 @@ module Admin
rescue Mastodon::NotPermittedError rescue Mastodon::NotPermittedError
flash[:alert] = I18n.t('admin.domain_blocks.not_permitted') flash[:alert] = I18n.t('admin.domain_blocks.not_permitted')
else else
redirect_to admin_instances_path(limited: '1'), notice: I18n.t('admin.domain_blocks.created_msg') flash[:notice] = I18n.t('admin.domain_blocks.created_msg')
ensure
redirect_to admin_instances_path(limited: '1')
end end
def new def new
@ -114,7 +116,12 @@ module Admin
end end
def form_domain_block_batch_params 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]) params
.expect(
form_domain_block_batch: [
domain_blocks_attributes: [[:enabled, :domain, :severity, :reject_media, :reject_reports, :private_comment, :public_comment, :obfuscate]],
]
)
end end
def action_from_button def action_from_button

View file

@ -5,7 +5,7 @@ module Admin
def index def index
authorize :email_domain_block, :index? authorize :email_domain_block, :index?
@email_domain_blocks = EmailDomainBlock.where(parent_id: nil).includes(:children).order(id: :desc).page(params[:page]) @email_domain_blocks = EmailDomainBlock.parents.includes(:children).order(id: :desc).page(params[:page])
@form = Form::EmailDomainBlockBatch.new @form = Form::EmailDomainBlockBatch.new
end end
@ -58,18 +58,17 @@ module Admin
private private
def set_resolved_records def set_resolved_records
Resolv::DNS.open do |dns| @resolved_records = DomainResource.new(@email_domain_block.domain).mx
dns.timeouts = 5
@resolved_records = dns.getresources(@email_domain_block.domain, Resolv::DNS::Resource::IN::MX).to_a
end
end end
def resource_params def resource_params
params.require(:email_domain_block).permit(:domain, :allow_with_approval, other_domains: []) params
.expect(email_domain_block: [:domain, :allow_with_approval, other_domains: []])
end end
def form_email_domain_block_batch_params def form_email_domain_block_batch_params
params.require(:form_email_domain_block_batch).permit(email_domain_block_ids: []) params
.expect(form_email_domain_block_batch: [email_domain_block_ids: []])
end end
def action_from_button def action_from_button

View file

@ -37,7 +37,8 @@ module Admin
end end
def form_account_batch_params def form_account_batch_params
params.require(:form_account_batch).permit(:action, account_ids: []) params
.expect(form_account_batch: [:action, account_ids: []])
end end
def filter_params def filter_params

View file

@ -5,6 +5,8 @@ module Admin
before_action :set_instances, only: :index before_action :set_instances, only: :index
before_action :set_instance, except: :index before_action :set_instance, except: :index
LOGS_LIMIT = 5
def index def index
authorize :instance, :index? authorize :instance, :index?
preload_delivery_failures! preload_delivery_failures!
@ -13,7 +15,7 @@ module Admin
def show def show
authorize :instance, :show? authorize :instance, :show?
@time_period = (6.days.ago.to_date...Time.now.utc.to_date) @time_period = (6.days.ago.to_date...Time.now.utc.to_date)
@action_logs = Admin::ActionLogFilter.new(target_domain: @instance.domain).results.limit(5) @action_logs = Admin::ActionLogFilter.new(target_domain: @instance.domain).results.limit(LOGS_LIMIT)
end end
def destroy def destroy

View file

@ -32,14 +32,15 @@ module Admin
def deactivate_all def deactivate_all
authorize :invite, :deactivate_all? authorize :invite, :deactivate_all?
Invite.available.in_batches.update_all(expires_at: Time.now.utc) Invite.available.in_batches.touch_all(:expires_at)
redirect_to admin_invites_path redirect_to admin_invites_path
end end
private private
def resource_params def resource_params
params.require(:invite).permit(:max_uses, :expires_in) params
.expect(invite: [:max_uses, :expires_in])
end end
def filtered_invites def filtered_invites

View file

@ -44,7 +44,8 @@ module Admin
private private
def resource_params def resource_params
params.require(:ip_block).permit(:ip, :severity, :comment, :expires_in) params
.expect(ip_block: [:ip, :severity, :comment, :expires_in])
end end
def action_from_button def action_from_button
@ -52,7 +53,8 @@ module Admin
end end
def form_ip_block_batch_params def form_ip_block_batch_params
params.require(:form_ip_block_batch).permit(ip_block_ids: []) params
.expect(form_ip_block_batch: [ip_block_ids: []])
end end
end end
end end

View file

@ -21,6 +21,7 @@ module Admin
@relay = Relay.new(resource_params) @relay = Relay.new(resource_params)
if @relay.save if @relay.save
log_action :create, @relay
@relay.enable! @relay.enable!
redirect_to admin_relays_path redirect_to admin_relays_path
else else
@ -31,18 +32,21 @@ module Admin
def destroy def destroy
authorize :relay, :update? authorize :relay, :update?
@relay.destroy @relay.destroy
log_action :destroy, @relay
redirect_to admin_relays_path redirect_to admin_relays_path
end end
def enable def enable
authorize :relay, :update? authorize :relay, :update?
@relay.enable! @relay.enable!
log_action :enable, @relay
redirect_to admin_relays_path redirect_to admin_relays_path
end end
def disable def disable
authorize :relay, :update? authorize :relay, :update?
@relay.disable! @relay.disable!
log_action :disable, @relay
redirect_to admin_relays_path redirect_to admin_relays_path
end end
@ -53,7 +57,8 @@ module Admin
end end
def resource_params def resource_params
params.require(:relay).permit(:inbox_url) params
.expect(relay: [:inbox_url])
end end
def warn_signatures_not_enabled! def warn_signatures_not_enabled!

View file

@ -47,10 +47,8 @@ module Admin
end end
def resource_params def resource_params
params.require(:report_note).permit( params
:content, .expect(report_note: [:content, :report_id])
:report_id
)
end end
def set_report_note def set_report_note

View file

@ -61,7 +61,8 @@ module Admin
end end
def resource_params def resource_params
params.require(:user_role).permit(:name, :color, :highlighted, :position, permissions_as_keys: []) params
.expect(user_role: [:name, :color, :highlighted, :position, permissions_as_keys: []])
end end
end end
end end

View file

@ -53,7 +53,8 @@ module Admin
end end
def resource_params def resource_params
params.require(:rule).permit(:text, :hint, :priority) params
.expect(rule: [:text, :hint, :priority])
end end
end end
end end

View file

@ -28,7 +28,8 @@ module Admin
end end
def settings_params def settings_params
params.require(:form_admin_settings).permit(*Form::AdminSettings::KEYS) params
.expect(form_admin_settings: [*Form::AdminSettings::KEYS])
end end
end end
end end

View file

@ -6,7 +6,7 @@ module Admin
def index def index
authorize :software_update, :index? authorize :software_update, :index?
@software_updates = SoftwareUpdate.all.sort_by(&:gem_version) @software_updates = SoftwareUpdate.by_version.filter(&:pending?)
end end
private private

View file

@ -16,6 +16,8 @@ module Admin
def show def show
authorize [:admin, @status], :show? authorize [:admin, @status], :show?
@status_batch_action = Admin::StatusBatchAction.new
end end
def batch def batch
@ -37,7 +39,8 @@ module Admin
helper_method :batched_ordered_status_edits helper_method :batched_ordered_status_edits
def admin_status_batch_action_params def admin_status_batch_action_params
params.require(:admin_status_batch_action).permit(status_ids: []) params
.expect(admin_status_batch_action: [status_ids: []])
end end
def after_create_redirect_path def after_create_redirect_path

View file

@ -37,7 +37,8 @@ module Admin
end end
def tag_params def tag_params
params.require(:tag).permit(:name, :display_name, :trendable, :usable, :listable) params
.expect(tag: [:name, :display_name, :trendable, :usable, :listable])
end end
def filtered_tags def filtered_tags

View file

@ -0,0 +1,18 @@
# frozen_string_literal: true
class Admin::TermsOfService::DistributionsController < Admin::BaseController
before_action :set_terms_of_service
def create
authorize @terms_of_service, :distribute?
@terms_of_service.touch(:notification_sent_at)
Admin::DistributeTermsOfServiceNotificationWorker.perform_async(@terms_of_service.id)
redirect_to admin_terms_of_service_index_path
end
private
def set_terms_of_service
@terms_of_service = TermsOfService.find(params[:terms_of_service_id])
end
end

View file

@ -0,0 +1,37 @@
# frozen_string_literal: true
class Admin::TermsOfService::DraftsController < Admin::BaseController
before_action :set_terms_of_service
def show
authorize :terms_of_service, :create?
end
def update
authorize @terms_of_service, :update?
@terms_of_service.published_at = Time.now.utc if params[:action_type] == 'publish'
if @terms_of_service.update(resource_params)
log_action(:publish, @terms_of_service) if @terms_of_service.published?
redirect_to @terms_of_service.published? ? admin_terms_of_service_index_path : admin_terms_of_service_draft_path
else
render :show
end
end
private
def set_terms_of_service
@terms_of_service = TermsOfService.draft.first || TermsOfService.new(text: current_terms_of_service&.text, effective_date: 10.days.from_now)
end
def current_terms_of_service
TermsOfService.live.first
end
def resource_params
params
.expect(terms_of_service: [:text, :changelog, :effective_date])
end
end

View file

@ -0,0 +1,38 @@
# frozen_string_literal: true
class Admin::TermsOfService::GeneratesController < Admin::BaseController
before_action :set_instance_presenter
def show
authorize :terms_of_service, :create?
@generator = TermsOfService::Generator.new(
domain: @instance_presenter.domain,
admin_email: @instance_presenter.contact.email
)
end
def create
authorize :terms_of_service, :create?
@generator = TermsOfService::Generator.new(resource_params)
if @generator.valid?
TermsOfService.create!(text: @generator.render)
redirect_to admin_terms_of_service_draft_path
else
render :show
end
end
private
def set_instance_presenter
@instance_presenter = InstancePresenter.new
end
def resource_params
params
.expect(terms_of_service_generator: [*TermsOfService::Generator::VARIABLES])
end
end

View file

@ -0,0 +1,8 @@
# frozen_string_literal: true
class Admin::TermsOfService::HistoriesController < Admin::BaseController
def show
authorize :terms_of_service, :index?
@terms_of_service = TermsOfService.published.all
end
end

View file

@ -0,0 +1,16 @@
# frozen_string_literal: true
class Admin::TermsOfService::PreviewsController < Admin::BaseController
before_action :set_terms_of_service
def show
authorize @terms_of_service, :distribute?
@user_count = @terms_of_service.scope_for_notification.count
end
private
def set_terms_of_service
@terms_of_service = TermsOfService.find(params[:terms_of_service_id])
end
end

View file

@ -0,0 +1,17 @@
# frozen_string_literal: true
class Admin::TermsOfService::TestsController < Admin::BaseController
before_action :set_terms_of_service
def create
authorize @terms_of_service, :distribute?
UserMailer.terms_of_service_changed(current_user, @terms_of_service).deliver_later!
redirect_to admin_terms_of_service_preview_path(@terms_of_service)
end
private
def set_terms_of_service
@terms_of_service = TermsOfService.find(params[:terms_of_service_id])
end
end

View file

@ -0,0 +1,8 @@
# frozen_string_literal: true
class Admin::TermsOfServiceController < Admin::BaseController
def index
authorize :terms_of_service, :index?
@terms_of_service = TermsOfService.published.first
end
end

View file

@ -31,7 +31,8 @@ class Admin::Trends::Links::PreviewCardProvidersController < Admin::BaseControll
end end
def trends_preview_card_provider_batch_params def trends_preview_card_provider_batch_params
params.require(:trends_preview_card_provider_batch).permit(:action, preview_card_provider_ids: []) params
.expect(trends_preview_card_provider_batch: [:action, preview_card_provider_ids: []])
end end
def action_from_button def action_from_button

View file

@ -4,7 +4,7 @@ class Admin::Trends::LinksController < Admin::BaseController
def index def index
authorize :preview_card, :review? authorize :preview_card, :review?
@locales = PreviewCardTrend.pluck('distinct language') @locales = PreviewCardTrend.locales
@preview_cards = filtered_preview_cards.page(params[:page]) @preview_cards = filtered_preview_cards.page(params[:page])
@form = Trends::PreviewCardBatch.new @form = Trends::PreviewCardBatch.new
end end
@ -31,7 +31,8 @@ class Admin::Trends::LinksController < Admin::BaseController
end end
def trends_preview_card_batch_params def trends_preview_card_batch_params
params.require(:trends_preview_card_batch).permit(:action, preview_card_ids: []) params
.expect(trends_preview_card_batch: [:action, preview_card_ids: []])
end end
def action_from_button def action_from_button

View file

@ -4,7 +4,7 @@ class Admin::Trends::StatusesController < Admin::BaseController
def index def index
authorize [:admin, :status], :review? authorize [:admin, :status], :review?
@locales = StatusTrend.pluck('distinct language') @locales = StatusTrend.locales
@statuses = filtered_statuses.page(params[:page]) @statuses = filtered_statuses.page(params[:page])
@form = Trends::StatusBatch.new @form = Trends::StatusBatch.new
end end
@ -31,7 +31,8 @@ class Admin::Trends::StatusesController < Admin::BaseController
end end
def trends_status_batch_params def trends_status_batch_params
params.require(:trends_status_batch).permit(:action, status_ids: []) params
.expect(trends_status_batch: [:action, status_ids: []])
end end
def action_from_button def action_from_button

View file

@ -31,7 +31,8 @@ class Admin::Trends::TagsController < Admin::BaseController
end end
def trends_tag_batch_params def trends_tag_batch_params
params.require(:trends_tag_batch).permit(:action, tag_ids: []) params
.expect(trends_tag_batch: [:action, tag_ids: []])
end end
def action_from_button def action_from_button

View file

@ -28,7 +28,8 @@ module Admin
end end
def resource_params def resource_params
params.require(:user).permit(:role_id) params
.expect(user: [:role_id])
end end
end end
end end

View file

@ -52,7 +52,8 @@ module Admin
end end
def warning_preset_params def warning_preset_params
params.require(:account_warning_preset).permit(:title, :text) params
.expect(account_warning_preset: [:title, :text])
end end
end end
end end

View file

@ -74,7 +74,8 @@ module Admin
end end
def resource_params def resource_params
params.require(:webhook).permit(:url, :template, events: []) params
.expect(webhook: [:url, :template, events: []])
end end
end end
end end

View file

@ -14,7 +14,7 @@ class Api::V1::Accounts::CredentialsController < Api::BaseController
@account = current_account @account = current_account
UpdateAccountService.new.call(@account, account_params, raise_error: true) UpdateAccountService.new.call(@account, account_params, raise_error: true)
current_user.update(user_params) if user_params current_user.update(user_params) if user_params
ActivityPub::UpdateDistributionWorker.perform_async(@account.id) ActivityPub::UpdateDistributionWorker.perform_in(ActivityPub::UpdateDistributionWorker::DEBOUNCE_DELAY, @account.id)
render json: @account, serializer: REST::CredentialAccountSerializer render json: @account, serializer: REST::CredentialAccountSerializer
rescue ActiveRecord::RecordInvalid => e rescue ActiveRecord::RecordInvalid => e
render json: ValidationErrorFormatter.new(e).as_json, status: 422 render json: ValidationErrorFormatter.new(e).as_json, status: 422
@ -33,6 +33,7 @@ class Api::V1::Accounts::CredentialsController < Api::BaseController
:discoverable, :discoverable,
:hide_collections, :hide_collections,
:indexable, :indexable,
attribution_domains: [],
fields_attributes: [:name, :value] fields_attributes: [:name, :value]
) )
end end

View file

@ -12,7 +12,7 @@ class Api::V1::Accounts::FamiliarFollowersController < Api::BaseController
private private
def set_accounts def set_accounts
@accounts = Account.without_suspended.where(id: account_ids).select('id, hide_collections') @accounts = Account.without_suspended.where(id: account_ids).select(:id, :hide_collections)
end end
def familiar_followers def familiar_followers

View file

@ -106,8 +106,8 @@ class Api::V1::AccountsController < Api::BaseController
render json: { error: I18n.t('accounts.self_follow_error') }, status: 403 if current_user.account.id == @account.id render json: { error: I18n.t('accounts.self_follow_error') }, status: 403 if current_user.account.id == @account.id
end end
def relationships(**options) def relationships(**)
AccountRelationshipsPresenter.new([@account], current_user.account_id, **options) AccountRelationshipsPresenter.new([@account], current_user.account_id, **)
end end
def account_ids def account_ids

View file

@ -17,6 +17,17 @@ class Api::V1::AnnualReportsController < Api::BaseController
relationships: @relationships relationships: @relationships
end end
def show
with_read_replica do
@presenter = AnnualReportsPresenter.new([@annual_report])
@relationships = StatusRelationshipsPresenter.new(@presenter.statuses, current_account.id)
end
render json: @presenter,
serializer: REST::AnnualReportsSerializer,
relationships: @relationships
end
def read def read
@annual_report.view! @annual_report.view!
render_empty render_empty

View file

@ -5,6 +5,8 @@ class Api::V1::FeaturedTags::SuggestionsController < Api::BaseController
before_action :require_user! before_action :require_user!
before_action :set_recently_used_tags, only: :index before_action :set_recently_used_tags, only: :index
RECENT_TAGS_LIMIT = 10
def index def index
render json: @recently_used_tags, each_serializer: REST::TagSerializer, relationships: TagRelationshipsPresenter.new(@recently_used_tags, current_user&.account_id) render json: @recently_used_tags, each_serializer: REST::TagSerializer, relationships: TagRelationshipsPresenter.new(@recently_used_tags, current_user&.account_id)
end end
@ -12,6 +14,6 @@ class Api::V1::FeaturedTags::SuggestionsController < Api::BaseController
private private
def set_recently_used_tags def set_recently_used_tags
@recently_used_tags = Tag.suggestions_for_account(current_account).limit(10) @recently_used_tags = Tag.suggestions_for_account(current_account).limit(RECENT_TAGS_LIMIT)
end end
end end

View file

@ -28,8 +28,8 @@ class Api::V1::FollowRequestsController < Api::BaseController
@account ||= Account.find(params[:id]) @account ||= Account.find(params[:id])
end end
def relationships(**options) def relationships(**)
AccountRelationshipsPresenter.new([account], current_user.account_id, **options) AccountRelationshipsPresenter.new([account], current_user.account_id, **)
end end
def load_accounts def load_accounts

View file

@ -31,7 +31,7 @@ class Api::V1::Instances::DomainBlocksController < Api::V1::Instances::BaseContr
end end
def show_domain_blocks_to_user? def show_domain_blocks_to_user?
Setting.show_domain_blocks == 'users' && user_signed_in? Setting.show_domain_blocks == 'users' && user_signed_in? && current_user.functional_or_moved?
end end
def set_domain_blocks def set_domain_blocks
@ -47,6 +47,6 @@ class Api::V1::Instances::DomainBlocksController < Api::V1::Instances::BaseContr
end end
def show_rationale_for_user? def show_rationale_for_user?
Setting.show_domain_blocks_rationale == 'users' && user_signed_in? Setting.show_domain_blocks_rationale == 'users' && user_signed_in? && current_user.functional_or_moved?
end end
end end

View file

@ -0,0 +1,22 @@
# frozen_string_literal: true
class Api::V1::Instances::TermsOfServicesController < Api::V1::Instances::BaseController
before_action :set_terms_of_service
def show
cache_even_if_authenticated!
render json: @terms_of_service, serializer: REST::TermsOfServiceSerializer
end
private
def set_terms_of_service
@terms_of_service = begin
if params[:date].present?
TermsOfService.published.find_by!(effective_date: params[:date])
else
TermsOfService.live.first || TermsOfService.published.first! # For the case when none of the published terms have become effective yet
end
end
end
end

View file

@ -15,17 +15,12 @@ class Api::V1::Lists::AccountsController < Api::BaseController
end end
def create def create
ApplicationRecord.transaction do AddAccountsToListService.new.call(@list, Account.find(account_ids))
list_accounts.each do |account|
@list.accounts << account
end
end
render_empty render_empty
end end
def destroy def destroy
ListAccount.where(list: @list, account_id: account_ids).destroy_all RemoveAccountsFromListService.new.call(@list, Account.where(id: account_ids))
render_empty render_empty
end end
@ -43,10 +38,6 @@ class Api::V1::Lists::AccountsController < Api::BaseController
end end
end end
def list_accounts
Account.find(account_ids)
end
def account_ids def account_ids
Array(resource_params[:account_ids]) Array(resource_params[:account_ids])
end end

View file

@ -3,8 +3,8 @@
class Api::V1::MediaController < Api::BaseController class Api::V1::MediaController < Api::BaseController
before_action -> { doorkeeper_authorize! :write, :'write:media' } before_action -> { doorkeeper_authorize! :write, :'write:media' }
before_action :require_user! before_action :require_user!
before_action :set_media_attachment, except: [:create] before_action :set_media_attachment, except: [:create, :destroy]
before_action :check_processing, except: [:create] before_action :check_processing, except: [:create, :destroy]
def show def show
render json: @media_attachment, serializer: REST::MediaAttachmentSerializer, status: status_code_for_media_attachment render json: @media_attachment, serializer: REST::MediaAttachmentSerializer, status: status_code_for_media_attachment
@ -25,6 +25,15 @@ class Api::V1::MediaController < Api::BaseController
render json: @media_attachment, serializer: REST::MediaAttachmentSerializer, status: status_code_for_media_attachment render json: @media_attachment, serializer: REST::MediaAttachmentSerializer, status: status_code_for_media_attachment
end end
def destroy
@media_attachment = current_account.media_attachments.find(params[:id])
return render json: in_usage_error, status: 422 unless @media_attachment.status_id.nil?
@media_attachment.destroy
render_empty
end
private private
def status_code_for_media_attachment def status_code_for_media_attachment
@ -54,4 +63,8 @@ class Api::V1::MediaController < Api::BaseController
def processing_error def processing_error
{ error: 'Error processing thumbnail for uploaded media' } { error: 'Error processing thumbnail for uploaded media' }
end end
def in_usage_error
{ error: 'Media attachment is currently used by a status' }
end
end end

View file

@ -15,7 +15,7 @@ class Api::V1::Polls::VotesController < Api::BaseController
private private
def set_poll def set_poll
@poll = Poll.attached.find(params[:poll_id]) @poll = Poll.find(params[:poll_id])
authorize @poll.status, :show? authorize @poll.status, :show?
rescue Mastodon::NotPermittedError rescue Mastodon::NotPermittedError
not_found not_found

View file

@ -15,7 +15,7 @@ class Api::V1::PollsController < Api::BaseController
private private
def set_poll def set_poll
@poll = Poll.attached.find(params[:id]) @poll = Poll.find(params[:id])
authorize @poll.status, :show? authorize @poll.status, :show?
rescue Mastodon::NotPermittedError rescue Mastodon::NotPermittedError
not_found not_found

View file

@ -7,7 +7,7 @@ class Api::V1::Profile::AvatarsController < Api::BaseController
def destroy def destroy
@account = current_account @account = current_account
UpdateAccountService.new.call(@account, { avatar: nil }, raise_error: true) UpdateAccountService.new.call(@account, { avatar: nil }, raise_error: true)
ActivityPub::UpdateDistributionWorker.perform_async(@account.id) ActivityPub::UpdateDistributionWorker.perform_in(ActivityPub::UpdateDistributionWorker::DEBOUNCE_DELAY, @account.id)
render json: @account, serializer: REST::CredentialAccountSerializer render json: @account, serializer: REST::CredentialAccountSerializer
end end
end end

View file

@ -7,7 +7,7 @@ class Api::V1::Profile::HeadersController < Api::BaseController
def destroy def destroy
@account = current_account @account = current_account
UpdateAccountService.new.call(@account, { header: nil }, raise_error: true) UpdateAccountService.new.call(@account, { header: nil }, raise_error: true)
ActivityPub::UpdateDistributionWorker.perform_async(@account.id) ActivityPub::UpdateDistributionWorker.perform_in(ActivityPub::UpdateDistributionWorker::DEBOUNCE_DELAY, @account.id)
render json: @account, serializer: REST::CredentialAccountSerializer render json: @account, serializer: REST::CredentialAccountSerializer
end end
end end

View file

@ -21,6 +21,7 @@ class Api::V1::Push::SubscriptionsController < Api::BaseController
endpoint: subscription_params[:endpoint], endpoint: subscription_params[:endpoint],
key_p256dh: subscription_params[:keys][:p256dh], key_p256dh: subscription_params[:keys][:p256dh],
key_auth: subscription_params[:keys][:auth], key_auth: subscription_params[:keys][:auth],
standard: subscription_params[:standard] || false,
data: data_params, data: data_params,
user_id: current_user.id, user_id: current_user.id,
access_token_id: doorkeeper_token.id access_token_id: doorkeeper_token.id
@ -55,12 +56,12 @@ class Api::V1::Push::SubscriptionsController < Api::BaseController
end end
def subscription_params def subscription_params
params.require(:subscription).permit(:endpoint, keys: [:auth, :p256dh]) params.expect(subscription: [:endpoint, :standard, keys: [:auth, :p256dh]])
end end
def data_params def data_params
return {} if params[:data].blank? return {} if params[:data].blank?
params.require(:data).permit(:policy, alerts: Notification::TYPES) params.expect(data: [:policy, alerts: Notification::TYPES])
end end
end end

View file

@ -58,6 +58,8 @@ class Api::V1::StatusesController < Api::BaseController
statuses = [@status] + @context.ancestors + @context.descendants statuses = [@status] + @context.ancestors + @context.descendants
render json: @context, serializer: REST::ContextSerializer, relationships: StatusRelationshipsPresenter.new(statuses, current_user&.account_id) render json: @context, serializer: REST::ContextSerializer, relationships: StatusRelationshipsPresenter.new(statuses, current_user&.account_id)
ActivityPub::FetchAllRepliesWorker.perform_async(@status.id) if !current_account.nil? && @status.should_fetch_replies?
end end
def create def create
@ -111,7 +113,7 @@ class Api::V1::StatusesController < Api::BaseController
@status.account.statuses_count = @status.account.statuses_count - 1 @status.account.statuses_count = @status.account.statuses_count - 1
json = render_to_body json: @status, serializer: REST::StatusSerializer, source_requested: true json = render_to_body json: @status, serializer: REST::StatusSerializer, source_requested: true
RemovalWorker.perform_async(@status.id, { 'redraft' => true }) RemovalWorker.perform_async(@status.id, { 'redraft' => !truthy_param?(:delete_media) })
render json: json render json: json
end end

View file

@ -27,7 +27,9 @@ class Api::V1::Trends::TagsController < Api::BaseController
end end
def tags_from_trends def tags_from_trends
Trends.tags.query.allowed scope = Trends.tags.query.allowed.in_locale(content_locale)
scope = scope.filtered_for(current_account) if user_signed_in?
scope
end end
def next_path def next_path

View file

@ -46,7 +46,7 @@ class Api::V2::NotificationsController < Api::BaseController
end end
def show def show
@notification = current_account.notifications.without_suspended.find_by!(group_key: params[:group_key]) @notification = current_account.notifications.without_suspended.by_group_key(params[:group_key]).take!
presenter = GroupedNotificationsPresenter.new(NotificationGroup.from_notifications([@notification])) presenter = GroupedNotificationsPresenter.new(NotificationGroup.from_notifications([@notification]))
render json: presenter, serializer: REST::DedupNotificationGroupSerializer render json: presenter, serializer: REST::DedupNotificationGroupSerializer
end end
@ -57,7 +57,7 @@ class Api::V2::NotificationsController < Api::BaseController
end end
def dismiss def dismiss
current_account.notifications.where(group_key: params[:group_key]).destroy_all current_account.notifications.by_group_key(params[:group_key]).destroy_all
render_empty render_empty
end end
@ -80,10 +80,31 @@ class Api::V2::NotificationsController < Api::BaseController
return [] if @notifications.empty? return [] if @notifications.empty?
MastodonOTELTracer.in_span('Api::V2::NotificationsController#load_grouped_notifications') do MastodonOTELTracer.in_span('Api::V2::NotificationsController#load_grouped_notifications') do
NotificationGroup.from_notifications(@notifications, pagination_range: (@notifications.last.id)..(@notifications.first.id), grouped_types: params[:grouped_types]) pagination_range = (@notifications.last.id)..@notifications.first.id
# If the page is incomplete, we know we are on the last page
if incomplete_page?
if paginating_up?
pagination_range = @notifications.last.id...(params[:max_id]&.to_i)
else
range_start = params[:since_id]&.to_i
range_start += 1 unless range_start.nil?
pagination_range = range_start..(@notifications.first.id)
end end
end end
NotificationGroup.from_notifications(@notifications, pagination_range: pagination_range, grouped_types: params[:grouped_types])
end
end
def incomplete_page?
@notifications.size < limit_param(DEFAULT_NOTIFICATIONS_LIMIT)
end
def paginating_up?
params[:min_id].present?
end
def browserable_account_notifications def browserable_account_notifications
current_account.notifications.without_suspended.browserable( current_account.notifications.without_suspended.browserable(
types: Array(browserable_params[:types]), types: Array(browserable_params[:types]),

View file

@ -66,7 +66,7 @@ class Api::Web::PushSubscriptionsController < Api::Web::BaseController
end end
def subscription_params def subscription_params
@subscription_params ||= params.require(:subscription).permit(:endpoint, keys: [:auth, :p256dh]) @subscription_params ||= params.expect(subscription: [:standard, :endpoint, keys: [:auth, :p256dh]])
end end
def web_push_subscription_params def web_push_subscription_params
@ -76,11 +76,12 @@ class Api::Web::PushSubscriptionsController < Api::Web::BaseController
endpoint: subscription_params[:endpoint], endpoint: subscription_params[:endpoint],
key_auth: subscription_params[:keys][:auth], key_auth: subscription_params[:keys][:auth],
key_p256dh: subscription_params[:keys][:p256dh], key_p256dh: subscription_params[:keys][:p256dh],
standard: subscription_params[:standard] || false,
user_id: active_session.user_id, user_id: active_session.user_id,
} }
end end
def data_params def data_params
@data_params ||= params.require(:data).permit(:policy, alerts: Notification::TYPES) @data_params ||= params.expect(data: [:policy, alerts: Notification::TYPES])
end end
end end

View file

@ -22,7 +22,6 @@ class ApplicationController < ActionController::Base
helper_method :use_seamless_external_login? helper_method :use_seamless_external_login?
helper_method :sso_account_settings helper_method :sso_account_settings
helper_method :limited_federation_mode? helper_method :limited_federation_mode?
helper_method :body_class_string
helper_method :skip_csrf_meta_tags? helper_method :skip_csrf_meta_tags?
rescue_from ActionController::ParameterMissing, Paperclip::AdapterRegistry::NoHandlerError, with: :bad_request rescue_from ActionController::ParameterMissing, Paperclip::AdapterRegistry::NoHandlerError, with: :bad_request
@ -71,7 +70,13 @@ class ApplicationController < ActionController::Base
end end
def require_functional! def require_functional!
redirect_to edit_user_registration_path unless current_user.functional? return if current_user.functional?
if current_user.confirmed?
redirect_to edit_user_registration_path
else
redirect_to auth_setup_path
end
end end
def skip_csrf_meta_tags? def skip_csrf_meta_tags?
@ -158,10 +163,6 @@ class ApplicationController < ActionController::Base
current_user.setting_theme current_user.setting_theme
end end
def body_class_string
@body_classes || ''
end
def respond_with_error(code) def respond_with_error(code)
respond_to do |format| respond_to do |format|
format.any { render "errors/#{code}", layout: 'error', status: code, formats: [:html] } format.any { render "errors/#{code}", layout: 'error', status: code, formats: [:html] }

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