Merge commit 'f3864db4090b8cec212fb99c3dabccfb6a6c7642' into bark-prod
All checks were successful
continuous-integration/drone Build is passing

This commit is contained in:
Dalite 2023-12-11 19:13:06 +01:00
commit 63cdb40dce
722 changed files with 11670 additions and 6633 deletions

View file

@ -70,7 +70,7 @@ services:
hard: -1 hard: -1
libretranslate: libretranslate:
image: libretranslate/libretranslate:v1.4.1 image: libretranslate/libretranslate:v1.5.2
restart: unless-stopped restart: unless-stopped
volumes: volumes:
- lt-data:/home/libretranslate/.local - lt-data:/home/libretranslate/.local

View file

@ -24,4 +24,4 @@ RAILS_ENV=development ./bin/rails db:setup
RAILS_ENV=development ./bin/rails assets:precompile RAILS_ENV=development ./bin/rails assets:precompile
# Precompile assets for test # Precompile assets for test
RAILS_ENV=test NODE_ENV=tests ./bin/rails assets:precompile RAILS_ENV=test ./bin/rails assets:precompile

View file

@ -3,7 +3,7 @@ name: mastodon
type: kubernetes type: kubernetes
steps: steps:
- name: build-image - name: build-mastodon
image: plugins/docker image: plugins/docker
environment: environment:
DOCKER_BUILDKIT: 1 DOCKER_BUILDKIT: 1
@ -15,5 +15,19 @@ steps:
from_secret: registry_pass from_secret: registry_pass
repo: git.greyfox.tech/bark/mastodon repo: git.greyfox.tech/bark/mastodon
dockerfile: Dockerfile dockerfile: Dockerfile
tags:
- prod
- name: build-streaming
image: plugins/docker
environment:
DOCKER_BUILDKIT: 1
settings:
registry: git.greyfox.tech
username:
from_secret: registry_user
password:
from_secret: registry_pass
repo: git.greyfox.tech/bark/mastodon-streaming
dockerfile: streaming/Dockerfile
tags: tags:
- prod - prod

View file

@ -1,4 +1,7 @@
module.exports = { // @ts-check
const { defineConfig } = require('eslint-define-config');
module.exports = defineConfig({
root: true, root: true,
extends: [ extends: [
@ -117,7 +120,6 @@ module.exports = {
'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',
'react/no-deprecated': 'off', 'react/no-deprecated': 'off',
'react/no-unknown-property': 'off',
'react/react-in-jsx-scope': 'off', // not needed with new JSX transform 'react/react-in-jsx-scope': 'off', // not needed with new JSX transform
'react/self-closing-comp': 'error', 'react/self-closing-comp': 'error',
@ -193,6 +195,7 @@ module.exports = {
'error', 'error',
{ {
devDependencies: [ devDependencies: [
'.eslintrc.js',
'config/webpack/**', 'config/webpack/**',
'app/javascript/mastodon/performance.js', 'app/javascript/mastodon/performance.js',
'app/javascript/mastodon/test_setup.js', 'app/javascript/mastodon/test_setup.js',
@ -280,7 +283,6 @@ module.exports = {
'formatjs/no-id': 'off', // IDs are used for translation keys 'formatjs/no-id': 'off', // IDs are used for translation keys
'formatjs/no-invalid-icu': 'error', 'formatjs/no-invalid-icu': 'error',
'formatjs/no-literal-string-in-jsx': 'off', // Should be looked at, but mainly flagging punctuation outside of strings 'formatjs/no-literal-string-in-jsx': 'off', // Should be looked at, but mainly flagging punctuation outside of strings
'formatjs/no-multiple-plurals': 'off', // Only used by hashtag.jsx
'formatjs/no-multiple-whitespaces': 'error', 'formatjs/no-multiple-whitespaces': 'error',
'formatjs/no-offset': 'error', 'formatjs/no-offset': 'error',
'formatjs/no-useless-message': 'error', 'formatjs/no-useless-message': 'error',
@ -299,6 +301,7 @@ module.exports = {
overrides: [ overrides: [
{ {
files: [ files: [
'.eslintrc.js',
'*.config.js', '*.config.js',
'.*rc.js', '.*rc.js',
'ide-helper.js', 'ide-helper.js',
@ -349,7 +352,7 @@ module.exports = {
'@typescript-eslint/consistent-type-definitions': ['warn', 'interface'], '@typescript-eslint/consistent-type-definitions': ['warn', 'interface'],
'@typescript-eslint/consistent-type-exports': 'error', '@typescript-eslint/consistent-type-exports': 'error',
'@typescript-eslint/consistent-type-imports': 'error', '@typescript-eslint/consistent-type-imports': 'error',
"@typescript-eslint/prefer-nullish-coalescing": ['error', {ignorePrimitives: {boolean: true}}], "@typescript-eslint/prefer-nullish-coalescing": ['error', { ignorePrimitives: { boolean: true } }],
'jsdoc/require-jsdoc': 'off', 'jsdoc/require-jsdoc': 'off',
@ -372,14 +375,6 @@ module.exports = {
env: { env: {
jest: true, jest: true,
}, },
}, }
{
files: [
'streaming/**/*',
],
rules: {
'import/no-commonjs': 'off',
},
},
], ],
}; });

View file

@ -9,7 +9,7 @@ runs:
using: 'composite' using: 'composite'
steps: steps:
- name: Set up Node.js - name: Set up Node.js
uses: actions/setup-node@v3 uses: actions/setup-node@v4
with: with:
node-version-file: '.nvmrc' node-version-file: '.nvmrc'

13
.github/codecov.yml vendored Normal file
View file

@ -0,0 +1,13 @@
coverage:
status:
project:
default:
# Github status check is not blocking
informational: true
patch:
default:
# Github status check is not blocking
informational: true
comment:
# Only write a comment in PR if there are changes
require_changes: true

View file

@ -22,6 +22,7 @@
'react-hotkeys', // Requires code changes 'react-hotkeys', // Requires code changes
// Requires Webpacker upgrade or replacement // Requires Webpacker upgrade or replacement
'@svgr/webpack',
'@types/webpack', '@types/webpack',
'babel-loader', 'babel-loader',
'compression-webpack-plugin', 'compression-webpack-plugin',
@ -49,7 +50,6 @@
matchManagers: ['bundler'], matchManagers: ['bundler'],
matchPackageNames: [ matchPackageNames: [
'rack', // Needs to be synced with Rails version 'rack', // Needs to be synced with Rails version
'sprockets', // Requires manual upgrade https://github.com/rails/sprockets/blob/master/UPGRADING.md#guide-to-upgrading-from-sprockets-3x-to-4x
'strong_migrations', // Requires manual upgrade 'strong_migrations', // Requires manual upgrade
'sidekiq', // Requires manual upgrade 'sidekiq', // Requires manual upgrade
'sidekiq-unique-jobs', // Requires manual upgrades and sync with Sidekiq version 'sidekiq-unique-jobs', // Requires manual upgrades and sync with Sidekiq version

View file

@ -21,6 +21,8 @@ on:
type: string type: string
labels: labels:
type: string type: string
file_to_build:
type: string
jobs: jobs:
build-image: build-image:
@ -86,6 +88,7 @@ jobs:
- uses: docker/build-push-action@v5 - uses: docker/build-push-action@v5
with: with:
context: . context: .
file: ${{ inputs.file_to_build }}
build-args: | build-args: |
MASTODON_VERSION_PRERELEASE=${{ inputs.version_prerelease }} MASTODON_VERSION_PRERELEASE=${{ inputs.version_prerelease }}
MASTODON_VERSION_METADATA=${{ inputs.version_metadata }} MASTODON_VERSION_METADATA=${{ inputs.version_metadata }}

View file

@ -25,6 +25,7 @@ jobs:
needs: compute-suffix needs: compute-suffix
uses: ./.github/workflows/build-container-image.yml uses: ./.github/workflows/build-container-image.yml
with: with:
file_to_build: Dockerfile
platforms: linux/amd64,linux/arm64 platforms: linux/amd64,linux/arm64
use_native_arm64_builder: true use_native_arm64_builder: true
cache: false cache: false
@ -41,3 +42,25 @@ jobs:
type=raw,value=nightly type=raw,value=nightly
type=schedule,pattern=${{ needs.compute-suffix.outputs.prerelease }} type=schedule,pattern=${{ needs.compute-suffix.outputs.prerelease }}
secrets: inherit secrets: inherit
build-image-streaming:
needs: compute-suffix
uses: ./.github/workflows/build-container-image.yml
with:
file_to_build: streaming/Dockerfile
platforms: linux/amd64,linux/arm64
use_native_arm64_builder: true
cache: false
push_to_images: |
tootsuite/mastodon-streaming
ghcr.io/mastodon/mastodon-streaming
version_prerelease: ${{ needs.compute-suffix.outputs.prerelease }}
labels: |
org.opencontainers.image.description=Nightly build image used for testing purposes
flavor: |
latest=auto
tags: |
type=raw,value=edge
type=raw,value=nightly
type=schedule,pattern=${{ needs.compute-suffix.outputs.prerelease }}
secrets: inherit

View file

@ -29,6 +29,7 @@ jobs:
needs: compute-suffix needs: compute-suffix
uses: ./.github/workflows/build-container-image.yml uses: ./.github/workflows/build-container-image.yml
with: with:
file_to_build: Dockerfile
platforms: linux/amd64,linux/arm64 platforms: linux/amd64,linux/arm64
use_native_arm64_builder: true use_native_arm64_builder: true
push_to_images: | push_to_images: |
@ -39,3 +40,19 @@ jobs:
tags: | tags: |
type=ref,event=pr type=ref,event=pr
secrets: inherit secrets: inherit
build-image-streaming:
needs: compute-suffix
uses: ./.github/workflows/build-container-image.yml
with:
file_to_build: streaming/Dockerfile
platforms: linux/amd64,linux/arm64
use_native_arm64_builder: true
push_to_images: |
ghcr.io/mastodon/mastodon-streaming
version_metadata: ${{ needs.compute-suffix.outputs.metadata }}
flavor: |
latest=auto
tags: |
type=ref,event=pr
secrets: inherit

View file

@ -12,6 +12,7 @@ jobs:
build-image: build-image:
uses: ./.github/workflows/build-container-image.yml uses: ./.github/workflows/build-container-image.yml
with: with:
file_to_build: Dockerfile
platforms: linux/amd64,linux/arm64 platforms: linux/amd64,linux/arm64
use_native_arm64_builder: true use_native_arm64_builder: true
push_to_images: | push_to_images: |
@ -27,3 +28,24 @@ jobs:
type=pep440,pattern={{raw}} type=pep440,pattern={{raw}}
type=pep440,pattern=v{{major}}.{{minor}} type=pep440,pattern=v{{major}}.{{minor}}
secrets: inherit secrets: inherit
build-image-streaming:
if: startsWith(github.ref, 'refs/tags/v4.3.')
uses: ./.github/workflows/build-container-image.yml
with:
file_to_build: streaming/Dockerfile
platforms: linux/amd64,linux/arm64
use_native_arm64_builder: true
push_to_images: |
tootsuite/mastodon-streaming
ghcr.io/mastodon/mastodon-streaming
# Do not use cache when building releases, so apt update is always ran and the release always contain the latest packages
cache: false
# Only tag with latest when ran against the latest stable branch
# This needs to be updated after each minor version release
flavor: |
latest=${{ startsWith(github.ref, 'refs/tags/v4.3.') }}
tags: |
type=pep440,pattern={{raw}}
type=pep440,pattern=v{{major}}.{{minor}}
secrets: inherit

View file

@ -7,6 +7,7 @@ on:
- .github/workflows/build-releases.yml - .github/workflows/build-releases.yml
- .github/workflows/test-image-build.yml - .github/workflows/test-image-build.yml
- Dockerfile - Dockerfile
- streaming/Dockerfile
permissions: permissions:
contents: read contents: read
@ -18,4 +19,17 @@ jobs:
uses: ./.github/workflows/build-container-image.yml uses: ./.github/workflows/build-container-image.yml
with: with:
file_to_build: Dockerfile
platforms: linux/amd64 # Testing only on native platform so it is performant platforms: linux/amd64 # Testing only on native platform so it is performant
cache: true
build-image-streaming:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}-streaming
cancel-in-progress: true
uses: ./.github/workflows/build-container-image.yml
with:
file_to_build: streaming/Dockerfile
platforms: linux/amd64 # Testing only on native platform so it is performant
cache: true

View file

@ -94,7 +94,7 @@ jobs:
DB_HOST: localhost DB_HOST: localhost
DB_USER: postgres DB_USER: postgres
DB_PASS: postgres DB_PASS: postgres
DISABLE_SIMPLECOV: true DISABLE_SIMPLECOV: ${{ matrix.ruby-version != '.ruby-version' }}
RAILS_ENV: test RAILS_ENV: test
ALLOW_NOPAM: true ALLOW_NOPAM: true
PAM_ENABLED: true PAM_ENABLED: true
@ -137,6 +137,12 @@ jobs:
- run: bin/rspec - run: bin/rspec
- name: Upload coverage reports to Codecov
if: matrix.ruby-version == '.ruby-version'
uses: codecov/codecov-action@v3
with:
files: coverage/lcov/mastodon.lcov
test-e2e: test-e2e:
name: End to End testing name: End to End testing
runs-on: ubuntu-latest runs-on: ubuntu-latest
@ -221,7 +227,7 @@ jobs:
path: tmp/screenshots/ path: tmp/screenshots/
test-search: test-search:
name: Testing search name: Elastic Search integration testing
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: needs:
@ -308,7 +314,7 @@ jobs:
- name: Load database schema - name: Load database schema
run: './bin/rails db:create db:schema:load db:seed' run: './bin/rails db:create db:schema:load db:seed'
- run: bundle exec rake spec:search - run: bin/rspec --tag search
- name: Archive logs - name: Archive logs
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3

View file

@ -26,7 +26,7 @@ Lint/NonLocalExitFromIterator:
# Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes. # Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes.
Metrics/AbcSize: Metrics/AbcSize:
Max: 144 Max: 125
# Configuration parameters: CountBlocks, Max. # Configuration parameters: CountBlocks, Max.
Metrics/BlockNesting: Metrics/BlockNesting:
@ -41,23 +41,6 @@ Metrics/CyclomaticComplexity:
Metrics/PerceivedComplexity: Metrics/PerceivedComplexity:
Max: 27 Max: 27
RSpec/AnyInstance:
Exclude:
- 'spec/controllers/activitypub/inboxes_controller_spec.rb'
- 'spec/controllers/admin/accounts_controller_spec.rb'
- 'spec/controllers/admin/resets_controller_spec.rb'
- 'spec/controllers/auth/sessions_controller_spec.rb'
- 'spec/controllers/settings/two_factor_authentication/confirmations_controller_spec.rb'
- 'spec/controllers/settings/two_factor_authentication/recovery_codes_controller_spec.rb'
- 'spec/lib/request_spec.rb'
- 'spec/lib/status_filter_spec.rb'
- 'spec/models/account_spec.rb'
- 'spec/models/setting_spec.rb'
- 'spec/services/activitypub/process_collection_service_spec.rb'
- 'spec/validators/follow_limit_validator_spec.rb'
- 'spec/workers/activitypub/delivery_worker_spec.rb'
- 'spec/workers/web/push_notification_worker_spec.rb'
# Configuration parameters: CountAsOne. # Configuration parameters: CountAsOne.
RSpec/ExampleLength: RSpec/ExampleLength:
Max: 22 Max: 22
@ -124,25 +107,11 @@ Rails/ApplicationController:
# Include: app/models/**/*.rb # Include: app/models/**/*.rb
Rails/HasAndBelongsToMany: Rails/HasAndBelongsToMany:
Exclude: Exclude:
- 'app/models/concerns/account_associations.rb' - 'app/models/concerns/account/associations.rb'
- 'app/models/preview_card.rb' - 'app/models/preview_card.rb'
- 'app/models/status.rb' - 'app/models/status.rb'
- 'app/models/tag.rb' - 'app/models/tag.rb'
# Configuration parameters: Include.
# Include: app/models/**/*.rb
Rails/HasManyOrHasOneDependent:
Exclude:
- 'app/models/concerns/account_counters.rb'
- 'app/models/conversation.rb'
- 'app/models/custom_emoji.rb'
- 'app/models/custom_emoji_category.rb'
- 'app/models/domain_block.rb'
- 'app/models/invite.rb'
- 'app/models/status.rb'
- 'app/models/user.rb'
- 'app/models/web/push_subscription.rb'
# Configuration parameters: Include. # Configuration parameters: Include.
# Include: app/controllers/**/*.rb, app/mailers/**/*.rb # Include: app/controllers/**/*.rb, app/mailers/**/*.rb
Rails/LexicallyScopedActionFilter: Rails/LexicallyScopedActionFilter:
@ -189,7 +158,7 @@ Rails/SkipsModelValidations:
Exclude: Exclude:
- 'app/controllers/admin/invites_controller.rb' - 'app/controllers/admin/invites_controller.rb'
- 'app/controllers/concerns/session_tracking_concern.rb' - 'app/controllers/concerns/session_tracking_concern.rb'
- 'app/models/concerns/account_merging.rb' - 'app/models/concerns/account/merging.rb'
- 'app/models/concerns/expireable.rb' - 'app/models/concerns/expireable.rb'
- 'app/models/status.rb' - 'app/models/status.rb'
- 'app/models/trends/links.rb' - 'app/models/trends/links.rb'
@ -269,7 +238,7 @@ Rails/WhereExists:
- 'app/lib/feed_manager.rb' - 'app/lib/feed_manager.rb'
- 'app/lib/status_cache_hydrator.rb' - 'app/lib/status_cache_hydrator.rb'
- 'app/lib/suspicious_sign_in_detector.rb' - 'app/lib/suspicious_sign_in_detector.rb'
- 'app/models/concerns/account_interactions.rb' - 'app/models/concerns/account/interactions.rb'
- 'app/models/featured_tag.rb' - 'app/models/featured_tag.rb'
- 'app/models/poll.rb' - 'app/models/poll.rb'
- 'app/models/session_activation.rb' - 'app/models/session_activation.rb'
@ -324,7 +293,7 @@ Style/FetchEnvVar:
- 'config/initializers/devise.rb' - 'config/initializers/devise.rb'
- 'config/initializers/paperclip.rb' - 'config/initializers/paperclip.rb'
- 'config/initializers/vapid.rb' - 'config/initializers/vapid.rb'
- 'lib/mastodon/premailer_webpack_strategy.rb' - 'lib/premailer_webpack_strategy.rb'
- 'lib/mastodon/redis_config.rb' - 'lib/mastodon/redis_config.rb'
- 'lib/tasks/repo.rake' - 'lib/tasks/repo.rake'
- 'spec/features/profile_spec.rb' - 'spec/features/profile_spec.rb'
@ -359,8 +328,8 @@ Style/GuardClause:
- 'app/lib/request_pool.rb' - 'app/lib/request_pool.rb'
- 'app/lib/webfinger.rb' - 'app/lib/webfinger.rb'
- 'app/lib/webfinger_resource.rb' - 'app/lib/webfinger_resource.rb'
- 'app/models/concerns/account_counters.rb' - 'app/models/concerns/account/counters.rb'
- 'app/models/concerns/ldap_authenticable.rb' - 'app/models/concerns/user/ldap_authenticable.rb'
- 'app/models/tag.rb' - 'app/models/tag.rb'
- 'app/models/user.rb' - 'app/models/user.rb'
- 'app/services/fan_out_on_write_service.rb' - 'app/services/fan_out_on_write_service.rb'
@ -374,8 +343,8 @@ Style/GuardClause:
- 'config/initializers/devise.rb' - 'config/initializers/devise.rb'
- 'db/migrate/20170901141119_truncate_preview_cards.rb' - 'db/migrate/20170901141119_truncate_preview_cards.rb'
- 'db/post_migrate/20220704024901_migrate_settings_to_user_roles.rb' - 'db/post_migrate/20220704024901_migrate_settings_to_user_roles.rb'
- 'lib/devise/two_factor_ldap_authenticatable.rb' - 'lib/devise/strategies/two_factor_ldap_authenticatable.rb'
- 'lib/devise/two_factor_pam_authenticatable.rb' - 'lib/devise/strategies/two_factor_pam_authenticatable.rb'
- 'lib/mastodon/cli/accounts.rb' - 'lib/mastodon/cli/accounts.rb'
- 'lib/mastodon/cli/maintenance.rb' - 'lib/mastodon/cli/maintenance.rb'
- 'lib/mastodon/cli/media.rb' - 'lib/mastodon/cli/media.rb'
@ -389,8 +358,8 @@ Style/HashAsLastArrayItem:
Exclude: Exclude:
- 'app/controllers/admin/statuses_controller.rb' - 'app/controllers/admin/statuses_controller.rb'
- 'app/controllers/api/v1/statuses_controller.rb' - 'app/controllers/api/v1/statuses_controller.rb'
- 'app/models/concerns/account_counters.rb' - 'app/models/concerns/account/counters.rb'
- 'app/models/concerns/status_threading_concern.rb' - 'app/models/concerns/status/threading_concern.rb'
- 'app/models/status.rb' - 'app/models/status.rb'
- 'app/services/batched_remove_status_service.rb' - 'app/services/batched_remove_status_service.rb'
- 'app/services/notify_service.rb' - 'app/services/notify_service.rb'
@ -503,15 +472,15 @@ Style/RedundantReturn:
# AllowedMethods: present?, blank?, presence, try, try! # AllowedMethods: present?, blank?, presence, try, try!
Style/SafeNavigation: Style/SafeNavigation:
Exclude: Exclude:
- 'app/models/concerns/account_finder_concern.rb' - 'app/models/concerns/account/finder_concern.rb'
# This cop supports safe autocorrection (--autocorrect). # This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: EnforcedStyle. # Configuration parameters: EnforcedStyle.
# SupportedStyles: only_raise, only_fail, semantic # SupportedStyles: only_raise, only_fail, semantic
Style/SignalException: Style/SignalException:
Exclude: Exclude:
- 'lib/devise/two_factor_ldap_authenticatable.rb' - 'lib/devise/strategies/two_factor_ldap_authenticatable.rb'
- 'lib/devise/two_factor_pam_authenticatable.rb' - 'lib/devise/strategies/two_factor_pam_authenticatable.rb'
# This cop supports unsafe autocorrection (--autocorrect-all). # This cop supports unsafe autocorrection (--autocorrect-all).
Style/SingleArgumentDig: Style/SingleArgumentDig:

22
.simplecov Normal file
View file

@ -0,0 +1,22 @@
# frozen_string_literal: true
if ENV['CI']
require 'simplecov-lcov'
SimpleCov::Formatter::LcovFormatter.config.report_with_single_file = true
SimpleCov.formatter = SimpleCov::Formatter::LcovFormatter
else
SimpleCov.formatter = SimpleCov::Formatter::HTMLFormatter
end
SimpleCov.start 'rails' do
enable_coverage :branch
add_filter 'lib/linter'
add_group 'Libraries', 'lib'
add_group 'Policies', 'app/policies'
add_group 'Presenters', 'app/presenters'
add_group 'Serializers', 'app/serializers'
add_group 'Services', 'app/services'
add_group 'Validators', 'app/validators'
end

3
.watchmanconfig Normal file
View file

@ -0,0 +1,3 @@
{
"ignore_dirs": ["node_modules/", "public/"]
}

View file

@ -11,6 +11,10 @@ You can contribute in the following ways:
If your contributions are accepted into Mastodon, you can request to be paid through [our OpenCollective](https://opencollective.com/mastodon). If your contributions are accepted into Mastodon, you can request to be paid through [our OpenCollective](https://opencollective.com/mastodon).
## 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).
## 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](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.

View file

@ -1,111 +1,259 @@
# syntax=docker/dockerfile:1.4 # syntax=docker/dockerfile:1.4
# This needs to be bookworm-slim because the Ruby image is built on bookworm-slim
ARG NODE_VERSION="20.9-bookworm-slim"
FROM ghcr.io/moritzheiber/ruby-jemalloc:3.2.2-slim as ruby # Please see https://docs.docker.com/engine/reference/builder for information about
FROM node:${NODE_VERSION} as build # the extended buildx capabilities used in this file.
# Make sure multiarch TARGETPLATFORM is available for interpolation
# See: https://docs.docker.com/build/building/multi-platform/
ARG TARGETPLATFORM=${TARGETPLATFORM}
ARG BUILDPLATFORM=${BUILDPLATFORM}
COPY --link --from=ruby /opt/ruby /opt/ruby # Ruby image to use for base image, change with [--build-arg RUBY_VERSION="3.2.2"]
ARG RUBY_VERSION="3.2.2"
# # Node version to use in base image, change with [--build-arg NODE_MAJOR_VERSION="20"]
ARG NODE_MAJOR_VERSION="20"
# Debian image to use for base image, change with [--build-arg DEBIAN_VERSION="bookworm"]
ARG DEBIAN_VERSION="bookworm"
# Node 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
# Ruby image to use for base image based on combined variables (ex: 3.2.2-slim-bookworm)
FROM docker.io/ruby:${RUBY_VERSION}-slim-${DEBIAN_VERSION} as ruby
ENV DEBIAN_FRONTEND="noninteractive" \ # Resulting version string is vX.X.X-MASTODON_VERSION_PRERELEASE+MASTODON_VERSION_METADATA
PATH="${PATH}:/opt/ruby/bin" # Example: v4.2.0-nightly.2023.11.09+something
# Overwrite existance of 'alpha.0' in version.rb [--build-arg MASTODON_VERSION_PRERELEASE="nightly.2023.11.09"]
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
WORKDIR /opt/mastodon
# hadolint ignore=DL3008
RUN apt-get update && \
apt-get -yq dist-upgrade && \
apt-get install -y --no-install-recommends build-essential \
git \
libicu-dev \
libidn-dev \
libpq-dev \
libjemalloc-dev \
zlib1g-dev \
libgdbm-dev \
libgmp-dev \
libssl-dev \
libyaml-dev \
ca-certificates \
libreadline8 \
python3 \
shared-mime-info && \
bundle config set --local deployment 'true' && \
bundle config set --local without 'development test' && \
bundle config set silence_root_warning true && \
corepack enable
COPY Gemfile* package.json yarn.lock .yarnrc.yml /opt/mastodon/
COPY .yarn /opt/mastodon/.yarn
RUN bundle install -j"$(nproc)"
RUN yarn workspaces focus --all --production && \
yarn cache clean
FROM node:${NODE_VERSION}
# Use those args to specify your own version flags & suffixes
ARG MASTODON_VERSION_PRERELEASE="bark" ARG MASTODON_VERSION_PRERELEASE="bark"
# Append build metadata or fork information to version.rb [--build-arg MASTODON_VERSION_METADATA="something"]
ARG MASTODON_VERSION_METADATA="prod" ARG MASTODON_VERSION_METADATA="prod"
# Allow Ruby on Rails to serve static files
# See: https://docs.joinmastodon.org/admin/config/#rails_serve_static_files
ARG RAILS_SERVE_STATIC_FILES="true"
# Allow to use YJIT compiler
# See: https://github.com/ruby/ruby/blob/master/doc/yjit/yjit.md
ARG RUBY_YJIT_ENABLE="1"
# Timezone used by the Docker container and runtime, change with [--build-arg TZ=Europe/Berlin]
ARG TZ="Etc/UTC"
# Linux UID (user id) for the mastodon user, change with [--build-arg UID=1234]
ARG UID="991" ARG UID="991"
# Linux GID (group id) for the mastodon user, change with [--build-arg GID=1234]
ARG GID="991" ARG GID="991"
COPY --link --from=ruby /opt/ruby /opt/ruby # Apply Mastodon build options based on options above
ENV \
# Apply Mastodon version information
MASTODON_VERSION_PRERELEASE="${MASTODON_VERSION_PRERELEASE}" \
MASTODON_VERSION_METADATA="${MASTODON_VERSION_METADATA}" \
# Apply Mastodon static files and YJIT options
RAILS_SERVE_STATIC_FILES=${RAILS_SERVE_STATIC_FILES} \
RUBY_YJIT_ENABLE=${RUBY_YJIT_ENABLE} \
# Apply timezone
TZ=${TZ}
SHELL ["/bin/bash", "-o", "pipefail", "-c"] ENV \
# Configure the IP to bind Mastodon to when serving traffic
BIND="0.0.0.0" \
# Use production settings for Yarn, Node and related nodejs based tools
NODE_ENV="production" \
# Use production settings for Ruby on Rails
RAILS_ENV="production" \
# Add Ruby and Mastodon installation to the PATH
DEBIAN_FRONTEND="noninteractive" \
PATH="${PATH}:/opt/ruby/bin:/opt/mastodon/bin" \
# Optimize jemalloc 5.x performance
MALLOC_CONF="narenas:2,background_thread:true,thp:never,dirty_decay_ms:1000,muzzy_decay_ms:0"
ENV DEBIAN_FRONTEND="noninteractive" \ # Set default shell used for running commands
PATH="${PATH}:/opt/ruby/bin:/opt/mastodon/bin" SHELL ["/bin/bash", "-o", "pipefail", "-o", "errexit", "-c"]
# Ignoring these here since we don't want to pin any versions and the Debian image removes apt-get content after use ARG TARGETPLATFORM
# hadolint ignore=DL3008,DL3009
RUN apt-get update && \
echo "Etc/UTC" > /etc/localtime && \
groupadd -g "${GID}" mastodon && \
useradd -l -u "$UID" -g "${GID}" -m -d /opt/mastodon mastodon && \
apt-get -y --no-install-recommends install whois \
wget \
procps \
libssl3 \
libpq5 \
imagemagick \
ffmpeg \
libjemalloc2 \
libicu72 \
libidn12 \
libyaml-0-2 \
file \
ca-certificates \
tzdata \
libreadline8 \
tini && \
ln -s /opt/mastodon /mastodon && \
corepack enable
# Note: no, cleaning here since Debian does this automatically RUN echo "Target platform is $TARGETPLATFORM"
# See the file /etc/apt/apt.conf.d/docker-clean within the Docker image's filesystem
COPY --chown=mastodon:mastodon . /opt/mastodon RUN \
COPY --chown=mastodon:mastodon --from=build /opt/mastodon /opt/mastodon # Remove automatic apt cache Docker cleanup scripts
rm -f /etc/apt/apt.conf.d/docker-clean; \
# Sets timezone
echo "${TZ}" > /etc/localtime; \
# Creates mastodon user/group and sets home directory
groupadd -g "${GID}" mastodon; \
useradd -l -u "${UID}" -g "${GID}" -m -d /opt/mastodon mastodon; \
# Creates /mastodon symlink to /opt/mastodon
ln -s /opt/mastodon /mastodon;
ENV RAILS_ENV="production" \ # Set /opt/mastodon as working directory
NODE_ENV="production" \
RAILS_SERVE_STATIC_FILES="true" \
BIND="0.0.0.0" \
MASTODON_VERSION_PRERELEASE="${MASTODON_VERSION_PRERELEASE}" \
MASTODON_VERSION_METADATA="${MASTODON_VERSION_METADATA}"
# Set the run user
USER mastodon
WORKDIR /opt/mastodon WORKDIR /opt/mastodon
# Precompile assets # hadolint ignore=DL3008,DL3005
RUN OTP_SECRET=precompile_placeholder SECRET_KEY_BASE=precompile_placeholder rails assets:precompile RUN \
# Mount Apt cache and lib directories from Docker buildx caches
--mount=type=cache,id=apt-cache-${TARGETPLATFORM},target=/var/cache/apt,sharing=locked \
--mount=type=cache,id=apt-lib-${TARGETPLATFORM},target=/var/lib/apt,sharing=locked \
# Apt update & upgrade to check for security updates to Debian image
apt-get update; \
apt-get dist-upgrade -yq; \
# Install jemalloc, curl and other necessary components
apt-get install -y --no-install-recommends \
ca-certificates \
curl \
ffmpeg \
file \
imagemagick \
libjemalloc2 \
patchelf \
procps \
tini \
tzdata \
; \
# Patch Ruby to use jemalloc
patchelf --add-needed libjemalloc.so.2 /usr/local/bin/ruby; \
# Discard patchelf after use
apt-get purge -y \
patchelf \
;
# Set the work dir and the container entry point # Create temporary build layer from base image
ENTRYPOINT ["/usr/bin/tini", "--"] FROM ruby as build
EXPOSE 3000 4000
# 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
# hadolint ignore=DL3008
RUN \
# Mount Apt cache and lib directories from Docker buildx caches
--mount=type=cache,id=apt-cache-${TARGETPLATFORM},target=/var/cache/apt,sharing=locked \
--mount=type=cache,id=apt-lib-${TARGETPLATFORM},target=/var/lib/apt,sharing=locked \
# Install build tools and bundler dependencies from APT
apt-get install -y --no-install-recommends \
g++ \
gcc \
git \
libgdbm-dev \
libgmp-dev \
libicu-dev \
libidn-dev \
libpq-dev \
libssl-dev \
make \
shared-mime-info \
zlib1g-dev \
;
RUN \
# Configure Corepack
rm /usr/local/bin/yarn*; \
corepack enable; \
corepack prepare --activate;
# Create temporary bundler specific build layer from build layer
FROM build as bundler
ARG TARGETPLATFORM
# Copy Gemfile config into working directory
COPY Gemfile* /opt/mastodon/
RUN \
# Mount Ruby Gem caches
--mount=type=cache,id=gem-cache-${TARGETPLATFORM},target=/usr/local/bundle/cache/,sharing=locked \
# Configure bundle to prevent changes to Gemfile and Gemfile.lock
bundle config set --global frozen "true"; \
# Configure bundle to not cache downloaded Gems
bundle config set --global cache_all "false"; \
# Configure bundle to only process production Gems
bundle config set --local without "development test"; \
# Configure bundle to not warn about root user
bundle config set silence_root_warning "true"; \
# Download and install required Gems
bundle install -j"$(nproc)";
# Create temporary node specific build layer from build layer
FROM build as yarn
ARG TARGETPLATFORM
# Copy Node package configuration files into working directory
COPY package.json yarn.lock .yarnrc.yml /opt/mastodon/
COPY streaming/package.json /opt/mastodon/streaming/
COPY .yarn /opt/mastodon/.yarn
# hadolint ignore=DL3008
RUN \
--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 \
# Install Node packages
yarn workspaces focus --production @mastodon/mastodon;
# Create temporary assets build layer from build layer
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/
ARG TARGETPLATFORM
RUN \
# Use Ruby on Rails to create Mastodon assets
OTP_SECRET=precompile_placeholder SECRET_KEY_BASE=precompile_placeholder bundle exec rails assets:precompile; \
# Cleanup temporary files
rm -fr /opt/mastodon/tmp;
# Prep final Mastodon Ruby layer
FROM ruby as mastodon
ARG TARGETPLATFORM
# hadolint ignore=DL3008
RUN \
# Mount Apt cache and lib directories from Docker buildx caches
--mount=type=cache,id=apt-cache-${TARGETPLATFORM},target=/var/cache/apt,sharing=locked \
--mount=type=cache,id=apt-lib-${TARGETPLATFORM},target=/var/lib/apt,sharing=locked \
# Mount Corepack and Yarn caches from Docker buildx caches
--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 \
# Apt update install non-dev versions of necessary components
apt-get install -y --no-install-recommends \
libssl3 \
libpq5 \
libicu72 \
libidn12 \
libreadline8 \
libyaml-0-2 \
;
# Copy Mastodon sources into final layer
COPY . /opt/mastodon/
# Copy compiled assets to layer
COPY --from=precompiler /opt/mastodon/public/packs /opt/mastodon/public/packs
COPY --from=precompiler /opt/mastodon/public/assets /opt/mastodon/public/assets
# Copy bundler components to layer
COPY --from=bundler /usr/local/bundle/ /usr/local/bundle/
RUN \
# Precompile bootsnap code for faster Rails startup
bundle exec bootsnap precompile --gemfile app/ lib/;
RUN \
# Pre-create and chown system volume to Mastodon user
mkdir -p /opt/mastodon/public/system; \
chown mastodon:mastodon /opt/mastodon/public/system; \
# Set Mastodon user as owner of tmp folder
chown -R mastodon:mastodon /opt/mastodon/tmp;
# Set the running user for resulting container
USER mastodon
# Expose default Puma ports
EXPOSE 3000
# Set container tini as default entry point
ENTRYPOINT ["/usr/bin/tini", "--"]

13
Gemfile
View file

@ -5,10 +5,13 @@ ruby '>= 3.0.0'
gem 'puma', '~> 6.3' gem 'puma', '~> 6.3'
gem 'rails', '~> 7.1.1' gem 'rails', '~> 7.1.1'
gem 'sprockets', '~> 3.7.2' gem 'propshaft'
gem 'thor', '~> 1.2' gem 'thor', '~> 1.2'
gem 'rack', '~> 2.2.7' gem 'rack', '~> 2.2.7'
# For why irb is in the Gemfile, see: https://ruby.social/@st0012/111444685161478182
gem 'irb', '~> 1.8'
gem 'haml-rails', '~>2.0' gem 'haml-rails', '~>2.0'
gem 'pg', '~> 1.5' gem 'pg', '~> 1.5'
gem 'pghero' gem 'pghero'
@ -86,7 +89,6 @@ gem 'sidekiq-unique-jobs', '~> 7.1'
gem 'sidekiq-bulk', '~> 0.2.0' gem 'sidekiq-bulk', '~> 0.2.0'
gem 'simple-navigation', '~> 4.4' gem 'simple-navigation', '~> 4.4'
gem 'simple_form', '~> 5.2' gem 'simple_form', '~> 5.2'
gem 'sprockets-rails', '~> 3.4', require: 'sprockets/railtie'
gem 'stoplight', '~> 3.0.1' gem 'stoplight', '~> 3.0.1'
gem 'strong_migrations', '1.6.4' gem 'strong_migrations', '1.6.4'
gem 'tty-prompt', '~> 0.23', require: false gem 'tty-prompt', '~> 0.23', require: false
@ -109,6 +111,9 @@ group :test do
# RSpec progress bar formatter # RSpec progress bar formatter
gem 'fuubar', '~> 2.5' gem 'fuubar', '~> 2.5'
# RSpec helpers for email specs
gem 'email_spec'
# Extra RSpec extenion methods and helpers for sidekiq # Extra RSpec extenion methods and helpers for sidekiq
gem 'rspec-sidekiq', '~> 4.0' gem 'rspec-sidekiq', '~> 4.0'
@ -139,6 +144,7 @@ group :test do
# Coverage formatter for RSpec test if DISABLE_SIMPLECOV is false # Coverage formatter for RSpec test if DISABLE_SIMPLECOV is false
gem 'simplecov', '~> 0.22', require: false gem 'simplecov', '~> 0.22', require: false
gem 'simplecov-lcov', '~> 0.8', require: false
# Stub web requests for specs # Stub web requests for specs
gem 'webmock', '~> 3.18' gem 'webmock', '~> 3.18'
@ -175,6 +181,9 @@ group :development do
end end
group :development, :test do group :development, :test do
# Interactive Debugging tools
gem 'debug', '~> 1.8'
# Profiling tools # Profiling tools
gem 'memory_profiler', require: false gem 'memory_profiler', require: false
gem 'ruby-prof', require: false gem 'ruby-prof', require: false

View file

@ -39,50 +39,51 @@ GIT
GEM GEM
remote: https://rubygems.org/ remote: https://rubygems.org/
specs: specs:
actioncable (7.1.1) actioncable (7.1.2)
actionpack (= 7.1.1) actionpack (= 7.1.2)
activesupport (= 7.1.1) activesupport (= 7.1.2)
nio4r (~> 2.0) nio4r (~> 2.0)
websocket-driver (>= 0.6.1) websocket-driver (>= 0.6.1)
zeitwerk (~> 2.6) zeitwerk (~> 2.6)
actionmailbox (7.1.1) actionmailbox (7.1.2)
actionpack (= 7.1.1) actionpack (= 7.1.2)
activejob (= 7.1.1) activejob (= 7.1.2)
activerecord (= 7.1.1) activerecord (= 7.1.2)
activestorage (= 7.1.1) activestorage (= 7.1.2)
activesupport (= 7.1.1) activesupport (= 7.1.2)
mail (>= 2.7.1) mail (>= 2.7.1)
net-imap net-imap
net-pop net-pop
net-smtp net-smtp
actionmailer (7.1.1) actionmailer (7.1.2)
actionpack (= 7.1.1) actionpack (= 7.1.2)
actionview (= 7.1.1) actionview (= 7.1.2)
activejob (= 7.1.1) activejob (= 7.1.2)
activesupport (= 7.1.1) activesupport (= 7.1.2)
mail (~> 2.5, >= 2.5.4) mail (~> 2.5, >= 2.5.4)
net-imap net-imap
net-pop net-pop
net-smtp net-smtp
rails-dom-testing (~> 2.2) rails-dom-testing (~> 2.2)
actionpack (7.1.1) actionpack (7.1.2)
actionview (= 7.1.1) actionview (= 7.1.2)
activesupport (= 7.1.1) activesupport (= 7.1.2)
nokogiri (>= 1.8.5) nokogiri (>= 1.8.5)
racc
rack (>= 2.2.4) rack (>= 2.2.4)
rack-session (>= 1.0.1) rack-session (>= 1.0.1)
rack-test (>= 0.6.3) rack-test (>= 0.6.3)
rails-dom-testing (~> 2.2) rails-dom-testing (~> 2.2)
rails-html-sanitizer (~> 1.6) rails-html-sanitizer (~> 1.6)
actiontext (7.1.1) actiontext (7.1.2)
actionpack (= 7.1.1) actionpack (= 7.1.2)
activerecord (= 7.1.1) activerecord (= 7.1.2)
activestorage (= 7.1.1) activestorage (= 7.1.2)
activesupport (= 7.1.1) activesupport (= 7.1.2)
globalid (>= 0.6.0) globalid (>= 0.6.0)
nokogiri (>= 1.8.5) nokogiri (>= 1.8.5)
actionview (7.1.1) actionview (7.1.2)
activesupport (= 7.1.1) activesupport (= 7.1.2)
builder (~> 3.1) builder (~> 3.1)
erubi (~> 1.11) erubi (~> 1.11)
rails-dom-testing (~> 2.2) rails-dom-testing (~> 2.2)
@ -92,22 +93,22 @@ GEM
activemodel (>= 4.1) activemodel (>= 4.1)
case_transform (>= 0.2) case_transform (>= 0.2)
jsonapi-renderer (>= 0.1.1.beta1, < 0.3) jsonapi-renderer (>= 0.1.1.beta1, < 0.3)
activejob (7.1.1) activejob (7.1.2)
activesupport (= 7.1.1) activesupport (= 7.1.2)
globalid (>= 0.3.6) globalid (>= 0.3.6)
activemodel (7.1.1) activemodel (7.1.2)
activesupport (= 7.1.1) activesupport (= 7.1.2)
activerecord (7.1.1) activerecord (7.1.2)
activemodel (= 7.1.1) activemodel (= 7.1.2)
activesupport (= 7.1.1) activesupport (= 7.1.2)
timeout (>= 0.4.0) timeout (>= 0.4.0)
activestorage (7.1.1) activestorage (7.1.2)
actionpack (= 7.1.1) actionpack (= 7.1.2)
activejob (= 7.1.1) activejob (= 7.1.2)
activerecord (= 7.1.1) activerecord (= 7.1.2)
activesupport (= 7.1.1) activesupport (= 7.1.2)
marcel (~> 1.0) marcel (~> 1.0)
activesupport (7.1.1) activesupport (7.1.2)
base64 base64
bigdecimal bigdecimal
concurrent-ruby (~> 1.0, >= 1.0.2) concurrent-ruby (~> 1.0, >= 1.0.2)
@ -117,7 +118,7 @@ GEM
minitest (>= 5.1) minitest (>= 5.1)
mutex_m mutex_m
tzinfo (~> 2.0) tzinfo (~> 2.0)
addressable (2.8.5) addressable (2.8.6)
public_suffix (>= 2.0.2, < 6.0) public_suffix (>= 2.0.2, < 6.0)
aes_key_wrap (1.1.0) aes_key_wrap (1.1.0)
android_key_attestation (0.3.0) android_key_attestation (0.3.0)
@ -129,21 +130,21 @@ GEM
encryptor (~> 3.0.0) encryptor (~> 3.0.0)
attr_required (1.0.1) attr_required (1.0.1)
awrence (1.2.1) awrence (1.2.1)
aws-eventstream (1.2.0) aws-eventstream (1.3.0)
aws-partitions (1.828.0) aws-partitions (1.857.0)
aws-sdk-core (3.183.1) aws-sdk-core (3.188.0)
aws-eventstream (~> 1, >= 1.0.2) aws-eventstream (~> 1, >= 1.0.2)
aws-partitions (~> 1, >= 1.651.0) aws-partitions (~> 1, >= 1.651.0)
aws-sigv4 (~> 1.5) aws-sigv4 (~> 1.5)
jmespath (~> 1, >= 1.6.1) jmespath (~> 1, >= 1.6.1)
aws-sdk-kms (1.71.0) aws-sdk-kms (1.73.0)
aws-sdk-core (~> 3, >= 3.177.0) aws-sdk-core (~> 3, >= 3.188.0)
aws-sigv4 (~> 1.1) aws-sigv4 (~> 1.1)
aws-sdk-s3 (1.136.0) aws-sdk-s3 (1.140.0)
aws-sdk-core (~> 3, >= 3.181.0) aws-sdk-core (~> 3, >= 3.188.0)
aws-sdk-kms (~> 1) aws-sdk-kms (~> 1)
aws-sigv4 (~> 1.6) aws-sigv4 (~> 1.6)
aws-sigv4 (1.6.0) aws-sigv4 (1.7.0)
aws-eventstream (~> 1, >= 1.0.2) aws-eventstream (~> 1, >= 1.0.2)
azure-storage-blob (2.0.3) azure-storage-blob (2.0.3)
azure-storage-common (~> 2.0) azure-storage-common (~> 2.0)
@ -153,9 +154,9 @@ GEM
faraday_middleware (~> 1.0, >= 1.0.0.rc1) faraday_middleware (~> 1.0, >= 1.0.0.rc1)
net-http-persistent (~> 4.0) net-http-persistent (~> 4.0)
nokogiri (~> 1, >= 1.10.8) nokogiri (~> 1, >= 1.10.8)
base64 (0.1.1) base64 (0.2.0)
bcp47_spec (0.2.1) bcp47_spec (0.2.1)
bcrypt (3.1.19) bcrypt (3.1.20)
better_errors (2.10.1) better_errors (2.10.1)
erubi (>= 1.0.0) erubi (>= 1.0.0)
rack (>= 0.9.0) rack (>= 0.9.0)
@ -174,7 +175,7 @@ GEM
blurhash (0.1.7) blurhash (0.1.7)
bootsnap (1.17.0) bootsnap (1.17.0)
msgpack (~> 1.2) msgpack (~> 1.2)
brakeman (6.0.1) brakeman (6.1.0)
browser (5.3.1) browser (5.3.1)
brpoplpush-redis_script (0.1.3) brpoplpush-redis_script (0.1.3)
concurrent-ruby (~> 1.0, >= 1.0.5) concurrent-ruby (~> 1.0, >= 1.0.5)
@ -196,7 +197,7 @@ GEM
activesupport activesupport
cbor (0.5.9.6) cbor (0.5.9.6)
charlock_holmes (0.7.7) charlock_holmes (0.7.7)
chewy (7.3.4) chewy (7.3.5)
activesupport (>= 5.2) activesupport (>= 5.2)
elasticsearch (>= 7.12.0, < 7.14.0) elasticsearch (>= 7.12.0, < 7.14.0)
elasticsearch-dsl elasticsearch-dsl
@ -218,7 +219,10 @@ GEM
activerecord (>= 5.a) activerecord (>= 5.a)
database_cleaner-core (~> 2.0.0) database_cleaner-core (~> 2.0.0)
database_cleaner-core (2.0.1) database_cleaner-core (2.0.1)
date (3.3.3) date (3.3.4)
debug (1.9.0)
irb (~> 1.10)
reline (>= 0.3.8)
debug_inspector (1.1.0) debug_inspector (1.1.0)
devise (4.9.3) devise (4.9.3)
bcrypt (~> 3.0) bcrypt (~> 3.0)
@ -241,13 +245,13 @@ GEM
docile (1.4.0) docile (1.4.0)
domain_name (0.5.20190701) domain_name (0.5.20190701)
unf (>= 0.0.5, < 1.0.0) unf (>= 0.0.5, < 1.0.0)
doorkeeper (5.6.6) doorkeeper (5.6.8)
railties (>= 5) railties (>= 5)
dotenv (2.8.1) dotenv (2.8.1)
dotenv-rails (2.8.1) dotenv-rails (2.8.1)
dotenv (= 2.8.1) dotenv (= 2.8.1)
railties (>= 3.2) railties (>= 3.2)
drb (2.1.1) drb (2.2.0)
ruby2_keywords ruby2_keywords
ed25519 (1.3.0) ed25519 (1.3.0)
elasticsearch (7.13.3) elasticsearch (7.13.3)
@ -259,12 +263,16 @@ GEM
elasticsearch-transport (7.13.3) elasticsearch-transport (7.13.3)
faraday (~> 1) faraday (~> 1)
multi_json multi_json
email_spec (2.2.2)
htmlentities (~> 4.3.3)
launchy (~> 2.1)
mail (~> 2.7)
encryptor (3.0.0) encryptor (3.0.0)
erubi (1.12.0) erubi (1.12.0)
et-orbi (1.2.7) et-orbi (1.2.7)
tzinfo tzinfo
excon (0.104.0) excon (0.104.0)
fabrication (2.30.0) fabrication (2.31.0)
faker (3.2.2) faker (3.2.2)
i18n (>= 1.8.11, < 2) i18n (>= 1.8.11, < 2)
faraday (1.10.3) faraday (1.10.3)
@ -369,20 +377,20 @@ GEM
terminal-table (>= 1.5.1) terminal-table (>= 1.5.1)
idn-ruby (0.1.5) idn-ruby (0.1.5)
io-console (0.6.0) io-console (0.6.0)
irb (1.8.1) irb (1.10.1)
rdoc rdoc
reline (>= 0.3.8) reline (>= 0.3.8)
jmespath (1.6.2) jmespath (1.6.2)
json (2.6.3) json (2.7.0)
json-canonicalization (0.3.2) json-canonicalization (1.0.0)
json-jwt (1.15.3) json-jwt (1.15.3)
activesupport (>= 4.2) activesupport (>= 4.2)
aes_key_wrap aes_key_wrap
bindata bindata
httpclient httpclient
json-ld (3.3.0) json-ld (3.3.1)
htmlentities (~> 4.3) htmlentities (~> 4.3)
json-canonicalization (~> 0.3, >= 0.3.2) json-canonicalization (~> 1.0)
link_header (~> 0.0, >= 0.0.8) link_header (~> 0.0, >= 0.0.8)
multi_json (~> 1.15) multi_json (~> 1.15)
rack (>= 2.2, < 4) rack (>= 2.2, < 4)
@ -431,7 +439,7 @@ GEM
activesupport (>= 4) activesupport (>= 4)
railties (>= 4) railties (>= 4)
request_store (~> 1.0) request_store (~> 1.0)
loofah (2.21.4) loofah (2.22.0)
crass (~> 1.0.2) crass (~> 1.0.2)
nokogiri (>= 1.12.0) nokogiri (>= 1.12.0)
mail (2.8.1) mail (2.8.1)
@ -457,26 +465,27 @@ GEM
msgpack (1.7.2) msgpack (1.7.2)
multi_json (1.15.0) multi_json (1.15.0)
multipart-post (2.3.0) multipart-post (2.3.0)
mutex_m (0.1.2) mutex_m (0.2.0)
net-http (0.4.0) net-http (0.4.0)
uri uri
net-http-persistent (4.0.2) net-http-persistent (4.0.2)
connection_pool (~> 2.2) connection_pool (~> 2.2)
net-imap (0.4.1) net-imap (0.4.4)
date date
net-protocol net-protocol
net-ldap (0.18.0) net-ldap (0.18.0)
net-pop (0.1.2) net-pop (0.1.2)
net-protocol net-protocol
net-protocol (0.2.1) net-protocol (0.2.2)
timeout timeout
net-smtp (0.4.0) net-smtp (0.4.0)
net-protocol net-protocol
nio4r (2.5.9) nio4r (2.5.9)
nokogiri (1.15.4) nokogiri (1.15.5)
mini_portile2 (~> 2.8.2) mini_portile2 (~> 2.8.2)
racc (~> 1.4) racc (~> 1.4)
oj (3.16.1) oj (3.16.2)
bigdecimal (~> 3.1)
omniauth (2.1.1) omniauth (2.1.1)
hashie (>= 3.4.6) hashie (>= 3.4.6)
rack (>= 2.2.3) rack (>= 2.2.3)
@ -514,7 +523,7 @@ GEM
pastel (0.8.0) pastel (0.8.0)
tty-color (~> 0.5) tty-color (~> 0.5)
pg (1.5.4) pg (1.5.4)
pghero (3.3.4) pghero (3.4.0)
activerecord (>= 6) activerecord (>= 6)
posix-spawn (0.3.15) posix-spawn (0.3.15)
premailer (1.21.0) premailer (1.21.0)
@ -526,9 +535,14 @@ GEM
net-smtp net-smtp
premailer (~> 1.7, >= 1.7.9) premailer (~> 1.7, >= 1.7.9)
private_address_check (0.5.0) private_address_check (0.5.0)
psych (5.1.1) propshaft (0.8.0)
actionpack (>= 7.0.0)
activesupport (>= 7.0.0)
rack
railties (>= 7.0.0)
psych (5.1.1.1)
stringio stringio
public_suffix (5.0.3) public_suffix (5.0.4)
puma (6.4.0) puma (6.4.0)
nio4r (~> 2.0) nio4r (~> 2.0)
pundit (2.3.1) pundit (2.3.1)
@ -557,20 +571,20 @@ GEM
rackup (1.0.0) rackup (1.0.0)
rack (< 3) rack (< 3)
webrick webrick
rails (7.1.1) rails (7.1.2)
actioncable (= 7.1.1) actioncable (= 7.1.2)
actionmailbox (= 7.1.1) actionmailbox (= 7.1.2)
actionmailer (= 7.1.1) actionmailer (= 7.1.2)
actionpack (= 7.1.1) actionpack (= 7.1.2)
actiontext (= 7.1.1) actiontext (= 7.1.2)
actionview (= 7.1.1) actionview (= 7.1.2)
activejob (= 7.1.1) activejob (= 7.1.2)
activemodel (= 7.1.1) activemodel (= 7.1.2)
activerecord (= 7.1.1) activerecord (= 7.1.2)
activestorage (= 7.1.1) activestorage (= 7.1.2)
activesupport (= 7.1.1) activesupport (= 7.1.2)
bundler (>= 1.15.0) bundler (>= 1.15.0)
railties (= 7.1.1) railties (= 7.1.2)
rails-controller-testing (1.0.5) rails-controller-testing (1.0.5)
actionpack (>= 5.0.1.rc1) actionpack (>= 5.0.1.rc1)
actionview (>= 5.0.1.rc1) actionview (>= 5.0.1.rc1)
@ -585,22 +599,22 @@ GEM
rails-i18n (7.0.8) rails-i18n (7.0.8)
i18n (>= 0.7, < 2) i18n (>= 0.7, < 2)
railties (>= 6.0.0, < 8) railties (>= 6.0.0, < 8)
railties (7.1.1) railties (7.1.2)
actionpack (= 7.1.1) actionpack (= 7.1.2)
activesupport (= 7.1.1) activesupport (= 7.1.2)
irb irb
rackup (>= 1.0.0) rackup (>= 1.0.0)
rake (>= 12.2) rake (>= 12.2)
thor (~> 1.0, >= 1.2.2) thor (~> 1.0, >= 1.2.2)
zeitwerk (~> 2.6) zeitwerk (~> 2.6)
rainbow (3.1.1) rainbow (3.1.1)
rake (13.0.6) rake (13.1.0)
rdf (3.3.1) rdf (3.3.1)
bcp47_spec (~> 0.2) bcp47_spec (~> 0.2)
link_header (~> 0.0, >= 0.0.8) link_header (~> 0.0, >= 0.0.8)
rdf-normalize (0.6.1) rdf-normalize (0.6.1)
rdf (~> 3.2) rdf (~> 3.2)
rdoc (6.5.0) rdoc (6.6.1)
psych (>= 4.0.0) psych (>= 4.0.0)
redcarpet (3.6.0) redcarpet (3.6.0)
redis (4.8.1) redis (4.8.1)
@ -609,7 +623,7 @@ GEM
redlock (1.3.2) redlock (1.3.2)
redis (>= 3.0.0, < 6.0) redis (>= 3.0.0, < 6.0)
regexp_parser (2.8.2) regexp_parser (2.8.2)
reline (0.3.9) reline (0.4.1)
io-console (~> 0.5) io-console (~> 0.5)
request_store (1.5.1) request_store (1.5.1)
rack (>= 1.4) rack (>= 1.4)
@ -634,7 +648,7 @@ GEM
rspec-mocks (3.12.6) rspec-mocks (3.12.6)
diff-lcs (>= 1.2.0, < 2.0) diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.12.0) rspec-support (~> 3.12.0)
rspec-rails (6.0.3) rspec-rails (6.1.0)
actionpack (>= 6.1) actionpack (>= 6.1)
activesupport (>= 6.1) activesupport (>= 6.1)
railties (>= 6.1) railties (>= 6.1)
@ -648,7 +662,7 @@ GEM
rspec-mocks (~> 3.0) rspec-mocks (~> 3.0)
sidekiq (>= 5, < 8) sidekiq (>= 5, < 8)
rspec-support (3.12.1) rspec-support (3.12.1)
rubocop (1.57.2) rubocop (1.58.0)
json (~> 2.3) json (~> 2.3)
language_server-protocol (>= 3.17.0) language_server-protocol (>= 3.17.0)
parallel (~> 1.10) parallel (~> 1.10)
@ -656,7 +670,7 @@ GEM
rainbow (>= 2.2.2, < 4.0) rainbow (>= 2.2.2, < 4.0)
regexp_parser (>= 1.8, < 3.0) regexp_parser (>= 1.8, < 3.0)
rexml (>= 3.2.5, < 4.0) rexml (>= 3.2.5, < 4.0)
rubocop-ast (>= 1.28.1, < 2.0) rubocop-ast (>= 1.30.0, < 2.0)
ruby-progressbar (~> 1.7) ruby-progressbar (~> 1.7)
unicode-display_width (>= 2.4.0, < 3.0) unicode-display_width (>= 2.4.0, < 3.0)
rubocop-ast (1.30.0) rubocop-ast (1.30.0)
@ -668,10 +682,11 @@ GEM
rubocop-performance (1.19.1) rubocop-performance (1.19.1)
rubocop (>= 1.7.0, < 2.0) rubocop (>= 1.7.0, < 2.0)
rubocop-ast (>= 0.4.0) rubocop-ast (>= 0.4.0)
rubocop-rails (2.22.1) rubocop-rails (2.22.2)
activesupport (>= 4.2.0) activesupport (>= 4.2.0)
rack (>= 1.1) rack (>= 1.1)
rubocop (>= 1.33.0, < 2.0) rubocop (>= 1.33.0, < 2.0)
rubocop-ast (>= 1.30.0, < 2.0)
rubocop-rspec (2.25.0) rubocop-rspec (2.25.0)
rubocop (~> 1.40) rubocop (~> 1.40)
rubocop-capybara (~> 2.17) rubocop-capybara (~> 2.17)
@ -693,7 +708,7 @@ GEM
scenic (1.7.0) scenic (1.7.0)
activerecord (>= 4.0.0) activerecord (>= 4.0.0)
railties (>= 4.0.0) railties (>= 4.0.0)
selenium-webdriver (4.15.0) selenium-webdriver (4.16.0)
rexml (~> 3.2, >= 3.2.5) rexml (~> 3.2, >= 3.2.5)
rubyzip (>= 1.2.2, < 3.0) rubyzip (>= 1.2.2, < 3.0)
websocket (~> 1.0) websocket (~> 1.0)
@ -724,20 +739,14 @@ GEM
simplecov-html (~> 0.11) simplecov-html (~> 0.11)
simplecov_json_formatter (~> 0.1) simplecov_json_formatter (~> 0.1)
simplecov-html (0.12.3) simplecov-html (0.12.3)
simplecov-lcov (0.8.0)
simplecov_json_formatter (0.1.4) simplecov_json_formatter (0.1.4)
smart_properties (1.17.0) smart_properties (1.17.0)
sprockets (3.7.2)
concurrent-ruby (~> 1.0)
rack (> 1, < 3)
sprockets-rails (3.4.2)
actionpack (>= 5.2)
activesupport (>= 5.2)
sprockets (>= 3.0.0)
stackprof (0.2.25) stackprof (0.2.25)
statsd-ruby (1.5.0) statsd-ruby (1.5.0)
stoplight (3.0.2) stoplight (3.0.2)
redlock (~> 1.0) redlock (~> 1.0)
stringio (3.0.8) stringio (3.1.0)
strong_migrations (1.6.4) strong_migrations (1.6.4)
activerecord (>= 5.2) activerecord (>= 5.2)
swd (1.3.0) swd (1.3.0)
@ -745,15 +754,15 @@ GEM
attr_required (>= 0.0.5) attr_required (>= 0.0.5)
httpclient (>= 2.4) httpclient (>= 2.4)
sysexits (1.2.0) sysexits (1.2.0)
temple (0.10.2) temple (0.10.3)
terminal-table (3.0.2) terminal-table (3.0.2)
unicode-display_width (>= 1.1.1, < 3) unicode-display_width (>= 1.1.1, < 3)
terrapin (0.6.0) terrapin (0.6.0)
climate_control (>= 0.0.3, < 1.0) climate_control (>= 0.0.3, < 1.0)
test-prof (1.2.3) test-prof (1.3.0)
thor (1.3.0) thor (1.3.0)
tilt (2.3.0) tilt (2.3.0)
timeout (0.4.0) timeout (0.4.1)
tpm-key_attestation (0.12.0) tpm-key_attestation (0.12.0)
bindata (~> 2.4) bindata (~> 2.4)
openssl (> 2.0) openssl (> 2.0)
@ -844,6 +853,7 @@ DEPENDENCIES
concurrent-ruby concurrent-ruby
connection_pool connection_pool
database_cleaner-active_record database_cleaner-active_record
debug (~> 1.8)
devise (~> 4.9) devise (~> 4.9)
devise-two-factor (~> 4.1) devise-two-factor (~> 4.1)
devise_pam_authenticatable2 (~> 9.2) devise_pam_authenticatable2 (~> 9.2)
@ -851,6 +861,7 @@ DEPENDENCIES
doorkeeper (~> 5.6) doorkeeper (~> 5.6)
dotenv-rails (~> 2.8) dotenv-rails (~> 2.8)
ed25519 (~> 1.3) ed25519 (~> 1.3)
email_spec
fabrication (~> 2.30) fabrication (~> 2.30)
faker (~> 3.2) faker (~> 3.2)
fast_blank (~> 1.0) fast_blank (~> 1.0)
@ -868,6 +879,7 @@ DEPENDENCIES
httplog (~> 1.6.2) httplog (~> 1.6.2)
i18n-tasks (~> 1.0) i18n-tasks (~> 1.0)
idn-ruby idn-ruby
irb (~> 1.8)
json-ld json-ld
json-ld-preloaded (~> 3.2) json-ld-preloaded (~> 3.2)
json-schema (~> 4.0) json-schema (~> 4.0)
@ -898,6 +910,7 @@ DEPENDENCIES
posix-spawn posix-spawn
premailer-rails premailer-rails
private_address_check (~> 0.5) private_address_check (~> 0.5)
propshaft
public_suffix (~> 5.0) public_suffix (~> 5.0)
puma (~> 6.3) puma (~> 6.3)
pundit (~> 2.3) pundit (~> 2.3)
@ -935,8 +948,7 @@ DEPENDENCIES
simple-navigation (~> 4.4) simple-navigation (~> 4.4)
simple_form (~> 5.2) simple_form (~> 5.2)
simplecov (~> 0.22) simplecov (~> 0.22)
sprockets (~> 3.7.2) simplecov-lcov (~> 0.8)
sprockets-rails (~> 3.4)
stackprof stackprof
stoplight (~> 3.0.1) stoplight (~> 3.0.1)
strong_migrations (= 1.6.4) strong_migrations (= 1.6.4)

View file

@ -1,4 +1,4 @@
web: env PORT=3000 RAILS_ENV=development bundle exec puma -C config/puma.rb web: env PORT=3000 RAILS_ENV=development bundle exec puma -C config/puma.rb
sidekiq: env PORT=3000 RAILS_ENV=development bundle exec sidekiq sidekiq: env PORT=3000 RAILS_ENV=development bundle exec sidekiq
stream: env PORT=4000 yarn run start stream: env PORT=4000 yarn workspace @mastodon/streaming start
webpack: bin/webpack-dev-server webpack: bin/webpack-dev-server

View file

@ -94,6 +94,7 @@ To set up **MacOS** for native development, complete the following steps:
- Run `brew install postgresql@14` - Run `brew install postgresql@14`
- Run `brew install redis` - Run `brew install redis`
- Run `brew install imagemagick` - Run `brew install imagemagick`
- Run `brew install libidn`
- Install Foreman or a similar tool (such as [overmind](https://github.com/DarthSim/overmind)) to handle multiple process launching. - Install Foreman or a similar tool (such as [overmind](https://github.com/DarthSim/overmind)) to handle multiple process launching.
- Navigate to Mastodon's root directory and run `brew install nvm` then `nvm use` to use the version from .nvmrc - Navigate to Mastodon's root directory and run `brew install nvm` then `nvm use` to use the version from .nvmrc
- Run `corepack enable && corepack prepare` - Run `corepack enable && corepack prepare`

6
Vagrantfile vendored
View file

@ -10,7 +10,11 @@ curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
sudo apt-add-repository 'deb https://dl.yarnpkg.com/debian/ stable main' sudo apt-add-repository 'deb https://dl.yarnpkg.com/debian/ stable main'
# Add repo for NodeJS # Add repo for NodeJS
curl -sL https://deb.nodesource.com/setup_16.x | sudo bash - sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | sudo gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg
NODE_MAJOR=20
echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$NODE_MAJOR.x nodistro main" | sudo tee /etc/apt/sources.list.d/nodesource.list
sudo apt-get update
# Add firewall rule to redirect 80 to PORT and save # Add firewall rule to redirect 80 to PORT and save
sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port #{ENV["PORT"]} sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port #{ENV["PORT"]}

View file

@ -1,6 +1,8 @@
# frozen_string_literal: true # frozen_string_literal: true
class AccountsIndex < Chewy::Index class AccountsIndex < Chewy::Index
include DatetimeClampingConcern
settings index: index_preset(refresh_interval: '30s'), analysis: { settings index: index_preset(refresh_interval: '30s'), analysis: {
filter: { filter: {
english_stop: { english_stop: {
@ -60,7 +62,7 @@ class AccountsIndex < Chewy::Index
field(:following_count, type: 'long') field(:following_count, type: 'long')
field(:followers_count, type: 'long') field(:followers_count, type: 'long')
field(:properties, type: 'keyword', value: ->(account) { account.searchable_properties }) field(:properties, type: 'keyword', value: ->(account) { account.searchable_properties })
field(:last_status_at, type: 'date', value: ->(account) { account.last_status_at || account.created_at }) field(:last_status_at, type: 'date', value: ->(account) { clamp_date(account.last_status_at || account.created_at) })
field(:display_name, type: 'text', analyzer: 'verbatim') { field :edge_ngram, type: 'text', analyzer: 'edge_ngram', search_analyzer: 'verbatim' } field(:display_name, type: 'text', analyzer: 'verbatim') { field :edge_ngram, type: 'text', analyzer: 'edge_ngram', search_analyzer: 'verbatim' }
field(:username, type: 'text', analyzer: 'verbatim', value: ->(account) { [account.username, account.domain].compact.join('@') }) { field :edge_ngram, type: 'text', analyzer: 'edge_ngram', search_analyzer: 'verbatim' } field(:username, type: 'text', analyzer: 'verbatim', value: ->(account) { [account.username, account.domain].compact.join('@') }) { field :edge_ngram, type: 'text', analyzer: 'edge_ngram', search_analyzer: 'verbatim' }
field(:text, type: 'text', analyzer: 'verbatim', value: ->(account) { account.searchable_text }) { field :stemmed, type: 'text', analyzer: 'natural' } field(:text, type: 'text', analyzer: 'verbatim', value: ->(account) { account.searchable_text }) { field :stemmed, type: 'text', analyzer: 'natural' }

View file

@ -0,0 +1,14 @@
# frozen_string_literal: true
module DatetimeClampingConcern
extend ActiveSupport::Concern
MIN_ISO8601_DATETIME = '0000-01-01T00:00:00Z'.to_datetime.freeze
MAX_ISO8601_DATETIME = '9999-12-31T23:59:59Z'.to_datetime.freeze
class_methods do
def clamp_date(datetime)
datetime.clamp(MIN_ISO8601_DATETIME, MAX_ISO8601_DATETIME)
end
end
end

View file

@ -1,6 +1,8 @@
# frozen_string_literal: true # frozen_string_literal: true
class PublicStatusesIndex < Chewy::Index class PublicStatusesIndex < Chewy::Index
include DatetimeClampingConcern
settings index: index_preset(refresh_interval: '30s', number_of_shards: 5), analysis: { settings index: index_preset(refresh_interval: '30s', number_of_shards: 5), analysis: {
filter: { filter: {
english_stop: { english_stop: {
@ -53,7 +55,7 @@ class PublicStatusesIndex < Chewy::Index
index_scope ::Status.unscoped index_scope ::Status.unscoped
.kept .kept
.indexable .indexable
.includes(:media_attachments, :preloadable_poll, :preview_cards, :tags) .includes(:media_attachments, :preloadable_poll, :tags, preview_cards_status: :preview_card)
root date_detection: false do root date_detection: false do
field(:id, type: 'long') field(:id, type: 'long')
@ -62,6 +64,6 @@ class PublicStatusesIndex < Chewy::Index
field(:tags, type: 'text', analyzer: 'hashtag', value: ->(status) { status.tags.map(&:display_name) }) field(:tags, type: 'text', analyzer: 'hashtag', value: ->(status) { status.tags.map(&:display_name) })
field(:language, type: 'keyword') field(:language, type: 'keyword')
field(:properties, type: 'keyword', value: ->(status) { status.searchable_properties }) field(:properties, type: 'keyword', value: ->(status) { status.searchable_properties })
field(:created_at, type: 'date') field(:created_at, type: 'date', value: ->(status) { clamp_date(status.created_at) })
end end
end end

View file

@ -1,6 +1,8 @@
# frozen_string_literal: true # frozen_string_literal: true
class StatusesIndex < Chewy::Index class StatusesIndex < Chewy::Index
include DatetimeClampingConcern
settings index: index_preset(refresh_interval: '30s', number_of_shards: 5), analysis: { settings index: index_preset(refresh_interval: '30s', number_of_shards: 5), analysis: {
filter: { filter: {
english_stop: { english_stop: {
@ -50,7 +52,7 @@ class StatusesIndex < Chewy::Index
}, },
} }
index_scope ::Status.unscoped.kept.without_reblogs.includes(:media_attachments, :preview_cards, :local_mentioned, :local_favorited, :local_reblogged, :local_bookmarked, :tags, preloadable_poll: :local_voters), delete_if: ->(status) { status.searchable_by.empty? } index_scope ::Status.unscoped.kept.without_reblogs.includes(:media_attachments, :local_mentioned, :local_favorited, :local_reblogged, :local_bookmarked, :tags, preview_cards_status: :preview_card, preloadable_poll: :local_voters), delete_if: ->(status) { status.searchable_by.empty? }
root date_detection: false do root date_detection: false do
field(:id, type: 'long') field(:id, type: 'long')
@ -60,6 +62,6 @@ class StatusesIndex < Chewy::Index
field(:searchable_by, type: 'long', value: ->(status) { status.searchable_by }) field(:searchable_by, type: 'long', value: ->(status) { status.searchable_by })
field(:language, type: 'keyword') field(:language, type: 'keyword')
field(:properties, type: 'keyword', value: ->(status) { status.searchable_properties }) field(:properties, type: 'keyword', value: ->(status) { status.searchable_properties })
field(:created_at, type: 'date') field(:created_at, type: 'date', value: ->(status) { clamp_date(status.created_at) })
end end
end end

View file

@ -1,6 +1,8 @@
# frozen_string_literal: true # frozen_string_literal: true
class TagsIndex < Chewy::Index class TagsIndex < Chewy::Index
include DatetimeClampingConcern
settings index: index_preset(refresh_interval: '30s'), analysis: { settings index: index_preset(refresh_interval: '30s'), analysis: {
analyzer: { analyzer: {
content: { content: {
@ -42,6 +44,6 @@ class TagsIndex < Chewy::Index
field(:name, type: 'text', analyzer: 'content', value: :display_name) { field(:edge_ngram, type: 'text', analyzer: 'edge_ngram', search_analyzer: 'content') } field(:name, type: 'text', analyzer: 'content', value: :display_name) { field(:edge_ngram, type: 'text', analyzer: 'edge_ngram', search_analyzer: 'content') }
field(:reviewed, type: 'boolean', value: ->(tag) { tag.reviewed? }) field(:reviewed, type: 'boolean', value: ->(tag) { tag.reviewed? })
field(:usage, type: 'long', value: ->(tag, crutches) { tag.history.aggregate(crutches.time_period).accounts }) field(:usage, type: 'long', value: ->(tag, crutches) { tag.history.aggregate(crutches.time_period).accounts })
field(:last_status_at, type: 'date', value: ->(tag) { tag.last_status_at || tag.created_at }) field(:last_status_at, type: 'date', value: ->(tag) { clamp_date(tag.last_status_at || tag.created_at) })
end end
end end

View file

@ -18,8 +18,6 @@ class AccountsController < ApplicationController
respond_to do |format| respond_to do |format|
format.html do format.html do
expires_in(15.seconds, public: true, stale_while_revalidate: 30.seconds, stale_if_error: 1.hour) unless user_signed_in? expires_in(15.seconds, public: true, stale_while_revalidate: 30.seconds, stale_if_error: 1.hour) unless user_signed_in?
@rss_url = rss_url
end end
format.rss do format.rss do
@ -52,7 +50,7 @@ class AccountsController < ApplicationController
end end
def only_media_scope def only_media_scope
Status.joins(:media_attachments).merge(@account.media_attachments.reorder(nil)).group(:id) Status.joins(:media_attachments).merge(@account.media_attachments).group(:id)
end end
def no_replies_scope def no_replies_scope
@ -84,29 +82,21 @@ class AccountsController < ApplicationController
short_account_url(@account, format: 'rss') short_account_url(@account, format: 'rss')
end end
end end
helper_method :rss_url
def media_requested? def media_requested?
request.path.split('.').first.end_with?('/media') && !tag_requested? path_without_format.end_with?('/media') && !tag_requested?
end end
def replies_requested? def replies_requested?
request.path.split('.').first.end_with?('/with_replies') && !tag_requested? path_without_format.end_with?('/with_replies') && !tag_requested?
end end
def tag_requested? def tag_requested?
request.path.split('.').first.end_with?(Addressable::URI.parse("/tagged/#{params[:tag]}").normalize) path_without_format.end_with?(Addressable::URI.parse("/tagged/#{params[:tag]}").normalize)
end end
def cached_filtered_status_page def path_without_format
cache_collection_paginated_by_id( request.path.split('.').first
filtered_statuses,
Status,
PAGE_SIZE,
params_slice(:max_id, :min_id, :since_id)
)
end
def params_slice(*keys)
params.slice(*keys).permit(*keys)
end end
end end

View file

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

View file

@ -6,7 +6,7 @@ module Admin
def index def index
authorize :audit_log, :index? authorize :audit_log, :index?
@auditable_accounts = Account.where(id: Admin::ActionLog.reorder(nil).select('distinct account_id')).select(:id, :username) @auditable_accounts = Account.where(id: Admin::ActionLog.select('distinct account_id')).select(:id, :username)
end end
private private

View file

@ -4,7 +4,7 @@ require 'csv'
module Admin module Admin
class ExportDomainAllowsController < BaseController class ExportDomainAllowsController < BaseController
include AdminExportControllerConcern include Admin::ExportControllerConcern
before_action :set_dummy_import!, only: [:new] before_action :set_dummy_import!, only: [:new]

View file

@ -4,7 +4,7 @@ require 'csv'
module Admin module Admin
class ExportDomainBlocksController < BaseController class ExportDomainBlocksController < BaseController
include AdminExportControllerConcern include Admin::ExportControllerConcern
before_action :set_dummy_import!, only: [:new] before_action :set_dummy_import!, only: [:new]

View file

@ -24,7 +24,7 @@ module Admin
@relay.enable! @relay.enable!
redirect_to admin_relays_path redirect_to admin_relays_path
else else
render action: :new render :new
end end
end end

View file

@ -26,7 +26,7 @@ module Admin
@form = Admin::StatusBatchAction.new @form = Admin::StatusBatchAction.new
@statuses = @report.statuses.with_includes @statuses = @report.statuses.with_includes
render template: 'admin/reports/show' render 'admin/reports/show'
end end
end end

View file

@ -32,7 +32,7 @@ module Admin
private private
def batched_ordered_status_edits def batched_ordered_status_edits
@status.edits.reorder(nil).includes(:account, status: [:account]).find_each(order: :asc) @status.edits.includes(:account, status: [:account]).find_each(order: :asc)
end end
helper_method :batched_ordered_status_edits helper_method :batched_ordered_status_edits

View file

@ -4,9 +4,10 @@ class Api::BaseController < ApplicationController
DEFAULT_STATUSES_LIMIT = 20 DEFAULT_STATUSES_LIMIT = 20
DEFAULT_ACCOUNTS_LIMIT = 40 DEFAULT_ACCOUNTS_LIMIT = 40
include RateLimitHeaders include Api::RateLimitHeaders
include AccessTokenTrackingConcern include Api::AccessTokenTrackingConcern
include ApiCachingConcern include Api::CachingConcern
include Api::ContentSecurityPolicy
skip_before_action :require_functional!, unless: :limited_federation_mode? skip_before_action :require_functional!, unless: :limited_federation_mode?
@ -17,26 +18,6 @@ class Api::BaseController < ApplicationController
protect_from_forgery with: :null_session protect_from_forgery with: :null_session
content_security_policy do |p|
# Set every directive that does not have a fallback
p.default_src :none
p.frame_ancestors :none
p.form_action :none
# Disable every directive with a fallback to cut on response size
p.base_uri false
p.font_src false
p.img_src false
p.style_src false
p.media_src false
p.frame_src false
p.manifest_src false
p.connect_src false
p.script_src false
p.child_src false
p.worker_src false
end
rescue_from ActiveRecord::RecordInvalid, Mastodon::ValidationError do |e| rescue_from ActiveRecord::RecordInvalid, Mastodon::ValidationError do |e|
render json: { error: e.to_s }, status: 422 render json: { error: e.to_s }, status: 422
end end
@ -83,7 +64,7 @@ class Api::BaseController < ApplicationController
end end
def doorkeeper_unauthorized_render_options(error: nil) def doorkeeper_unauthorized_render_options(error: nil)
{ json: { error: (error.try(:description) || 'Not authorized') } } { json: { error: error.try(:description) || 'Not authorized' } }
end end
def doorkeeper_forbidden_render_options(*) def doorkeeper_forbidden_render_options(*)
@ -124,7 +105,7 @@ class Api::BaseController < ApplicationController
end end
def require_not_suspended! def require_not_suspended!
render json: { error: 'Your login is currently disabled' }, status: 403 if current_user&.account&.suspended? render json: { error: 'Your login is currently disabled' }, status: 403 if current_user&.account&.unavailable?
end end
def require_user! def require_user!

View file

@ -16,6 +16,8 @@ class Api::V1::Accounts::CredentialsController < Api::BaseController
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_async(@account.id)
render json: @account, serializer: REST::CredentialAccountSerializer render json: @account, serializer: REST::CredentialAccountSerializer
rescue ActiveRecord::RecordInvalid => e
render json: ValidationErrorFormatter.new(e).as_json, status: 422
end end
private private

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').index_by(&:id).values_at(*account_ids).compact @accounts = Account.without_suspended.where(id: account_ids).select('id, hide_collections')
end end
def familiar_followers def familiar_followers

View file

@ -26,7 +26,7 @@ class Api::V1::Accounts::FollowerAccountsController < Api::BaseController
end end
def hide_results? def hide_results?
@account.suspended? || (@account.hides_followers? && current_account&.id != @account.id) || (current_account && @account.blocking?(current_account)) @account.unavailable? || (@account.hides_followers? && current_account&.id != @account.id) || (current_account && @account.blocking?(current_account))
end end
def default_accounts def default_accounts

View file

@ -26,7 +26,7 @@ class Api::V1::Accounts::FollowingAccountsController < Api::BaseController
end end
def hide_results? def hide_results?
@account.suspended? || (@account.hides_following? && current_account&.id != @account.id) || (current_account && @account.blocking?(current_account)) @account.unavailable? || (@account.hides_following? && current_account&.id != @account.id) || (current_account && @account.blocking?(current_account))
end end
def default_accounts def default_accounts

View file

@ -5,11 +5,8 @@ class Api::V1::Accounts::RelationshipsController < Api::BaseController
before_action :require_user! before_action :require_user!
def index def index
scope = Account.where(id: account_ids).select('id') @accounts = Account.where(id: account_ids).select('id')
scope.merge!(Account.without_suspended) unless truthy_param?(:with_suspended) @accounts.merge!(Account.without_suspended) unless truthy_param?(:with_suspended)
# .where doesn't guarantee that our results are in the same order
# we requested them, so return the "right" order to the requestor.
@accounts = scope.index_by(&:id).values_at(*account_ids).compact
render json: @accounts, each_serializer: REST::RelationshipSerializer, relationships: relationships render json: @accounts, each_serializer: REST::RelationshipSerializer, relationships: relationships
end end

View file

@ -19,7 +19,7 @@ class Api::V1::Accounts::StatusesController < Api::BaseController
end end
def load_statuses def load_statuses
@account.suspended? ? [] : cached_account_statuses @account.unavailable? ? [] : cached_account_statuses
end end
def cached_account_statuses def cached_account_statuses

View file

@ -1,6 +1,8 @@
# frozen_string_literal: true # frozen_string_literal: true
class Api::V1::AccountsController < Api::BaseController class Api::V1::AccountsController < Api::BaseController
include RegistrationHelper
before_action -> { authorize_if_got_token! :read, :'read:accounts' }, except: [:create, :follow, :unfollow, :remove_from_followers, :block, :unblock, :mute, :unmute] before_action -> { authorize_if_got_token! :read, :'read:accounts' }, except: [:create, :follow, :unfollow, :remove_from_followers, :block, :unblock, :mute, :unmute]
before_action -> { doorkeeper_authorize! :follow, :write, :'write:follows' }, only: [:follow, :unfollow, :remove_from_followers] before_action -> { doorkeeper_authorize! :follow, :write, :'write:follows' }, only: [:follow, :unfollow, :remove_from_followers]
before_action -> { doorkeeper_authorize! :follow, :write, :'write:mutes' }, only: [:mute, :unmute] before_action -> { doorkeeper_authorize! :follow, :write, :'write:mutes' }, only: [:mute, :unmute]
@ -47,7 +49,7 @@ class Api::V1::AccountsController < Api::BaseController
end end
def mute def mute
MuteService.new.call(current_user.account, @account, notifications: truthy_param?(:notifications), duration: (params[:duration]&.to_i || 0)) MuteService.new.call(current_user.account, @account, notifications: truthy_param?(:notifications), duration: params[:duration].to_i)
render json: @account, serializer: REST::RelationshipSerializer, relationships: relationships render json: @account, serializer: REST::RelationshipSerializer, relationships: relationships
end end
@ -90,18 +92,14 @@ class Api::V1::AccountsController < Api::BaseController
end end
def account_params def account_params
params.permit(:username, :email, :password, :agreement, :locale, :reason, :time_zone) params.permit(:username, :email, :password, :agreement, :locale, :reason, :time_zone, :invite_code)
end
def invite
Invite.find_by(code: params[:invite_code]) if params[:invite_code].present?
end end
def check_enabled_registrations def check_enabled_registrations
forbidden if single_user_mode? || omniauth_only? || !allowed_registrations? forbidden unless allowed_registration?(request.remote_ip, invite)
end
def allowed_registrations?
Setting.registrations_mode != 'none'
end
def omniauth_only?
ENV['OMNIAUTH_ONLY'] == 'true'
end end
end end

View file

@ -41,10 +41,10 @@ class Api::V1::ConversationsController < Api::BaseController
account: :account_stat, account: :account_stat,
last_status: [ last_status: [
:media_attachments, :media_attachments,
:preview_cards,
:status_stat, :status_stat,
:tags, :tags,
{ {
preview_cards_status: :preview_card,
active_mentions: [account: :account_stat], active_mentions: [account: :account_stat],
account: :account_stat, account: :account_stat,
}, },

View file

@ -1,11 +1,9 @@
# frozen_string_literal: true # frozen_string_literal: true
class Api::V1::Instances::ActivityController < Api::BaseController class Api::V1::Instances::ActivityController < Api::V1::Instances::BaseController
before_action :require_enabled_api! before_action :require_enabled_api!
skip_before_action :require_authenticated_user!, unless: :limited_federation_mode? WEEKS_OF_ACTIVITY = 12
vary_by ''
def show def show
cache_even_if_authenticated! cache_even_if_authenticated!
@ -15,23 +13,40 @@ class Api::V1::Instances::ActivityController < Api::BaseController
private private
def activity def activity
statuses_tracker = ActivityTracker.new('activity:statuses:local', :basic) activity_weeks.map do |weeks_ago|
logins_tracker = ActivityTracker.new('activity:logins', :unique) activity_json(*week_edge_days(weeks_ago))
registrations_tracker = ActivityTracker.new('activity:accounts:local', :basic)
(0...12).map do |i|
start_of_week = i.weeks.ago
end_of_week = start_of_week + 6.days
{
week: start_of_week.to_i.to_s,
statuses: statuses_tracker.sum(start_of_week, end_of_week).to_s,
logins: logins_tracker.sum(start_of_week, end_of_week).to_s,
registrations: registrations_tracker.sum(start_of_week, end_of_week).to_s,
}
end end
end end
def activity_json(start_of_week, end_of_week)
{
week: start_of_week.to_i.to_s,
statuses: statuses_tracker.sum(start_of_week, end_of_week).to_s,
logins: logins_tracker.sum(start_of_week, end_of_week).to_s,
registrations: registrations_tracker.sum(start_of_week, end_of_week).to_s,
}
end
def activity_weeks
0...WEEKS_OF_ACTIVITY
end
def week_edge_days(num)
[num.weeks.ago, num.weeks.ago + 6.days]
end
def statuses_tracker
ActivityTracker.new('activity:statuses:local', :basic)
end
def logins_tracker
ActivityTracker.new('activity:logins', :unique)
end
def registrations_tracker
ActivityTracker.new('activity:accounts:local', :basic)
end
def require_enabled_api! def require_enabled_api!
head 404 unless Setting.activity_api_enabled && !limited_federation_mode? head 404 unless Setting.activity_api_enabled && !limited_federation_mode?
end end

View file

@ -0,0 +1,8 @@
# frozen_string_literal: true
class Api::V1::Instances::BaseController < Api::BaseController
skip_before_action :require_authenticated_user!,
unless: :limited_federation_mode?
vary_by ''
end

View file

@ -1,8 +1,6 @@
# frozen_string_literal: true # frozen_string_literal: true
class Api::V1::Instances::DomainBlocksController < Api::BaseController class Api::V1::Instances::DomainBlocksController < Api::V1::Instances::BaseController
skip_before_action :require_authenticated_user!, unless: :limited_federation_mode?
before_action :require_enabled_api! before_action :require_enabled_api!
before_action :set_domain_blocks before_action :set_domain_blocks
@ -15,16 +13,40 @@ class Api::V1::Instances::DomainBlocksController < Api::BaseController
cache_if_unauthenticated! cache_if_unauthenticated!
end end
render json: @domain_blocks, each_serializer: REST::DomainBlockSerializer, with_comment: (Setting.show_domain_blocks_rationale == 'all' || (Setting.show_domain_blocks_rationale == 'users' && user_signed_in?)) render json: @domain_blocks, each_serializer: REST::DomainBlockSerializer, with_comment: show_rationale_in_response?
end end
private private
def require_enabled_api! def require_enabled_api!
head 404 unless Setting.show_domain_blocks == 'all' || (Setting.show_domain_blocks == 'users' && user_signed_in?) head 404 unless api_enabled?
end
def api_enabled?
show_domain_blocks_for_all? || show_domain_blocks_to_user?
end
def show_domain_blocks_for_all?
Setting.show_domain_blocks == 'all'
end
def show_domain_blocks_to_user?
Setting.show_domain_blocks == 'users' && user_signed_in?
end end
def set_domain_blocks def set_domain_blocks
@domain_blocks = DomainBlock.with_user_facing_limitations.by_severity @domain_blocks = DomainBlock.with_user_facing_limitations.by_severity
end end
def show_rationale_in_response?
always_show_rationale? || show_rationale_for_user?
end
def always_show_rationale?
Setting.show_domain_blocks_rationale == 'all'
end
def show_rationale_for_user?
Setting.show_domain_blocks_rationale == 'users' && user_signed_in?
end
end end

View file

@ -1,13 +1,10 @@
# frozen_string_literal: true # frozen_string_literal: true
class Api::V1::Instances::ExtendedDescriptionsController < Api::BaseController class Api::V1::Instances::ExtendedDescriptionsController < Api::V1::Instances::BaseController
skip_before_action :require_authenticated_user!, unless: :limited_federation_mode?
skip_around_action :set_locale skip_around_action :set_locale
before_action :set_extended_description before_action :set_extended_description
vary_by ''
# Override `current_user` to avoid reading session cookies unless in whitelist mode # Override `current_user` to avoid reading session cookies unless in whitelist mode
def current_user def current_user
super if limited_federation_mode? super if limited_federation_mode?

View file

@ -1,13 +1,10 @@
# frozen_string_literal: true # frozen_string_literal: true
class Api::V1::Instances::LanguagesController < Api::BaseController class Api::V1::Instances::LanguagesController < Api::V1::Instances::BaseController
skip_before_action :require_authenticated_user!, unless: :limited_federation_mode?
skip_around_action :set_locale skip_around_action :set_locale
before_action :set_languages before_action :set_languages
vary_by ''
def show def show
cache_even_if_authenticated! cache_even_if_authenticated!
render json: @languages, each_serializer: REST::LanguageSerializer render json: @languages, each_serializer: REST::LanguageSerializer

View file

@ -1,13 +1,10 @@
# frozen_string_literal: true # frozen_string_literal: true
class Api::V1::Instances::PeersController < Api::BaseController class Api::V1::Instances::PeersController < Api::V1::Instances::BaseController
before_action :require_enabled_api! before_action :require_enabled_api!
skip_before_action :require_authenticated_user!, unless: :limited_federation_mode?
skip_around_action :set_locale skip_around_action :set_locale
vary_by ''
# Override `current_user` to avoid reading session cookies unless in whitelist mode # Override `current_user` to avoid reading session cookies unless in whitelist mode
def current_user def current_user
super if limited_federation_mode? super if limited_federation_mode?

View file

@ -1,12 +1,8 @@
# frozen_string_literal: true # frozen_string_literal: true
class Api::V1::Instances::PrivacyPoliciesController < Api::BaseController class Api::V1::Instances::PrivacyPoliciesController < Api::V1::Instances::BaseController
skip_before_action :require_authenticated_user!, unless: :limited_federation_mode?
before_action :set_privacy_policy before_action :set_privacy_policy
vary_by ''
def show def show
cache_even_if_authenticated! cache_even_if_authenticated!
render json: @privacy_policy, serializer: REST::PrivacyPolicySerializer render json: @privacy_policy, serializer: REST::PrivacyPolicySerializer

View file

@ -1,13 +1,10 @@
# frozen_string_literal: true # frozen_string_literal: true
class Api::V1::Instances::RulesController < Api::BaseController class Api::V1::Instances::RulesController < Api::V1::Instances::BaseController
skip_before_action :require_authenticated_user!, unless: :limited_federation_mode?
skip_around_action :set_locale skip_around_action :set_locale
before_action :set_rules before_action :set_rules
vary_by ''
# Override `current_user` to avoid reading session cookies unless in whitelist mode # Override `current_user` to avoid reading session cookies unless in whitelist mode
def current_user def current_user
super if limited_federation_mode? super if limited_federation_mode?

View file

@ -1,12 +1,8 @@
# frozen_string_literal: true # frozen_string_literal: true
class Api::V1::Instances::TranslationLanguagesController < Api::BaseController class Api::V1::Instances::TranslationLanguagesController < Api::V1::Instances::BaseController
skip_before_action :require_authenticated_user!, unless: :limited_federation_mode?
before_action :set_languages before_action :set_languages
vary_by ''
def show def show
cache_even_if_authenticated! cache_even_if_authenticated!
render json: @languages render json: @languages

View file

@ -0,0 +1,30 @@
# frozen_string_literal: true
class Api::V1::InvitesController < Api::BaseController
include RegistrationHelper
skip_before_action :require_authenticated_user!
skip_around_action :set_locale
before_action :set_invite
before_action :check_enabled_registrations!
# Override `current_user` to avoid reading session cookies
def current_user; end
def show
render json: { invite_code: params[:invite_code], instance_api_url: api_v2_instance_url }, status: 200
end
private
def set_invite
@invite = Invite.find_by!(code: params[:invite_code])
end
def check_enabled_registrations!
return render json: { error: I18n.t('invites.invalid') }, status: 401 unless @invite.valid_for_use?
raise Mastodon::NotPermittedError unless allowed_registration?(request.remote_ip, @invite)
end
end

View file

@ -0,0 +1,16 @@
# frozen_string_literal: true
class Api::V1::Statuses::BaseController < Api::BaseController
include Authorization
before_action :set_status
private
def set_status
@status = Status.find(params[:status_id])
authorize @status, :show?
rescue Mastodon::NotPermittedError
not_found
end
end

View file

@ -1,11 +1,9 @@
# frozen_string_literal: true # frozen_string_literal: true
class Api::V1::Statuses::BookmarksController < Api::BaseController class Api::V1::Statuses::BookmarksController < Api::V1::Statuses::BaseController
include Authorization
before_action -> { doorkeeper_authorize! :write, :'write:bookmarks' } before_action -> { doorkeeper_authorize! :write, :'write:bookmarks' }
before_action :require_user! before_action :require_user!
before_action :set_status, only: [:create] skip_before_action :set_status, only: [:destroy]
def create def create
current_account.bookmarks.find_or_create_by!(account: current_account, status: @status) current_account.bookmarks.find_or_create_by!(account: current_account, status: @status)
@ -28,13 +26,4 @@ class Api::V1::Statuses::BookmarksController < Api::BaseController
rescue Mastodon::NotPermittedError rescue Mastodon::NotPermittedError
not_found not_found
end end
private
def set_status
@status = Status.find(params[:status_id])
authorize @status, :show?
rescue Mastodon::NotPermittedError
not_found
end
end end

View file

@ -1,10 +1,7 @@
# frozen_string_literal: true # frozen_string_literal: true
class Api::V1::Statuses::FavouritedByAccountsController < Api::BaseController class Api::V1::Statuses::FavouritedByAccountsController < Api::V1::Statuses::BaseController
include Authorization
before_action -> { authorize_if_got_token! :read, :'read:accounts' } before_action -> { authorize_if_got_token! :read, :'read:accounts' }
before_action :set_status
after_action :insert_pagination_headers after_action :insert_pagination_headers
def index def index
@ -61,13 +58,6 @@ class Api::V1::Statuses::FavouritedByAccountsController < Api::BaseController
@accounts.size == limit_param(DEFAULT_ACCOUNTS_LIMIT) @accounts.size == limit_param(DEFAULT_ACCOUNTS_LIMIT)
end end
def set_status
@status = Status.find(params[:status_id])
authorize @status, :show?
rescue Mastodon::NotPermittedError
not_found
end
def pagination_params(core_params) def pagination_params(core_params)
params.slice(:limit).permit(:limit).merge(core_params) params.slice(:limit).permit(:limit).merge(core_params)
end end

View file

@ -1,11 +1,9 @@
# frozen_string_literal: true # frozen_string_literal: true
class Api::V1::Statuses::FavouritesController < Api::BaseController class Api::V1::Statuses::FavouritesController < Api::V1::Statuses::BaseController
include Authorization
before_action -> { doorkeeper_authorize! :write, :'write:favourites' } before_action -> { doorkeeper_authorize! :write, :'write:favourites' }
before_action :require_user! before_action :require_user!
before_action :set_status, only: [:create] skip_before_action :set_status, only: [:destroy]
def create def create
FavouriteService.new.call(current_account, @status) FavouriteService.new.call(current_account, @status)
@ -30,13 +28,4 @@ class Api::V1::Statuses::FavouritesController < Api::BaseController
rescue Mastodon::NotPermittedError rescue Mastodon::NotPermittedError
not_found not_found
end end
private
def set_status
@status = Status.find(params[:status_id])
authorize @status, :show?
rescue Mastodon::NotPermittedError
not_found
end
end end

View file

@ -1,10 +1,7 @@
# frozen_string_literal: true # frozen_string_literal: true
class Api::V1::Statuses::HistoriesController < Api::BaseController class Api::V1::Statuses::HistoriesController < Api::V1::Statuses::BaseController
include Authorization
before_action -> { authorize_if_got_token! :read, :'read:statuses' } before_action -> { authorize_if_got_token! :read, :'read:statuses' }
before_action :set_status
def show def show
cache_if_unauthenticated! cache_if_unauthenticated!
@ -14,13 +11,6 @@ class Api::V1::Statuses::HistoriesController < Api::BaseController
private private
def status_edits def status_edits
@status.edits.includes(:account, status: [:account]).to_a.presence || [@status.build_snapshot(at_time: @status.edited_at || @status.created_at)] @status.edits.ordered.includes(:account, status: [:account]).to_a.presence || [@status.build_snapshot(at_time: @status.edited_at || @status.created_at)]
end
def set_status
@status = Status.find(params[:status_id])
authorize @status, :show?
rescue Mastodon::NotPermittedError
not_found
end end
end end

View file

@ -1,11 +1,8 @@
# frozen_string_literal: true # frozen_string_literal: true
class Api::V1::Statuses::MutesController < Api::BaseController class Api::V1::Statuses::MutesController < Api::V1::Statuses::BaseController
include Authorization
before_action -> { doorkeeper_authorize! :write, :'write:mutes' } before_action -> { doorkeeper_authorize! :write, :'write:mutes' }
before_action :require_user! before_action :require_user!
before_action :set_status
before_action :set_conversation before_action :set_conversation
def create def create
@ -24,13 +21,6 @@ class Api::V1::Statuses::MutesController < Api::BaseController
private private
def set_status
@status = Status.find(params[:status_id])
authorize @status, :show?
rescue Mastodon::NotPermittedError
not_found
end
def set_conversation def set_conversation
@conversation = @status.conversation @conversation = @status.conversation
raise Mastodon::ValidationError if @conversation.nil? raise Mastodon::ValidationError if @conversation.nil?

View file

@ -1,11 +1,8 @@
# frozen_string_literal: true # frozen_string_literal: true
class Api::V1::Statuses::PinsController < Api::BaseController class Api::V1::Statuses::PinsController < Api::V1::Statuses::BaseController
include Authorization
before_action -> { doorkeeper_authorize! :write, :'write:accounts' } before_action -> { doorkeeper_authorize! :write, :'write:accounts' }
before_action :require_user! before_action :require_user!
before_action :set_status
def create def create
StatusPin.create!(account: current_account, status: @status) StatusPin.create!(account: current_account, status: @status)
@ -26,10 +23,6 @@ class Api::V1::Statuses::PinsController < Api::BaseController
private private
def set_status
@status = Status.find(params[:status_id])
end
def distribute_add_activity! def distribute_add_activity!
json = ActiveModelSerializers::SerializableResource.new( json = ActiveModelSerializers::SerializableResource.new(
@status, @status,

View file

@ -1,10 +1,7 @@
# frozen_string_literal: true # frozen_string_literal: true
class Api::V1::Statuses::RebloggedByAccountsController < Api::BaseController class Api::V1::Statuses::RebloggedByAccountsController < Api::V1::Statuses::BaseController
include Authorization
before_action -> { authorize_if_got_token! :read, :'read:accounts' } before_action -> { authorize_if_got_token! :read, :'read:accounts' }
before_action :set_status
after_action :insert_pagination_headers after_action :insert_pagination_headers
def index def index
@ -57,13 +54,6 @@ class Api::V1::Statuses::RebloggedByAccountsController < Api::BaseController
@accounts.size == limit_param(DEFAULT_ACCOUNTS_LIMIT) @accounts.size == limit_param(DEFAULT_ACCOUNTS_LIMIT)
end end
def set_status
@status = Status.find(params[:status_id])
authorize @status, :show?
rescue Mastodon::NotPermittedError
not_found
end
def pagination_params(core_params) def pagination_params(core_params)
params.slice(:limit).permit(:limit).merge(core_params) params.slice(:limit).permit(:limit).merge(core_params)
end end

View file

@ -1,13 +1,13 @@
# frozen_string_literal: true # frozen_string_literal: true
class Api::V1::Statuses::ReblogsController < Api::BaseController class Api::V1::Statuses::ReblogsController < Api::V1::Statuses::BaseController
include Authorization
include Redisable include Redisable
include Lockable include Lockable
before_action -> { doorkeeper_authorize! :write, :'write:statuses' } before_action -> { doorkeeper_authorize! :write, :'write:statuses' }
before_action :require_user! before_action :require_user!
before_action :set_reblog, only: [:create] before_action :set_reblog, only: [:create]
skip_before_action :set_status
override_rate_limit_headers :create, family: :statuses override_rate_limit_headers :create, family: :statuses

View file

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

View file

@ -1,10 +1,7 @@
# frozen_string_literal: true # frozen_string_literal: true
class Api::V1::Statuses::TranslationsController < Api::BaseController class Api::V1::Statuses::TranslationsController < Api::V1::Statuses::BaseController
include Authorization
before_action -> { doorkeeper_authorize! :read, :'read:statuses' } before_action -> { doorkeeper_authorize! :read, :'read:statuses' }
before_action :set_status
before_action :set_translation before_action :set_translation
rescue_from TranslationService::NotConfiguredError, with: :not_found rescue_from TranslationService::NotConfiguredError, with: :not_found
@ -24,13 +21,6 @@ class Api::V1::Statuses::TranslationsController < Api::BaseController
private private
def set_status
@status = Status.find(params[:status_id])
authorize @status, :show?
rescue Mastodon::NotPermittedError
not_found
end
def set_translation def set_translation
@translation = TranslateStatusService.new.call(@status, content_locale) @translation = TranslateStatusService.new.call(@status, content_locale)
end end

View file

@ -0,0 +1,33 @@
# frozen_string_literal: true
class Api::V1::Timelines::BaseController < Api::BaseController
after_action :insert_pagination_headers, unless: -> { @statuses.empty? }
private
def insert_pagination_headers
set_pagination_headers(next_path, prev_path)
end
def pagination_max_id
@statuses.last.id
end
def pagination_since_id
@statuses.first.id
end
def next_path_params
permitted_params.merge(max_id: pagination_max_id)
end
def prev_path_params
permitted_params.merge(min_id: pagination_since_id)
end
def permitted_params
params
.slice(*self.class::PERMITTED_PARAMS)
.permit(*self.class::PERMITTED_PARAMS)
end
end

View file

@ -1,9 +1,10 @@
# frozen_string_literal: true # frozen_string_literal: true
class Api::V1::Timelines::HomeController < Api::BaseController class Api::V1::Timelines::HomeController < Api::V1::Timelines::BaseController
before_action -> { doorkeeper_authorize! :read, :'read:statuses' }, only: [:show] before_action -> { doorkeeper_authorize! :read, :'read:statuses' }, only: [:show]
before_action :require_user!, only: [:show] before_action :require_user!, only: [:show]
after_action :insert_pagination_headers, unless: -> { @statuses.empty? }
PERMITTED_PARAMS = %i(local limit).freeze
def show def show
with_read_replica do with_read_replica do
@ -40,27 +41,11 @@ class Api::V1::Timelines::HomeController < Api::BaseController
HomeFeed.new(current_account) HomeFeed.new(current_account)
end end
def insert_pagination_headers
set_pagination_headers(next_path, prev_path)
end
def pagination_params(core_params)
params.slice(:local, :limit).permit(:local, :limit).merge(core_params)
end
def next_path def next_path
api_v1_timelines_home_url pagination_params(max_id: pagination_max_id) api_v1_timelines_home_url next_path_params
end end
def prev_path def prev_path
api_v1_timelines_home_url pagination_params(min_id: pagination_since_id) api_v1_timelines_home_url prev_path_params
end
def pagination_max_id
@statuses.last.id
end
def pagination_since_id
@statuses.first.id
end end
end end

View file

@ -1,12 +1,12 @@
# frozen_string_literal: true # frozen_string_literal: true
class Api::V1::Timelines::ListController < Api::BaseController class Api::V1::Timelines::ListController < Api::V1::Timelines::BaseController
before_action -> { doorkeeper_authorize! :read, :'read:lists' } before_action -> { doorkeeper_authorize! :read, :'read:lists' }
before_action :require_user! before_action :require_user!
before_action :set_list before_action :set_list
before_action :set_statuses before_action :set_statuses
after_action :insert_pagination_headers, unless: -> { @statuses.empty? } PERMITTED_PARAMS = %i(limit).freeze
def show def show
render json: @statuses, render json: @statuses,
@ -41,27 +41,11 @@ class Api::V1::Timelines::ListController < Api::BaseController
ListFeed.new(@list) ListFeed.new(@list)
end end
def insert_pagination_headers
set_pagination_headers(next_path, prev_path)
end
def pagination_params(core_params)
params.slice(:limit).permit(:limit).merge(core_params)
end
def next_path def next_path
api_v1_timelines_list_url params[:id], pagination_params(max_id: pagination_max_id) api_v1_timelines_list_url params[:id], next_path_params
end end
def prev_path def prev_path
api_v1_timelines_list_url params[:id], pagination_params(min_id: pagination_since_id) api_v1_timelines_list_url params[:id], prev_path_params
end
def pagination_max_id
@statuses.last.id
end
def pagination_since_id
@statuses.first.id
end end
end end

View file

@ -1,8 +1,9 @@
# frozen_string_literal: true # frozen_string_literal: true
class Api::V1::Timelines::PublicController < Api::BaseController class Api::V1::Timelines::PublicController < Api::V1::Timelines::BaseController
before_action :require_user!, only: [:show], if: :require_auth? before_action :require_user!, only: [:show], if: :require_auth?
after_action :insert_pagination_headers, unless: -> { @statuses.empty? }
PERMITTED_PARAMS = %i(local remote limit only_media).freeze
def show def show
cache_if_unauthenticated! cache_if_unauthenticated!
@ -42,27 +43,11 @@ class Api::V1::Timelines::PublicController < Api::BaseController
) )
end end
def insert_pagination_headers
set_pagination_headers(next_path, prev_path)
end
def pagination_params(core_params)
params.slice(:local, :remote, :limit, :only_media).permit(:local, :remote, :limit, :only_media).merge(core_params)
end
def next_path def next_path
api_v1_timelines_public_url pagination_params(max_id: pagination_max_id) api_v1_timelines_public_url next_path_params
end end
def prev_path def prev_path
api_v1_timelines_public_url pagination_params(min_id: pagination_since_id) api_v1_timelines_public_url prev_path_params
end
def pagination_max_id
@statuses.last.id
end
def pagination_since_id
@statuses.first.id
end end
end end

View file

@ -1,9 +1,10 @@
# frozen_string_literal: true # frozen_string_literal: true
class Api::V1::Timelines::TagController < Api::BaseController class Api::V1::Timelines::TagController < Api::V1::Timelines::BaseController
before_action -> { doorkeeper_authorize! :read, :'read:statuses' }, only: :show, if: :require_auth? before_action -> { doorkeeper_authorize! :read, :'read:statuses' }, only: :show, if: :require_auth?
before_action :load_tag before_action :load_tag
after_action :insert_pagination_headers, unless: -> { @statuses.empty? }
PERMITTED_PARAMS = %i(local limit only_media).freeze
def show def show
cache_if_unauthenticated! cache_if_unauthenticated!
@ -51,27 +52,11 @@ class Api::V1::Timelines::TagController < Api::BaseController
) )
end end
def insert_pagination_headers
set_pagination_headers(next_path, prev_path)
end
def pagination_params(core_params)
params.slice(:local, :limit, :only_media).permit(:local, :limit, :only_media).merge(core_params)
end
def next_path def next_path
api_v1_timelines_tag_url params[:id], pagination_params(max_id: pagination_max_id) api_v1_timelines_tag_url params[:id], next_path_params
end end
def prev_path def prev_path
api_v1_timelines_tag_url params[:id], pagination_params(min_id: pagination_since_id) api_v1_timelines_tag_url params[:id], prev_path_params
end
def pagination_max_id
@statuses.last.id
end
def pagination_since_id
@statuses.first.id
end end
end end

View file

@ -2,12 +2,22 @@
class Api::V2::MediaController < Api::V1::MediaController class Api::V2::MediaController < Api::V1::MediaController
def create def create
@media_attachment = current_account.media_attachments.create!({ delay_processing: true }.merge(media_attachment_params)) @media_attachment = current_account.media_attachments.create!(media_and_delay_params)
render json: @media_attachment, serializer: REST::MediaAttachmentSerializer, status: @media_attachment.not_processed? ? 202 : 200 render json: @media_attachment, serializer: REST::MediaAttachmentSerializer, status: status_from_media_processing
rescue Paperclip::Errors::NotIdentifiedByImageMagickError rescue Paperclip::Errors::NotIdentifiedByImageMagickError
render json: file_type_error, status: 422 render json: file_type_error, status: 422
rescue Paperclip::Error => e rescue Paperclip::Error => e
Rails.logger.error "#{e.class}: #{e.message}" Rails.logger.error "#{e.class}: #{e.message}"
render json: processing_error, status: 500 render json: processing_error, status: 500
end end
private
def media_and_delay_params
{ delay_processing: true }.merge(media_attachment_params)
end
def status_from_media_processing
@media_attachment.not_processed? ? 202 : 200
end
end end

View file

@ -8,6 +8,11 @@ class Api::V2::SearchController < Api::BaseController
before_action -> { authorize_if_got_token! :read, :'read:search' } before_action -> { authorize_if_got_token! :read, :'read:search' }
before_action :validate_search_params! before_action :validate_search_params!
with_options unless: :user_signed_in? do
before_action :query_pagination_error, if: :pagination_requested?
before_action :remote_resolve_error, if: :remote_resolve_requested?
end
def index def index
@search = Search.new(search_results) @search = Search.new(search_results)
render json: @search, serializer: REST::SearchSerializer render json: @search, serializer: REST::SearchSerializer
@ -21,12 +26,22 @@ class Api::V2::SearchController < Api::BaseController
def validate_search_params! def validate_search_params!
params.require(:q) params.require(:q)
end
return if user_signed_in? def query_pagination_error
render json: { error: 'Search queries pagination is not supported without authentication' }, status: 401
end
return render json: { error: 'Search queries pagination is not supported without authentication' }, status: 401 if params[:offset].present? def remote_resolve_error
render json: { error: 'Search queries that resolve remote resources are not supported without authentication' }, status: 401
end
render json: { error: 'Search queries that resolve remote resources are not supported without authentication' }, status: 401 if truthy_param?(:resolve) def remote_resolve_requested?
truthy_param?(:resolve)
end
def pagination_requested?
params[:offset].present?
end end
def search_results def search_results
@ -34,7 +49,15 @@ class Api::V2::SearchController < Api::BaseController
params[:q], params[:q],
current_account, current_account,
limit_param(RESULTS_LIMIT), limit_param(RESULTS_LIMIT),
search_params.merge(resolve: truthy_param?(:resolve), exclude_unreviewed: truthy_param?(:exclude_unreviewed), following: truthy_param?(:following)) combined_search_params
)
end
def combined_search_params
search_params.merge(
resolve: truthy_param?(:resolve),
exclude_unreviewed: truthy_param?(:exclude_unreviewed),
following: truthy_param?(:following)
) )
end end

View file

@ -3,37 +3,13 @@
class Api::Web::PushSubscriptionsController < Api::Web::BaseController class Api::Web::PushSubscriptionsController < Api::Web::BaseController
before_action :require_user! before_action :require_user!
before_action :set_push_subscription, only: :update before_action :set_push_subscription, only: :update
before_action :destroy_previous_subscriptions, only: :create, if: :prior_subscriptions?
after_action :update_session_with_subscription, only: :create
def create def create
active_session = current_session @push_subscription = ::Web::PushSubscription.create!(web_push_subscription_params)
unless active_session.web_push_subscription.nil? render json: @push_subscription, serializer: REST::WebPushSubscriptionSerializer
active_session.web_push_subscription.destroy!
active_session.update!(web_push_subscription: nil)
end
# Mobile devices do not support regular notifications, so we enable push notifications by default
alerts_enabled = active_session.detection.device.mobile? || active_session.detection.device.tablet?
data = {
policy: 'all',
alerts: Notification::TYPES.index_with { alerts_enabled },
}
data.deep_merge!(data_params) if params[:data]
push_subscription = ::Web::PushSubscription.create!(
endpoint: subscription_params[:endpoint],
key_p256dh: subscription_params[:keys][:p256dh],
key_auth: subscription_params[:keys][:auth],
data: data,
user_id: active_session.user_id,
access_token_id: active_session.access_token_id
)
active_session.update!(web_push_subscription: push_subscription)
render json: push_subscription, serializer: REST::WebPushSubscriptionSerializer
end end
def update def update
@ -43,6 +19,41 @@ class Api::Web::PushSubscriptionsController < Api::Web::BaseController
private private
def active_session
@active_session ||= current_session
end
def destroy_previous_subscriptions
active_session.web_push_subscription.destroy!
active_session.update!(web_push_subscription: nil)
end
def prior_subscriptions?
active_session.web_push_subscription.present?
end
def subscription_data
default_subscription_data.tap do |data|
data.deep_merge!(data_params) if params[:data]
end
end
def default_subscription_data
{
policy: 'all',
alerts: Notification::TYPES.index_with { alerts_enabled },
}
end
def alerts_enabled
# Mobile devices do not support regular notifications, so we enable push notifications by default
active_session.detection.device.mobile? || active_session.detection.device.tablet?
end
def update_session_with_subscription
active_session.update!(web_push_subscription: @push_subscription)
end
def set_push_subscription def set_push_subscription
@push_subscription = ::Web::PushSubscription.find(params[:id]) @push_subscription = ::Web::PushSubscription.find(params[:id])
end end
@ -51,6 +62,17 @@ class Api::Web::PushSubscriptionsController < Api::Web::BaseController
@subscription_params ||= params.require(:subscription).permit(:endpoint, keys: [:auth, :p256dh]) @subscription_params ||= params.require(:subscription).permit(:endpoint, keys: [:auth, :p256dh])
end end
def web_push_subscription_params
{
access_token_id: active_session.access_token_id,
data: subscription_data,
endpoint: subscription_params[:endpoint],
key_auth: subscription_params[:keys][:auth],
key_p256dh: subscription_params[:keys][:p256dh],
user_id: active_session.user_id,
}
end
def data_params def data_params
@data_params ||= params.require(:data).permit(:policy, alerts: Notification::TYPES) @data_params ||= params.require(:data).permit(:policy, alerts: Notification::TYPES)
end end

View file

@ -1,7 +1,7 @@
# frozen_string_literal: true # frozen_string_literal: true
class Auth::ConfirmationsController < Devise::ConfirmationsController class Auth::ConfirmationsController < Devise::ConfirmationsController
include CaptchaConcern include Auth::CaptchaConcern
layout 'auth' layout 'auth'

View file

@ -1,7 +1,8 @@
# frozen_string_literal: true # frozen_string_literal: true
class Auth::RegistrationsController < Devise::RegistrationsController class Auth::RegistrationsController < Devise::RegistrationsController
include RegistrationSpamConcern include RegistrationHelper
include Auth::RegistrationSpamConcern
layout :determine_layout layout :determine_layout
@ -82,19 +83,7 @@ class Auth::RegistrationsController < Devise::RegistrationsController
end end
def check_enabled_registrations def check_enabled_registrations
redirect_to root_path if single_user_mode? || omniauth_only? || !allowed_registrations? || ip_blocked? redirect_to root_path unless allowed_registration?(request.remote_ip, @invite)
end
def allowed_registrations?
Setting.registrations_mode != 'none' || @invite&.valid_for_use?
end
def omniauth_only?
ENV['OMNIAUTH_ONLY'] == 'true'
end
def ip_blocked?
IpBlock.where(severity: :sign_up_block).where('ip >>= ?', request.remote_ip.to_s).exists?
end end
def invite_code def invite_code
@ -131,7 +120,7 @@ class Auth::RegistrationsController < Devise::RegistrationsController
end end
def require_not_suspended! def require_not_suspended!
forbidden if current_account.suspended? forbidden if current_account.unavailable?
end end
def set_rules def set_rules

View file

@ -10,7 +10,7 @@ class Auth::SessionsController < Devise::SessionsController
prepend_before_action :check_suspicious!, only: [:create] prepend_before_action :check_suspicious!, only: [:create]
include TwoFactorAuthenticationConcern include Auth::TwoFactorAuthenticationConcern
before_action :set_body_classes before_action :set_body_classes

View file

@ -34,8 +34,8 @@ module AccountOwnedConcern
end end
def check_account_suspension def check_account_suspension
if @account.suspended_permanently? if @account.permanently_unavailable?
permanent_suspension_response permanent_unavailability_response
elsif @account.suspended? && !skip_temporary_suspension_response? elsif @account.suspended? && !skip_temporary_suspension_response?
temporary_suspension_response temporary_suspension_response
end end
@ -45,7 +45,7 @@ module AccountOwnedConcern
false false
end end
def permanent_suspension_response def permanent_unavailability_response
expires_in(3.minutes, public: true) expires_in(3.minutes, public: true)
gone gone
end end

View file

@ -1,6 +1,6 @@
# frozen_string_literal: true # frozen_string_literal: true
module AdminExportControllerConcern module Admin::ExportControllerConcern
extend ActiveSupport::Concern extend ActiveSupport::Concern
private private

View file

@ -1,6 +1,6 @@
# frozen_string_literal: true # frozen_string_literal: true
module AccessTokenTrackingConcern module Api::AccessTokenTrackingConcern
extend ActiveSupport::Concern extend ActiveSupport::Concern
ACCESS_TOKEN_UPDATE_FREQUENCY = 24.hours.freeze ACCESS_TOKEN_UPDATE_FREQUENCY = 24.hours.freeze

View file

@ -1,6 +1,6 @@
# frozen_string_literal: true # frozen_string_literal: true
module ApiCachingConcern module Api::CachingConcern
extend ActiveSupport::Concern extend ActiveSupport::Concern
def cache_if_unauthenticated! def cache_if_unauthenticated!

View file

@ -0,0 +1,27 @@
# frozen_string_literal: true
module Api::ContentSecurityPolicy
extend ActiveSupport::Concern
included do
content_security_policy do |policy|
# Set every directive that does not have a fallback
policy.default_src :none
policy.frame_ancestors :none
policy.form_action :none
# Disable every directive with a fallback to cut on response size
policy.base_uri false
policy.font_src false
policy.img_src false
policy.style_src false
policy.media_src false
policy.frame_src false
policy.manifest_src false
policy.connect_src false
policy.script_src false
policy.child_src false
policy.worker_src false
end
end
end

View file

@ -1,6 +1,6 @@
# frozen_string_literal: true # frozen_string_literal: true
module RateLimitHeaders module Api::RateLimitHeaders
extend ActiveSupport::Concern extend ActiveSupport::Concern
class_methods do class_methods do

View file

@ -1,6 +1,6 @@
# frozen_string_literal: true # frozen_string_literal: true
module CaptchaConcern module Auth::CaptchaConcern
extend ActiveSupport::Concern extend ActiveSupport::Concern
include Hcaptcha::Adapters::ViewMethods include Hcaptcha::Adapters::ViewMethods

View file

@ -1,6 +1,6 @@
# frozen_string_literal: true # frozen_string_literal: true
module RegistrationSpamConcern module Auth::RegistrationSpamConcern
extend ActiveSupport::Concern extend ActiveSupport::Concern
def set_registration_form_time def set_registration_form_time

View file

@ -1,6 +1,6 @@
# frozen_string_literal: true # frozen_string_literal: true
module TwoFactorAuthenticationConcern module Auth::TwoFactorAuthenticationConcern
extend ActiveSupport::Concern extend ActiveSupport::Concern
included do included do

View file

@ -43,7 +43,7 @@ module ChallengableConcern
def render_challenge def render_challenge
@body_classes = 'lighter' @body_classes = 'lighter'
render template: 'auth/challenges/new', layout: 'auth' render 'auth/challenges/new', layout: 'auth'
end end
def challenge_passed? def challenge_passed?

View file

@ -1,6 +1,6 @@
# frozen_string_literal: true # frozen_string_literal: true
module ExportControllerConcern module Settings::ExportControllerConcern
extend ActiveSupport::Concern extend ActiveSupport::Concern
included do included do

View file

@ -11,7 +11,7 @@ class Disputes::AppealsController < Disputes::BaseController
redirect_to disputes_strike_path(@strike), notice: I18n.t('disputes.strikes.appealed_msg') redirect_to disputes_strike_path(@strike), notice: I18n.t('disputes.strikes.appealed_msg')
rescue ActiveRecord::RecordInvalid => e rescue ActiveRecord::RecordInvalid => e
@appeal = e.record @appeal = e.record
render template: 'disputes/strikes/show' render 'disputes/strikes/show'
end end
private private

View file

@ -25,7 +25,7 @@ class FiltersController < ApplicationController
if @filter.save if @filter.save
redirect_to filters_path redirect_to filters_path
else else
render action: :new render :new
end end
end end
@ -33,7 +33,7 @@ class FiltersController < ApplicationController
if @filter.update(resource_params) if @filter.update(resource_params)
redirect_to filters_path redirect_to filters_path
else else
render action: :edit render :edit
end end
end end

View file

@ -31,7 +31,7 @@ class Oauth::AuthorizedApplicationsController < Doorkeeper::AuthorizedApplicatio
end end
def require_not_suspended! def require_not_suspended!
forbidden if current_account.suspended? forbidden if current_account.unavailable?
end end
def set_cache_headers def set_cache_headers

View file

@ -18,6 +18,6 @@ class Settings::BaseController < ApplicationController
end end
def require_not_suspended! def require_not_suspended!
forbidden if current_account.suspended? forbidden if current_account.unavailable?
end end
end end

View file

@ -25,7 +25,7 @@ class Settings::DeletesController < Settings::BaseController
end end
def require_not_suspended! def require_not_suspended!
forbidden if current_account.suspended? forbidden if current_account.unavailable?
end end
def challenge_passed? def challenge_passed?

View file

@ -3,7 +3,7 @@
module Settings module Settings
module Exports module Exports
class BlockedAccountsController < BaseController class BlockedAccountsController < BaseController
include ExportControllerConcern include Settings::ExportControllerConcern
def index def index
send_export_file send_export_file

View file

@ -3,7 +3,7 @@
module Settings module Settings
module Exports module Exports
class BlockedDomainsController < BaseController class BlockedDomainsController < BaseController
include ExportControllerConcern include Settings::ExportControllerConcern
def index def index
send_export_file send_export_file

View file

@ -3,7 +3,7 @@
module Settings module Settings
module Exports module Exports
class BookmarksController < BaseController class BookmarksController < BaseController
include ExportControllerConcern include Settings::ExportControllerConcern
def index def index
send_export_file send_export_file

View file

@ -3,7 +3,7 @@
module Settings module Settings
module Exports module Exports
class FollowingAccountsController < BaseController class FollowingAccountsController < BaseController
include ExportControllerConcern include Settings::ExportControllerConcern
def index def index
send_export_file send_export_file

View file

@ -3,7 +3,7 @@
module Settings module Settings
module Exports module Exports
class ListsController < BaseController class ListsController < BaseController
include ExportControllerConcern include Settings::ExportControllerConcern
def index def index
send_export_file send_export_file

View file

@ -3,7 +3,7 @@
module Settings module Settings
module Exports module Exports
class MutedAccountsController < BaseController class MutedAccountsController < BaseController
include ExportControllerConcern include Settings::ExportControllerConcern
def index def index
send_export_file send_export_file

View file

@ -14,7 +14,7 @@ class StatusesCleanupController < ApplicationController
if @policy.update(resource_params) if @policy.update(resource_params)
redirect_to statuses_cleanup_path, notice: I18n.t('generic.changes_saved_msg') redirect_to statuses_cleanup_path, notice: I18n.t('generic.changes_saved_msg')
else else
render action: :show render :show
end end
rescue ActionController::ParameterMissing rescue ActionController::ParameterMissing
# Do nothing # Do nothing

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