Merge branch 'main' into media-display-options
# Conflicts: # config/locales/simple_form.en.yml
59
.annotaterb.yml
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
---
|
||||||
|
:position: before
|
||||||
|
:position_in_additional_file_patterns: before
|
||||||
|
:position_in_class: before
|
||||||
|
:position_in_factory: before
|
||||||
|
:position_in_fixture: before
|
||||||
|
:position_in_routes: before
|
||||||
|
:position_in_serializer: before
|
||||||
|
:position_in_test: before
|
||||||
|
:classified_sort: true
|
||||||
|
:exclude_controllers: true
|
||||||
|
:exclude_factories: true
|
||||||
|
:exclude_fixtures: true
|
||||||
|
:exclude_helpers: true
|
||||||
|
:exclude_scaffolds: true
|
||||||
|
:exclude_serializers: true
|
||||||
|
:exclude_sti_subclasses: true
|
||||||
|
:exclude_tests: true
|
||||||
|
:force: false
|
||||||
|
:format_markdown: false
|
||||||
|
:format_rdoc: false
|
||||||
|
:format_yard: false
|
||||||
|
:frozen: false
|
||||||
|
:ignore_model_sub_dir: false
|
||||||
|
:ignore_unknown_models: false
|
||||||
|
:include_version: false
|
||||||
|
:show_complete_foreign_keys: false
|
||||||
|
:show_foreign_keys: false
|
||||||
|
:show_indexes: false
|
||||||
|
:simple_indexes: false
|
||||||
|
:sort: false
|
||||||
|
:timestamp: false
|
||||||
|
:trace: false
|
||||||
|
:with_comment: true
|
||||||
|
:with_column_comments: true
|
||||||
|
:with_table_comments: true
|
||||||
|
:active_admin: false
|
||||||
|
:command:
|
||||||
|
:debug: false
|
||||||
|
:hide_default_column_types: ''
|
||||||
|
:hide_limit_column_types: 'integer,boolean'
|
||||||
|
:ignore_columns:
|
||||||
|
:ignore_routes:
|
||||||
|
:models: true
|
||||||
|
:routes: false
|
||||||
|
:skip_on_db_migrate: false
|
||||||
|
:target_action: :do_annotations
|
||||||
|
:wrapper:
|
||||||
|
:wrapper_close:
|
||||||
|
:wrapper_open:
|
||||||
|
:classes_default_to_s: []
|
||||||
|
:additional_file_patterns: []
|
||||||
|
:model_dir:
|
||||||
|
- app/models
|
||||||
|
:require: []
|
||||||
|
:root_dir:
|
||||||
|
- ''
|
||||||
|
|
||||||
|
:show_check_constraints: false
|
@ -69,7 +69,7 @@ services:
|
|||||||
hard: -1
|
hard: -1
|
||||||
|
|
||||||
libretranslate:
|
libretranslate:
|
||||||
image: libretranslate/libretranslate:v1.6.1
|
image: libretranslate/libretranslate:v1.6.2
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
volumes:
|
volumes:
|
||||||
- lt-data:/home/libretranslate/.local
|
- lt-data:/home/libretranslate/.local
|
||||||
|
@ -45,6 +45,17 @@ ES_PASS=password
|
|||||||
SECRET_KEY_BASE=
|
SECRET_KEY_BASE=
|
||||||
OTP_SECRET=
|
OTP_SECRET=
|
||||||
|
|
||||||
|
# Encryption secrets
|
||||||
|
# ------------------
|
||||||
|
# Must be available (and set to same values) for all server processes
|
||||||
|
# These are private/secret values, do not share outside hosting environment
|
||||||
|
# Use `bin/rails db:encryption:init` to generate fresh secrets
|
||||||
|
# Do not change these secrets once in use, as this would cause data loss and other issues
|
||||||
|
# ------------------
|
||||||
|
# ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY=
|
||||||
|
# ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT=
|
||||||
|
# ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY=
|
||||||
|
|
||||||
# Web Push
|
# Web Push
|
||||||
# --------
|
# --------
|
||||||
# Generate with `bundle exec rails mastodon:webpush:generate_vapid_key`
|
# Generate with `bundle exec rails mastodon:webpush:generate_vapid_key`
|
||||||
|
@ -64,7 +64,6 @@ module.exports = defineConfig({
|
|||||||
'indent': ['error', 2],
|
'indent': ['error', 2],
|
||||||
'jsx-quotes': ['error', 'prefer-single'],
|
'jsx-quotes': ['error', 'prefer-single'],
|
||||||
'semi': ['error', 'always'],
|
'semi': ['error', 'always'],
|
||||||
'no-case-declarations': 'off',
|
|
||||||
'no-catch-shadow': 'error',
|
'no-catch-shadow': 'error',
|
||||||
'no-console': [
|
'no-console': [
|
||||||
'warn',
|
'warn',
|
||||||
|
13
.github/ISSUE_TEMPLATE/1.web_bug_report.yml
vendored
@ -1,6 +1,7 @@
|
|||||||
name: Bug Report (Web Interface)
|
name: Bug Report (Web Interface)
|
||||||
description: If you are using Mastodon's web interface and something is not working as expected
|
description: There is a problem using Mastodon's web interface.
|
||||||
labels: [bug, 'status/to triage', 'area/web interface']
|
labels: ['status/to triage', 'area/web interface']
|
||||||
|
type: Bug
|
||||||
body:
|
body:
|
||||||
- type: markdown
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
@ -47,8 +48,8 @@ body:
|
|||||||
attributes:
|
attributes:
|
||||||
label: Mastodon version
|
label: Mastodon version
|
||||||
description: |
|
description: |
|
||||||
This is displayed at the bottom of the About page, eg. `v4.1.2+nightly-20230627`
|
This is displayed at the bottom of the About page, eg. `v4.4.0-alpha.1`
|
||||||
placeholder: v4.1.2
|
placeholder: v4.3.0
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: input
|
- type: input
|
||||||
@ -56,7 +57,7 @@ body:
|
|||||||
label: Browser name and version
|
label: Browser name and version
|
||||||
description: |
|
description: |
|
||||||
What browser are you using when getting this bug? Please specify the version as well.
|
What browser are you using when getting this bug? Please specify the version as well.
|
||||||
placeholder: Firefox 105.0.3
|
placeholder: Firefox 131.0.0
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: input
|
- type: input
|
||||||
@ -64,7 +65,7 @@ body:
|
|||||||
label: Operating system
|
label: Operating system
|
||||||
description: |
|
description: |
|
||||||
What OS are you running? Please specify the version as well.
|
What OS are you running? Please specify the version as well.
|
||||||
placeholder: macOS 13.4.1
|
placeholder: macOS 15.0.1
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
|
13
.github/ISSUE_TEMPLATE/2.server_bug_report.yml
vendored
@ -1,7 +1,8 @@
|
|||||||
name: Bug Report (server / API)
|
name: Bug Report (server / API)
|
||||||
description: |
|
description: |
|
||||||
If something is not working as expected, but is not from using the web interface.
|
There is a problem with the HTTP server, REST API, ActivityPub interaction, etc.
|
||||||
labels: [bug, 'status/to triage']
|
labels: ['status/to triage']
|
||||||
|
type: 'Bug'
|
||||||
body:
|
body:
|
||||||
- type: markdown
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
@ -48,8 +49,8 @@ body:
|
|||||||
attributes:
|
attributes:
|
||||||
label: Mastodon version
|
label: Mastodon version
|
||||||
description: |
|
description: |
|
||||||
This is displayed at the bottom of the About page, eg. `v4.1.2+nightly-20230627`
|
This is displayed at the bottom of the About page, eg. `v4.4.0-alpha.1`
|
||||||
placeholder: v4.1.2
|
placeholder: v4.3.0
|
||||||
validations:
|
validations:
|
||||||
required: false
|
required: false
|
||||||
- type: textarea
|
- type: textarea
|
||||||
@ -59,7 +60,7 @@ body:
|
|||||||
Any additional technical details you may have, like logs or error traces
|
Any additional technical details you may have, like logs or error traces
|
||||||
value: |
|
value: |
|
||||||
If this is happening on your own Mastodon server, please fill out those:
|
If this is happening on your own Mastodon server, please fill out those:
|
||||||
- Ruby version: (from `ruby --version`, eg. v3.1.2)
|
- Ruby version: (from `ruby --version`, eg. v3.3.5)
|
||||||
- Node.js version: (from `node --version`, eg. v18.16.0)
|
- Node.js version: (from `node --version`, eg. v20.18.0)
|
||||||
validations:
|
validations:
|
||||||
required: false
|
required: false
|
||||||
|
74
.github/ISSUE_TEMPLATE/3.troubleshooting.yml
vendored
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
name: Deployment troubleshooting
|
||||||
|
description: |
|
||||||
|
You are a server administrator and you are encountering a technical issue during installation, upgrade or operations of Mastodon.
|
||||||
|
labels: ['status/to triage']
|
||||||
|
type: 'Troubleshooting'
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
Make sure that you are submitting a new bug that was not previously reported or already fixed.
|
||||||
|
|
||||||
|
Please use a concise and distinct title for the issue.
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Steps to reproduce the problem
|
||||||
|
description: What were you trying to do?
|
||||||
|
value: |
|
||||||
|
1.
|
||||||
|
2.
|
||||||
|
3.
|
||||||
|
...
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: input
|
||||||
|
attributes:
|
||||||
|
label: Expected behaviour
|
||||||
|
description: What should have happened?
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: input
|
||||||
|
attributes:
|
||||||
|
label: Actual behaviour
|
||||||
|
description: What happened?
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Detailed description
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
- type: input
|
||||||
|
attributes:
|
||||||
|
label: Mastodon instance
|
||||||
|
description: The address of the Mastodon instance where you experienced the issue
|
||||||
|
placeholder: mastodon.social
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: input
|
||||||
|
attributes:
|
||||||
|
label: Mastodon version
|
||||||
|
description: |
|
||||||
|
This is displayed at the bottom of the About page, eg. `v4.4.0-alpha.1`
|
||||||
|
placeholder: v4.3.0
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Environment
|
||||||
|
description: |
|
||||||
|
Details about your environment, like how Mastodon is deployed, if containers are used, version numbers, etc.
|
||||||
|
value: |
|
||||||
|
Please at least include those informations:
|
||||||
|
- Operating system: (eg. Ubuntu 22.04)
|
||||||
|
- Ruby version: (from `ruby --version`, eg. v3.3.5)
|
||||||
|
- Node.js version: (from `node --version`, eg. v20.18.0)
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Technical details
|
||||||
|
description: |
|
||||||
|
Any additional technical details you may have, like logs or error traces
|
||||||
|
validations:
|
||||||
|
required: false
|
@ -1,6 +1,6 @@
|
|||||||
name: Feature Request
|
name: Feature Request
|
||||||
description: I have a suggestion
|
description: I have a suggestion
|
||||||
labels: [suggestion]
|
type: Suggestion
|
||||||
body:
|
body:
|
||||||
- type: markdown
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
1
.github/workflows/build-container-image.yml
vendored
@ -92,6 +92,7 @@ jobs:
|
|||||||
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 }}
|
||||||
|
SOURCE_COMMIT=${{ github.sha }}
|
||||||
platforms: ${{ inputs.platforms }}
|
platforms: ${{ inputs.platforms }}
|
||||||
provenance: false
|
provenance: false
|
||||||
builder: ${{ steps.buildx.outputs.name || steps.buildx-native.outputs.name }}
|
builder: ${{ steps.buildx.outputs.name || steps.buildx-native.outputs.name }}
|
||||||
|
6
.github/workflows/build-push-pr.yml
vendored
@ -21,9 +21,11 @@ jobs:
|
|||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
- id: version_vars
|
- id: version_vars
|
||||||
run: |
|
run: |
|
||||||
echo mastodon_version_metadata=pr-${{ github.event.pull_request.number }}-$(git rev-parse --short HEAD) >> $GITHUB_OUTPUT
|
echo mastodon_version_metadata=pr-${{ github.event.pull_request.number }}-$(git rev-parse --short ${{github.event.pull_request.head.sha}}) >> $GITHUB_OUTPUT
|
||||||
|
echo mastodon_short_sha=$(git rev-parse --short ${{github.event.pull_request.head.sha}}) >> $GITHUB_OUTPUT
|
||||||
outputs:
|
outputs:
|
||||||
metadata: ${{ steps.version_vars.outputs.mastodon_version_metadata }}
|
metadata: ${{ steps.version_vars.outputs.mastodon_version_metadata }}
|
||||||
|
short_sha: ${{ steps.version_vars.outputs.mastodon_short_sha }}
|
||||||
|
|
||||||
build-image:
|
build-image:
|
||||||
needs: compute-suffix
|
needs: compute-suffix
|
||||||
@ -39,6 +41,7 @@ jobs:
|
|||||||
latest=auto
|
latest=auto
|
||||||
tags: |
|
tags: |
|
||||||
type=ref,event=pr
|
type=ref,event=pr
|
||||||
|
type=ref,event=pr,suffix=-${{ needs.compute-suffix.outputs.short_sha }}
|
||||||
secrets: inherit
|
secrets: inherit
|
||||||
|
|
||||||
build-image-streaming:
|
build-image-streaming:
|
||||||
@ -55,4 +58,5 @@ jobs:
|
|||||||
latest=auto
|
latest=auto
|
||||||
tags: |
|
tags: |
|
||||||
type=ref,event=pr
|
type=ref,event=pr
|
||||||
|
type=ref,event=pr,suffix=-${{ needs.compute-suffix.outputs.short_sha }}
|
||||||
secrets: inherit
|
secrets: inherit
|
||||||
|
2
.github/workflows/build-releases.yml
vendored
@ -23,7 +23,7 @@ jobs:
|
|||||||
# Only tag with latest when ran against the latest stable branch
|
# Only tag with latest when ran against the latest stable branch
|
||||||
# This needs to be updated after each minor version release
|
# This needs to be updated after each minor version release
|
||||||
flavor: |
|
flavor: |
|
||||||
latest=${{ startsWith(github.ref, 'refs/tags/v4.2.') }}
|
latest=${{ startsWith(github.ref, 'refs/tags/v4.3.') }}
|
||||||
tags: |
|
tags: |
|
||||||
type=pep440,pattern={{raw}}
|
type=pep440,pattern={{raw}}
|
||||||
type=pep440,pattern=v{{major}}.{{minor}}
|
type=pep440,pattern=v{{major}}.{{minor}}
|
||||||
|
2
.github/workflows/bundler-audit.yml
vendored
@ -36,4 +36,4 @@ jobs:
|
|||||||
bundler-cache: true
|
bundler-cache: true
|
||||||
|
|
||||||
- name: Run bundler-audit
|
- name: Run bundler-audit
|
||||||
run: bundle exec bundler-audit check --update
|
run: bin/bundler-audit check --update
|
||||||
|
12
.github/workflows/check-i18n.yml
vendored
@ -18,7 +18,7 @@ permissions:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
check-i18n:
|
check-i18n:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-24.04
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
@ -35,18 +35,18 @@ jobs:
|
|||||||
git diff --exit-code
|
git diff --exit-code
|
||||||
|
|
||||||
- name: Check locale file normalization
|
- name: Check locale file normalization
|
||||||
run: bundle exec i18n-tasks check-normalized
|
run: bin/i18n-tasks check-normalized
|
||||||
|
|
||||||
- name: Check for unused strings
|
- name: Check for unused strings
|
||||||
run: bundle exec i18n-tasks unused
|
run: bin/i18n-tasks unused
|
||||||
|
|
||||||
- name: Check for missing strings in English YML
|
- name: Check for missing strings in English YML
|
||||||
run: |
|
run: |
|
||||||
bundle exec i18n-tasks add-missing -l en
|
bin/i18n-tasks add-missing -l en
|
||||||
git diff --exit-code
|
git diff --exit-code
|
||||||
|
|
||||||
- name: Check for wrong string interpolations
|
- name: Check for wrong string interpolations
|
||||||
run: bundle exec i18n-tasks check-consistent-interpolations
|
run: bin/i18n-tasks check-consistent-interpolations
|
||||||
|
|
||||||
- name: Check that all required locale files exist
|
- name: Check that all required locale files exist
|
||||||
run: bundle exec rake repo:check_locales_files
|
run: bin/rake repo:check_locales_files
|
||||||
|
69
.github/workflows/crowdin-download-stable.yml
vendored
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
name: Crowdin / Download translations (stable branches)
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
pull-requests: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
download-translations-stable:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: github.repository == 'mastodon/mastodon'
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Increase Git http.postBuffer
|
||||||
|
# This is needed due to a bug in Ubuntu's cURL version?
|
||||||
|
# See https://github.com/orgs/community/discussions/55820
|
||||||
|
run: |
|
||||||
|
git config --global http.version HTTP/1.1
|
||||||
|
git config --global http.postBuffer 157286400
|
||||||
|
|
||||||
|
# Download the translation files from Crowdin
|
||||||
|
- name: crowdin action
|
||||||
|
uses: crowdin/github-action@v2
|
||||||
|
with:
|
||||||
|
upload_sources: false
|
||||||
|
upload_translations: false
|
||||||
|
download_translations: true
|
||||||
|
crowdin_branch_name: ${{ github.base_ref || github.ref_name }}
|
||||||
|
push_translations: false
|
||||||
|
create_pull_request: false
|
||||||
|
env:
|
||||||
|
CROWDIN_PROJECT_ID: ${{ vars.CROWDIN_PROJECT_ID }}
|
||||||
|
CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }}
|
||||||
|
|
||||||
|
# As the files are extracted from a Docker container, they belong to root:root
|
||||||
|
# We need to fix this before the next steps
|
||||||
|
- name: Fix file permissions
|
||||||
|
run: sudo chown -R runner:docker .
|
||||||
|
|
||||||
|
# This is needed to run the normalize step
|
||||||
|
- name: Set up Ruby environment
|
||||||
|
uses: ./.github/actions/setup-ruby
|
||||||
|
|
||||||
|
- name: Run i18n normalize task
|
||||||
|
run: bin/i18n-tasks normalize
|
||||||
|
|
||||||
|
# Create or update the pull request
|
||||||
|
- name: Create Pull Request
|
||||||
|
uses: peter-evans/create-pull-request@v7.0.5
|
||||||
|
with:
|
||||||
|
commit-message: 'New Crowdin translations'
|
||||||
|
title: 'New Crowdin Translations for ${{ github.base_ref || github.ref_name }} (automated)'
|
||||||
|
author: 'GitHub Actions <noreply@github.com>'
|
||||||
|
body: |
|
||||||
|
New Crowdin translations, automated with GitHub Actions
|
||||||
|
|
||||||
|
See `.github/workflows/crowdin-download.yml`
|
||||||
|
|
||||||
|
This PR will be updated every day with new translations.
|
||||||
|
|
||||||
|
Due to a limitation in GitHub Actions, checks are not running on this PR without manual action.
|
||||||
|
If you want to run the checks, then close and re-open it.
|
||||||
|
branch: i18n/crowdin/translations-${{ github.base_ref || github.ref_name }}
|
||||||
|
base: ${{ github.base_ref || github.ref_name }}
|
||||||
|
labels: i18n
|
4
.github/workflows/crowdin-download.yml
vendored
@ -48,11 +48,11 @@ jobs:
|
|||||||
uses: ./.github/actions/setup-ruby
|
uses: ./.github/actions/setup-ruby
|
||||||
|
|
||||||
- name: Run i18n normalize task
|
- name: Run i18n normalize task
|
||||||
run: bundle exec i18n-tasks normalize
|
run: bin/i18n-tasks normalize
|
||||||
|
|
||||||
# Create or update the pull request
|
# Create or update the pull request
|
||||||
- name: Create Pull Request
|
- name: Create Pull Request
|
||||||
uses: peter-evans/create-pull-request@v7.0.1
|
uses: peter-evans/create-pull-request@v7.0.5
|
||||||
with:
|
with:
|
||||||
commit-message: 'New Crowdin translations'
|
commit-message: 'New Crowdin translations'
|
||||||
title: 'New Crowdin Translations (automated)'
|
title: 'New Crowdin Translations (automated)'
|
||||||
|
3
.github/workflows/crowdin-upload.yml
vendored
@ -1,7 +1,6 @@
|
|||||||
name: Crowdin / Upload translations
|
name: Crowdin / Upload translations
|
||||||
|
|
||||||
on:
|
on:
|
||||||
merge_group:
|
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- 'main'
|
- 'main'
|
||||||
@ -31,7 +30,7 @@ jobs:
|
|||||||
upload_sources: true
|
upload_sources: true
|
||||||
upload_translations: false
|
upload_translations: false
|
||||||
download_translations: false
|
download_translations: false
|
||||||
crowdin_branch_name: main
|
crowdin_branch_name: ${{ github.base_ref || github.ref_name }}
|
||||||
|
|
||||||
env:
|
env:
|
||||||
CROWDIN_PROJECT_ID: ${{ vars.CROWDIN_PROJECT_ID }}
|
CROWDIN_PROJECT_ID: ${{ vars.CROWDIN_PROJECT_ID }}
|
||||||
|
2
.github/workflows/lint-haml.yml
vendored
@ -43,4 +43,4 @@ jobs:
|
|||||||
- name: Run haml-lint
|
- name: Run haml-lint
|
||||||
run: |
|
run: |
|
||||||
echo "::add-matcher::.github/workflows/haml-lint-problem-matcher.json"
|
echo "::add-matcher::.github/workflows/haml-lint-problem-matcher.json"
|
||||||
bundle exec haml-lint --reporter github
|
bin/haml-lint --reporter github
|
||||||
|
8
.github/workflows/test-migrations.yml
vendored
@ -12,6 +12,7 @@ on:
|
|||||||
- '**/*.rb'
|
- '**/*.rb'
|
||||||
- '.github/workflows/test-migrations.yml'
|
- '.github/workflows/test-migrations.yml'
|
||||||
- 'lib/tasks/tests.rake'
|
- 'lib/tasks/tests.rake'
|
||||||
|
- 'lib/tasks/db.rake'
|
||||||
|
|
||||||
pull_request:
|
pull_request:
|
||||||
paths:
|
paths:
|
||||||
@ -32,6 +33,8 @@ jobs:
|
|||||||
postgres:
|
postgres:
|
||||||
- 14-alpine
|
- 14-alpine
|
||||||
- 15-alpine
|
- 15-alpine
|
||||||
|
- 16-alpine
|
||||||
|
- 17-alpine
|
||||||
|
|
||||||
services:
|
services:
|
||||||
postgres:
|
postgres:
|
||||||
@ -88,6 +91,11 @@ jobs:
|
|||||||
bin/rails db:drop
|
bin/rails db:drop
|
||||||
bin/rails db:create
|
bin/rails db:create
|
||||||
SKIP_POST_DEPLOYMENT_MIGRATIONS=true bin/rails tests:migrations:prepare_database
|
SKIP_POST_DEPLOYMENT_MIGRATIONS=true bin/rails tests:migrations:prepare_database
|
||||||
|
|
||||||
|
# Migrate up to v4.2.0 breakpoint
|
||||||
|
bin/rails db:migrate VERSION=20230907150100
|
||||||
|
|
||||||
|
# Migrate the rest
|
||||||
SKIP_POST_DEPLOYMENT_MIGRATIONS=true bin/rails db:migrate
|
SKIP_POST_DEPLOYMENT_MIGRATIONS=true bin/rails db:migrate
|
||||||
bin/rails db:migrate
|
bin/rails db:migrate
|
||||||
bin/rails tests:migrations:check_database
|
bin/rails tests:migrations:check_database
|
||||||
|
12
.github/workflows/test-ruby.yml
vendored
@ -124,7 +124,6 @@ jobs:
|
|||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
ruby-version:
|
ruby-version:
|
||||||
- '3.1'
|
|
||||||
- '3.2'
|
- '3.2'
|
||||||
- '.ruby-version'
|
- '.ruby-version'
|
||||||
steps:
|
steps:
|
||||||
@ -143,7 +142,7 @@ jobs:
|
|||||||
uses: ./.github/actions/setup-ruby
|
uses: ./.github/actions/setup-ruby
|
||||||
with:
|
with:
|
||||||
ruby-version: ${{ matrix.ruby-version}}
|
ruby-version: ${{ matrix.ruby-version}}
|
||||||
additional-system-dependencies: ffmpeg libpam-dev
|
additional-system-dependencies: ffmpeg imagemagick libpam-dev
|
||||||
|
|
||||||
- name: Load database schema
|
- name: Load database schema
|
||||||
run: |
|
run: |
|
||||||
@ -226,7 +225,6 @@ jobs:
|
|||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
ruby-version:
|
ruby-version:
|
||||||
- '3.1'
|
|
||||||
- '3.2'
|
- '3.2'
|
||||||
- '.ruby-version'
|
- '.ruby-version'
|
||||||
steps:
|
steps:
|
||||||
@ -245,7 +243,7 @@ jobs:
|
|||||||
uses: ./.github/actions/setup-ruby
|
uses: ./.github/actions/setup-ruby
|
||||||
with:
|
with:
|
||||||
ruby-version: ${{ matrix.ruby-version}}
|
ruby-version: ${{ matrix.ruby-version}}
|
||||||
additional-system-dependencies: ffmpeg libpam-dev libyaml-dev
|
additional-system-dependencies: ffmpeg libpam-dev
|
||||||
|
|
||||||
- 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'
|
||||||
@ -305,7 +303,6 @@ jobs:
|
|||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
ruby-version:
|
ruby-version:
|
||||||
- '3.1'
|
|
||||||
- '3.2'
|
- '3.2'
|
||||||
- '.ruby-version'
|
- '.ruby-version'
|
||||||
|
|
||||||
@ -325,7 +322,7 @@ jobs:
|
|||||||
uses: ./.github/actions/setup-ruby
|
uses: ./.github/actions/setup-ruby
|
||||||
with:
|
with:
|
||||||
ruby-version: ${{ matrix.ruby-version}}
|
ruby-version: ${{ matrix.ruby-version}}
|
||||||
additional-system-dependencies: ffmpeg
|
additional-system-dependencies: ffmpeg imagemagick
|
||||||
|
|
||||||
- name: Set up Javascript environment
|
- name: Set up Javascript environment
|
||||||
uses: ./.github/actions/setup-javascript
|
uses: ./.github/actions/setup-javascript
|
||||||
@ -422,7 +419,6 @@ jobs:
|
|||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
ruby-version:
|
ruby-version:
|
||||||
- '3.1'
|
|
||||||
- '3.2'
|
- '3.2'
|
||||||
- '.ruby-version'
|
- '.ruby-version'
|
||||||
search-image:
|
search-image:
|
||||||
@ -445,7 +441,7 @@ jobs:
|
|||||||
uses: ./.github/actions/setup-ruby
|
uses: ./.github/actions/setup-ruby
|
||||||
with:
|
with:
|
||||||
ruby-version: ${{ matrix.ruby-version}}
|
ruby-version: ${{ matrix.ruby-version}}
|
||||||
additional-system-dependencies: ffmpeg
|
additional-system-dependencies: ffmpeg imagemagick
|
||||||
|
|
||||||
- name: Set up Javascript environment
|
- name: Set up Javascript environment
|
||||||
uses: ./.github/actions/setup-javascript
|
uses: ./.github/actions/setup-javascript
|
||||||
|
@ -8,7 +8,7 @@ AllCops:
|
|||||||
- lib/mastodon/migration_helpers.rb
|
- lib/mastodon/migration_helpers.rb
|
||||||
ExtraDetails: true
|
ExtraDetails: true
|
||||||
NewCops: enable
|
NewCops: enable
|
||||||
TargetRubyVersion: 3.1 # Oldest supported ruby version
|
TargetRubyVersion: 3.2 # Oldest supported ruby version
|
||||||
|
|
||||||
inherit_from:
|
inherit_from:
|
||||||
- .rubocop/layout.yml
|
- .rubocop/layout.yml
|
||||||
|
@ -1 +1 @@
|
|||||||
3.3.5
|
3.3.6
|
||||||
|
110
CHANGELOG.md
@ -2,7 +2,49 @@
|
|||||||
|
|
||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
## [4.3.0] - UNRELEASED
|
## [4.3.1] - 2024-10-21
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Add more explicit explanations about author attribution and `fediverse:creator` (#32383 by @ClearlyClaire)
|
||||||
|
- Add ability to group follow notifications in WebUI, can be disabled in the column settings (#32520 by @renchap)
|
||||||
|
- Add back a 6 hours mute duration option (#32522 by @renchap)
|
||||||
|
- Add note about not changing ActiveRecord encryption secrets once they are set (#32413, #32476, #32512, and #32537 by @ClearlyClaire and @mjankowski)
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Change translation feature to translate to selected regional variant (e.g. pt-BR) if available (#32428 by @c960657)
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
|
||||||
|
- Remove ability to get embed code for remote posts (#32578 by @ClearlyClaire)\
|
||||||
|
Getting the embed code is only reliable for local posts.\
|
||||||
|
It never worked for non-Mastodon servers, and stopped working correctly with the changes made in 4.3.0.\
|
||||||
|
We have therefore decided to remove the menu entry while we investigate solutions.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Fix follow recommendation moderation page default language when using regional variant (#32580 by @ClearlyClaire)
|
||||||
|
- Fix column-settings spacing in local timeline in advanced view (#32567 by @lindwurm)
|
||||||
|
- Fix broken i18n in text welcome mailer tags area (#32571 by @mjankowski)
|
||||||
|
- Fix missing or incorrect cache-control headers for Streaming server (#32551 by @ThisIsMissEm)
|
||||||
|
- Fix only the first paragraph being displayed in some notifications (#32348 by @ClearlyClaire)
|
||||||
|
- Fix reblog icons on account media view (#32506 by @tribela)
|
||||||
|
- Fix Content-Security-Policy not allowing OpenStack SWIFT object storage URI (#32439 by @kenkiku1021)
|
||||||
|
- Fix back arrow pointing to the incorrect direction in RTL languages (#32485 by @renchap)
|
||||||
|
- Fix streaming server using `REDIS_USERNAME` instead of `REDIS_USER` (#32493 by @ThisIsMissEm)
|
||||||
|
- Fix follow recommendation carrousel scrolling on RTL layouts (#32462 and #32505 by @ClearlyClaire)
|
||||||
|
- Fix follow recommendation suppressions not applying immediately (#32392 by @ClearlyClaire)
|
||||||
|
- Fix language of push notifications (#32415 by @ClearlyClaire)
|
||||||
|
- Fix mute duration not being shown in list of muted accounts in web UI (#32388 by @ClearlyClaire)
|
||||||
|
- Fix “Mark every notification as read” not updating the read marker if scrolled down (#32385 by @ClearlyClaire)
|
||||||
|
- Fix “Mention” appearing for otherwise filtered posts (#32356 by @ClearlyClaire)
|
||||||
|
- Fix notification requests from suspended accounts still being listed (#32354 by @ClearlyClaire)
|
||||||
|
- Fix list edition modal styling (#32358 and #32367 by @ClearlyClaire and @vmstan)
|
||||||
|
- Fix 4 columns barely not fitting on 1920px screen (#32361 by @ClearlyClaire)
|
||||||
|
- Fix icon alignment in applications list (#32293 by @mjankowski)
|
||||||
|
|
||||||
|
## [4.3.0] - 2024-10-08
|
||||||
|
|
||||||
The following changelog entries focus on changes visible to users, administrators, client developers or federated software developers, but there has also been a lot of code modernization, refactoring, and tooling work, in particular by @mjankowski.
|
The following changelog entries focus on changes visible to users, administrators, client developers or federated software developers, but there has also been a lot of code modernization, refactoring, and tooling work, in particular by @mjankowski.
|
||||||
|
|
||||||
@ -10,12 +52,13 @@ The following changelog entries focus on changes visible to users, administrator
|
|||||||
|
|
||||||
- **Add confirmation interstitial instead of silently redirecting logged-out visitors to remote resources** (#27792, #28902, and #30651 by @ClearlyClaire and @Gargron)\
|
- **Add confirmation interstitial instead of silently redirecting logged-out visitors to remote resources** (#27792, #28902, and #30651 by @ClearlyClaire and @Gargron)\
|
||||||
This fixes a longstanding open redirect in Mastodon, at the cost of added friction when local links to remote resources are shared.
|
This fixes a longstanding open redirect in Mastodon, at the cost of added friction when local links to remote resources are shared.
|
||||||
- Change `form-action` Content-Security-Policy directive to be more restrictive (#26897 by @ClearlyClaire)
|
- Fix ReDoS vulnerability on some Ruby versions ([GHSA-jpxp-r43f-rhvx](https://github.com/mastodon/mastodon/security/advisories/GHSA-jpxp-r43f-rhvx))
|
||||||
|
- Change `form-action` Content-Security-Policy directive to be more restrictive (#26897 and #32241 by @ClearlyClaire)
|
||||||
- Update dependencies
|
- Update dependencies
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- **Add server-side notification grouping** (#29889, #30576, #30685, #30688, #30707, #30776, #30779, #30781, #30440, #31062, #31098, #31076, #31111, #31123, #31223, #31214, #31224, #31299, #31325, #31347, #31304, #31326, #31384, #31403, #31433, #31509, #31486, #31513, #31592, #31594, #31638, #31746, #31652, #31709, #31725, #31745, #31613, #31657, #31840, #31610 and #31929 by @ClearlyClaire, @Gargron, @mgmn, and @renchap)\
|
- **Add server-side notification grouping** (#29889, #30576, #30685, #30688, #30707, #30776, #30779, #30781, #30440, #31062, #31098, #31076, #31111, #31123, #31223, #31214, #31224, #31299, #31325, #31347, #31304, #31326, #31384, #31403, #31433, #31509, #31486, #31513, #31592, #31594, #31638, #31746, #31652, #31709, #31725, #31745, #31613, #31657, #31840, #31610, #31929, #32089, #32085, #32243, #32179 and #32254 by @ClearlyClaire, @Gargron, @mgmn, and @renchap)\
|
||||||
Group notifications of the same type for the same target, so that your notifications no longer get cluttered by boost and favorite notifications as soon as a couple of your posts get traction.\
|
Group notifications of the same type for the same target, so that your notifications no longer get cluttered by boost and favorite notifications as soon as a couple of your posts get traction.\
|
||||||
This is done server-side so that clients can efficiently get relevant groups without having to go through numerous pages of individual notifications.\
|
This is done server-side so that clients can efficiently get relevant groups without having to go through numerous pages of individual notifications.\
|
||||||
As part of this, the visual design of the entire notifications feature has been revamped.\
|
As part of this, the visual design of the entire notifications feature has been revamped.\
|
||||||
@ -25,9 +68,9 @@ The following changelog entries focus on changes visible to users, administrator
|
|||||||
- `GET /api/v2/notifications`: https://docs.joinmastodon.org/methods/grouped_notifications/#get-grouped
|
- `GET /api/v2/notifications`: https://docs.joinmastodon.org/methods/grouped_notifications/#get-grouped
|
||||||
- `GET /api/v2/notifications/:group_key`: https://docs.joinmastodon.org/methods/grouped_notifications/#get-notification-group
|
- `GET /api/v2/notifications/:group_key`: https://docs.joinmastodon.org/methods/grouped_notifications/#get-notification-group
|
||||||
- `GET /api/v2/notifications/:group_key/accounts`: https://docs.joinmastodon.org/methods/grouped_notifications/#get-group-accounts
|
- `GET /api/v2/notifications/:group_key/accounts`: https://docs.joinmastodon.org/methods/grouped_notifications/#get-group-accounts
|
||||||
- `POST /api/v2/notifications/:group_key/dimsiss`: https://docs.joinmastodon.org/methods/grouped_notifications/#dismiss-group
|
- `POST /api/v2/notifications/:group_key/dismiss`: https://docs.joinmastodon.org/methods/grouped_notifications/#dismiss-group
|
||||||
- `GET /api/v2/notifications/:unread_count`: https://docs.joinmastodon.org/methods/grouped_notifications/#unread-group-count
|
- `GET /api/v2/notifications/:unread_count`: https://docs.joinmastodon.org/methods/grouped_notifications/#unread-group-count
|
||||||
- **Add notification policies, filtered notifications and notification requests** (#29366, #29529, #29433, #29565, #29567, #29572, #29575, #29588, #29646, #29652, #29658, #29666, #29693, #29699, #29737, #29706, #29570, #29752, #29810, #29826, #30114, #30251, #30559, #29868, #31008, #31011, #30996, #31149, #31220, #31222, #31225, #31242, #31262, #31250, #31273, #31310, #31316, #31322, #31329, #31324, #31331, #31343, #31342, #31309, #31358, #31378, #31406, #31256, #31456, #31419, #31457, #31508, #31540, #31541, and #31723 by @ClearlyClaire, @Gargron, @TheEssem, @mgmn, @oneiros, and @renchap)\
|
- **Add notification policies, filtered notifications and notification requests** (#29366, #29529, #29433, #29565, #29567, #29572, #29575, #29588, #29646, #29652, #29658, #29666, #29693, #29699, #29737, #29706, #29570, #29752, #29810, #29826, #30114, #30251, #30559, #29868, #31008, #31011, #30996, #31149, #31220, #31222, #31225, #31242, #31262, #31250, #31273, #31310, #31316, #31322, #31329, #31324, #31331, #31343, #31342, #31309, #31358, #31378, #31406, #31256, #31456, #31419, #31457, #31508, #31540, #31541, #31723, #32062 and #32281 by @ClearlyClaire, @Gargron, @TheEssem, @mgmn, @oneiros, and @renchap)\
|
||||||
The old “Block notifications from non-followers”, “Block notifications from people you don't follow” and “Block direct messages from people you don't follow” notification settings have been replaced by a new set of settings found directly in the notification column.\
|
The old “Block notifications from non-followers”, “Block notifications from people you don't follow” and “Block direct messages from people you don't follow” notification settings have been replaced by a new set of settings found directly in the notification column.\
|
||||||
You can now separately filter or drop notifications from people you don't follow, people who don't follow you, accounts created within the past 30 days, as well as unsolicited private mentions, and accounts limited by the moderation.\
|
You can now separately filter or drop notifications from people you don't follow, people who don't follow you, accounts created within the past 30 days, as well as unsolicited private mentions, and accounts limited by the moderation.\
|
||||||
Instead of being outright dropped, notifications that you chose to filter are put in a separate “Filtered notifications” box that you can review separately without it clogging your main notifications.\
|
Instead of being outright dropped, notifications that you chose to filter are put in a separate “Filtered notifications” box that you can review separately without it clogging your main notifications.\
|
||||||
@ -60,13 +103,13 @@ The following changelog entries focus on changes visible to users, administrator
|
|||||||
- **Add timeline of public posts about a trending link** (#30381 and #30840 by @Gargron)\
|
- **Add timeline of public posts about a trending link** (#30381 and #30840 by @Gargron)\
|
||||||
You can now see public posts mentioning currently-trending articles from people who have opted into discovery features.\
|
You can now see public posts mentioning currently-trending articles from people who have opted into discovery features.\
|
||||||
This adds a new REST API endpoint: https://docs.joinmastodon.org/methods/timelines/#link
|
This adds a new REST API endpoint: https://docs.joinmastodon.org/methods/timelines/#link
|
||||||
- **Add author highlight for news articles whose authors are on the fediverse** (#30398, #30670, #30521, #30846, #31819, and #31900 by @Gargron and @oneiros)\
|
- **Add author highlight for news articles whose authors are on the fediverse** (#30398, #30670, #30521, #30846, #31819, #31900 and #32188 by @Gargron, @mjankowski and @oneiros)\
|
||||||
This adds a mechanism to [highlight the author of news articles](https://blog.joinmastodon.org/2024/07/highlighting-journalism-on-mastodon/) shared on Mastodon.\
|
This adds a mechanism to [highlight the author of news articles](https://blog.joinmastodon.org/2024/07/highlighting-journalism-on-mastodon/) shared on Mastodon.\
|
||||||
Articles hosted outside the fediverse can indicate a fediverse author with a meta tag:
|
Articles hosted outside the fediverse can indicate a fediverse author with a meta tag:
|
||||||
```html
|
```html
|
||||||
<meta name="fediverse:creator" content="username@domain" />
|
<meta name="fediverse:creator" content="username@domain" />
|
||||||
```
|
```
|
||||||
On the API side, this is represented by a new `authors` attribute to the `PreviewCard` entity: https://docs.joinmastodon.org/entities/PreviewCard/#authors\
|
On the API side, this is represented by a new `authors` attribute to the `PreviewCard` entity: https://docs.joinmastodon.org/entities/PreviewCard/#authors \
|
||||||
Users can allow arbitrary domains to use `fediverse:creator` to credit them by visiting `/settings/verification`.\
|
Users can allow arbitrary domains to use `fediverse:creator` to credit them by visiting `/settings/verification`.\
|
||||||
This is federated as a new `attributionDomains` property in the `http://joinmastodon.org/ns` namespace, containing an array of domain names: https://docs.joinmastodon.org/spec/activitypub/#properties-used-1
|
This is federated as a new `attributionDomains` property in the `http://joinmastodon.org/ns` namespace, containing an array of domain names: https://docs.joinmastodon.org/spec/activitypub/#properties-used-1
|
||||||
- **Add in-app notifications for moderation actions and warnings** (#30065, #30082, and #30081 by @ClearlyClaire)\
|
- **Add in-app notifications for moderation actions and warnings** (#30065, #30082, and #30081 by @ClearlyClaire)\
|
||||||
@ -76,7 +119,11 @@ The following changelog entries focus on changes visible to users, administrator
|
|||||||
Clicking the domain of a user in their profile will now open a tooltip with a short explanation about servers and federation.
|
Clicking the domain of a user in their profile will now open a tooltip with a short explanation about servers and federation.
|
||||||
- **Add support for Redis sentinel** (#31694, #31623, #31744, #31767, and #31768 by @ThisIsMissEm and @oneiros)\
|
- **Add support for Redis sentinel** (#31694, #31623, #31744, #31767, and #31768 by @ThisIsMissEm and @oneiros)\
|
||||||
See https://docs.joinmastodon.org/admin/scaling/#redis-sentinel
|
See https://docs.joinmastodon.org/admin/scaling/#redis-sentinel
|
||||||
- Add ability to reorder uploaded media before posting in web UI (#28456 by @Gargron)
|
- **Add ability to reorder uploaded media before posting in web UI** (#28456 and #32093 by @Gargron)
|
||||||
|
- Add “A Mastodon update is available.” message on admin dashboard for non-bugfix updates (#32106 by @ClearlyClaire)
|
||||||
|
- Add ability to view alt text by clicking the ALT badge in web UI (#32058 by @Gargron)
|
||||||
|
- Add preview of followers removed in domain block modal in web UI (#32032 and #32105 by @ClearlyClaire and @Gargron)
|
||||||
|
- Add reblogs and favourites counts to statuses in ActivityPub (#32007 by @Gargron)
|
||||||
- Add moderation interface for searching hashtags (#30880 by @ThisIsMissEm)
|
- Add moderation interface for searching hashtags (#30880 by @ThisIsMissEm)
|
||||||
- Add ability for admins to configure instance favicon and logo (#30040, #30208, #30259, #30375, #30734, #31016, and #30205 by @ClearlyClaire, @FawazFarid, @JasonPunyon, @mgmn, and @renchap)\
|
- Add ability for admins to configure instance favicon and logo (#30040, #30208, #30259, #30375, #30734, #31016, and #30205 by @ClearlyClaire, @FawazFarid, @JasonPunyon, @mgmn, and @renchap)\
|
||||||
This is also exposed through the REST API: https://docs.joinmastodon.org/entities/Instance/#icon
|
This is also exposed through the REST API: https://docs.joinmastodon.org/entities/Instance/#icon
|
||||||
@ -122,14 +169,14 @@ The following changelog entries focus on changes visible to users, administrator
|
|||||||
- Add Interlingue and Interlingua to interface languages (#28630 and #30828 by @Dhghomon and @renchap)
|
- Add Interlingue and Interlingua to interface languages (#28630 and #30828 by @Dhghomon and @renchap)
|
||||||
- Add Kashubian, Pennsylvania Dutch, Vai, Jawi Malay, Mohawk and Low German to posting languages (#26024, #26634, #27136, #29098, #27115, and #27434 by @EngineerDali, @HelgeKrueger, and @gunchleoc)
|
- Add Kashubian, Pennsylvania Dutch, Vai, Jawi Malay, Mohawk and Low German to posting languages (#26024, #26634, #27136, #29098, #27115, and #27434 by @EngineerDali, @HelgeKrueger, and @gunchleoc)
|
||||||
- Add option to use native Ruby driver for Redis through `REDIS_DRIVER=ruby` (#30717 by @vmstan)
|
- Add option to use native Ruby driver for Redis through `REDIS_DRIVER=ruby` (#30717 by @vmstan)
|
||||||
- Add support for libvips in addition to ImageMagick (#30090, #30590, #30597, #30632, #30857, #30869, and #30858 by @ClearlyClaire, @Gargron, and @mjankowski)\
|
- Add support for libvips in addition to ImageMagick (#30090, #30590, #30597, #30632, #30857, #30869, #30858 and #32104 by @ClearlyClaire, @Gargron, and @mjankowski)\
|
||||||
Server admins can now use libvips as a faster and lighter alternative to ImageMagick for processing user-uploaded images.\
|
Server admins can now use libvips as a faster and lighter alternative to ImageMagick for processing user-uploaded images.\
|
||||||
This requires libvips 8.13 or newer, and needs to be enabled with `MASTODON_USE_LIBVIPS=true`.\
|
This requires libvips 8.13 or newer, and needs to be enabled with `MASTODON_USE_LIBVIPS=true`.\
|
||||||
This is enabled by default in the official Docker images, and is intended to completely replace ImageMagick in the future.
|
This is enabled by default in the official Docker images, and is intended to completely replace ImageMagick in the future.
|
||||||
- Add validations to `Web::PushSubscription` (#30540 and #30542 by @ThisIsMissEm)
|
- Add validations to `Web::PushSubscription` (#30540 and #30542 by @ThisIsMissEm)
|
||||||
- Add anchors to each authorized application in `/oauth/authorized_applications` (#31677 by @fowl2)
|
- Add anchors to each authorized application in `/oauth/authorized_applications` (#31677 by @fowl2)
|
||||||
- Add active animation to header settings button (#30221, #30307, and #30388 by @daudix)
|
- Add active animation to header settings button (#30221, #30307, and #30388 by @daudix)
|
||||||
- Add OpenTelemetry instrumentation (#30130, #30322, #30353, and #30350 by @julianocosta89, @renchap, and @robbkidd)\
|
- Add OpenTelemetry instrumentation (#30130, #30322, #30353, #30350 and #31998 by @julianocosta89, @renchap, @robbkidd and @timetinytim)\
|
||||||
See https://docs.joinmastodon.org/admin/config/#otel for documentation
|
See https://docs.joinmastodon.org/admin/config/#otel for documentation
|
||||||
- Add API to get multiple accounts and statuses (#27871 and #30465 by @ClearlyClaire)\
|
- Add API to get multiple accounts and statuses (#27871 and #30465 by @ClearlyClaire)\
|
||||||
This adds `GET /api/v1/accounts` and `GET /api/v1/statuses` to the REST API, see https://docs.joinmastodon.org/methods/accounts/#index and https://docs.joinmastodon.org/methods/statuses/#index
|
This adds `GET /api/v1/accounts` and `GET /api/v1/statuses` to the REST API, see https://docs.joinmastodon.org/methods/accounts/#index and https://docs.joinmastodon.org/methods/statuses/#index
|
||||||
@ -138,7 +185,6 @@ The following changelog entries focus on changes visible to users, administrator
|
|||||||
- Add RFC8414 OAuth 2.0 server metadata (#29191 by @ThisIsMissEm)
|
- Add RFC8414 OAuth 2.0 server metadata (#29191 by @ThisIsMissEm)
|
||||||
- Add loading indicator and empty result message to advanced interface search (#30085 by @ClearlyClaire)
|
- Add loading indicator and empty result message to advanced interface search (#30085 by @ClearlyClaire)
|
||||||
- Add `profile` OAuth 2.0 scope, allowing more limited access to user data (#29087 and #30357 by @ThisIsMissEm)
|
- Add `profile` OAuth 2.0 scope, allowing more limited access to user data (#29087 and #30357 by @ThisIsMissEm)
|
||||||
- Add global Regexp timeout (#31928 by @ClearlyClaire)
|
|
||||||
- Add the role ID to the badge component (#29707 by @renchap)
|
- Add the role ID to the badge component (#29707 by @renchap)
|
||||||
- Add diagnostic message for failure during CLI search deploy (#29462 by @mjankowski)
|
- Add diagnostic message for failure during CLI search deploy (#29462 by @mjankowski)
|
||||||
- Add pagination `Link` headers on API accounts/statuses when pinned true (#29442 by @mjankowski)
|
- Add pagination `Link` headers on API accounts/statuses when pinned true (#29442 by @mjankowski)
|
||||||
@ -146,10 +192,12 @@ The following changelog entries focus on changes visible to users, administrator
|
|||||||
- Add groundwork for annual reports for accounts (#28693 by @Gargron)\
|
- Add groundwork for annual reports for accounts (#28693 by @Gargron)\
|
||||||
This lays the groundwork for a “year-in-review”/“wrapped” style report for local users, but is currently not in use.
|
This lays the groundwork for a “year-in-review”/“wrapped” style report for local users, but is currently not in use.
|
||||||
- Add notification email on invalid second authenticator (#28822 by @ClearlyClaire)
|
- Add notification email on invalid second authenticator (#28822 by @ClearlyClaire)
|
||||||
|
- Add date of account deletion in list of accounts in the admin interface (#25640 by @tribela)
|
||||||
- Add new emojis from `jdecked/twemoji` 15.0 (#28404 by @TheEssem)
|
- Add new emojis from `jdecked/twemoji` 15.0 (#28404 by @TheEssem)
|
||||||
- Add configurable error handling in attachment batch deletion (#28184 by @vmstan)\
|
- Add configurable error handling in attachment batch deletion (#28184 by @vmstan)\
|
||||||
This makes the S3 batch size configurable through the `S3_BATCH_DELETE_LIMIT` environment variable (defaults to 1000), and adds some retry logic, configurable through the `S3_BATCH_DELETE_RETRY` environment variable (defaults to 3).
|
This makes the S3 batch size configurable through the `S3_BATCH_DELETE_LIMIT` environment variable (defaults to 1000), and adds some retry logic, configurable through the `S3_BATCH_DELETE_RETRY` environment variable (defaults to 3).
|
||||||
- Add VAPID public key to instance serializer (#28006 by @ThisIsMissEm)
|
- Add VAPID public key to instance serializer (#28006 by @ThisIsMissEm)
|
||||||
|
- Add support for serving JRD `/.well-known/host-meta.json` in addition to XRD host-meta (#32206 by @c960657)
|
||||||
- Add `nodeName` and `nodeDescription` to nodeinfo `metadata` (#28079 by @6543)
|
- Add `nodeName` and `nodeDescription` to nodeinfo `metadata` (#28079 by @6543)
|
||||||
- Add Thai diacritics and tone marks in `HASHTAG_INVALID_CHARS_RE` (#26576 by @ppnplus)
|
- Add Thai diacritics and tone marks in `HASHTAG_INVALID_CHARS_RE` (#26576 by @ppnplus)
|
||||||
- Add variable delay before link verification of remote account links (#27774 by @ClearlyClaire)
|
- Add variable delay before link verification of remote account links (#27774 by @ClearlyClaire)
|
||||||
@ -164,18 +212,18 @@ The following changelog entries focus on changes visible to users, administrator
|
|||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- **Change icons throughout the web interface** (#27385, #27539, #27555, #27579, #27700, #27817, #28519, #28709, #28064, #28775, #28780, #27924, #29294, #29395, #29537, #29569, #29610, #29612, #29649, #29844, #27780, #30974, #30963, #30962, #30961, #31362, #31363, #31359, #31371, #31360, #31512, #31511, and #31525 by @ClearlyClaire, @Gargron, @arbolitoloco1, @mjankowski, @nclm, @renchap, @ronilaukkarinen, and @zunda)\
|
- **Change icons throughout the web interface** (#27385, #27539, #27555, #27579, #27700, #27817, #28519, #28709, #28064, #28775, #28780, #27924, #29294, #29395, #29537, #29569, #29610, #29612, #29649, #29844, #27780, #30974, #30963, #30962, #30961, #31362, #31363, #31359, #31371, #31360, #31512, #31511, #31525, #32153, and #32201 by @ClearlyClaire, @Gargron, @arbolitoloco1, @mjankowski, @nclm, @renchap, @ronilaukkarinen, and @zunda)\
|
||||||
This changes all the interface icons from FontAwesome to Material Symbols for a more modern look, consistent with the official Mastodon Android app.\
|
This changes all the interface icons from FontAwesome to Material Symbols for a more modern look, consistent with the official Mastodon Android app.\
|
||||||
In addition, better care is given to pixel alignment, and icon variants are used to better highlight active/inactive state.
|
In addition, better care is given to pixel alignment, and icon variants are used to better highlight active/inactive state.
|
||||||
- **Change design of compose form in web UI** (#28119, #29059, #29248, #29372, #29384, #29417, #29456, #29406, #29651, #29659, and #31889 by @ClearlyClaire, @Gargron, @eai04191, @hinaloe, and @ronilaukkarinen)\
|
- **Change design of compose form in web UI** (#28119, #29059, #29248, #29372, #29384, #29417, #29456, #29406, #29651, #29659, #31889 and #32033 by @ClearlyClaire, @Gargron, @eai04191, @hinaloe, and @ronilaukkarinen)\
|
||||||
The compose form has been completely redesigned for a more modern and consistent look, as well as spelling out the chosen privacy setting and language name at all times.\
|
The compose form has been completely redesigned for a more modern and consistent look, as well as spelling out the chosen privacy setting and language name at all times.\
|
||||||
As part of this, the “Unlisted” privacy setting has been renamed to “Quiet public”.
|
As part of this, the “Unlisted” privacy setting has been renamed to “Quiet public”.
|
||||||
- **Change design of modals in the web UI** (#29576, #29614, #29640, #29644, #30131, #30884, #31399, #31555, #31752, #31801, #31883, #31844, #31864, and #31943 by @ClearlyClaire, @Gargron, @tribela and @vmstan)\
|
- **Change design of modals in the web UI** (#29576, #29614, #29640, #29644, #30131, #30884, #31399, #31555, #31752, #31801, #31883, #31844, #31864, and #31943 by @ClearlyClaire, @Gargron, @tribela and @vmstan)\
|
||||||
The mute, block, and domain block confirmation modals have been completely redesigned to be clearer and include more detailed information on the action to be performed.\
|
The mute, block, and domain block confirmation modals have been completely redesigned to be clearer and include more detailed information on the action to be performed.\
|
||||||
They also have a more modern and consistent design, along with other confirmation modals in the application.
|
They also have a more modern and consistent design, along with other confirmation modals in the application.
|
||||||
- **Change colors throughout the web UI** (#29522, #29584, #29653, #29779, #29803, #29809, #29808, #29828, #31034, #31168, #31266, #31348, #31349, #31361, and #31510 by @ClearlyClaire, @Gargron, @renchap, and @vmstan)
|
- **Change colors throughout the web UI** (#29522, #29584, #29653, #29779, #29803, #29809, #29808, #29828, #31034, #31168, #31266, #31348, #31349, #31361, #31510 and #32128 by @ClearlyClaire, @Gargron, @mjankowski, @renchap, and @vmstan)
|
||||||
- **Change onboarding prompt to follow suggestions carousel in web UI** (#28878, #29272, and #31912 by @Gargron)
|
- **Change onboarding prompt to follow suggestions carousel in web UI** (#28878, #29272, and #31912 by @Gargron)
|
||||||
- **Change email templates** (#28416, #28755, #28814, #29064, #28883, #29470, #29607, #29761, #29760, and #29879 by @ClearlyClaire, @Gargron, @hteumeuleu, and @mjankowski)\
|
- **Change email templates** (#28416, #28755, #28814, #29064, #28883, #29470, #29607, #29761, #29760, #29879, #32073 and #32132 by @c960657, @ClearlyClaire, @Gargron, @hteumeuleu, and @mjankowski)\
|
||||||
All emails to end-users have been completely redesigned with a fresh new look, providing more information while making them easier to read and keeping maximum compatibility across mail clients.
|
All emails to end-users have been completely redesigned with a fresh new look, providing more information while making them easier to read and keeping maximum compatibility across mail clients.
|
||||||
- **Change follow recommendations algorithm** (#28314, #28433, #29017, #29108, #29306, #29550, #29619, and #31474 by @ClearlyClaire, @Gargron, @kernal053, @mjankowski, and @wheatear-dev)\
|
- **Change follow recommendations algorithm** (#28314, #28433, #29017, #29108, #29306, #29550, #29619, and #31474 by @ClearlyClaire, @Gargron, @kernal053, @mjankowski, and @wheatear-dev)\
|
||||||
This replaces the “past interactions” recommendation algorithm with a “friends of friends” algorithm that suggests accounts followed by people you follow, and a “similar profiles” algorithm that suggests accounts with a profile similar to your most recent follows.\
|
This replaces the “past interactions” recommendation algorithm with a “friends of friends” algorithm that suggests accounts followed by people you follow, and a “similar profiles” algorithm that suggests accounts with a profile similar to your most recent follows.\
|
||||||
@ -188,10 +236,17 @@ The following changelog entries focus on changes visible to users, administrator
|
|||||||
Administrators may need to update their setup accordingly.
|
Administrators may need to update their setup accordingly.
|
||||||
- Change how content warnings and filters are displayed in web UI (#31365, and #31761 by @Gargron)
|
- Change how content warnings and filters are displayed in web UI (#31365, and #31761 by @Gargron)
|
||||||
- Change preview card processing to ignore `undefined` as canonical url (#31882 by @oneiros)
|
- Change preview card processing to ignore `undefined` as canonical url (#31882 by @oneiros)
|
||||||
- Change embedded posts to use web UI (#31766 by @Gargron)
|
- Change embedded posts to use web UI (#31766, #32135 and #32271 by @Gargron)
|
||||||
- Change inner borders in media galleries in web UI (#31852 by @Gargron)
|
- Change inner borders in media galleries in web UI (#31852 by @Gargron)
|
||||||
- Change design of hide media button in web UI (#31807 by @Gargron)
|
- Change design of media attachments and profile media tab in web UI (#31807, #32048, #31967, #32217, #32224 and #32257 by @ClearlyClaire and @Gargron)
|
||||||
- Change labels on thread indicators in web UI (#31806 by @Gargron)
|
- Change labels on thread indicators in web UI (#31806 by @Gargron)
|
||||||
|
- Change label of "Data export" menu item in settings interface (#32099 by @c960657)
|
||||||
|
- Change responsive break points on navigation panel in web UI (#32034 by @Gargron)
|
||||||
|
- Change cursor to `not-allowed` on disabled buttons (#32076 by @mjankowski)
|
||||||
|
- Change OAuth authorization prompt to not refer to apps as “third-party” (#32005 by @Gargron)
|
||||||
|
- Change Mastodon to issue correct HTTP signatures by default (#31994 by @ClearlyClaire)
|
||||||
|
- Change zoom icon in web UI (#29683 by @Gargron)
|
||||||
|
- Change directory page to use URL query strings for options (#31980, #31977 and #31984 by @ClearlyClaire and @renchap)
|
||||||
- Change report action buttons to be disabled when action has already been taken (#31773, #31822, and #31899 by @ClearlyClaire and @ThisIsMissEm)
|
- Change report action buttons to be disabled when action has already been taken (#31773, #31822, and #31899 by @ClearlyClaire and @ThisIsMissEm)
|
||||||
- Change width of columns in advanced web UI (#31762 by @Gargron)
|
- Change width of columns in advanced web UI (#31762 by @Gargron)
|
||||||
- Change design of unread conversations in web UI (#31763 by @Gargron)
|
- Change design of unread conversations in web UI (#31763 by @Gargron)
|
||||||
@ -254,6 +309,7 @@ The following changelog entries focus on changes visible to users, administrator
|
|||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
|
|
||||||
|
- Remove unused E2EE messaging code and related `crypto` OAuth scope (#31193, #31945, #31963, and #31964 by @ClearlyClaire and @mjankowski)
|
||||||
- Remove StatsD integration (replaced by OpenTelemetry) (#30240 by @mjankowski)
|
- Remove StatsD integration (replaced by OpenTelemetry) (#30240 by @mjankowski)
|
||||||
- Remove `CacheBuster` default options (#30718 by @mjankowski)
|
- Remove `CacheBuster` default options (#30718 by @mjankowski)
|
||||||
- Remove home marker updates from the Web UI (#22721 by @davbeck)\
|
- Remove home marker updates from the Web UI (#22721 by @davbeck)\
|
||||||
@ -269,9 +325,22 @@ The following changelog entries focus on changes visible to users, administrator
|
|||||||
- Fix log out from user menu not working on Safari (#31402 by @renchap)
|
- Fix log out from user menu not working on Safari (#31402 by @renchap)
|
||||||
- Fix various issues when in link preview card generation (#28748, #30017, #30362, #30173, #30853, #30929, #30933, #30957, #30987, and #31144 by @adamniedzielski, @oneiros, @phocks, @timothyjrogers, and @tribela)
|
- Fix various issues when in link preview card generation (#28748, #30017, #30362, #30173, #30853, #30929, #30933, #30957, #30987, and #31144 by @adamniedzielski, @oneiros, @phocks, @timothyjrogers, and @tribela)
|
||||||
- Fix handling of missing links in Webfinger responses (#31030 by @adamniedzielski)
|
- Fix handling of missing links in Webfinger responses (#31030 by @adamniedzielski)
|
||||||
|
- Fix error when accepting an appeal for sensitive posts deleted in the meantime (#32037 by @ClearlyClaire)
|
||||||
|
- Fix error when encountering reblog of deleted post in feed rebuild (#32001 by @ClearlyClaire)
|
||||||
|
- Fix Safari browser glitch related to horizontal scrolling (#31960 by @Gargron)
|
||||||
|
- Fix unresolvable mentions sometimes preventing processing incoming posts (#29215 by @tribela and @ClearlyClaire)
|
||||||
|
- Fix too many requests caused by relationship look-ups in web UI (#32042 by @Gargron)
|
||||||
|
- Fix links for reblogs in moderation interface (#31979 by @ClearlyClaire)
|
||||||
|
- Fix the appearance of avatars when they do not load (#31966 and #32270 by @Gargron and @renchap)
|
||||||
|
- Fix spurious error notifications for aborted requests in web UI (#31952 by @c960657)
|
||||||
- Fix HTTP 500 error in `/api/v1/polls/:id/votes` when required `choices` parameter is missing (#25598 by @danielmbrasil)
|
- Fix HTTP 500 error in `/api/v1/polls/:id/votes` when required `choices` parameter is missing (#25598 by @danielmbrasil)
|
||||||
- Fix security context sometimes not being added in LD-Signed activities (#31871 by @ClearlyClaire)
|
- Fix security context sometimes not being added in LD-Signed activities (#31871 by @ClearlyClaire)
|
||||||
- Fix cross-origin loading of `inert.css` polyfill (#30687 by @louis77)
|
- Fix cross-origin loading of `inert.css` polyfill (#30687 by @louis77)
|
||||||
|
- Fix wrapping in dashboard quick access buttons (#32043 by @renchap)
|
||||||
|
- Fix recently used tags hint being displayed in profile edition page when there is none (#32120 by @mjankowski)
|
||||||
|
- Fix checkbox lists on narrow screens in the settings interface (#32112 by @mjankowski)
|
||||||
|
- Fix the position of status action buttons being affected by interaction counters (#32084 by @renchap)
|
||||||
|
- Fix the summary of converted ActivityPub object types to be treated as HTML (#28629 by @Menrath)
|
||||||
- Fix cutoff of instance name in sign-up form (#30598 by @oneiros)
|
- Fix cutoff of instance name in sign-up form (#30598 by @oneiros)
|
||||||
- Fix invalid date searches returning 503 errors (#31526 by @notchairmk)
|
- Fix invalid date searches returning 503 errors (#31526 by @notchairmk)
|
||||||
- Fix invalid `visibility` values in `POST /api/v1/statuses` returning 500 errors (#31571 by @c960657)
|
- Fix invalid `visibility` values in `POST /api/v1/statuses` returning 500 errors (#31571 by @c960657)
|
||||||
@ -285,10 +354,12 @@ The following changelog entries focus on changes visible to users, administrator
|
|||||||
- Fix “Redirect URI” field not being marked as required in “New application” form (#30311 by @ThisIsMissEm)
|
- Fix “Redirect URI” field not being marked as required in “New application” form (#30311 by @ThisIsMissEm)
|
||||||
- Fix right-to-left text in preview cards (#30930 by @ClearlyClaire)
|
- Fix right-to-left text in preview cards (#30930 by @ClearlyClaire)
|
||||||
- Fix rack attack `match_type` value typo in logging config (#30514 by @mjankowski)
|
- Fix rack attack `match_type` value typo in logging config (#30514 by @mjankowski)
|
||||||
- Fix various cases of duplicate, missing, or inconsistent borders or scrollbar styles (#31068, #31286, #31268, #31275, #31284, #31305, #31346, #31372, #31373, #31389, #31432, #31391, and #31445 by @valtlai and @vmstan)
|
- Fix various cases of duplicate, missing, or inconsistent borders or scrollbar styles (#31068, #31286, #31268, #31275, #31284, #31305, #31346, #31372, #31373, #31389, #31432, #31391, #31445, #32091, #32147 and #32137 by @ClearlyClaire, @mjankowski, @valtlai and @vmstan)
|
||||||
|
- Fix editing description of media uploads with custom thumbnails (#32221 by @ClearlyClaire)
|
||||||
- Fix race condition in `POST /api/v1/push/subscription` (#30166 by @ClearlyClaire)
|
- Fix race condition in `POST /api/v1/push/subscription` (#30166 by @ClearlyClaire)
|
||||||
- Fix post deletion not being delayed when those are part of an account warning (#30163 by @ClearlyClaire)
|
- Fix post deletion not being delayed when those are part of an account warning (#30163 by @ClearlyClaire)
|
||||||
- Fix rendering error on `/start` when not logged in (#30023 by @timothyjrogers)
|
- Fix rendering error on `/start` when not logged in (#30023 by @timothyjrogers)
|
||||||
|
- Fix unneeded requests to blocked domains when receiving relayed signed activities from them (#31161 by @ClearlyClaire)
|
||||||
- Fix logo pushing header buttons out of view on certain conditions in mobile layout (#29787 by @ClearlyClaire)
|
- Fix logo pushing header buttons out of view on certain conditions in mobile layout (#29787 by @ClearlyClaire)
|
||||||
- Fix notification-related records not being reattributed when merging accounts (#29694 by @ClearlyClaire)
|
- Fix notification-related records not being reattributed when merging accounts (#29694 by @ClearlyClaire)
|
||||||
- Fix results/query in `api/v1/featured_tags/suggestions` (#29597 by @mjankowski)
|
- Fix results/query in `api/v1/featured_tags/suggestions` (#29597 by @mjankowski)
|
||||||
@ -298,6 +369,7 @@ The following changelog entries focus on changes visible to users, administrator
|
|||||||
- Fix full date display not respecting the locale 12/24h format (#29448 by @renchap)
|
- Fix full date display not respecting the locale 12/24h format (#29448 by @renchap)
|
||||||
- Fix filters title and keywords overflow (#29396 by @GeopJr)
|
- Fix filters title and keywords overflow (#29396 by @GeopJr)
|
||||||
- Fix incorrect date format in “Follows and followers” (#29390 by @JasonPunyon)
|
- Fix incorrect date format in “Follows and followers” (#29390 by @JasonPunyon)
|
||||||
|
- Fix navigation item active highlight for some paths (#32159 by @mjankowski)
|
||||||
- Fix “Edit media” modal sizing and layout when space-constrained (#27095 by @ronilaukkarinen)
|
- Fix “Edit media” modal sizing and layout when space-constrained (#27095 by @ronilaukkarinen)
|
||||||
- Fix modal container bounds (#29185 by @nico3333fr)
|
- Fix modal container bounds (#29185 by @nico3333fr)
|
||||||
- Fix inefficient HTTP signature parsing using regexps and `StringScanner` (#29133 by @ClearlyClaire)
|
- Fix inefficient HTTP signature parsing using regexps and `StringScanner` (#29133 by @ClearlyClaire)
|
||||||
@ -327,7 +399,7 @@ The following changelog entries focus on changes visible to users, administrator
|
|||||||
- Fix empty environment variables not using default nil value (#27400 by @renchap)
|
- Fix empty environment variables not using default nil value (#27400 by @renchap)
|
||||||
- Fix language sorting in settings (#27158 by @gunchleoc)
|
- Fix language sorting in settings (#27158 by @gunchleoc)
|
||||||
|
|
||||||
## |4.2.11] - 2024-08-16
|
## [4.2.11] - 2024-08-16
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
|
317
Dockerfile
@ -1,4 +1,4 @@
|
|||||||
# syntax=docker/dockerfile:1.9
|
# syntax=docker/dockerfile:1.12
|
||||||
|
|
||||||
# This file is designed for production server deployment, not local development work
|
# This file is designed for production server deployment, not local development work
|
||||||
# For a containerized local dev environment, see: https://github.com/mastodon/mastodon/blob/main/README.md#docker
|
# For a containerized local dev environment, see: https://github.com/mastodon/mastodon/blob/main/README.md#docker
|
||||||
@ -12,10 +12,10 @@ ARG BUILDPLATFORM=${BUILDPLATFORM}
|
|||||||
|
|
||||||
# Ruby image to use for base image, change with [--build-arg RUBY_VERSION="3.3.x"]
|
# Ruby image to use for base image, change with [--build-arg RUBY_VERSION="3.3.x"]
|
||||||
# renovate: datasource=docker depName=docker.io/ruby
|
# renovate: datasource=docker depName=docker.io/ruby
|
||||||
ARG RUBY_VERSION="3.3.5"
|
ARG RUBY_VERSION="3.3.6"
|
||||||
# # Node version to use in base image, change with [--build-arg NODE_MAJOR_VERSION="20"]
|
# # Node version to use in base image, change with [--build-arg NODE_MAJOR_VERSION="20"]
|
||||||
# renovate: datasource=node-version depName=node
|
# renovate: datasource=node-version depName=node
|
||||||
ARG NODE_MAJOR_VERSION="20"
|
ARG NODE_MAJOR_VERSION="22"
|
||||||
# Debian image to use for base image, change with [--build-arg DEBIAN_VERSION="bookworm"]
|
# Debian image to use for base image, change with [--build-arg DEBIAN_VERSION="bookworm"]
|
||||||
ARG DEBIAN_VERSION="bookworm"
|
ARG DEBIAN_VERSION="bookworm"
|
||||||
# Node image to use for base image based on combined variables (ex: 20-bookworm-slim)
|
# Node image to use for base image based on combined variables (ex: 20-bookworm-slim)
|
||||||
@ -29,6 +29,8 @@ FROM docker.io/ruby:${RUBY_VERSION}-slim-${DEBIAN_VERSION} AS ruby
|
|||||||
ARG MASTODON_VERSION_PRERELEASE=""
|
ARG MASTODON_VERSION_PRERELEASE=""
|
||||||
# Append build metadata or fork information to version.rb [--build-arg MASTODON_VERSION_METADATA="pr-123456"]
|
# Append build metadata or fork information to version.rb [--build-arg MASTODON_VERSION_METADATA="pr-123456"]
|
||||||
ARG MASTODON_VERSION_METADATA=""
|
ARG MASTODON_VERSION_METADATA=""
|
||||||
|
# Will be available as Mastodon::Version.source_commit
|
||||||
|
ARG SOURCE_COMMIT=""
|
||||||
|
|
||||||
# Allow Ruby on Rails to serve static files
|
# Allow Ruby on Rails to serve static files
|
||||||
# See: https://docs.joinmastodon.org/admin/config/#rails_serve_static_files
|
# See: https://docs.joinmastodon.org/admin/config/#rails_serve_static_files
|
||||||
@ -45,30 +47,31 @@ ARG GID="991"
|
|||||||
|
|
||||||
# Apply Mastodon build options based on options above
|
# Apply Mastodon build options based on options above
|
||||||
ENV \
|
ENV \
|
||||||
# Apply Mastodon version information
|
# Apply Mastodon version information
|
||||||
MASTODON_VERSION_PRERELEASE="${MASTODON_VERSION_PRERELEASE}" \
|
MASTODON_VERSION_PRERELEASE="${MASTODON_VERSION_PRERELEASE}" \
|
||||||
MASTODON_VERSION_METADATA="${MASTODON_VERSION_METADATA}" \
|
MASTODON_VERSION_METADATA="${MASTODON_VERSION_METADATA}" \
|
||||||
# Apply Mastodon static files and YJIT options
|
SOURCE_COMMIT="${SOURCE_COMMIT}" \
|
||||||
|
# Apply Mastodon static files and YJIT options
|
||||||
RAILS_SERVE_STATIC_FILES=${RAILS_SERVE_STATIC_FILES} \
|
RAILS_SERVE_STATIC_FILES=${RAILS_SERVE_STATIC_FILES} \
|
||||||
RUBY_YJIT_ENABLE=${RUBY_YJIT_ENABLE} \
|
RUBY_YJIT_ENABLE=${RUBY_YJIT_ENABLE} \
|
||||||
# Apply timezone
|
# Apply timezone
|
||||||
TZ=${TZ}
|
TZ=${TZ}
|
||||||
|
|
||||||
ENV \
|
ENV \
|
||||||
# Configure the IP to bind Mastodon to when serving traffic
|
# Configure the IP to bind Mastodon to when serving traffic
|
||||||
BIND="0.0.0.0" \
|
BIND="0.0.0.0" \
|
||||||
# Use production settings for Yarn, Node and related nodejs based tools
|
# Use production settings for Yarn, Node and related nodejs based tools
|
||||||
NODE_ENV="production" \
|
NODE_ENV="production" \
|
||||||
# Use production settings for Ruby on Rails
|
# Use production settings for Ruby on Rails
|
||||||
RAILS_ENV="production" \
|
RAILS_ENV="production" \
|
||||||
# Add Ruby and Mastodon installation to the PATH
|
# Add Ruby and Mastodon installation to the PATH
|
||||||
DEBIAN_FRONTEND="noninteractive" \
|
DEBIAN_FRONTEND="noninteractive" \
|
||||||
PATH="${PATH}:/opt/ruby/bin:/opt/mastodon/bin" \
|
PATH="${PATH}:/opt/ruby/bin:/opt/mastodon/bin" \
|
||||||
# Optimize jemalloc 5.x performance
|
# Optimize jemalloc 5.x performance
|
||||||
MALLOC_CONF="narenas:2,background_thread:true,thp:never,dirty_decay_ms:1000,muzzy_decay_ms:0" \
|
MALLOC_CONF="narenas:2,background_thread:true,thp:never,dirty_decay_ms:1000,muzzy_decay_ms:0" \
|
||||||
# Enable libvips, should not be changed
|
# Enable libvips, should not be changed
|
||||||
MASTODON_USE_LIBVIPS=true \
|
MASTODON_USE_LIBVIPS=true \
|
||||||
# Sidekiq will touch tmp/sidekiq_process_has_started_and_will_begin_processing_jobs to indicate it is ready. This can be used for a readiness check in Kubernetes
|
# Sidekiq will touch tmp/sidekiq_process_has_started_and_will_begin_processing_jobs to indicate it is ready. This can be used for a readiness check in Kubernetes
|
||||||
MASTODON_SIDEKIQ_READY_FILENAME=sidekiq_process_has_started_and_will_begin_processing_jobs
|
MASTODON_SIDEKIQ_READY_FILENAME=sidekiq_process_has_started_and_will_begin_processing_jobs
|
||||||
|
|
||||||
# Set default shell used for running commands
|
# Set default shell used for running commands
|
||||||
@ -79,14 +82,14 @@ ARG TARGETPLATFORM
|
|||||||
RUN echo "Target platform is $TARGETPLATFORM"
|
RUN echo "Target platform is $TARGETPLATFORM"
|
||||||
|
|
||||||
RUN \
|
RUN \
|
||||||
# Remove automatic apt cache Docker cleanup scripts
|
# Remove automatic apt cache Docker cleanup scripts
|
||||||
rm -f /etc/apt/apt.conf.d/docker-clean; \
|
rm -f /etc/apt/apt.conf.d/docker-clean; \
|
||||||
# Sets timezone
|
# Sets timezone
|
||||||
echo "${TZ}" > /etc/localtime; \
|
echo "${TZ}" > /etc/localtime; \
|
||||||
# Creates mastodon user/group and sets home directory
|
# Creates mastodon user/group and sets home directory
|
||||||
groupadd -g "${GID}" mastodon; \
|
groupadd -g "${GID}" mastodon; \
|
||||||
useradd -l -u "${UID}" -g "${GID}" -m -d /opt/mastodon mastodon; \
|
useradd -l -u "${UID}" -g "${GID}" -m -d /opt/mastodon mastodon; \
|
||||||
# Creates /mastodon symlink to /opt/mastodon
|
# Creates /mastodon symlink to /opt/mastodon
|
||||||
ln -s /opt/mastodon /mastodon;
|
ln -s /opt/mastodon /mastodon;
|
||||||
|
|
||||||
# Set /opt/mastodon as working directory
|
# Set /opt/mastodon as working directory
|
||||||
@ -94,28 +97,28 @@ WORKDIR /opt/mastodon
|
|||||||
|
|
||||||
# hadolint ignore=DL3008,DL3005
|
# hadolint ignore=DL3008,DL3005
|
||||||
RUN \
|
RUN \
|
||||||
# Mount Apt cache and lib directories from Docker buildx caches
|
# Mount Apt cache and lib directories from Docker buildx caches
|
||||||
--mount=type=cache,id=apt-cache-${TARGETPLATFORM},target=/var/cache/apt,sharing=locked \
|
--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=type=cache,id=apt-lib-${TARGETPLATFORM},target=/var/lib/apt,sharing=locked \
|
||||||
# Apt update & upgrade to check for security updates to Debian image
|
# Apt update & upgrade to check for security updates to Debian image
|
||||||
apt-get update; \
|
apt-get update; \
|
||||||
apt-get dist-upgrade -yq; \
|
apt-get dist-upgrade -yq; \
|
||||||
# Install jemalloc, curl and other necessary components
|
# Install jemalloc, curl and other necessary components
|
||||||
apt-get install -y --no-install-recommends \
|
apt-get install -y --no-install-recommends \
|
||||||
curl \
|
curl \
|
||||||
file \
|
file \
|
||||||
libjemalloc2 \
|
libjemalloc2 \
|
||||||
patchelf \
|
patchelf \
|
||||||
procps \
|
procps \
|
||||||
tini \
|
tini \
|
||||||
tzdata \
|
tzdata \
|
||||||
wget \
|
wget \
|
||||||
; \
|
; \
|
||||||
# Patch Ruby to use jemalloc
|
# Patch Ruby to use jemalloc
|
||||||
patchelf --add-needed libjemalloc.so.2 /usr/local/bin/ruby; \
|
patchelf --add-needed libjemalloc.so.2 /usr/local/bin/ruby; \
|
||||||
# Discard patchelf after use
|
# Discard patchelf after use
|
||||||
apt-get purge -y \
|
apt-get purge -y \
|
||||||
patchelf \
|
patchelf \
|
||||||
;
|
;
|
||||||
|
|
||||||
# Create temporary build layer from base image
|
# Create temporary build layer from base image
|
||||||
@ -132,56 +135,56 @@ ARG TARGETPLATFORM
|
|||||||
|
|
||||||
# hadolint ignore=DL3008
|
# hadolint ignore=DL3008
|
||||||
RUN \
|
RUN \
|
||||||
# Mount Apt cache and lib directories from Docker buildx caches
|
# 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-cache-${TARGETPLATFORM},target=/var/cache/apt,sharing=locked \
|
||||||
--mount=type=cache,id=apt-lib-${TARGETPLATFORM},target=/var/lib/apt,sharing=locked \
|
--mount=type=cache,id=apt-lib-${TARGETPLATFORM},target=/var/lib/apt,sharing=locked \
|
||||||
# Install build tools and bundler dependencies from APT
|
# Install build tools and bundler dependencies from APT
|
||||||
apt-get install -y --no-install-recommends \
|
apt-get install -y --no-install-recommends \
|
||||||
autoconf \
|
autoconf \
|
||||||
automake \
|
automake \
|
||||||
build-essential \
|
build-essential \
|
||||||
cmake \
|
cmake \
|
||||||
git \
|
git \
|
||||||
libgdbm-dev \
|
libgdbm-dev \
|
||||||
libglib2.0-dev \
|
libglib2.0-dev \
|
||||||
libgmp-dev \
|
libgmp-dev \
|
||||||
libicu-dev \
|
libicu-dev \
|
||||||
libidn-dev \
|
libidn-dev \
|
||||||
libpq-dev \
|
libpq-dev \
|
||||||
libssl-dev \
|
libssl-dev \
|
||||||
libtool \
|
libtool \
|
||||||
meson \
|
meson \
|
||||||
nasm \
|
nasm \
|
||||||
pkg-config \
|
pkg-config \
|
||||||
shared-mime-info \
|
shared-mime-info \
|
||||||
xz-utils \
|
xz-utils \
|
||||||
# libvips components
|
# libvips components
|
||||||
libcgif-dev \
|
libcgif-dev \
|
||||||
libexif-dev \
|
libexif-dev \
|
||||||
libexpat1-dev \
|
libexpat1-dev \
|
||||||
libgirepository1.0-dev \
|
libgirepository1.0-dev \
|
||||||
libheif-dev \
|
libheif-dev \
|
||||||
libimagequant-dev \
|
libimagequant-dev \
|
||||||
libjpeg62-turbo-dev \
|
libjpeg62-turbo-dev \
|
||||||
liblcms2-dev \
|
liblcms2-dev \
|
||||||
liborc-dev \
|
liborc-dev \
|
||||||
libspng-dev \
|
libspng-dev \
|
||||||
libtiff-dev \
|
libtiff-dev \
|
||||||
libwebp-dev \
|
libwebp-dev \
|
||||||
# ffmpeg components
|
# ffmpeg components
|
||||||
libdav1d-dev \
|
libdav1d-dev \
|
||||||
liblzma-dev \
|
liblzma-dev \
|
||||||
libmp3lame-dev \
|
libmp3lame-dev \
|
||||||
libopus-dev \
|
libopus-dev \
|
||||||
libsnappy-dev \
|
libsnappy-dev \
|
||||||
libvorbis-dev \
|
libvorbis-dev \
|
||||||
libvpx-dev \
|
libvpx-dev \
|
||||||
libx264-dev \
|
libx264-dev \
|
||||||
libx265-dev \
|
libx265-dev \
|
||||||
;
|
;
|
||||||
|
|
||||||
RUN \
|
RUN \
|
||||||
# Configure Corepack
|
# Configure Corepack
|
||||||
rm /usr/local/bin/yarn*; \
|
rm /usr/local/bin/yarn*; \
|
||||||
corepack enable; \
|
corepack enable; \
|
||||||
corepack prepare --activate;
|
corepack prepare --activate;
|
||||||
@ -191,7 +194,7 @@ FROM build AS libvips
|
|||||||
|
|
||||||
# libvips version to compile, change with [--build-arg VIPS_VERSION="8.15.2"]
|
# libvips version to compile, change with [--build-arg VIPS_VERSION="8.15.2"]
|
||||||
# renovate: datasource=github-releases depName=libvips packageName=libvips/libvips
|
# renovate: datasource=github-releases depName=libvips packageName=libvips/libvips
|
||||||
ARG VIPS_VERSION=8.15.3
|
ARG VIPS_VERSION=8.16.0
|
||||||
# libvips download URL, change with [--build-arg VIPS_URL="https://github.com/libvips/libvips/releases/download"]
|
# libvips download URL, change with [--build-arg VIPS_URL="https://github.com/libvips/libvips/releases/download"]
|
||||||
ARG VIPS_URL=https://github.com/libvips/libvips/releases/download
|
ARG VIPS_URL=https://github.com/libvips/libvips/releases/download
|
||||||
|
|
||||||
@ -214,7 +217,7 @@ FROM build AS ffmpeg
|
|||||||
|
|
||||||
# ffmpeg version to compile, change with [--build-arg FFMPEG_VERSION="7.0.x"]
|
# ffmpeg version to compile, change with [--build-arg FFMPEG_VERSION="7.0.x"]
|
||||||
# renovate: datasource=repology depName=ffmpeg packageName=openpkg_current/ffmpeg
|
# renovate: datasource=repology depName=ffmpeg packageName=openpkg_current/ffmpeg
|
||||||
ARG FFMPEG_VERSION=7.0.2
|
ARG FFMPEG_VERSION=7.1
|
||||||
# ffmpeg download URL, change with [--build-arg FFMPEG_URL="https://ffmpeg.org/releases"]
|
# ffmpeg download URL, change with [--build-arg FFMPEG_URL="https://ffmpeg.org/releases"]
|
||||||
ARG FFMPEG_URL=https://ffmpeg.org/releases
|
ARG FFMPEG_URL=https://ffmpeg.org/releases
|
||||||
|
|
||||||
@ -228,28 +231,28 @@ WORKDIR /usr/local/ffmpeg/src/ffmpeg-${FFMPEG_VERSION}
|
|||||||
# Configure and compile ffmpeg
|
# Configure and compile ffmpeg
|
||||||
RUN \
|
RUN \
|
||||||
./configure \
|
./configure \
|
||||||
--prefix=/usr/local/ffmpeg \
|
--prefix=/usr/local/ffmpeg \
|
||||||
--toolchain=hardened \
|
--toolchain=hardened \
|
||||||
--disable-debug \
|
--disable-debug \
|
||||||
--disable-devices \
|
--disable-devices \
|
||||||
--disable-doc \
|
--disable-doc \
|
||||||
--disable-ffplay \
|
--disable-ffplay \
|
||||||
--disable-network \
|
--disable-network \
|
||||||
--disable-static \
|
--disable-static \
|
||||||
--enable-ffmpeg \
|
--enable-ffmpeg \
|
||||||
--enable-ffprobe \
|
--enable-ffprobe \
|
||||||
--enable-gpl \
|
--enable-gpl \
|
||||||
--enable-libdav1d \
|
--enable-libdav1d \
|
||||||
--enable-libmp3lame \
|
--enable-libmp3lame \
|
||||||
--enable-libopus \
|
--enable-libopus \
|
||||||
--enable-libsnappy \
|
--enable-libsnappy \
|
||||||
--enable-libvorbis \
|
--enable-libvorbis \
|
||||||
--enable-libvpx \
|
--enable-libvpx \
|
||||||
--enable-libwebp \
|
--enable-libwebp \
|
||||||
--enable-libx264 \
|
--enable-libx264 \
|
||||||
--enable-libx265 \
|
--enable-libx265 \
|
||||||
--enable-shared \
|
--enable-shared \
|
||||||
--enable-version3 \
|
--enable-version3 \
|
||||||
; \
|
; \
|
||||||
make -j$(nproc); \
|
make -j$(nproc); \
|
||||||
make install;
|
make install;
|
||||||
@ -263,17 +266,17 @@ ARG TARGETPLATFORM
|
|||||||
COPY Gemfile* /opt/mastodon/
|
COPY Gemfile* /opt/mastodon/
|
||||||
|
|
||||||
RUN \
|
RUN \
|
||||||
# Mount Ruby Gem caches
|
# Mount Ruby Gem caches
|
||||||
--mount=type=cache,id=gem-cache-${TARGETPLATFORM},target=/usr/local/bundle/cache/,sharing=locked \
|
--mount=type=cache,id=gem-cache-${TARGETPLATFORM},target=/usr/local/bundle/cache/,sharing=locked \
|
||||||
# Configure bundle to prevent changes to Gemfile and Gemfile.lock
|
# Configure bundle to prevent changes to Gemfile and Gemfile.lock
|
||||||
bundle config set --global frozen "true"; \
|
bundle config set --global frozen "true"; \
|
||||||
# Configure bundle to not cache downloaded Gems
|
# Configure bundle to not cache downloaded Gems
|
||||||
bundle config set --global cache_all "false"; \
|
bundle config set --global cache_all "false"; \
|
||||||
# Configure bundle to only process production Gems
|
# Configure bundle to only process production Gems
|
||||||
bundle config set --local without "development test"; \
|
bundle config set --local without "development test"; \
|
||||||
# Configure bundle to not warn about root user
|
# Configure bundle to not warn about root user
|
||||||
bundle config set silence_root_warning "true"; \
|
bundle config set silence_root_warning "true"; \
|
||||||
# Download and install required Gems
|
# Download and install required Gems
|
||||||
bundle install -j"$(nproc)";
|
bundle install -j"$(nproc)";
|
||||||
|
|
||||||
# Create temporary node specific build layer from build layer
|
# Create temporary node specific build layer from build layer
|
||||||
@ -288,9 +291,9 @@ COPY .yarn /opt/mastodon/.yarn
|
|||||||
|
|
||||||
# hadolint ignore=DL3008
|
# hadolint ignore=DL3008
|
||||||
RUN \
|
RUN \
|
||||||
--mount=type=cache,id=corepack-cache-${TARGETPLATFORM},target=/usr/local/share/.cache/corepack,sharing=locked \
|
--mount=type=cache,id=corepack-cache-${TARGETPLATFORM},target=/usr/local/share/.cache/corepack,sharing=locked \
|
||||||
--mount=type=cache,id=yarn-cache-${TARGETPLATFORM},target=/usr/local/share/.cache/yarn,sharing=locked \
|
--mount=type=cache,id=yarn-cache-${TARGETPLATFORM},target=/usr/local/share/.cache/yarn,sharing=locked \
|
||||||
# Install Node packages
|
# Install Node packages
|
||||||
yarn workspaces focus --production @mastodon/mastodon;
|
yarn workspaces focus --production @mastodon/mastodon;
|
||||||
|
|
||||||
# Create temporary assets build layer from build layer
|
# Create temporary assets build layer from build layer
|
||||||
@ -311,10 +314,10 @@ ARG TARGETPLATFORM
|
|||||||
|
|
||||||
RUN \
|
RUN \
|
||||||
ldconfig; \
|
ldconfig; \
|
||||||
# Use Ruby on Rails to create Mastodon assets
|
# Use Ruby on Rails to create Mastodon assets
|
||||||
SECRET_KEY_BASE_DUMMY=1 \
|
SECRET_KEY_BASE_DUMMY=1 \
|
||||||
bundle exec rails assets:precompile; \
|
bundle exec rails assets:precompile; \
|
||||||
# Cleanup temporary files
|
# Cleanup temporary files
|
||||||
rm -fr /opt/mastodon/tmp;
|
rm -fr /opt/mastodon/tmp;
|
||||||
|
|
||||||
# Prep final Mastodon Ruby layer
|
# Prep final Mastodon Ruby layer
|
||||||
@ -324,49 +327,49 @@ ARG TARGETPLATFORM
|
|||||||
|
|
||||||
# hadolint ignore=DL3008
|
# hadolint ignore=DL3008
|
||||||
RUN \
|
RUN \
|
||||||
# Mount Apt cache and lib directories from Docker buildx caches
|
# 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-cache-${TARGETPLATFORM},target=/var/cache/apt,sharing=locked \
|
||||||
--mount=type=cache,id=apt-lib-${TARGETPLATFORM},target=/var/lib/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 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=corepack-cache-${TARGETPLATFORM},target=/usr/local/share/.cache/corepack,sharing=locked \
|
||||||
--mount=type=cache,id=yarn-cache-${TARGETPLATFORM},target=/usr/local/share/.cache/yarn,sharing=locked \
|
--mount=type=cache,id=yarn-cache-${TARGETPLATFORM},target=/usr/local/share/.cache/yarn,sharing=locked \
|
||||||
# Apt update install non-dev versions of necessary components
|
# Apt update install non-dev versions of necessary components
|
||||||
apt-get install -y --no-install-recommends \
|
apt-get install -y --no-install-recommends \
|
||||||
libexpat1 \
|
libexpat1 \
|
||||||
libglib2.0-0 \
|
libglib2.0-0 \
|
||||||
libicu72 \
|
libicu72 \
|
||||||
libidn12 \
|
libidn12 \
|
||||||
libpq5 \
|
libpq5 \
|
||||||
libreadline8 \
|
libreadline8 \
|
||||||
libssl3 \
|
libssl3 \
|
||||||
libyaml-0-2 \
|
libyaml-0-2 \
|
||||||
# libvips components
|
# libvips components
|
||||||
libcgif0 \
|
libcgif0 \
|
||||||
libexif12 \
|
libexif12 \
|
||||||
libheif1 \
|
libheif1 \
|
||||||
libimagequant0 \
|
libimagequant0 \
|
||||||
libjpeg62-turbo \
|
libjpeg62-turbo \
|
||||||
liblcms2-2 \
|
liblcms2-2 \
|
||||||
liborc-0.4-0 \
|
liborc-0.4-0 \
|
||||||
libspng0 \
|
libspng0 \
|
||||||
libtiff6 \
|
libtiff6 \
|
||||||
libwebp7 \
|
libwebp7 \
|
||||||
libwebpdemux2 \
|
libwebpdemux2 \
|
||||||
libwebpmux3 \
|
libwebpmux3 \
|
||||||
# ffmpeg components
|
# ffmpeg components
|
||||||
libdav1d6 \
|
libdav1d6 \
|
||||||
libmp3lame0 \
|
libmp3lame0 \
|
||||||
libopencore-amrnb0 \
|
libopencore-amrnb0 \
|
||||||
libopencore-amrwb0 \
|
libopencore-amrwb0 \
|
||||||
libopus0 \
|
libopus0 \
|
||||||
libsnappy1v5 \
|
libsnappy1v5 \
|
||||||
libtheora0 \
|
libtheora0 \
|
||||||
libvorbis0a \
|
libvorbis0a \
|
||||||
libvorbisenc2 \
|
libvorbisenc2 \
|
||||||
libvorbisfile3 \
|
libvorbisfile3 \
|
||||||
libvpx7 \
|
libvpx7 \
|
||||||
libx264-164 \
|
libx264-164 \
|
||||||
libx265-199 \
|
libx265-199 \
|
||||||
;
|
;
|
||||||
|
|
||||||
# Copy Mastodon sources into final layer
|
# Copy Mastodon sources into final layer
|
||||||
@ -386,7 +389,7 @@ COPY --from=ffmpeg /usr/local/ffmpeg/lib /usr/local/lib
|
|||||||
|
|
||||||
RUN \
|
RUN \
|
||||||
ldconfig; \
|
ldconfig; \
|
||||||
# Smoketest media processors
|
# Smoketest media processors
|
||||||
vips -v; \
|
vips -v; \
|
||||||
ffmpeg -version; \
|
ffmpeg -version; \
|
||||||
ffprobe -version;
|
ffprobe -version;
|
||||||
@ -396,10 +399,10 @@ RUN \
|
|||||||
bundle exec bootsnap precompile --gemfile app/ lib/;
|
bundle exec bootsnap precompile --gemfile app/ lib/;
|
||||||
|
|
||||||
RUN \
|
RUN \
|
||||||
# Pre-create and chown system volume to Mastodon user
|
# Pre-create and chown system volume to Mastodon user
|
||||||
mkdir -p /opt/mastodon/public/system; \
|
mkdir -p /opt/mastodon/public/system; \
|
||||||
chown mastodon:mastodon /opt/mastodon/public/system; \
|
chown mastodon:mastodon /opt/mastodon/public/system; \
|
||||||
# Set Mastodon user as owner of tmp folder
|
# Set Mastodon user as owner of tmp folder
|
||||||
chown -R mastodon:mastodon /opt/mastodon/tmp;
|
chown -R mastodon:mastodon /opt/mastodon/tmp;
|
||||||
|
|
||||||
# Set the running user for resulting container
|
# Set the running user for resulting container
|
||||||
|
24
Gemfile
@ -1,12 +1,12 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
source 'https://rubygems.org'
|
source 'https://rubygems.org'
|
||||||
ruby '>= 3.1.0'
|
ruby '>= 3.2.0'
|
||||||
|
|
||||||
gem 'propshaft'
|
gem 'propshaft'
|
||||||
gem 'puma', '~> 6.3'
|
gem 'puma', '~> 6.3'
|
||||||
gem 'rack', '~> 2.2.7'
|
gem 'rack', '~> 2.2.7'
|
||||||
gem 'rails', '~> 7.1.1'
|
gem 'rails', '~> 7.2.0'
|
||||||
gem 'thor', '~> 1.2'
|
gem 'thor', '~> 1.2'
|
||||||
|
|
||||||
gem 'dotenv'
|
gem 'dotenv'
|
||||||
@ -16,16 +16,16 @@ gem 'pghero'
|
|||||||
|
|
||||||
gem 'aws-sdk-s3', '~> 1.123', require: false
|
gem 'aws-sdk-s3', '~> 1.123', require: false
|
||||||
gem 'blurhash', '~> 0.1'
|
gem 'blurhash', '~> 0.1'
|
||||||
gem 'fog-core', '<= 2.5.0'
|
gem 'fog-core', '<= 2.6.0'
|
||||||
gem 'fog-openstack', '~> 1.0', require: false
|
gem 'fog-openstack', '~> 1.0', require: false
|
||||||
|
gem 'jd-paperclip-azure', '~> 3.0', require: false
|
||||||
gem 'kt-paperclip', '~> 7.2'
|
gem 'kt-paperclip', '~> 7.2'
|
||||||
gem 'md-paperclip-azure', '~> 2.2', require: false
|
|
||||||
gem 'ruby-vips', '~> 2.2', require: false
|
gem 'ruby-vips', '~> 2.2', require: false
|
||||||
|
|
||||||
gem 'active_model_serializers', '~> 0.10'
|
gem 'active_model_serializers', '~> 0.10'
|
||||||
gem 'addressable', '~> 2.8'
|
gem 'addressable', '~> 2.8'
|
||||||
gem 'bootsnap', '~> 1.18.0', require: false
|
gem 'bootsnap', '~> 1.18.0', require: false
|
||||||
gem 'browser', '< 6' # https://github.com/fnando/browser/issues/543
|
gem 'browser'
|
||||||
gem 'charlock_holmes', '~> 0.7.7'
|
gem 'charlock_holmes', '~> 0.7.7'
|
||||||
gem 'chewy', '~> 7.3'
|
gem 'chewy', '~> 7.3'
|
||||||
gem 'devise', '~> 4.9'
|
gem 'devise', '~> 4.9'
|
||||||
@ -47,13 +47,14 @@ gem 'color_diff', '~> 0.1'
|
|||||||
gem 'csv', '~> 3.2'
|
gem 'csv', '~> 3.2'
|
||||||
gem 'discard', '~> 1.2'
|
gem 'discard', '~> 1.2'
|
||||||
gem 'doorkeeper', '~> 5.6'
|
gem 'doorkeeper', '~> 5.6'
|
||||||
|
gem 'faraday-httpclient'
|
||||||
gem 'fast_blank', '~> 1.0'
|
gem 'fast_blank', '~> 1.0'
|
||||||
gem 'fastimage'
|
gem 'fastimage'
|
||||||
gem 'hiredis', '~> 0.6'
|
gem 'hiredis', '~> 0.6'
|
||||||
gem 'htmlentities', '~> 4.3'
|
gem 'htmlentities', '~> 4.3'
|
||||||
gem 'http', '~> 5.2.0'
|
gem 'http', '~> 5.2.0'
|
||||||
gem 'http_accept_language', '~> 2.1'
|
gem 'http_accept_language', '~> 2.1'
|
||||||
gem 'httplog', '~> 1.7.0'
|
gem 'httplog', '~> 1.7.0', require: false
|
||||||
gem 'i18n'
|
gem 'i18n'
|
||||||
gem 'idn-ruby', require: 'idn'
|
gem 'idn-ruby', require: 'idn'
|
||||||
gem 'inline_svg'
|
gem 'inline_svg'
|
||||||
@ -61,7 +62,8 @@ gem 'irb', '~> 1.8'
|
|||||||
gem 'kaminari', '~> 1.2'
|
gem 'kaminari', '~> 1.2'
|
||||||
gem 'link_header', '~> 0.0'
|
gem 'link_header', '~> 0.0'
|
||||||
gem 'mario-redis-lock', '~> 1.2', require: 'redis_lock'
|
gem 'mario-redis-lock', '~> 1.2', require: 'redis_lock'
|
||||||
gem 'mime-types', '~> 3.5.0', require: 'mime/types/columnar'
|
gem 'mime-types', '~> 3.6.0', require: 'mime/types/columnar'
|
||||||
|
gem 'mutex_m'
|
||||||
gem 'nokogiri', '~> 1.15'
|
gem 'nokogiri', '~> 1.15'
|
||||||
gem 'oj', '~> 3.14'
|
gem 'oj', '~> 3.14'
|
||||||
gem 'ox', '~> 2.14'
|
gem 'ox', '~> 2.14'
|
||||||
@ -111,8 +113,8 @@ group :opentelemetry do
|
|||||||
gem 'opentelemetry-instrumentation-http_client', '~> 0.22.3', require: false
|
gem 'opentelemetry-instrumentation-http_client', '~> 0.22.3', require: false
|
||||||
gem 'opentelemetry-instrumentation-net_http', '~> 0.22.4', require: false
|
gem 'opentelemetry-instrumentation-net_http', '~> 0.22.4', require: false
|
||||||
gem 'opentelemetry-instrumentation-pg', '~> 0.29.0', require: false
|
gem 'opentelemetry-instrumentation-pg', '~> 0.29.0', require: false
|
||||||
gem 'opentelemetry-instrumentation-rack', '~> 0.24.1', require: false
|
gem 'opentelemetry-instrumentation-rack', '~> 0.25.0', require: false
|
||||||
gem 'opentelemetry-instrumentation-rails', '~> 0.31.0', require: false
|
gem 'opentelemetry-instrumentation-rails', '~> 0.33.0', require: false
|
||||||
gem 'opentelemetry-instrumentation-redis', '~> 0.25.3', require: false
|
gem 'opentelemetry-instrumentation-redis', '~> 0.25.3', require: false
|
||||||
gem 'opentelemetry-instrumentation-sidekiq', '~> 0.25.2', require: false
|
gem 'opentelemetry-instrumentation-sidekiq', '~> 0.25.2', require: false
|
||||||
gem 'opentelemetry-sdk', '~> 1.4', require: false
|
gem 'opentelemetry-sdk', '~> 1.4', require: false
|
||||||
@ -170,7 +172,7 @@ group :development do
|
|||||||
gem 'rubocop-rspec_rails', require: false
|
gem 'rubocop-rspec_rails', require: false
|
||||||
|
|
||||||
# Annotates modules with schema
|
# Annotates modules with schema
|
||||||
gem 'annotate', '~> 3.2'
|
gem 'annotaterb', '~> 4.13'
|
||||||
|
|
||||||
# Enhanced error message pages for development
|
# Enhanced error message pages for development
|
||||||
gem 'better_errors', '~> 2.9'
|
gem 'better_errors', '~> 2.9'
|
||||||
@ -220,7 +222,7 @@ gem 'concurrent-ruby', require: false
|
|||||||
gem 'connection_pool', require: false
|
gem 'connection_pool', require: false
|
||||||
gem 'xorcist', '~> 1.1'
|
gem 'xorcist', '~> 1.1'
|
||||||
|
|
||||||
gem 'net-http', '~> 0.4.0'
|
gem 'net-http', '~> 0.5.0'
|
||||||
gem 'rubyzip', '~> 2.3'
|
gem 'rubyzip', '~> 2.3'
|
||||||
|
|
||||||
gem 'hcaptcha', '~> 7.1'
|
gem 'hcaptcha', '~> 7.1'
|
||||||
|
411
Gemfile.lock
@ -10,122 +10,111 @@ GIT
|
|||||||
GEM
|
GEM
|
||||||
remote: https://rubygems.org/
|
remote: https://rubygems.org/
|
||||||
specs:
|
specs:
|
||||||
actioncable (7.1.4)
|
actioncable (7.2.2)
|
||||||
actionpack (= 7.1.4)
|
actionpack (= 7.2.2)
|
||||||
activesupport (= 7.1.4)
|
activesupport (= 7.2.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.4)
|
actionmailbox (7.2.2)
|
||||||
actionpack (= 7.1.4)
|
actionpack (= 7.2.2)
|
||||||
activejob (= 7.1.4)
|
activejob (= 7.2.2)
|
||||||
activerecord (= 7.1.4)
|
activerecord (= 7.2.2)
|
||||||
activestorage (= 7.1.4)
|
activestorage (= 7.2.2)
|
||||||
activesupport (= 7.1.4)
|
activesupport (= 7.2.2)
|
||||||
mail (>= 2.7.1)
|
mail (>= 2.8.0)
|
||||||
net-imap
|
actionmailer (7.2.2)
|
||||||
net-pop
|
actionpack (= 7.2.2)
|
||||||
net-smtp
|
actionview (= 7.2.2)
|
||||||
actionmailer (7.1.4)
|
activejob (= 7.2.2)
|
||||||
actionpack (= 7.1.4)
|
activesupport (= 7.2.2)
|
||||||
actionview (= 7.1.4)
|
mail (>= 2.8.0)
|
||||||
activejob (= 7.1.4)
|
|
||||||
activesupport (= 7.1.4)
|
|
||||||
mail (~> 2.5, >= 2.5.4)
|
|
||||||
net-imap
|
|
||||||
net-pop
|
|
||||||
net-smtp
|
|
||||||
rails-dom-testing (~> 2.2)
|
rails-dom-testing (~> 2.2)
|
||||||
actionpack (7.1.4)
|
actionpack (7.2.2)
|
||||||
actionview (= 7.1.4)
|
actionview (= 7.2.2)
|
||||||
activesupport (= 7.1.4)
|
activesupport (= 7.2.2)
|
||||||
nokogiri (>= 1.8.5)
|
nokogiri (>= 1.8.5)
|
||||||
racc
|
racc
|
||||||
rack (>= 2.2.4)
|
rack (>= 2.2.4, < 3.2)
|
||||||
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.4)
|
useragent (~> 0.16)
|
||||||
actionpack (= 7.1.4)
|
actiontext (7.2.2)
|
||||||
activerecord (= 7.1.4)
|
actionpack (= 7.2.2)
|
||||||
activestorage (= 7.1.4)
|
activerecord (= 7.2.2)
|
||||||
activesupport (= 7.1.4)
|
activestorage (= 7.2.2)
|
||||||
|
activesupport (= 7.2.2)
|
||||||
globalid (>= 0.6.0)
|
globalid (>= 0.6.0)
|
||||||
nokogiri (>= 1.8.5)
|
nokogiri (>= 1.8.5)
|
||||||
actionview (7.1.4)
|
actionview (7.2.2)
|
||||||
activesupport (= 7.1.4)
|
activesupport (= 7.2.2)
|
||||||
builder (~> 3.1)
|
builder (~> 3.1)
|
||||||
erubi (~> 1.11)
|
erubi (~> 1.11)
|
||||||
rails-dom-testing (~> 2.2)
|
rails-dom-testing (~> 2.2)
|
||||||
rails-html-sanitizer (~> 1.6)
|
rails-html-sanitizer (~> 1.6)
|
||||||
active_model_serializers (0.10.14)
|
active_model_serializers (0.10.15)
|
||||||
actionpack (>= 4.1)
|
actionpack (>= 4.1)
|
||||||
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.4)
|
activejob (7.2.2)
|
||||||
activesupport (= 7.1.4)
|
activesupport (= 7.2.2)
|
||||||
globalid (>= 0.3.6)
|
globalid (>= 0.3.6)
|
||||||
activemodel (7.1.4)
|
activemodel (7.2.2)
|
||||||
activesupport (= 7.1.4)
|
activesupport (= 7.2.2)
|
||||||
activerecord (7.1.4)
|
activerecord (7.2.2)
|
||||||
activemodel (= 7.1.4)
|
activemodel (= 7.2.2)
|
||||||
activesupport (= 7.1.4)
|
activesupport (= 7.2.2)
|
||||||
timeout (>= 0.4.0)
|
timeout (>= 0.4.0)
|
||||||
activestorage (7.1.4)
|
activestorage (7.2.2)
|
||||||
actionpack (= 7.1.4)
|
actionpack (= 7.2.2)
|
||||||
activejob (= 7.1.4)
|
activejob (= 7.2.2)
|
||||||
activerecord (= 7.1.4)
|
activerecord (= 7.2.2)
|
||||||
activesupport (= 7.1.4)
|
activesupport (= 7.2.2)
|
||||||
marcel (~> 1.0)
|
marcel (~> 1.0)
|
||||||
activesupport (7.1.4)
|
activesupport (7.2.2)
|
||||||
base64
|
base64
|
||||||
|
benchmark (>= 0.3)
|
||||||
bigdecimal
|
bigdecimal
|
||||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
concurrent-ruby (~> 1.0, >= 1.3.1)
|
||||||
connection_pool (>= 2.2.5)
|
connection_pool (>= 2.2.5)
|
||||||
drb
|
drb
|
||||||
i18n (>= 1.6, < 2)
|
i18n (>= 1.6, < 2)
|
||||||
|
logger (>= 1.4.2)
|
||||||
minitest (>= 5.1)
|
minitest (>= 5.1)
|
||||||
mutex_m
|
securerandom (>= 0.3)
|
||||||
tzinfo (~> 2.0)
|
tzinfo (~> 2.0, >= 2.0.5)
|
||||||
addressable (2.8.7)
|
addressable (2.8.7)
|
||||||
public_suffix (>= 2.0.2, < 7.0)
|
public_suffix (>= 2.0.2, < 7.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)
|
||||||
annotate (3.2.0)
|
annotaterb (4.13.0)
|
||||||
activerecord (>= 3.2, < 8.0)
|
|
||||||
rake (>= 10.4, < 14.0)
|
|
||||||
ast (2.4.2)
|
ast (2.4.2)
|
||||||
attr_required (1.0.2)
|
attr_required (1.0.2)
|
||||||
awrence (1.2.1)
|
|
||||||
aws-eventstream (1.3.0)
|
aws-eventstream (1.3.0)
|
||||||
aws-partitions (1.978.0)
|
aws-partitions (1.1017.0)
|
||||||
aws-sdk-core (3.209.0)
|
aws-sdk-core (3.214.0)
|
||||||
aws-eventstream (~> 1, >= 1.3.0)
|
aws-eventstream (~> 1, >= 1.3.0)
|
||||||
aws-partitions (~> 1, >= 1.651.0)
|
aws-partitions (~> 1, >= 1.992.0)
|
||||||
aws-sigv4 (~> 1.9)
|
aws-sigv4 (~> 1.9)
|
||||||
jmespath (~> 1, >= 1.6.1)
|
jmespath (~> 1, >= 1.6.1)
|
||||||
aws-sdk-kms (1.94.0)
|
aws-sdk-kms (1.96.0)
|
||||||
aws-sdk-core (~> 3, >= 3.207.0)
|
aws-sdk-core (~> 3, >= 3.210.0)
|
||||||
aws-sigv4 (~> 1.5)
|
aws-sigv4 (~> 1.5)
|
||||||
aws-sdk-s3 (1.166.0)
|
aws-sdk-s3 (1.176.0)
|
||||||
aws-sdk-core (~> 3, >= 3.207.0)
|
aws-sdk-core (~> 3, >= 3.210.0)
|
||||||
aws-sdk-kms (~> 1)
|
aws-sdk-kms (~> 1)
|
||||||
aws-sigv4 (~> 1.5)
|
aws-sigv4 (~> 1.5)
|
||||||
aws-sigv4 (1.10.0)
|
aws-sigv4 (1.10.1)
|
||||||
aws-eventstream (~> 1, >= 1.0.2)
|
aws-eventstream (~> 1, >= 1.0.2)
|
||||||
azure-storage-blob (2.0.3)
|
azure-blob (0.5.3)
|
||||||
azure-storage-common (~> 2.0)
|
rexml
|
||||||
nokogiri (~> 1, >= 1.10.8)
|
|
||||||
azure-storage-common (2.0.4)
|
|
||||||
faraday (~> 1.0)
|
|
||||||
faraday_middleware (~> 1.0, >= 1.0.0.rc1)
|
|
||||||
net-http-persistent (~> 4.0)
|
|
||||||
nokogiri (~> 1, >= 1.10.8)
|
|
||||||
base64 (0.2.0)
|
base64 (0.2.0)
|
||||||
bcp47_spec (0.2.1)
|
bcp47_spec (0.2.1)
|
||||||
bcrypt (3.1.20)
|
bcrypt (3.1.20)
|
||||||
|
benchmark (0.4.0)
|
||||||
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)
|
||||||
@ -134,12 +123,12 @@ GEM
|
|||||||
bindata (2.5.0)
|
bindata (2.5.0)
|
||||||
binding_of_caller (1.0.1)
|
binding_of_caller (1.0.1)
|
||||||
debug_inspector (>= 1.2.0)
|
debug_inspector (>= 1.2.0)
|
||||||
blurhash (0.1.7)
|
blurhash (0.1.8)
|
||||||
bootsnap (1.18.4)
|
bootsnap (1.18.4)
|
||||||
msgpack (~> 1.2)
|
msgpack (~> 1.2)
|
||||||
brakeman (6.2.1)
|
brakeman (6.2.2)
|
||||||
racc
|
racc
|
||||||
browser (5.3.1)
|
browser (6.2.0)
|
||||||
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)
|
||||||
redis (>= 1.0, < 6)
|
redis (>= 1.0, < 6)
|
||||||
@ -179,14 +168,14 @@ GEM
|
|||||||
bigdecimal
|
bigdecimal
|
||||||
rexml
|
rexml
|
||||||
crass (1.0.6)
|
crass (1.0.6)
|
||||||
css_parser (1.19.0)
|
css_parser (1.19.1)
|
||||||
addressable
|
addressable
|
||||||
csv (3.3.0)
|
csv (3.3.0)
|
||||||
database_cleaner-active_record (2.2.0)
|
database_cleaner-active_record (2.2.0)
|
||||||
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.4)
|
date (3.4.0)
|
||||||
debug (1.9.2)
|
debug (1.9.2)
|
||||||
irb (~> 1.10)
|
irb (~> 1.10)
|
||||||
reline (>= 0.3.8)
|
reline (>= 0.3.8)
|
||||||
@ -197,20 +186,20 @@ GEM
|
|||||||
railties (>= 4.1.0)
|
railties (>= 4.1.0)
|
||||||
responders
|
responders
|
||||||
warden (~> 1.2.3)
|
warden (~> 1.2.3)
|
||||||
devise-two-factor (6.0.0)
|
devise-two-factor (6.1.0)
|
||||||
activesupport (~> 7.0)
|
activesupport (>= 7.0, < 8.1)
|
||||||
devise (~> 4.0)
|
devise (~> 4.0)
|
||||||
railties (~> 7.0)
|
railties (>= 7.0, < 8.1)
|
||||||
rotp (~> 6.0)
|
rotp (~> 6.0)
|
||||||
devise_pam_authenticatable2 (9.2.0)
|
devise_pam_authenticatable2 (9.2.0)
|
||||||
devise (>= 4.0.0)
|
devise (>= 4.0.0)
|
||||||
rpam2 (~> 4.0)
|
rpam2 (~> 4.0)
|
||||||
diff-lcs (1.5.1)
|
diff-lcs (1.5.1)
|
||||||
discard (1.3.0)
|
discard (1.4.0)
|
||||||
activerecord (>= 4.2, < 8)
|
activerecord (>= 4.2, < 9.0)
|
||||||
docile (1.4.1)
|
docile (1.4.1)
|
||||||
domain_name (0.6.20240107)
|
domain_name (0.6.20240107)
|
||||||
doorkeeper (5.7.1)
|
doorkeeper (5.8.0)
|
||||||
railties (>= 5)
|
railties (>= 5)
|
||||||
dotenv (3.1.4)
|
dotenv (3.1.4)
|
||||||
drb (2.2.1)
|
drb (2.2.1)
|
||||||
@ -231,38 +220,21 @@ GEM
|
|||||||
erubi (1.13.0)
|
erubi (1.13.0)
|
||||||
et-orbi (1.2.11)
|
et-orbi (1.2.11)
|
||||||
tzinfo
|
tzinfo
|
||||||
excon (0.111.0)
|
excon (0.112.0)
|
||||||
fabrication (2.31.0)
|
fabrication (2.31.0)
|
||||||
faker (3.4.2)
|
faker (3.5.1)
|
||||||
i18n (>= 1.8.11, < 2)
|
i18n (>= 1.8.11, < 2)
|
||||||
faraday (1.10.3)
|
faraday (2.12.0)
|
||||||
faraday-em_http (~> 1.0)
|
faraday-net_http (>= 2.0, < 3.4)
|
||||||
faraday-em_synchrony (~> 1.0)
|
json
|
||||||
faraday-excon (~> 1.1)
|
logger
|
||||||
faraday-httpclient (~> 1.0)
|
faraday-httpclient (2.0.1)
|
||||||
faraday-multipart (~> 1.0)
|
httpclient (>= 2.2)
|
||||||
faraday-net_http (~> 1.0)
|
faraday-net_http (3.3.0)
|
||||||
faraday-net_http_persistent (~> 1.0)
|
net-http
|
||||||
faraday-patron (~> 1.0)
|
|
||||||
faraday-rack (~> 1.0)
|
|
||||||
faraday-retry (~> 1.0)
|
|
||||||
ruby2_keywords (>= 0.0.4)
|
|
||||||
faraday-em_http (1.0.0)
|
|
||||||
faraday-em_synchrony (1.0.0)
|
|
||||||
faraday-excon (1.1.0)
|
|
||||||
faraday-httpclient (1.0.1)
|
|
||||||
faraday-multipart (1.0.4)
|
|
||||||
multipart-post (~> 2)
|
|
||||||
faraday-net_http (1.0.2)
|
|
||||||
faraday-net_http_persistent (1.2.0)
|
|
||||||
faraday-patron (1.0.0)
|
|
||||||
faraday-rack (1.0.0)
|
|
||||||
faraday-retry (1.0.3)
|
|
||||||
faraday_middleware (1.2.0)
|
|
||||||
faraday (~> 1.0)
|
|
||||||
fast_blank (1.0.1)
|
fast_blank (1.0.1)
|
||||||
fastimage (2.3.1)
|
fastimage (2.3.1)
|
||||||
ffi (1.16.3)
|
ffi (1.17.0)
|
||||||
ffi-compiler (1.3.2)
|
ffi-compiler (1.3.2)
|
||||||
ffi (>= 1.15.5)
|
ffi (>= 1.15.5)
|
||||||
rake
|
rake
|
||||||
@ -301,7 +273,7 @@ GEM
|
|||||||
activesupport (>= 5.1)
|
activesupport (>= 5.1)
|
||||||
haml (>= 4.0.6)
|
haml (>= 4.0.6)
|
||||||
railties (>= 5.1)
|
railties (>= 5.1)
|
||||||
haml_lint (0.58.0)
|
haml_lint (0.59.0)
|
||||||
haml (>= 5.0)
|
haml (>= 5.0)
|
||||||
parallel (~> 1.10)
|
parallel (~> 1.10)
|
||||||
rainbow
|
rainbow
|
||||||
@ -347,11 +319,15 @@ GEM
|
|||||||
activesupport (>= 3.0)
|
activesupport (>= 3.0)
|
||||||
nokogiri (>= 1.6)
|
nokogiri (>= 1.6)
|
||||||
io-console (0.7.2)
|
io-console (0.7.2)
|
||||||
irb (1.14.0)
|
irb (1.14.1)
|
||||||
rdoc (>= 4.0.0)
|
rdoc (>= 4.0.0)
|
||||||
reline (>= 0.4.2)
|
reline (>= 0.4.2)
|
||||||
|
jd-paperclip-azure (3.0.0)
|
||||||
|
addressable (~> 2.5)
|
||||||
|
azure-blob (~> 0.5.2)
|
||||||
|
hashie (~> 5.0)
|
||||||
jmespath (1.6.2)
|
jmespath (1.6.2)
|
||||||
json (2.7.2)
|
json (2.8.1)
|
||||||
json-canonicalization (1.0.0)
|
json-canonicalization (1.0.0)
|
||||||
json-jwt (1.15.3.1)
|
json-jwt (1.15.3.1)
|
||||||
activesupport (>= 4.2)
|
activesupport (>= 4.2)
|
||||||
@ -366,13 +342,15 @@ GEM
|
|||||||
rack (>= 2.2, < 4)
|
rack (>= 2.2, < 4)
|
||||||
rdf (~> 3.3)
|
rdf (~> 3.3)
|
||||||
rexml (~> 3.2)
|
rexml (~> 3.2)
|
||||||
json-ld-preloaded (3.3.0)
|
json-ld-preloaded (3.3.1)
|
||||||
json-ld (~> 3.3)
|
json-ld (~> 3.3)
|
||||||
rdf (~> 3.3)
|
rdf (~> 3.3)
|
||||||
json-schema (5.0.0)
|
json-schema (5.1.1)
|
||||||
addressable (~> 2.8)
|
addressable (~> 2.8)
|
||||||
|
bigdecimal (~> 3.1)
|
||||||
jsonapi-renderer (0.2.2)
|
jsonapi-renderer (0.2.2)
|
||||||
jwt (2.7.1)
|
jwt (2.9.3)
|
||||||
|
base64
|
||||||
kaminari (1.2.2)
|
kaminari (1.2.2)
|
||||||
activesupport (>= 4.1.0)
|
activesupport (>= 4.1.0)
|
||||||
kaminari-actionview (= 1.2.2)
|
kaminari-actionview (= 1.2.2)
|
||||||
@ -412,7 +390,7 @@ GEM
|
|||||||
activesupport (>= 4)
|
activesupport (>= 4)
|
||||||
railties (>= 4)
|
railties (>= 4)
|
||||||
request_store (~> 1.0)
|
request_store (~> 1.0)
|
||||||
loofah (2.22.0)
|
loofah (2.23.1)
|
||||||
crass (~> 1.0.2)
|
crass (~> 1.0.2)
|
||||||
nokogiri (>= 1.12.0)
|
nokogiri (>= 1.12.0)
|
||||||
mail (2.8.1)
|
mail (2.8.1)
|
||||||
@ -424,26 +402,20 @@ GEM
|
|||||||
mario-redis-lock (1.2.1)
|
mario-redis-lock (1.2.1)
|
||||||
redis (>= 3.0.5)
|
redis (>= 3.0.5)
|
||||||
matrix (0.4.2)
|
matrix (0.4.2)
|
||||||
md-paperclip-azure (2.2.0)
|
|
||||||
addressable (~> 2.5)
|
|
||||||
azure-storage-blob (~> 2.0.1)
|
|
||||||
hashie (~> 5.0)
|
|
||||||
memory_profiler (1.1.0)
|
memory_profiler (1.1.0)
|
||||||
mime-types (3.5.2)
|
mime-types (3.6.0)
|
||||||
|
logger
|
||||||
mime-types-data (~> 3.2015)
|
mime-types-data (~> 3.2015)
|
||||||
mime-types-data (3.2024.0820)
|
mime-types-data (3.2024.1105)
|
||||||
mini_mime (1.1.5)
|
mini_mime (1.1.5)
|
||||||
mini_portile2 (2.8.7)
|
mini_portile2 (2.8.8)
|
||||||
minitest (5.25.1)
|
minitest (5.25.2)
|
||||||
msgpack (1.7.2)
|
msgpack (1.7.5)
|
||||||
multi_json (1.15.0)
|
multi_json (1.15.0)
|
||||||
multipart-post (2.4.1)
|
mutex_m (0.3.0)
|
||||||
mutex_m (0.2.0)
|
net-http (0.5.0)
|
||||||
net-http (0.4.1)
|
|
||||||
uri
|
uri
|
||||||
net-http-persistent (4.0.2)
|
net-imap (0.5.1)
|
||||||
connection_pool (~> 2.2)
|
|
||||||
net-imap (0.4.15)
|
|
||||||
date
|
date
|
||||||
net-protocol
|
net-protocol
|
||||||
net-ldap (0.19.0)
|
net-ldap (0.19.0)
|
||||||
@ -453,11 +425,11 @@ GEM
|
|||||||
timeout
|
timeout
|
||||||
net-smtp (0.5.0)
|
net-smtp (0.5.0)
|
||||||
net-protocol
|
net-protocol
|
||||||
nio4r (2.7.3)
|
nio4r (2.7.4)
|
||||||
nokogiri (1.16.7)
|
nokogiri (1.16.8)
|
||||||
mini_portile2 (~> 2.8.2)
|
mini_portile2 (~> 2.8.2)
|
||||||
racc (~> 1.4)
|
racc (~> 1.4)
|
||||||
oj (3.16.6)
|
oj (3.16.7)
|
||||||
bigdecimal (>= 3.0)
|
bigdecimal (>= 3.0)
|
||||||
ostruct (>= 0.2)
|
ostruct (>= 0.2)
|
||||||
omniauth (2.1.2)
|
omniauth (2.1.2)
|
||||||
@ -494,34 +466,35 @@ GEM
|
|||||||
opentelemetry-api (1.4.0)
|
opentelemetry-api (1.4.0)
|
||||||
opentelemetry-common (0.21.0)
|
opentelemetry-common (0.21.0)
|
||||||
opentelemetry-api (~> 1.0)
|
opentelemetry-api (~> 1.0)
|
||||||
opentelemetry-exporter-otlp (0.29.0)
|
opentelemetry-exporter-otlp (0.29.1)
|
||||||
google-protobuf (>= 3.18)
|
google-protobuf (>= 3.18)
|
||||||
googleapis-common-protos-types (~> 1.3)
|
googleapis-common-protos-types (~> 1.3)
|
||||||
opentelemetry-api (~> 1.1)
|
opentelemetry-api (~> 1.1)
|
||||||
opentelemetry-common (~> 0.20)
|
opentelemetry-common (~> 0.20)
|
||||||
opentelemetry-sdk (~> 1.2)
|
opentelemetry-sdk (~> 1.2)
|
||||||
opentelemetry-semantic_conventions
|
opentelemetry-semantic_conventions
|
||||||
opentelemetry-helpers-sql-obfuscation (0.2.0)
|
opentelemetry-helpers-sql-obfuscation (0.2.1)
|
||||||
opentelemetry-common (~> 0.21)
|
opentelemetry-common (~> 0.21)
|
||||||
opentelemetry-instrumentation-action_mailer (0.1.0)
|
opentelemetry-instrumentation-action_mailer (0.2.0)
|
||||||
opentelemetry-api (~> 1.0)
|
opentelemetry-api (~> 1.0)
|
||||||
opentelemetry-instrumentation-active_support (~> 0.1)
|
opentelemetry-instrumentation-active_support (~> 0.1)
|
||||||
opentelemetry-instrumentation-base (~> 0.22.1)
|
opentelemetry-instrumentation-base (~> 0.22.1)
|
||||||
opentelemetry-instrumentation-action_pack (0.9.0)
|
opentelemetry-instrumentation-action_pack (0.10.0)
|
||||||
opentelemetry-api (~> 1.0)
|
opentelemetry-api (~> 1.0)
|
||||||
opentelemetry-instrumentation-base (~> 0.22.1)
|
opentelemetry-instrumentation-base (~> 0.22.1)
|
||||||
opentelemetry-instrumentation-rack (~> 0.21)
|
opentelemetry-instrumentation-rack (~> 0.21)
|
||||||
opentelemetry-instrumentation-action_view (0.7.2)
|
opentelemetry-instrumentation-action_view (0.7.3)
|
||||||
opentelemetry-api (~> 1.0)
|
opentelemetry-api (~> 1.0)
|
||||||
opentelemetry-instrumentation-active_support (~> 0.1)
|
opentelemetry-instrumentation-active_support (~> 0.6)
|
||||||
opentelemetry-instrumentation-base (~> 0.22.1)
|
opentelemetry-instrumentation-base (~> 0.22.1)
|
||||||
opentelemetry-instrumentation-active_job (0.7.7)
|
opentelemetry-instrumentation-active_job (0.7.8)
|
||||||
opentelemetry-api (~> 1.0)
|
opentelemetry-api (~> 1.0)
|
||||||
opentelemetry-instrumentation-base (~> 0.22.1)
|
opentelemetry-instrumentation-base (~> 0.22.1)
|
||||||
opentelemetry-instrumentation-active_model_serializers (0.20.2)
|
opentelemetry-instrumentation-active_model_serializers (0.20.3)
|
||||||
opentelemetry-api (~> 1.0)
|
opentelemetry-api (~> 1.0)
|
||||||
|
opentelemetry-instrumentation-active_support (>= 0.6.0)
|
||||||
opentelemetry-instrumentation-base (~> 0.22.1)
|
opentelemetry-instrumentation-base (~> 0.22.1)
|
||||||
opentelemetry-instrumentation-active_record (0.7.3)
|
opentelemetry-instrumentation-active_record (0.8.1)
|
||||||
opentelemetry-api (~> 1.0)
|
opentelemetry-api (~> 1.0)
|
||||||
opentelemetry-instrumentation-base (~> 0.22.1)
|
opentelemetry-instrumentation-base (~> 0.22.1)
|
||||||
opentelemetry-instrumentation-active_support (0.6.0)
|
opentelemetry-instrumentation-active_support (0.6.0)
|
||||||
@ -534,35 +507,35 @@ GEM
|
|||||||
opentelemetry-instrumentation-concurrent_ruby (0.21.4)
|
opentelemetry-instrumentation-concurrent_ruby (0.21.4)
|
||||||
opentelemetry-api (~> 1.0)
|
opentelemetry-api (~> 1.0)
|
||||||
opentelemetry-instrumentation-base (~> 0.22.1)
|
opentelemetry-instrumentation-base (~> 0.22.1)
|
||||||
opentelemetry-instrumentation-excon (0.22.4)
|
opentelemetry-instrumentation-excon (0.22.5)
|
||||||
opentelemetry-api (~> 1.0)
|
opentelemetry-api (~> 1.0)
|
||||||
opentelemetry-instrumentation-base (~> 0.22.1)
|
opentelemetry-instrumentation-base (~> 0.22.1)
|
||||||
opentelemetry-instrumentation-faraday (0.24.6)
|
opentelemetry-instrumentation-faraday (0.24.7)
|
||||||
opentelemetry-api (~> 1.0)
|
opentelemetry-api (~> 1.0)
|
||||||
opentelemetry-instrumentation-base (~> 0.22.1)
|
opentelemetry-instrumentation-base (~> 0.22.1)
|
||||||
opentelemetry-instrumentation-http (0.23.4)
|
opentelemetry-instrumentation-http (0.23.5)
|
||||||
opentelemetry-api (~> 1.0)
|
opentelemetry-api (~> 1.0)
|
||||||
opentelemetry-instrumentation-base (~> 0.22.1)
|
opentelemetry-instrumentation-base (~> 0.22.1)
|
||||||
opentelemetry-instrumentation-http_client (0.22.7)
|
opentelemetry-instrumentation-http_client (0.22.8)
|
||||||
opentelemetry-api (~> 1.0)
|
opentelemetry-api (~> 1.0)
|
||||||
opentelemetry-instrumentation-base (~> 0.22.1)
|
opentelemetry-instrumentation-base (~> 0.22.1)
|
||||||
opentelemetry-instrumentation-net_http (0.22.7)
|
opentelemetry-instrumentation-net_http (0.22.8)
|
||||||
opentelemetry-api (~> 1.0)
|
opentelemetry-api (~> 1.0)
|
||||||
opentelemetry-instrumentation-base (~> 0.22.1)
|
opentelemetry-instrumentation-base (~> 0.22.1)
|
||||||
opentelemetry-instrumentation-pg (0.29.0)
|
opentelemetry-instrumentation-pg (0.29.1)
|
||||||
opentelemetry-api (~> 1.0)
|
opentelemetry-api (~> 1.0)
|
||||||
opentelemetry-helpers-sql-obfuscation
|
opentelemetry-helpers-sql-obfuscation
|
||||||
opentelemetry-instrumentation-base (~> 0.22.1)
|
opentelemetry-instrumentation-base (~> 0.22.1)
|
||||||
opentelemetry-instrumentation-rack (0.24.6)
|
opentelemetry-instrumentation-rack (0.25.0)
|
||||||
opentelemetry-api (~> 1.0)
|
opentelemetry-api (~> 1.0)
|
||||||
opentelemetry-instrumentation-base (~> 0.22.1)
|
opentelemetry-instrumentation-base (~> 0.22.1)
|
||||||
opentelemetry-instrumentation-rails (0.31.2)
|
opentelemetry-instrumentation-rails (0.33.1)
|
||||||
opentelemetry-api (~> 1.0)
|
opentelemetry-api (~> 1.0)
|
||||||
opentelemetry-instrumentation-action_mailer (~> 0.1.0)
|
opentelemetry-instrumentation-action_mailer (~> 0.2.0)
|
||||||
opentelemetry-instrumentation-action_pack (~> 0.9.0)
|
opentelemetry-instrumentation-action_pack (~> 0.10.0)
|
||||||
opentelemetry-instrumentation-action_view (~> 0.7.0)
|
opentelemetry-instrumentation-action_view (~> 0.7.0)
|
||||||
opentelemetry-instrumentation-active_job (~> 0.7.0)
|
opentelemetry-instrumentation-active_job (~> 0.7.0)
|
||||||
opentelemetry-instrumentation-active_record (~> 0.7.0)
|
opentelemetry-instrumentation-active_record (~> 0.8.0)
|
||||||
opentelemetry-instrumentation-active_support (~> 0.6.0)
|
opentelemetry-instrumentation-active_support (~> 0.6.0)
|
||||||
opentelemetry-instrumentation-base (~> 0.22.1)
|
opentelemetry-instrumentation-base (~> 0.22.1)
|
||||||
opentelemetry-instrumentation-redis (0.25.7)
|
opentelemetry-instrumentation-redis (0.25.7)
|
||||||
@ -573,7 +546,7 @@ GEM
|
|||||||
opentelemetry-instrumentation-base (~> 0.22.1)
|
opentelemetry-instrumentation-base (~> 0.22.1)
|
||||||
opentelemetry-registry (0.3.1)
|
opentelemetry-registry (0.3.1)
|
||||||
opentelemetry-api (~> 1.1)
|
opentelemetry-api (~> 1.1)
|
||||||
opentelemetry-sdk (1.5.0)
|
opentelemetry-sdk (1.6.0)
|
||||||
opentelemetry-api (~> 1.1)
|
opentelemetry-api (~> 1.1)
|
||||||
opentelemetry-common (~> 0.20)
|
opentelemetry-common (~> 0.20)
|
||||||
opentelemetry-registry (~> 0.2)
|
opentelemetry-registry (~> 0.2)
|
||||||
@ -581,17 +554,17 @@ GEM
|
|||||||
opentelemetry-semantic_conventions (1.10.1)
|
opentelemetry-semantic_conventions (1.10.1)
|
||||||
opentelemetry-api (~> 1.0)
|
opentelemetry-api (~> 1.0)
|
||||||
orm_adapter (0.5.0)
|
orm_adapter (0.5.0)
|
||||||
ostruct (0.6.0)
|
ostruct (0.6.1)
|
||||||
ox (2.14.18)
|
ox (2.14.18)
|
||||||
parallel (1.26.3)
|
parallel (1.26.3)
|
||||||
parser (3.3.5.0)
|
parser (3.3.6.0)
|
||||||
ast (~> 2.4.1)
|
ast (~> 2.4.1)
|
||||||
racc
|
racc
|
||||||
parslet (2.0.0)
|
parslet (2.0.0)
|
||||||
pastel (0.8.0)
|
pastel (0.8.0)
|
||||||
tty-color (~> 0.5)
|
tty-color (~> 0.5)
|
||||||
pg (1.5.8)
|
pg (1.5.9)
|
||||||
pghero (3.6.0)
|
pghero (3.6.1)
|
||||||
activerecord (>= 6.1)
|
activerecord (>= 6.1)
|
||||||
premailer (1.27.0)
|
premailer (1.27.0)
|
||||||
addressable
|
addressable
|
||||||
@ -601,21 +574,21 @@ GEM
|
|||||||
actionmailer (>= 3)
|
actionmailer (>= 3)
|
||||||
net-smtp
|
net-smtp
|
||||||
premailer (~> 1.7, >= 1.7.9)
|
premailer (~> 1.7, >= 1.7.9)
|
||||||
propshaft (1.0.0)
|
propshaft (1.1.0)
|
||||||
actionpack (>= 7.0.0)
|
actionpack (>= 7.0.0)
|
||||||
activesupport (>= 7.0.0)
|
activesupport (>= 7.0.0)
|
||||||
rack
|
rack
|
||||||
railties (>= 7.0.0)
|
railties (>= 7.0.0)
|
||||||
psych (5.1.2)
|
psych (5.2.0)
|
||||||
stringio
|
stringio
|
||||||
public_suffix (6.0.1)
|
public_suffix (6.0.1)
|
||||||
puma (6.4.3)
|
puma (6.5.0)
|
||||||
nio4r (~> 2.0)
|
nio4r (~> 2.0)
|
||||||
pundit (2.4.0)
|
pundit (2.4.0)
|
||||||
activesupport (>= 3.0.0)
|
activesupport (>= 3.0.0)
|
||||||
raabro (1.4.0)
|
raabro (1.4.0)
|
||||||
racc (1.8.1)
|
racc (1.8.1)
|
||||||
rack (2.2.9)
|
rack (2.2.10)
|
||||||
rack-attack (6.7.0)
|
rack-attack (6.7.0)
|
||||||
rack (>= 1.0, < 4)
|
rack (>= 1.0, < 4)
|
||||||
rack-cors (2.0.2)
|
rack-cors (2.0.2)
|
||||||
@ -638,20 +611,20 @@ GEM
|
|||||||
rackup (1.0.0)
|
rackup (1.0.0)
|
||||||
rack (< 3)
|
rack (< 3)
|
||||||
webrick
|
webrick
|
||||||
rails (7.1.4)
|
rails (7.2.2)
|
||||||
actioncable (= 7.1.4)
|
actioncable (= 7.2.2)
|
||||||
actionmailbox (= 7.1.4)
|
actionmailbox (= 7.2.2)
|
||||||
actionmailer (= 7.1.4)
|
actionmailer (= 7.2.2)
|
||||||
actionpack (= 7.1.4)
|
actionpack (= 7.2.2)
|
||||||
actiontext (= 7.1.4)
|
actiontext (= 7.2.2)
|
||||||
actionview (= 7.1.4)
|
actionview (= 7.2.2)
|
||||||
activejob (= 7.1.4)
|
activejob (= 7.2.2)
|
||||||
activemodel (= 7.1.4)
|
activemodel (= 7.2.2)
|
||||||
activerecord (= 7.1.4)
|
activerecord (= 7.2.2)
|
||||||
activestorage (= 7.1.4)
|
activestorage (= 7.2.2)
|
||||||
activesupport (= 7.1.4)
|
activesupport (= 7.2.2)
|
||||||
bundler (>= 1.15.0)
|
bundler (>= 1.15.0)
|
||||||
railties (= 7.1.4)
|
railties (= 7.2.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)
|
||||||
@ -660,16 +633,16 @@ GEM
|
|||||||
activesupport (>= 5.0.0)
|
activesupport (>= 5.0.0)
|
||||||
minitest
|
minitest
|
||||||
nokogiri (>= 1.6)
|
nokogiri (>= 1.6)
|
||||||
rails-html-sanitizer (1.6.0)
|
rails-html-sanitizer (1.6.1)
|
||||||
loofah (~> 2.21)
|
loofah (~> 2.21)
|
||||||
nokogiri (~> 1.14)
|
nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0)
|
||||||
rails-i18n (7.0.9)
|
rails-i18n (7.0.10)
|
||||||
i18n (>= 0.7, < 2)
|
i18n (>= 0.7, < 2)
|
||||||
railties (>= 6.0.0, < 8)
|
railties (>= 6.0.0, < 8)
|
||||||
railties (7.1.4)
|
railties (7.2.2)
|
||||||
actionpack (= 7.1.4)
|
actionpack (= 7.2.2)
|
||||||
activesupport (= 7.1.4)
|
activesupport (= 7.2.2)
|
||||||
irb
|
irb (~> 1.13)
|
||||||
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)
|
||||||
@ -691,16 +664,16 @@ GEM
|
|||||||
redlock (1.3.2)
|
redlock (1.3.2)
|
||||||
redis (>= 3.0.0, < 6.0)
|
redis (>= 3.0.0, < 6.0)
|
||||||
regexp_parser (2.9.2)
|
regexp_parser (2.9.2)
|
||||||
reline (0.5.10)
|
reline (0.5.11)
|
||||||
io-console (~> 0.5)
|
io-console (~> 0.5)
|
||||||
request_store (1.6.0)
|
request_store (1.6.0)
|
||||||
rack (>= 1.4)
|
rack (>= 1.4)
|
||||||
responders (3.1.1)
|
responders (3.1.1)
|
||||||
actionpack (>= 5.2)
|
actionpack (>= 5.2)
|
||||||
railties (>= 5.2)
|
railties (>= 5.2)
|
||||||
rexml (3.3.7)
|
rexml (3.3.9)
|
||||||
rotp (6.3.0)
|
rotp (6.3.0)
|
||||||
rouge (4.3.0)
|
rouge (4.5.1)
|
||||||
rpam2 (4.0.2)
|
rpam2 (4.0.2)
|
||||||
rqrcode (2.2.0)
|
rqrcode (2.2.0)
|
||||||
chunky_png (~> 1.0)
|
chunky_png (~> 1.0)
|
||||||
@ -710,17 +683,17 @@ GEM
|
|||||||
rspec-core (~> 3.13.0)
|
rspec-core (~> 3.13.0)
|
||||||
rspec-expectations (~> 3.13.0)
|
rspec-expectations (~> 3.13.0)
|
||||||
rspec-mocks (~> 3.13.0)
|
rspec-mocks (~> 3.13.0)
|
||||||
rspec-core (3.13.1)
|
rspec-core (3.13.2)
|
||||||
rspec-support (~> 3.13.0)
|
rspec-support (~> 3.13.0)
|
||||||
rspec-expectations (3.13.2)
|
rspec-expectations (3.13.3)
|
||||||
diff-lcs (>= 1.2.0, < 2.0)
|
diff-lcs (>= 1.2.0, < 2.0)
|
||||||
rspec-support (~> 3.13.0)
|
rspec-support (~> 3.13.0)
|
||||||
rspec-github (2.4.0)
|
rspec-github (2.4.0)
|
||||||
rspec-core (~> 3.0)
|
rspec-core (~> 3.0)
|
||||||
rspec-mocks (3.13.1)
|
rspec-mocks (3.13.2)
|
||||||
diff-lcs (>= 1.2.0, < 2.0)
|
diff-lcs (>= 1.2.0, < 2.0)
|
||||||
rspec-support (~> 3.13.0)
|
rspec-support (~> 3.13.0)
|
||||||
rspec-rails (7.0.1)
|
rspec-rails (7.1.0)
|
||||||
actionpack (>= 7.0)
|
actionpack (>= 7.0)
|
||||||
activesupport (>= 7.0)
|
activesupport (>= 7.0)
|
||||||
railties (>= 7.0)
|
railties (>= 7.0)
|
||||||
@ -748,20 +721,20 @@ GEM
|
|||||||
parser (>= 3.3.1.0)
|
parser (>= 3.3.1.0)
|
||||||
rubocop-capybara (2.21.0)
|
rubocop-capybara (2.21.0)
|
||||||
rubocop (~> 1.41)
|
rubocop (~> 1.41)
|
||||||
rubocop-performance (1.21.1)
|
rubocop-performance (1.22.1)
|
||||||
rubocop (>= 1.48.1, < 2.0)
|
rubocop (>= 1.48.1, < 2.0)
|
||||||
rubocop-ast (>= 1.31.1, < 2.0)
|
rubocop-ast (>= 1.31.1, < 2.0)
|
||||||
rubocop-rails (2.25.1)
|
rubocop-rails (2.27.0)
|
||||||
activesupport (>= 4.2.0)
|
activesupport (>= 4.2.0)
|
||||||
rack (>= 1.1)
|
rack (>= 1.1)
|
||||||
rubocop (>= 1.33.0, < 2.0)
|
rubocop (>= 1.52.0, < 2.0)
|
||||||
rubocop-ast (>= 1.31.1, < 2.0)
|
rubocop-ast (>= 1.31.1, < 2.0)
|
||||||
rubocop-rspec (3.0.4)
|
rubocop-rspec (3.2.0)
|
||||||
rubocop (~> 1.61)
|
rubocop (~> 1.61)
|
||||||
rubocop-rspec_rails (2.30.0)
|
rubocop-rspec_rails (2.30.0)
|
||||||
rubocop (~> 1.61)
|
rubocop (~> 1.61)
|
||||||
rubocop-rspec (~> 3, >= 3.0.1)
|
rubocop-rspec (~> 3, >= 3.0.1)
|
||||||
ruby-prof (1.7.0)
|
ruby-prof (1.7.1)
|
||||||
ruby-progressbar (1.13.0)
|
ruby-progressbar (1.13.0)
|
||||||
ruby-saml (1.17.0)
|
ruby-saml (1.17.0)
|
||||||
nokogiri (>= 1.13.10)
|
nokogiri (>= 1.13.10)
|
||||||
@ -769,7 +742,6 @@ GEM
|
|||||||
ruby-vips (2.2.2)
|
ruby-vips (2.2.2)
|
||||||
ffi (~> 1.12)
|
ffi (~> 1.12)
|
||||||
logger
|
logger
|
||||||
ruby2_keywords (0.0.5)
|
|
||||||
rubyzip (2.3.2)
|
rubyzip (2.3.2)
|
||||||
rufus-scheduler (3.9.1)
|
rufus-scheduler (3.9.1)
|
||||||
fugit (~> 1.1, >= 1.1.6)
|
fugit (~> 1.1, >= 1.1.6)
|
||||||
@ -781,7 +753,8 @@ GEM
|
|||||||
scenic (1.8.0)
|
scenic (1.8.0)
|
||||||
activerecord (>= 4.0.0)
|
activerecord (>= 4.0.0)
|
||||||
railties (>= 4.0.0)
|
railties (>= 4.0.0)
|
||||||
selenium-webdriver (4.25.0)
|
securerandom (0.3.2)
|
||||||
|
selenium-webdriver (4.27.0)
|
||||||
base64 (~> 0.2)
|
base64 (~> 0.2)
|
||||||
logger (~> 1.4)
|
logger (~> 1.4)
|
||||||
rexml (~> 3.2, >= 3.2.5)
|
rexml (~> 3.2, >= 3.2.5)
|
||||||
@ -815,14 +788,14 @@ GEM
|
|||||||
docile (~> 1.1)
|
docile (~> 1.1)
|
||||||
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.13.1)
|
||||||
simplecov-lcov (0.8.0)
|
simplecov-lcov (0.8.0)
|
||||||
simplecov_json_formatter (0.1.4)
|
simplecov_json_formatter (0.1.4)
|
||||||
stackprof (0.2.26)
|
stackprof (0.2.26)
|
||||||
stoplight (4.1.0)
|
stoplight (4.1.0)
|
||||||
redlock (~> 1.0)
|
redlock (~> 1.0)
|
||||||
stringio (3.1.1)
|
stringio (3.1.2)
|
||||||
strong_migrations (2.0.0)
|
strong_migrations (2.1.0)
|
||||||
activerecord (>= 6.1)
|
activerecord (>= 6.1)
|
||||||
swd (1.3.0)
|
swd (1.3.0)
|
||||||
activesupport (>= 3)
|
activesupport (>= 3)
|
||||||
@ -837,7 +810,7 @@ GEM
|
|||||||
test-prof (1.4.2)
|
test-prof (1.4.2)
|
||||||
thor (1.3.2)
|
thor (1.3.2)
|
||||||
tilt (2.4.0)
|
tilt (2.4.0)
|
||||||
timeout (0.4.1)
|
timeout (0.4.2)
|
||||||
tpm-key_attestation (0.12.1)
|
tpm-key_attestation (0.12.1)
|
||||||
bindata (~> 2.4)
|
bindata (~> 2.4)
|
||||||
openssl (> 2.0)
|
openssl (> 2.0)
|
||||||
@ -862,8 +835,9 @@ GEM
|
|||||||
unf (0.1.4)
|
unf (0.1.4)
|
||||||
unf_ext
|
unf_ext
|
||||||
unf_ext (0.0.9.1)
|
unf_ext (0.0.9.1)
|
||||||
unicode-display_width (2.5.0)
|
unicode-display_width (2.6.0)
|
||||||
uri (0.13.1)
|
uri (0.13.1)
|
||||||
|
useragent (0.16.10)
|
||||||
validate_email (0.1.6)
|
validate_email (0.1.6)
|
||||||
activemodel (>= 3.0)
|
activemodel (>= 3.0)
|
||||||
mail (>= 2.2.5)
|
mail (>= 2.2.5)
|
||||||
@ -872,9 +846,8 @@ GEM
|
|||||||
public_suffix
|
public_suffix
|
||||||
warden (1.2.9)
|
warden (1.2.9)
|
||||||
rack (>= 2.0.9)
|
rack (>= 2.0.9)
|
||||||
webauthn (3.1.0)
|
webauthn (3.2.2)
|
||||||
android_key_attestation (~> 0.3.0)
|
android_key_attestation (~> 0.3.0)
|
||||||
awrence (~> 1.1)
|
|
||||||
bindata (~> 2.4)
|
bindata (~> 2.4)
|
||||||
cbor (~> 0.5.9)
|
cbor (~> 0.5.9)
|
||||||
cose (~> 1.1)
|
cose (~> 1.1)
|
||||||
@ -884,7 +857,7 @@ GEM
|
|||||||
webfinger (1.2.0)
|
webfinger (1.2.0)
|
||||||
activesupport
|
activesupport
|
||||||
httpclient (>= 2.4)
|
httpclient (>= 2.4)
|
||||||
webmock (3.23.1)
|
webmock (3.24.0)
|
||||||
addressable (>= 2.8.0)
|
addressable (>= 2.8.0)
|
||||||
crack (>= 0.3.2)
|
crack (>= 0.3.2)
|
||||||
hashdiff (>= 0.4.0, < 2.0.0)
|
hashdiff (>= 0.4.0, < 2.0.0)
|
||||||
@ -893,7 +866,7 @@ GEM
|
|||||||
rack-proxy (>= 0.6.1)
|
rack-proxy (>= 0.6.1)
|
||||||
railties (>= 5.2)
|
railties (>= 5.2)
|
||||||
semantic_range (>= 2.3.0)
|
semantic_range (>= 2.3.0)
|
||||||
webrick (1.8.2)
|
webrick (1.9.0)
|
||||||
websocket (1.2.11)
|
websocket (1.2.11)
|
||||||
websocket-driver (0.7.6)
|
websocket-driver (0.7.6)
|
||||||
websocket-extensions (>= 0.1.0)
|
websocket-extensions (>= 0.1.0)
|
||||||
@ -902,7 +875,7 @@ GEM
|
|||||||
xorcist (1.1.3)
|
xorcist (1.1.3)
|
||||||
xpath (3.2.0)
|
xpath (3.2.0)
|
||||||
nokogiri (~> 1.8)
|
nokogiri (~> 1.8)
|
||||||
zeitwerk (2.6.18)
|
zeitwerk (2.7.1)
|
||||||
|
|
||||||
PLATFORMS
|
PLATFORMS
|
||||||
ruby
|
ruby
|
||||||
@ -910,14 +883,14 @@ PLATFORMS
|
|||||||
DEPENDENCIES
|
DEPENDENCIES
|
||||||
active_model_serializers (~> 0.10)
|
active_model_serializers (~> 0.10)
|
||||||
addressable (~> 2.8)
|
addressable (~> 2.8)
|
||||||
annotate (~> 3.2)
|
annotaterb (~> 4.13)
|
||||||
aws-sdk-s3 (~> 1.123)
|
aws-sdk-s3 (~> 1.123)
|
||||||
better_errors (~> 2.9)
|
better_errors (~> 2.9)
|
||||||
binding_of_caller (~> 1.0)
|
binding_of_caller (~> 1.0)
|
||||||
blurhash (~> 0.1)
|
blurhash (~> 0.1)
|
||||||
bootsnap (~> 1.18.0)
|
bootsnap (~> 1.18.0)
|
||||||
brakeman (~> 6.0)
|
brakeman (~> 6.0)
|
||||||
browser (< 6)
|
browser
|
||||||
bundler-audit (~> 0.9)
|
bundler-audit (~> 0.9)
|
||||||
capybara (~> 3.39)
|
capybara (~> 3.39)
|
||||||
charlock_holmes (~> 0.7.7)
|
charlock_holmes (~> 0.7.7)
|
||||||
@ -939,10 +912,11 @@ DEPENDENCIES
|
|||||||
email_spec
|
email_spec
|
||||||
fabrication (~> 2.30)
|
fabrication (~> 2.30)
|
||||||
faker (~> 3.2)
|
faker (~> 3.2)
|
||||||
|
faraday-httpclient
|
||||||
fast_blank (~> 1.0)
|
fast_blank (~> 1.0)
|
||||||
fastimage
|
fastimage
|
||||||
flatware-rspec
|
flatware-rspec
|
||||||
fog-core (<= 2.5.0)
|
fog-core (<= 2.6.0)
|
||||||
fog-openstack (~> 1.0)
|
fog-openstack (~> 1.0)
|
||||||
haml-rails (~> 2.0)
|
haml-rails (~> 2.0)
|
||||||
haml_lint
|
haml_lint
|
||||||
@ -957,6 +931,7 @@ DEPENDENCIES
|
|||||||
idn-ruby
|
idn-ruby
|
||||||
inline_svg
|
inline_svg
|
||||||
irb (~> 1.8)
|
irb (~> 1.8)
|
||||||
|
jd-paperclip-azure (~> 3.0)
|
||||||
json-ld
|
json-ld
|
||||||
json-ld-preloaded (~> 3.2)
|
json-ld-preloaded (~> 3.2)
|
||||||
json-schema (~> 5.0)
|
json-schema (~> 5.0)
|
||||||
@ -968,10 +943,10 @@ DEPENDENCIES
|
|||||||
lograge (~> 0.12)
|
lograge (~> 0.12)
|
||||||
mail (~> 2.8)
|
mail (~> 2.8)
|
||||||
mario-redis-lock (~> 1.2)
|
mario-redis-lock (~> 1.2)
|
||||||
md-paperclip-azure (~> 2.2)
|
|
||||||
memory_profiler
|
memory_profiler
|
||||||
mime-types (~> 3.5.0)
|
mime-types (~> 3.6.0)
|
||||||
net-http (~> 0.4.0)
|
mutex_m
|
||||||
|
net-http (~> 0.5.0)
|
||||||
net-ldap (~> 0.18)
|
net-ldap (~> 0.18)
|
||||||
nokogiri (~> 1.15)
|
nokogiri (~> 1.15)
|
||||||
oj (~> 3.14)
|
oj (~> 3.14)
|
||||||
@ -991,8 +966,8 @@ DEPENDENCIES
|
|||||||
opentelemetry-instrumentation-http_client (~> 0.22.3)
|
opentelemetry-instrumentation-http_client (~> 0.22.3)
|
||||||
opentelemetry-instrumentation-net_http (~> 0.22.4)
|
opentelemetry-instrumentation-net_http (~> 0.22.4)
|
||||||
opentelemetry-instrumentation-pg (~> 0.29.0)
|
opentelemetry-instrumentation-pg (~> 0.29.0)
|
||||||
opentelemetry-instrumentation-rack (~> 0.24.1)
|
opentelemetry-instrumentation-rack (~> 0.25.0)
|
||||||
opentelemetry-instrumentation-rails (~> 0.31.0)
|
opentelemetry-instrumentation-rails (~> 0.33.0)
|
||||||
opentelemetry-instrumentation-redis (~> 0.25.3)
|
opentelemetry-instrumentation-redis (~> 0.25.3)
|
||||||
opentelemetry-instrumentation-sidekiq (~> 0.25.2)
|
opentelemetry-instrumentation-sidekiq (~> 0.25.2)
|
||||||
opentelemetry-sdk (~> 1.4)
|
opentelemetry-sdk (~> 1.4)
|
||||||
@ -1009,7 +984,7 @@ DEPENDENCIES
|
|||||||
rack-attack (~> 6.6)
|
rack-attack (~> 6.6)
|
||||||
rack-cors (~> 2.0)
|
rack-cors (~> 2.0)
|
||||||
rack-test (~> 2.1)
|
rack-test (~> 2.1)
|
||||||
rails (~> 7.1.1)
|
rails (~> 7.2.0)
|
||||||
rails-controller-testing (~> 1.0)
|
rails-controller-testing (~> 1.0)
|
||||||
rails-i18n (~> 7.0)
|
rails-i18n (~> 7.0)
|
||||||
rdf-normalize (~> 0.5)
|
rdf-normalize (~> 0.5)
|
||||||
@ -1057,7 +1032,7 @@ DEPENDENCIES
|
|||||||
xorcist (~> 1.1)
|
xorcist (~> 1.1)
|
||||||
|
|
||||||
RUBY VERSION
|
RUBY VERSION
|
||||||
ruby 3.3.4p94
|
ruby 3.3.6p108
|
||||||
|
|
||||||
BUNDLED WITH
|
BUNDLED WITH
|
||||||
2.5.18
|
2.5.23
|
||||||
|
@ -69,7 +69,7 @@ Mastodon acts as an OAuth2 provider, so 3rd party apps can use the REST and Stre
|
|||||||
|
|
||||||
- **PostgreSQL** 12+
|
- **PostgreSQL** 12+
|
||||||
- **Redis** 4+
|
- **Redis** 4+
|
||||||
- **Ruby** 3.1+
|
- **Ruby** 3.2+
|
||||||
- **Node.js** 18+
|
- **Node.js** 18+
|
||||||
|
|
||||||
The repository includes deployment configurations for **Docker and docker-compose** as well as specific platforms like **Heroku**, and **Scalingo**. For Helm charts, reference the [mastodon/chart repository](https://github.com/mastodon/chart). The [**standalone** installation guide](https://docs.joinmastodon.org/admin/install/) is available in the documentation.
|
The repository includes deployment configurations for **Docker and docker-compose** as well as specific platforms like **Heroku**, and **Scalingo**. For Helm charts, reference the [mastodon/chart repository](https://github.com/mastodon/chart). The [**standalone** installation guide](https://docs.joinmastodon.org/admin/install/) is available in the documentation.
|
||||||
|
2
Rakefile
@ -3,6 +3,6 @@
|
|||||||
# Add your own tasks in files placed in lib/tasks ending in .rake,
|
# Add your own tasks in files placed in lib/tasks ending in .rake,
|
||||||
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
|
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
|
||||||
|
|
||||||
require File.expand_path('config/application', __dir__)
|
require_relative 'config/application'
|
||||||
|
|
||||||
Rails.application.load_tasks
|
Rails.application.load_tasks
|
||||||
|
11
SECURITY.md
@ -13,8 +13,9 @@ A "vulnerability in Mastodon" is a vulnerability in the code distributed through
|
|||||||
|
|
||||||
## Supported Versions
|
## Supported Versions
|
||||||
|
|
||||||
| Version | Supported |
|
| Version | Supported |
|
||||||
| ------- | --------- |
|
| ------- | ---------------- |
|
||||||
| 4.2.x | Yes |
|
| 4.3.x | Yes |
|
||||||
| 4.1.x | Yes |
|
| 4.2.x | Yes |
|
||||||
| < 4.1 | No |
|
| 4.1.x | Until 2025-04-08 |
|
||||||
|
| < 4.1 | No |
|
||||||
|
@ -41,11 +41,11 @@ class ActivityPub::OutboxesController < ActivityPub::BaseController
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def outbox_url(**kwargs)
|
def outbox_url(**)
|
||||||
if params[:account_username].present?
|
if params[:account_username].present?
|
||||||
account_outbox_url(@account, **kwargs)
|
account_outbox_url(@account, **)
|
||||||
else
|
else
|
||||||
instance_actor_outbox_url(**kwargs)
|
instance_actor_outbox_url(**)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@ class Admin::AnnouncementsController < Admin::BaseController
|
|||||||
|
|
||||||
def index
|
def index
|
||||||
authorize :announcement, :index?
|
authorize :announcement, :index?
|
||||||
|
@published_announcements_count = Announcement.published.async_count
|
||||||
end
|
end
|
||||||
|
|
||||||
def new
|
def new
|
||||||
|
@ -6,6 +6,7 @@ class Admin::Disputes::AppealsController < Admin::BaseController
|
|||||||
def index
|
def index
|
||||||
authorize :appeal, :index?
|
authorize :appeal, :index?
|
||||||
|
|
||||||
|
@pending_appeals_count = Appeal.pending.async_count
|
||||||
@appeals = filtered_appeals.page(params[:page])
|
@appeals = filtered_appeals.page(params[:page])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ module Admin
|
|||||||
def index
|
def index
|
||||||
authorize :email_domain_block, :index?
|
authorize :email_domain_block, :index?
|
||||||
|
|
||||||
@email_domain_blocks = EmailDomainBlock.where(parent_id: nil).includes(:children).order(id: :desc).page(params[:page])
|
@email_domain_blocks = EmailDomainBlock.parents.includes(:children).order(id: :desc).page(params[:page])
|
||||||
@form = Form::EmailDomainBlockBatch.new
|
@form = Form::EmailDomainBlockBatch.new
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -58,10 +58,7 @@ module Admin
|
|||||||
private
|
private
|
||||||
|
|
||||||
def set_resolved_records
|
def set_resolved_records
|
||||||
Resolv::DNS.open do |dns|
|
@resolved_records = DomainResource.new(@email_domain_block.domain).mx
|
||||||
dns.timeouts = 5
|
|
||||||
@resolved_records = dns.getresources(@email_domain_block.domain, Resolv::DNS::Resource::IN::MX).to_a
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def resource_params
|
def resource_params
|
||||||
|
@ -5,6 +5,8 @@ module Admin
|
|||||||
before_action :set_instances, only: :index
|
before_action :set_instances, only: :index
|
||||||
before_action :set_instance, except: :index
|
before_action :set_instance, except: :index
|
||||||
|
|
||||||
|
LOGS_LIMIT = 5
|
||||||
|
|
||||||
def index
|
def index
|
||||||
authorize :instance, :index?
|
authorize :instance, :index?
|
||||||
preload_delivery_failures!
|
preload_delivery_failures!
|
||||||
@ -13,7 +15,7 @@ module Admin
|
|||||||
def show
|
def show
|
||||||
authorize :instance, :show?
|
authorize :instance, :show?
|
||||||
@time_period = (6.days.ago.to_date...Time.now.utc.to_date)
|
@time_period = (6.days.ago.to_date...Time.now.utc.to_date)
|
||||||
@action_logs = Admin::ActionLogFilter.new(target_domain: @instance.domain).results.limit(5)
|
@action_logs = Admin::ActionLogFilter.new(target_domain: @instance.domain).results.limit(LOGS_LIMIT)
|
||||||
end
|
end
|
||||||
|
|
||||||
def destroy
|
def destroy
|
||||||
|
@ -32,7 +32,7 @@ module Admin
|
|||||||
|
|
||||||
def deactivate_all
|
def deactivate_all
|
||||||
authorize :invite, :deactivate_all?
|
authorize :invite, :deactivate_all?
|
||||||
Invite.available.in_batches.update_all(expires_at: Time.now.utc)
|
Invite.available.in_batches.touch_all(:expires_at)
|
||||||
redirect_to admin_invites_path
|
redirect_to admin_invites_path
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -21,6 +21,7 @@ module Admin
|
|||||||
@relay = Relay.new(resource_params)
|
@relay = Relay.new(resource_params)
|
||||||
|
|
||||||
if @relay.save
|
if @relay.save
|
||||||
|
log_action :create, @relay
|
||||||
@relay.enable!
|
@relay.enable!
|
||||||
redirect_to admin_relays_path
|
redirect_to admin_relays_path
|
||||||
else
|
else
|
||||||
@ -31,18 +32,21 @@ module Admin
|
|||||||
def destroy
|
def destroy
|
||||||
authorize :relay, :update?
|
authorize :relay, :update?
|
||||||
@relay.destroy
|
@relay.destroy
|
||||||
|
log_action :destroy, @relay
|
||||||
redirect_to admin_relays_path
|
redirect_to admin_relays_path
|
||||||
end
|
end
|
||||||
|
|
||||||
def enable
|
def enable
|
||||||
authorize :relay, :update?
|
authorize :relay, :update?
|
||||||
@relay.enable!
|
@relay.enable!
|
||||||
|
log_action :enable, @relay
|
||||||
redirect_to admin_relays_path
|
redirect_to admin_relays_path
|
||||||
end
|
end
|
||||||
|
|
||||||
def disable
|
def disable
|
||||||
authorize :relay, :update?
|
authorize :relay, :update?
|
||||||
@relay.disable!
|
@relay.disable!
|
||||||
|
log_action :disable, @relay
|
||||||
redirect_to admin_relays_path
|
redirect_to admin_relays_path
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -16,6 +16,8 @@ module Admin
|
|||||||
|
|
||||||
def show
|
def show
|
||||||
authorize [:admin, @status], :show?
|
authorize [:admin, @status], :show?
|
||||||
|
|
||||||
|
@status_batch_action = Admin::StatusBatchAction.new
|
||||||
end
|
end
|
||||||
|
|
||||||
def batch
|
def batch
|
||||||
|
@ -4,6 +4,7 @@ class Admin::Trends::Links::PreviewCardProvidersController < Admin::BaseControll
|
|||||||
def index
|
def index
|
||||||
authorize :preview_card_provider, :review?
|
authorize :preview_card_provider, :review?
|
||||||
|
|
||||||
|
@pending_preview_card_providers_count = PreviewCardProvider.unreviewed.async_count
|
||||||
@preview_card_providers = filtered_preview_card_providers.page(params[:page])
|
@preview_card_providers = filtered_preview_card_providers.page(params[:page])
|
||||||
@form = Trends::PreviewCardProviderBatch.new
|
@form = Trends::PreviewCardProviderBatch.new
|
||||||
end
|
end
|
||||||
|
@ -4,7 +4,7 @@ class Admin::Trends::LinksController < Admin::BaseController
|
|||||||
def index
|
def index
|
||||||
authorize :preview_card, :review?
|
authorize :preview_card, :review?
|
||||||
|
|
||||||
@locales = PreviewCardTrend.pluck('distinct language')
|
@locales = PreviewCardTrend.locales
|
||||||
@preview_cards = filtered_preview_cards.page(params[:page])
|
@preview_cards = filtered_preview_cards.page(params[:page])
|
||||||
@form = Trends::PreviewCardBatch.new
|
@form = Trends::PreviewCardBatch.new
|
||||||
end
|
end
|
||||||
|
@ -4,7 +4,7 @@ class Admin::Trends::StatusesController < Admin::BaseController
|
|||||||
def index
|
def index
|
||||||
authorize [:admin, :status], :review?
|
authorize [:admin, :status], :review?
|
||||||
|
|
||||||
@locales = StatusTrend.pluck('distinct language')
|
@locales = StatusTrend.locales
|
||||||
@statuses = filtered_statuses.page(params[:page])
|
@statuses = filtered_statuses.page(params[:page])
|
||||||
@form = Trends::StatusBatch.new
|
@form = Trends::StatusBatch.new
|
||||||
end
|
end
|
||||||
|
@ -4,6 +4,7 @@ class Admin::Trends::TagsController < Admin::BaseController
|
|||||||
def index
|
def index
|
||||||
authorize :tag, :review?
|
authorize :tag, :review?
|
||||||
|
|
||||||
|
@pending_tags_count = Tag.pending_review.async_count
|
||||||
@tags = filtered_tags.page(params[:page])
|
@tags = filtered_tags.page(params[:page])
|
||||||
@form = Trends::TagBatch.new
|
@form = Trends::TagBatch.new
|
||||||
end
|
end
|
||||||
|
@ -12,7 +12,7 @@ class Api::V1::Accounts::FamiliarFollowersController < Api::BaseController
|
|||||||
private
|
private
|
||||||
|
|
||||||
def set_accounts
|
def set_accounts
|
||||||
@accounts = Account.without_suspended.where(id: account_ids).select('id, hide_collections')
|
@accounts = Account.without_suspended.where(id: account_ids).select(:id, :hide_collections)
|
||||||
end
|
end
|
||||||
|
|
||||||
def familiar_followers
|
def familiar_followers
|
||||||
|
@ -16,6 +16,7 @@ class Api::V1::AccountsController < Api::BaseController
|
|||||||
before_action :check_account_confirmation, except: [:index, :create]
|
before_action :check_account_confirmation, except: [:index, :create]
|
||||||
before_action :check_enabled_registrations, only: [:create]
|
before_action :check_enabled_registrations, only: [:create]
|
||||||
before_action :check_accounts_limit, only: [:index]
|
before_action :check_accounts_limit, only: [:index]
|
||||||
|
before_action :check_following_self, only: [:follow]
|
||||||
|
|
||||||
skip_before_action :require_authenticated_user!, only: :create
|
skip_before_action :require_authenticated_user!, only: :create
|
||||||
|
|
||||||
@ -101,8 +102,12 @@ class Api::V1::AccountsController < Api::BaseController
|
|||||||
raise(Mastodon::ValidationError) if account_ids.size > DEFAULT_ACCOUNTS_LIMIT
|
raise(Mastodon::ValidationError) if account_ids.size > DEFAULT_ACCOUNTS_LIMIT
|
||||||
end
|
end
|
||||||
|
|
||||||
def relationships(**options)
|
def check_following_self
|
||||||
AccountRelationshipsPresenter.new([@account], current_user.account_id, **options)
|
render json: { error: I18n.t('accounts.self_follow_error') }, status: 403 if current_user.account.id == @account.id
|
||||||
|
end
|
||||||
|
|
||||||
|
def relationships(**)
|
||||||
|
AccountRelationshipsPresenter.new([@account], current_user.account_id, **)
|
||||||
end
|
end
|
||||||
|
|
||||||
def account_ids
|
def account_ids
|
||||||
|
@ -17,6 +17,17 @@ class Api::V1::AnnualReportsController < Api::BaseController
|
|||||||
relationships: @relationships
|
relationships: @relationships
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def show
|
||||||
|
with_read_replica do
|
||||||
|
@presenter = AnnualReportsPresenter.new([@annual_report])
|
||||||
|
@relationships = StatusRelationshipsPresenter.new(@presenter.statuses, current_account.id)
|
||||||
|
end
|
||||||
|
|
||||||
|
render json: @presenter,
|
||||||
|
serializer: REST::AnnualReportsSerializer,
|
||||||
|
relationships: @relationships
|
||||||
|
end
|
||||||
|
|
||||||
def read
|
def read
|
||||||
@annual_report.view!
|
@annual_report.view!
|
||||||
render_empty
|
render_empty
|
||||||
|
@ -5,6 +5,8 @@ class Api::V1::FeaturedTags::SuggestionsController < Api::BaseController
|
|||||||
before_action :require_user!
|
before_action :require_user!
|
||||||
before_action :set_recently_used_tags, only: :index
|
before_action :set_recently_used_tags, only: :index
|
||||||
|
|
||||||
|
RECENT_TAGS_LIMIT = 10
|
||||||
|
|
||||||
def index
|
def index
|
||||||
render json: @recently_used_tags, each_serializer: REST::TagSerializer, relationships: TagRelationshipsPresenter.new(@recently_used_tags, current_user&.account_id)
|
render json: @recently_used_tags, each_serializer: REST::TagSerializer, relationships: TagRelationshipsPresenter.new(@recently_used_tags, current_user&.account_id)
|
||||||
end
|
end
|
||||||
@ -12,6 +14,6 @@ class Api::V1::FeaturedTags::SuggestionsController < Api::BaseController
|
|||||||
private
|
private
|
||||||
|
|
||||||
def set_recently_used_tags
|
def set_recently_used_tags
|
||||||
@recently_used_tags = Tag.suggestions_for_account(current_account).limit(10)
|
@recently_used_tags = Tag.suggestions_for_account(current_account).limit(RECENT_TAGS_LIMIT)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -28,8 +28,8 @@ class Api::V1::FollowRequestsController < Api::BaseController
|
|||||||
@account ||= Account.find(params[:id])
|
@account ||= Account.find(params[:id])
|
||||||
end
|
end
|
||||||
|
|
||||||
def relationships(**options)
|
def relationships(**)
|
||||||
AccountRelationshipsPresenter.new([account], current_user.account_id, **options)
|
AccountRelationshipsPresenter.new([account], current_user.account_id, **)
|
||||||
end
|
end
|
||||||
|
|
||||||
def load_accounts
|
def load_accounts
|
||||||
|
@ -15,17 +15,12 @@ class Api::V1::Lists::AccountsController < Api::BaseController
|
|||||||
end
|
end
|
||||||
|
|
||||||
def create
|
def create
|
||||||
ApplicationRecord.transaction do
|
AddAccountsToListService.new.call(@list, Account.find(account_ids))
|
||||||
list_accounts.each do |account|
|
|
||||||
@list.accounts << account
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
render_empty
|
render_empty
|
||||||
end
|
end
|
||||||
|
|
||||||
def destroy
|
def destroy
|
||||||
ListAccount.where(list: @list, account_id: account_ids).destroy_all
|
RemoveAccountsFromListService.new.call(@list, Account.where(id: account_ids))
|
||||||
render_empty
|
render_empty
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -43,10 +38,6 @@ class Api::V1::Lists::AccountsController < Api::BaseController
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def list_accounts
|
|
||||||
Account.find(account_ids)
|
|
||||||
end
|
|
||||||
|
|
||||||
def account_ids
|
def account_ids
|
||||||
Array(resource_params[:account_ids])
|
Array(resource_params[:account_ids])
|
||||||
end
|
end
|
||||||
|
@ -52,7 +52,7 @@ class Api::V1::Notifications::RequestsController < Api::BaseController
|
|||||||
private
|
private
|
||||||
|
|
||||||
def load_requests
|
def load_requests
|
||||||
requests = NotificationRequest.where(account: current_account).includes(:last_status, from_account: [:account_stat, :user]).to_a_paginated_by_id(
|
requests = NotificationRequest.where(account: current_account).without_suspended.includes(:last_status, from_account: [:account_stat, :user]).to_a_paginated_by_id(
|
||||||
limit_param(DEFAULT_ACCOUNTS_LIMIT),
|
limit_param(DEFAULT_ACCOUNTS_LIMIT),
|
||||||
params_slice(:max_id, :since_id, :min_id)
|
params_slice(:max_id, :since_id, :min_id)
|
||||||
)
|
)
|
||||||
|
@ -23,6 +23,6 @@ class Api::V1::Statuses::TranslationsController < Api::V1::Statuses::BaseControl
|
|||||||
private
|
private
|
||||||
|
|
||||||
def set_translation
|
def set_translation
|
||||||
@translation = TranslateStatusService.new.call(@status, content_locale)
|
@translation = TranslateStatusService.new.call(@status, I18n.locale.to_s)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -27,7 +27,9 @@ class Api::V1::Trends::TagsController < Api::BaseController
|
|||||||
end
|
end
|
||||||
|
|
||||||
def tags_from_trends
|
def tags_from_trends
|
||||||
Trends.tags.query.allowed
|
scope = Trends.tags.query.allowed.in_locale(content_locale)
|
||||||
|
scope = scope.filtered_for(current_account) if user_signed_in?
|
||||||
|
scope
|
||||||
end
|
end
|
||||||
|
|
||||||
def next_path
|
def next_path
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class Api::Web::PushSubscriptionsController < Api::Web::BaseController
|
class Api::Web::PushSubscriptionsController < Api::Web::BaseController
|
||||||
before_action :require_user!
|
before_action :require_user!, except: :destroy
|
||||||
before_action :set_push_subscription, only: :update
|
before_action :set_push_subscription, only: :update
|
||||||
before_action :destroy_previous_subscriptions, only: :create, if: :prior_subscriptions?
|
before_action :destroy_previous_subscriptions, only: :create, if: :prior_subscriptions?
|
||||||
after_action :update_session_with_subscription, only: :create
|
after_action :update_session_with_subscription, only: :create
|
||||||
@ -17,6 +17,13 @@ class Api::Web::PushSubscriptionsController < Api::Web::BaseController
|
|||||||
render json: @push_subscription, serializer: REST::WebPushSubscriptionSerializer
|
render json: @push_subscription, serializer: REST::WebPushSubscriptionSerializer
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def destroy
|
||||||
|
push_subscription = ::Web::PushSubscription.find_by_token_for(:unsubscribe, params[:id])
|
||||||
|
push_subscription&.destroy
|
||||||
|
|
||||||
|
head 200
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def active_session
|
def active_session
|
||||||
|
@ -22,7 +22,6 @@ class ApplicationController < ActionController::Base
|
|||||||
helper_method :use_seamless_external_login?
|
helper_method :use_seamless_external_login?
|
||||||
helper_method :sso_account_settings
|
helper_method :sso_account_settings
|
||||||
helper_method :limited_federation_mode?
|
helper_method :limited_federation_mode?
|
||||||
helper_method :body_class_string
|
|
||||||
helper_method :skip_csrf_meta_tags?
|
helper_method :skip_csrf_meta_tags?
|
||||||
|
|
||||||
rescue_from ActionController::ParameterMissing, Paperclip::AdapterRegistry::NoHandlerError, with: :bad_request
|
rescue_from ActionController::ParameterMissing, Paperclip::AdapterRegistry::NoHandlerError, with: :bad_request
|
||||||
@ -32,7 +31,7 @@ class ApplicationController < ActionController::Base
|
|||||||
rescue_from ActionController::InvalidAuthenticityToken, with: :unprocessable_entity
|
rescue_from ActionController::InvalidAuthenticityToken, with: :unprocessable_entity
|
||||||
rescue_from Mastodon::RateLimitExceededError, with: :too_many_requests
|
rescue_from Mastodon::RateLimitExceededError, with: :too_many_requests
|
||||||
|
|
||||||
rescue_from HTTP::Error, OpenSSL::SSL::SSLError, with: :internal_server_error
|
rescue_from(*Mastodon::HTTP_CONNECTION_ERRORS, with: :internal_server_error)
|
||||||
rescue_from Mastodon::RaceConditionError, Stoplight::Error::RedLight, ActiveRecord::SerializationFailure, with: :service_unavailable
|
rescue_from Mastodon::RaceConditionError, Stoplight::Error::RedLight, ActiveRecord::SerializationFailure, with: :service_unavailable
|
||||||
|
|
||||||
rescue_from Seahorse::Client::NetworkingError do |e|
|
rescue_from Seahorse::Client::NetworkingError do |e|
|
||||||
@ -158,10 +157,6 @@ class ApplicationController < ActionController::Base
|
|||||||
current_user.setting_theme
|
current_user.setting_theme
|
||||||
end
|
end
|
||||||
|
|
||||||
def body_class_string
|
|
||||||
@body_classes || ''
|
|
||||||
end
|
|
||||||
|
|
||||||
def respond_with_error(code)
|
def respond_with_error(code)
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.any { render "errors/#{code}", layout: 'error', status: code, formats: [:html] }
|
format.any { render "errors/#{code}", layout: 'error', status: code, formats: [:html] }
|
||||||
|
@ -20,7 +20,7 @@ module Api::ErrorHandling
|
|||||||
render json: { error: 'Record not found' }, status: 404
|
render json: { error: 'Record not found' }, status: 404
|
||||||
end
|
end
|
||||||
|
|
||||||
rescue_from HTTP::Error, Mastodon::UnexpectedResponseError do
|
rescue_from(*Mastodon::HTTP_CONNECTION_ERRORS, Mastodon::UnexpectedResponseError) do
|
||||||
render json: { error: 'Remote data could not be fetched' }, status: 503
|
render json: { error: 'Remote data could not be fetched' }, status: 503
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ module Auth::CaptchaConcern
|
|||||||
end
|
end
|
||||||
|
|
||||||
def captcha_available?
|
def captcha_available?
|
||||||
ENV['HCAPTCHA_SECRET_KEY'].present? && ENV['HCAPTCHA_SITE_KEY'].present?
|
Rails.configuration.x.captcha.secret_key.present? && Rails.configuration.x.captcha.site_key.present?
|
||||||
end
|
end
|
||||||
|
|
||||||
def captcha_enabled?
|
def captcha_enabled?
|
||||||
|
@ -28,7 +28,7 @@ module CacheConcern
|
|||||||
def render_with_cache(**options)
|
def render_with_cache(**options)
|
||||||
raise ArgumentError, 'Only JSON render calls are supported' unless options.key?(:json) || block_given?
|
raise ArgumentError, 'Only JSON render calls are supported' unless options.key?(:json) || block_given?
|
||||||
|
|
||||||
key = options.delete(:key) || [[params[:controller], params[:action]].join('/'), options[:json].respond_to?(:cache_key) ? options[:json].cache_key : nil, options[:fields].nil? ? nil : options[:fields].join(',')].compact.join(':')
|
key = options.delete(:key) || [[params[:controller], params[:action]].join('/'), options[:json].respond_to?(:cache_key) ? options[:json].cache_key : nil, options[:fields]&.join(',')].compact.join(':')
|
||||||
expires_in = options.delete(:expires_in) || 3.minutes
|
expires_in = options.delete(:expires_in) || 3.minutes
|
||||||
body = Rails.cache.read(key, raw: true)
|
body = Rails.cache.read(key, raw: true)
|
||||||
|
|
||||||
|
@ -80,7 +80,7 @@ module SignatureVerification
|
|||||||
fail_with! "Verification failed for #{actor.to_log_human_identifier} #{actor.uri} using rsa-sha256 (RSASSA-PKCS1-v1_5 with SHA-256)", signed_string: compare_signed_string, signature: signature_params['signature']
|
fail_with! "Verification failed for #{actor.to_log_human_identifier} #{actor.uri} using rsa-sha256 (RSASSA-PKCS1-v1_5 with SHA-256)", signed_string: compare_signed_string, signature: signature_params['signature']
|
||||||
rescue SignatureVerificationError => e
|
rescue SignatureVerificationError => e
|
||||||
fail_with! e.message
|
fail_with! e.message
|
||||||
rescue HTTP::Error, OpenSSL::SSL::SSLError => e
|
rescue *Mastodon::HTTP_CONNECTION_ERRORS => e
|
||||||
fail_with! "Failed to fetch remote data: #{e.message}"
|
fail_with! "Failed to fetch remote data: #{e.message}"
|
||||||
rescue Mastodon::UnexpectedResponseError
|
rescue Mastodon::UnexpectedResponseError
|
||||||
fail_with! 'Failed to fetch remote data (got unexpected reply from server)'
|
fail_with! 'Failed to fetch remote data (got unexpected reply from server)'
|
||||||
|
@ -7,13 +7,12 @@ module WebAppControllerConcern
|
|||||||
vary_by 'Accept, Accept-Language, Cookie'
|
vary_by 'Accept, Accept-Language, Cookie'
|
||||||
|
|
||||||
before_action :redirect_unauthenticated_to_permalinks!
|
before_action :redirect_unauthenticated_to_permalinks!
|
||||||
before_action :set_app_body_class
|
|
||||||
|
|
||||||
content_security_policy do |p|
|
content_security_policy do |p|
|
||||||
policy = ContentSecurityPolicy.new
|
policy = ContentSecurityPolicy.new
|
||||||
|
|
||||||
if policy.sso_host.present?
|
if policy.sso_host.present?
|
||||||
p.form_action policy.sso_host
|
p.form_action policy.sso_host, -> { "https://#{request.host}/auth/auth/" }
|
||||||
else
|
else
|
||||||
p.form_action :none
|
p.form_action :none
|
||||||
end
|
end
|
||||||
@ -24,10 +23,6 @@ module WebAppControllerConcern
|
|||||||
!(ENV['ONE_CLICK_SSO_LOGIN'] == 'true' && ENV['OMNIAUTH_ONLY'] == 'true' && Devise.omniauth_providers.length == 1) && current_user.nil?
|
!(ENV['ONE_CLICK_SSO_LOGIN'] == 'true' && ENV['OMNIAUTH_ONLY'] == 'true' && Devise.omniauth_providers.length == 1) && current_user.nil?
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_app_body_class
|
|
||||||
@body_classes = 'app-body'
|
|
||||||
end
|
|
||||||
|
|
||||||
def redirect_unauthenticated_to_permalinks!
|
def redirect_unauthenticated_to_permalinks!
|
||||||
return if user_signed_in? && current_account.moved_to_account_id.nil?
|
return if user_signed_in? && current_account.moved_to_account_id.nil?
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ class MediaProxyController < ApplicationController
|
|||||||
rescue_from ActiveRecord::RecordInvalid, with: :not_found
|
rescue_from ActiveRecord::RecordInvalid, with: :not_found
|
||||||
rescue_from Mastodon::UnexpectedResponseError, with: :not_found
|
rescue_from Mastodon::UnexpectedResponseError, with: :not_found
|
||||||
rescue_from Mastodon::NotPermittedError, with: :not_found
|
rescue_from Mastodon::NotPermittedError, with: :not_found
|
||||||
rescue_from HTTP::TimeoutError, HTTP::ConnectionError, OpenSSL::SSL::SSLError, with: :internal_server_error
|
rescue_from(*Mastodon::HTTP_CONNECTION_ERRORS, with: :internal_server_error)
|
||||||
|
|
||||||
def show
|
def show
|
||||||
with_redis_lock("media_download:#{params[:id]}") do
|
with_redis_lock("media_download:#{params[:id]}") do
|
||||||
|
@ -35,12 +35,6 @@ class Oauth::AuthorizedApplicationsController < Doorkeeper::AuthorizedApplicatio
|
|||||||
end
|
end
|
||||||
|
|
||||||
def set_last_used_at_by_app
|
def set_last_used_at_by_app
|
||||||
@last_used_at_by_app = Doorkeeper::AccessToken
|
@last_used_at_by_app = current_resource_owner.applications_last_used
|
||||||
.select('DISTINCT ON (application_id) application_id, last_used_at')
|
|
||||||
.where(resource_owner_id: current_resource_owner.id)
|
|
||||||
.where.not(last_used_at: nil)
|
|
||||||
.order(application_id: :desc, last_used_at: :desc)
|
|
||||||
.pluck(:application_id, :last_used_at)
|
|
||||||
.to_h
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
11
app/controllers/oauth/userinfo_controller.rb
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class Oauth::UserinfoController < Api::BaseController
|
||||||
|
before_action -> { doorkeeper_authorize! :profile }, only: [:show]
|
||||||
|
before_action :require_user!
|
||||||
|
|
||||||
|
def show
|
||||||
|
@account = current_account
|
||||||
|
render json: @account, serializer: OauthUserinfoSerializer
|
||||||
|
end
|
||||||
|
end
|
@ -9,7 +9,7 @@ class Settings::ExportsController < Settings::BaseController
|
|||||||
skip_before_action :require_functional!
|
skip_before_action :require_functional!
|
||||||
|
|
||||||
def show
|
def show
|
||||||
@export = Export.new(current_account)
|
@export_summary = ExportSummary.new(preloaded_account)
|
||||||
@backups = current_user.backups
|
@backups = current_user.backups
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -25,4 +25,15 @@ class Settings::ExportsController < Settings::BaseController
|
|||||||
|
|
||||||
redirect_to settings_export_path
|
redirect_to settings_export_path
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def preloaded_account
|
||||||
|
current_account.tap do |account|
|
||||||
|
ActiveRecord::Associations::Preloader.new(
|
||||||
|
records: [account],
|
||||||
|
associations: :account_stat
|
||||||
|
).call
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
@ -5,6 +5,8 @@ class Settings::FeaturedTagsController < Settings::BaseController
|
|||||||
before_action :set_featured_tag, except: [:index, :create]
|
before_action :set_featured_tag, except: [:index, :create]
|
||||||
before_action :set_recently_used_tags, only: :index
|
before_action :set_recently_used_tags, only: :index
|
||||||
|
|
||||||
|
RECENT_TAGS_LIMIT = 10
|
||||||
|
|
||||||
def index
|
def index
|
||||||
@featured_tag = FeaturedTag.new
|
@featured_tag = FeaturedTag.new
|
||||||
end
|
end
|
||||||
@ -38,7 +40,7 @@ class Settings::FeaturedTagsController < Settings::BaseController
|
|||||||
end
|
end
|
||||||
|
|
||||||
def set_recently_used_tags
|
def set_recently_used_tags
|
||||||
@recently_used_tags = Tag.suggestions_for_account(current_account).limit(10)
|
@recently_used_tags = Tag.suggestions_for_account(current_account).limit(RECENT_TAGS_LIMIT)
|
||||||
end
|
end
|
||||||
|
|
||||||
def featured_tag_params
|
def featured_tag_params
|
||||||
|
@ -24,6 +24,8 @@ class Settings::ImportsController < Settings::BaseController
|
|||||||
lists: false,
|
lists: false,
|
||||||
}.freeze
|
}.freeze
|
||||||
|
|
||||||
|
RECENT_IMPORTS_LIMIT = 10
|
||||||
|
|
||||||
def index
|
def index
|
||||||
@import = Form::Import.new(current_account: current_account)
|
@import = Form::Import.new(current_account: current_account)
|
||||||
end
|
end
|
||||||
@ -96,6 +98,6 @@ class Settings::ImportsController < Settings::BaseController
|
|||||||
end
|
end
|
||||||
|
|
||||||
def set_recent_imports
|
def set_recent_imports
|
||||||
@recent_imports = current_account.bulk_imports.reorder(id: :desc).limit(10)
|
@recent_imports = current_account.bulk_imports.reorder(id: :desc).limit(RECENT_IMPORTS_LIMIT)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -15,7 +15,7 @@ module Settings
|
|||||||
end
|
end
|
||||||
|
|
||||||
def create
|
def create
|
||||||
session[:new_otp_secret] = User.generate_otp_secret(32)
|
session[:new_otp_secret] = User.generate_otp_secret
|
||||||
|
|
||||||
redirect_to new_settings_two_factor_authentication_confirmation_path
|
redirect_to new_settings_two_factor_authentication_confirmation_path
|
||||||
end
|
end
|
||||||
|
@ -7,7 +7,23 @@ module WellKnown
|
|||||||
def show
|
def show
|
||||||
@webfinger_template = "#{webfinger_url}?resource={uri}"
|
@webfinger_template = "#{webfinger_url}?resource={uri}"
|
||||||
expires_in 3.days, public: true
|
expires_in 3.days, public: true
|
||||||
render content_type: 'application/xrd+xml', formats: [:xml]
|
|
||||||
|
respond_to do |format|
|
||||||
|
format.any do
|
||||||
|
render content_type: 'application/xrd+xml', formats: [:xml]
|
||||||
|
end
|
||||||
|
|
||||||
|
format.json do
|
||||||
|
render json: {
|
||||||
|
links: [
|
||||||
|
{
|
||||||
|
rel: 'lrdd',
|
||||||
|
template: @webfinger_template,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -12,12 +12,12 @@ module Admin::AccountModerationNotesHelper
|
|||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def admin_account_inline_link_to(account)
|
def admin_account_inline_link_to(account, path: nil)
|
||||||
return if account.nil?
|
return if account.nil?
|
||||||
|
|
||||||
link_to(
|
link_to(
|
||||||
account_inline_text(account),
|
account_inline_text(account),
|
||||||
admin_account_path(account.id),
|
path || admin_account_path(account.id),
|
||||||
class: class_names('inline-name-tag', suspended: suspended_account?(account)),
|
class: class_names('inline-name-tag', suspended: suspended_account?(account)),
|
||||||
title: account.acct
|
title: account.acct
|
||||||
)
|
)
|
||||||
|
@ -33,6 +33,15 @@ module Admin::ActionLogsHelper
|
|||||||
else
|
else
|
||||||
I18n.t('admin.action_logs.deleted_account')
|
I18n.t('admin.action_logs.deleted_account')
|
||||||
end
|
end
|
||||||
|
when 'Relay'
|
||||||
|
link_to log.human_identifier, admin_relays_path
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def sorted_action_log_types
|
||||||
|
Admin::ActionLogFilter::ACTION_TYPE_MAP
|
||||||
|
.keys
|
||||||
|
.map { |key| [I18n.t("admin.action_logs.action_types.#{key}"), key] }
|
||||||
|
.sort_by(&:first)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
@ -18,6 +18,11 @@ module Admin::DashboardHelper
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def date_range(range)
|
||||||
|
[l(range.first), l(range.last)]
|
||||||
|
.join(' - ')
|
||||||
|
end
|
||||||
|
|
||||||
def relevant_account_timestamp(account)
|
def relevant_account_timestamp(account)
|
||||||
timestamp, exact = if account.user_current_sign_in_at && account.user_current_sign_in_at < 24.hours.ago
|
timestamp, exact = if account.user_current_sign_in_at && account.user_current_sign_in_at < 24.hours.ago
|
||||||
[account.user_current_sign_in_at, true]
|
[account.user_current_sign_in_at, true]
|
||||||
@ -25,6 +30,8 @@ module Admin::DashboardHelper
|
|||||||
[account.user_current_sign_in_at, false]
|
[account.user_current_sign_in_at, false]
|
||||||
elsif account.user_pending?
|
elsif account.user_pending?
|
||||||
[account.user_created_at, true]
|
[account.user_created_at, true]
|
||||||
|
elsif account.suspended_at.present? && account.local? && account.user.nil?
|
||||||
|
[account.suspended_at, true]
|
||||||
elsif account.last_status_at.present?
|
elsif account.last_status_at.present?
|
||||||
[account.last_status_at, true]
|
[account.last_status_at, true]
|
||||||
else
|
else
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
module Admin::SettingsHelper
|
module Admin::SettingsHelper
|
||||||
def captcha_available?
|
def captcha_available?
|
||||||
ENV['HCAPTCHA_SECRET_KEY'].present? && ENV['HCAPTCHA_SITE_KEY'].present?
|
Rails.configuration.x.captcha.secret_key.present? && Rails.configuration.x.captcha.site_key.present?
|
||||||
end
|
end
|
||||||
|
|
||||||
def login_activity_title(activity)
|
def login_activity_title(activity)
|
||||||
|
@ -1,12 +1,6 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
module ApplicationHelper
|
module ApplicationHelper
|
||||||
DANGEROUS_SCOPES = %w(
|
|
||||||
read
|
|
||||||
write
|
|
||||||
follow
|
|
||||||
).freeze
|
|
||||||
|
|
||||||
RTL_LOCALES = %i(
|
RTL_LOCALES = %i(
|
||||||
ar
|
ar
|
||||||
ckb
|
ckb
|
||||||
@ -85,7 +79,7 @@ module ApplicationHelper
|
|||||||
|
|
||||||
def html_title
|
def html_title
|
||||||
safe_join(
|
safe_join(
|
||||||
[content_for(:page_title).to_s.chomp, title]
|
[content_for(:page_title), title]
|
||||||
.compact_blank,
|
.compact_blank,
|
||||||
' - '
|
' - '
|
||||||
)
|
)
|
||||||
@ -95,8 +89,11 @@ module ApplicationHelper
|
|||||||
Rails.env.production? ? site_title : "#{site_title} (Dev)"
|
Rails.env.production? ? site_title : "#{site_title} (Dev)"
|
||||||
end
|
end
|
||||||
|
|
||||||
def class_for_scope(scope)
|
def label_for_scope(scope)
|
||||||
'scope-danger' if DANGEROUS_SCOPES.include?(scope.to_s)
|
safe_join [
|
||||||
|
tag.samp(scope, class: { 'scope-danger' => SessionActivation::DEFAULT_SCOPES.include?(scope.to_s) }),
|
||||||
|
tag.span(t("doorkeeper.scopes.#{scope}"), class: :hint),
|
||||||
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
def can?(action, record)
|
def can?(action, record)
|
||||||
@ -123,18 +120,6 @@ module ApplicationHelper
|
|||||||
inline_svg_tag 'check.svg'
|
inline_svg_tag 'check.svg'
|
||||||
end
|
end
|
||||||
|
|
||||||
def visibility_icon(status)
|
|
||||||
if status.public_visibility?
|
|
||||||
material_symbol('globe', title: I18n.t('statuses.visibilities.public'))
|
|
||||||
elsif status.unlisted_visibility?
|
|
||||||
material_symbol('lock_open', title: I18n.t('statuses.visibilities.unlisted'))
|
|
||||||
elsif status.private_visibility? || status.limited_visibility?
|
|
||||||
material_symbol('lock', title: I18n.t('statuses.visibilities.private'))
|
|
||||||
elsif status.direct_visibility?
|
|
||||||
material_symbol('alternate_email', title: I18n.t('statuses.visibilities.direct'))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def interrelationships_icon(relationships, account_id)
|
def interrelationships_icon(relationships, account_id)
|
||||||
if relationships.following[account_id] && relationships.followed_by[account_id]
|
if relationships.following[account_id] && relationships.followed_by[account_id]
|
||||||
material_symbol('sync_alt', title: I18n.t('relationships.mutual'), class: 'active passive')
|
material_symbol('sync_alt', title: I18n.t('relationships.mutual'), class: 'active passive')
|
||||||
@ -158,10 +143,11 @@ module ApplicationHelper
|
|||||||
end
|
end
|
||||||
|
|
||||||
def body_classes
|
def body_classes
|
||||||
output = body_class_string.split
|
output = []
|
||||||
output << content_for(:body_classes)
|
output << content_for(:body_classes)
|
||||||
output << "theme-#{current_theme.parameterize}"
|
output << "theme-#{current_theme.parameterize}"
|
||||||
output << 'system-font' if current_account&.user&.setting_system_font_ui
|
output << 'system-font' if current_account&.user&.setting_system_font_ui
|
||||||
|
output << 'custom-scrollbars' unless current_account&.user&.setting_system_scrollbars_ui
|
||||||
output << (current_account&.user&.setting_reduce_motion ? 'reduce-motion' : 'no-reduce-motion')
|
output << (current_account&.user&.setting_reduce_motion ? 'reduce-motion' : 'no-reduce-motion')
|
||||||
output << 'rtl' if locale_direction == 'rtl'
|
output << 'rtl' if locale_direction == 'rtl'
|
||||||
output.compact_blank.join(' ')
|
output.compact_blank.join(' ')
|
||||||
@ -243,6 +229,15 @@ module ApplicationHelper
|
|||||||
full_asset_url(instance_presenter.mascot&.file&.url || frontend_asset_path('images/elephant_ui_plane.svg'))
|
full_asset_url(instance_presenter.mascot&.file&.url || frontend_asset_path('images/elephant_ui_plane.svg'))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def copyable_input(options = {})
|
||||||
|
tag.input(type: :text, maxlength: 999, spellcheck: false, readonly: true, **options)
|
||||||
|
end
|
||||||
|
|
||||||
|
def recent_tag_usage(tag)
|
||||||
|
people = tag.history.aggregate(2.days.ago.to_date..Time.zone.today).accounts
|
||||||
|
I18n.t 'user_mailer.welcome.hashtags_recent_count', people: number_with_delimiter(people), count: people
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def storage_host_var
|
def storage_host_var
|
||||||
|
@ -1,6 +1,14 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
module FormattingHelper
|
module FormattingHelper
|
||||||
|
SYNDICATED_EMOJI_STYLES = <<~CSS.squish
|
||||||
|
height: 1.1em;
|
||||||
|
margin: -.2ex .15em .2ex;
|
||||||
|
object-fit: contain;
|
||||||
|
vertical-align: middle;
|
||||||
|
width: 1.1em;
|
||||||
|
CSS
|
||||||
|
|
||||||
def html_aware_format(text, local, options = {})
|
def html_aware_format(text, local, options = {})
|
||||||
HtmlAwareFormatter.new(text, local, options).to_s
|
HtmlAwareFormatter.new(text, local, options).to_s
|
||||||
end
|
end
|
||||||
@ -19,42 +27,33 @@ module FormattingHelper
|
|||||||
module_function :extract_status_plain_text
|
module_function :extract_status_plain_text
|
||||||
|
|
||||||
def status_content_format(status)
|
def status_content_format(status)
|
||||||
html_aware_format(status.text, status.local?, preloaded_accounts: [status.account] + (status.respond_to?(:active_mentions) ? status.active_mentions.map(&:account) : []))
|
MastodonOTELTracer.in_span('HtmlAwareFormatter rendering') do |span|
|
||||||
|
span.add_attributes(
|
||||||
|
'app.formatter.content.type' => 'status',
|
||||||
|
'app.formatter.content.origin' => status.local? ? 'local' : 'remote'
|
||||||
|
)
|
||||||
|
|
||||||
|
html_aware_format(status.text, status.local?, preloaded_accounts: [status.account] + (status.respond_to?(:active_mentions) ? status.active_mentions.map(&:account) : []))
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def rss_status_content_format(status)
|
def rss_status_content_format(status)
|
||||||
html = status_content_format(status)
|
|
||||||
|
|
||||||
before_html = if status.spoiler_text?
|
|
||||||
tag.p do
|
|
||||||
tag.strong do
|
|
||||||
I18n.t('rss.content_warning', locale: available_locale_or_nil(status.language) || I18n.default_locale)
|
|
||||||
end
|
|
||||||
|
|
||||||
status.spoiler_text
|
|
||||||
end + tag.hr
|
|
||||||
end
|
|
||||||
|
|
||||||
after_html = if status.preloadable_poll
|
|
||||||
tag.p do
|
|
||||||
safe_join(
|
|
||||||
status.preloadable_poll.options.map do |o|
|
|
||||||
tag.send(status.preloadable_poll.multiple? ? 'checkbox' : 'radio', o, disabled: true)
|
|
||||||
end,
|
|
||||||
tag.br
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
prerender_custom_emojis(
|
prerender_custom_emojis(
|
||||||
safe_join([before_html, html, after_html]),
|
wrapped_status_content_format(status),
|
||||||
status.emojis,
|
status.emojis,
|
||||||
style: 'width: 1.1em; height: 1.1em; object-fit: contain; vertical-align: middle; margin: -.2ex .15em .2ex'
|
style: SYNDICATED_EMOJI_STYLES
|
||||||
).to_str
|
).to_str
|
||||||
end
|
end
|
||||||
|
|
||||||
def account_bio_format(account)
|
def account_bio_format(account)
|
||||||
html_aware_format(account.note, account.local?)
|
MastodonOTELTracer.in_span('HtmlAwareFormatter rendering') do |span|
|
||||||
|
span.add_attributes(
|
||||||
|
'app.formatter.content.type' => 'account_bio',
|
||||||
|
'app.formatter.content.origin' => account.local? ? 'local' : 'remote'
|
||||||
|
)
|
||||||
|
|
||||||
|
html_aware_format(account.note, account.local?)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def account_field_value_format(field, with_rel_me: true)
|
def account_field_value_format(field, with_rel_me: true)
|
||||||
@ -64,4 +63,47 @@ module FormattingHelper
|
|||||||
html_aware_format(field.value, field.account.local?, with_rel_me: with_rel_me, with_domains: true, multiline: false)
|
html_aware_format(field.value, field.account.local?, with_rel_me: with_rel_me, with_domains: true, multiline: false)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def wrapped_status_content_format(status)
|
||||||
|
safe_join [
|
||||||
|
rss_content_preroll(status),
|
||||||
|
status_content_format(status),
|
||||||
|
rss_content_postroll(status),
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
def rss_content_preroll(status)
|
||||||
|
if status.spoiler_text?
|
||||||
|
safe_join [
|
||||||
|
tag.p { spoiler_with_warning(status) },
|
||||||
|
tag.hr,
|
||||||
|
]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def spoiler_with_warning(status)
|
||||||
|
safe_join [
|
||||||
|
tag.strong { I18n.t('rss.content_warning', locale: available_locale_or_nil(status.language) || I18n.default_locale) },
|
||||||
|
status.spoiler_text,
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
def rss_content_postroll(status)
|
||||||
|
if status.preloadable_poll
|
||||||
|
tag.p do
|
||||||
|
poll_option_tags(status)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def poll_option_tags(status)
|
||||||
|
safe_join(
|
||||||
|
status.preloadable_poll.options.map do |option|
|
||||||
|
tag.send(status.preloadable_poll.multiple? ? 'checkbox' : 'radio', option, disabled: true)
|
||||||
|
end,
|
||||||
|
tag.br
|
||||||
|
)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
@ -162,7 +162,7 @@ module LanguagesHelper
|
|||||||
th: ['Thai', 'ไทย'].freeze,
|
th: ['Thai', 'ไทย'].freeze,
|
||||||
ti: ['Tigrinya', 'ትግርኛ'].freeze,
|
ti: ['Tigrinya', 'ትግርኛ'].freeze,
|
||||||
tk: ['Turkmen', 'Türkmen'].freeze,
|
tk: ['Turkmen', 'Türkmen'].freeze,
|
||||||
tl: ['Tagalog', 'Wikang Tagalog'].freeze,
|
tl: ['Tagalog', 'Tagalog'].freeze,
|
||||||
tn: ['Tswana', 'Setswana'].freeze,
|
tn: ['Tswana', 'Setswana'].freeze,
|
||||||
to: ['Tonga', 'faka Tonga'].freeze,
|
to: ['Tonga', 'faka Tonga'].freeze,
|
||||||
tr: ['Turkish', 'Türkçe'].freeze,
|
tr: ['Turkish', 'Türkçe'].freeze,
|
||||||
@ -193,6 +193,7 @@ module LanguagesHelper
|
|||||||
ckb: ['Sorani (Kurdish)', 'سۆرانی'].freeze,
|
ckb: ['Sorani (Kurdish)', 'سۆرانی'].freeze,
|
||||||
cnr: ['Montenegrin', 'crnogorski'].freeze,
|
cnr: ['Montenegrin', 'crnogorski'].freeze,
|
||||||
csb: ['Kashubian', 'Kaszëbsczi'].freeze,
|
csb: ['Kashubian', 'Kaszëbsczi'].freeze,
|
||||||
|
gsw: ['Swiss German', 'Schwiizertütsch'].freeze,
|
||||||
jbo: ['Lojban', 'la .lojban.'].freeze,
|
jbo: ['Lojban', 'la .lojban.'].freeze,
|
||||||
kab: ['Kabyle', 'Taqbaylit'].freeze,
|
kab: ['Kabyle', 'Taqbaylit'].freeze,
|
||||||
ldn: ['Láadan', 'Láadan'].freeze,
|
ldn: ['Láadan', 'Láadan'].freeze,
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
module MediaComponentHelper
|
module MediaComponentHelper
|
||||||
def render_video_component(status, **options)
|
def render_video_component(status, **)
|
||||||
video = status.ordered_media_attachments.first
|
video = status.ordered_media_attachments.first
|
||||||
|
|
||||||
meta = video.file.meta || {}
|
meta = video.file.meta || {}
|
||||||
@ -18,14 +18,14 @@ module MediaComponentHelper
|
|||||||
media: [
|
media: [
|
||||||
serialize_media_attachment(video),
|
serialize_media_attachment(video),
|
||||||
].as_json,
|
].as_json,
|
||||||
}.merge(**options)
|
}.merge(**)
|
||||||
|
|
||||||
react_component :video, component_params do
|
react_component :video, component_params do
|
||||||
render partial: 'statuses/attachment_list', locals: { attachments: status.ordered_media_attachments }
|
render partial: 'statuses/attachment_list', locals: { attachments: status.ordered_media_attachments }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def render_audio_component(status, **options)
|
def render_audio_component(status, **)
|
||||||
audio = status.ordered_media_attachments.first
|
audio = status.ordered_media_attachments.first
|
||||||
|
|
||||||
meta = audio.file.meta || {}
|
meta = audio.file.meta || {}
|
||||||
@ -38,19 +38,19 @@ module MediaComponentHelper
|
|||||||
foregroundColor: meta.dig('colors', 'foreground'),
|
foregroundColor: meta.dig('colors', 'foreground'),
|
||||||
accentColor: meta.dig('colors', 'accent'),
|
accentColor: meta.dig('colors', 'accent'),
|
||||||
duration: meta.dig('original', 'duration'),
|
duration: meta.dig('original', 'duration'),
|
||||||
}.merge(**options)
|
}.merge(**)
|
||||||
|
|
||||||
react_component :audio, component_params do
|
react_component :audio, component_params do
|
||||||
render partial: 'statuses/attachment_list', locals: { attachments: status.ordered_media_attachments }
|
render partial: 'statuses/attachment_list', locals: { attachments: status.ordered_media_attachments }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def render_media_gallery_component(status, **options)
|
def render_media_gallery_component(status, **)
|
||||||
component_params = {
|
component_params = {
|
||||||
sensitive: sensitive_viewer?(status, current_account),
|
sensitive: sensitive_viewer?(status, current_account),
|
||||||
autoplay: prefers_autoplay?,
|
autoplay: prefers_autoplay?,
|
||||||
media: status.ordered_media_attachments.map { |a| serialize_media_attachment(a).as_json },
|
media: status.ordered_media_attachments.map { |a| serialize_media_attachment(a).as_json },
|
||||||
}.merge(**options)
|
}.merge(**)
|
||||||
|
|
||||||
react_component :media_gallery, component_params do
|
react_component :media_gallery, component_params do
|
||||||
render partial: 'statuses/attachment_list', locals: { attachments: status.ordered_media_attachments }
|
render partial: 'statuses/attachment_list', locals: { attachments: status.ordered_media_attachments }
|
||||||
|
@ -16,6 +16,6 @@ module RegistrationHelper
|
|||||||
end
|
end
|
||||||
|
|
||||||
def ip_blocked?(remote_ip)
|
def ip_blocked?(remote_ip)
|
||||||
IpBlock.where(severity: :sign_up_block).exists?(['ip >>= ?', remote_ip.to_s])
|
IpBlock.severity_sign_up_block.containing(remote_ip.to_s).exists?
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -14,8 +14,8 @@ module RoutingHelper
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def full_asset_url(source, **options)
|
def full_asset_url(source, **)
|
||||||
source = ActionController::Base.helpers.asset_url(source, **options) unless use_storage?
|
source = ActionController::Base.helpers.asset_url(source, **) unless use_storage?
|
||||||
|
|
||||||
URI.join(asset_host, source).to_s
|
URI.join(asset_host, source).to_s
|
||||||
end
|
end
|
||||||
@ -24,12 +24,12 @@ module RoutingHelper
|
|||||||
Rails.configuration.action_controller.asset_host || root_url
|
Rails.configuration.action_controller.asset_host || root_url
|
||||||
end
|
end
|
||||||
|
|
||||||
def frontend_asset_path(source, **options)
|
def frontend_asset_path(source, **)
|
||||||
asset_pack_path("media/#{source}", **options)
|
asset_pack_path("media/#{source}", **)
|
||||||
end
|
end
|
||||||
|
|
||||||
def frontend_asset_url(source, **options)
|
def frontend_asset_url(source, **)
|
||||||
full_asset_url(frontend_asset_path(source, **options))
|
full_asset_url(frontend_asset_path(source, **))
|
||||||
end
|
end
|
||||||
|
|
||||||
def use_storage?
|
def use_storage?
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
module SelfDestructHelper
|
module SelfDestructHelper
|
||||||
|
VERIFY_PURPOSE = 'self-destruct'
|
||||||
|
|
||||||
def self.self_destruct?
|
def self.self_destruct?
|
||||||
value = ENV.fetch('SELF_DESTRUCT', nil)
|
value = Rails.configuration.x.mastodon.self_destruct_value
|
||||||
value.present? && Rails.application.message_verifier('self-destruct').verify(value) == ENV['LOCAL_DOMAIN']
|
value.present? && Rails.application.message_verifier(VERIFY_PURPOSE).verify(value) == ENV['LOCAL_DOMAIN']
|
||||||
rescue ActiveSupport::MessageVerifier::InvalidSignature
|
rescue ActiveSupport::MessageVerifier::InvalidSignature
|
||||||
false
|
false
|
||||||
end
|
end
|
||||||
|
@ -10,16 +10,17 @@ module SettingsHelper
|
|||||||
end
|
end
|
||||||
|
|
||||||
def featured_tags_hint(recently_used_tags)
|
def featured_tags_hint(recently_used_tags)
|
||||||
safe_join(
|
recently_used_tags.present? &&
|
||||||
[
|
safe_join(
|
||||||
t('simple_form.hints.featured_tag.name'),
|
[
|
||||||
safe_join(
|
t('simple_form.hints.featured_tag.name'),
|
||||||
links_for_featured_tags(recently_used_tags),
|
safe_join(
|
||||||
', '
|
links_for_featured_tags(recently_used_tags),
|
||||||
),
|
', '
|
||||||
],
|
),
|
||||||
' '
|
],
|
||||||
)
|
' '
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def session_device_icon(session)
|
def session_device_icon(session)
|
||||||
|
@ -1,9 +1,6 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
module StatusesHelper
|
module StatusesHelper
|
||||||
EMBEDDED_CONTROLLER = 'statuses'
|
|
||||||
EMBEDDED_ACTION = 'embed'
|
|
||||||
|
|
||||||
VISIBLITY_ICONS = {
|
VISIBLITY_ICONS = {
|
||||||
public: 'globe',
|
public: 'globe',
|
||||||
unlisted: 'lock_open',
|
unlisted: 'lock_open',
|
||||||
@ -12,7 +9,7 @@ module StatusesHelper
|
|||||||
}.freeze
|
}.freeze
|
||||||
|
|
||||||
def nothing_here(extra_classes = '')
|
def nothing_here(extra_classes = '')
|
||||||
content_tag(:div, class: "nothing-here #{extra_classes}") do
|
tag.div(class: ['nothing-here', extra_classes]) do
|
||||||
t('accounts.nothing_here')
|
t('accounts.nothing_here')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -60,18 +57,10 @@ module StatusesHelper
|
|||||||
components.compact_blank.join("\n\n")
|
components.compact_blank.join("\n\n")
|
||||||
end
|
end
|
||||||
|
|
||||||
def stream_link_target
|
|
||||||
embedded_view? ? '_blank' : nil
|
|
||||||
end
|
|
||||||
|
|
||||||
def visibility_icon(status)
|
def visibility_icon(status)
|
||||||
VISIBLITY_ICONS[status.visibility.to_sym]
|
VISIBLITY_ICONS[status.visibility.to_sym]
|
||||||
end
|
end
|
||||||
|
|
||||||
def embedded_view?
|
|
||||||
params[:controller] == EMBEDDED_CONTROLLER && params[:action] == EMBEDDED_ACTION
|
|
||||||
end
|
|
||||||
|
|
||||||
def prefers_autoplay?
|
def prefers_autoplay?
|
||||||
ActiveModel::Type::Boolean.new.cast(params[:autoplay]) || current_user&.setting_auto_play_gif
|
ActiveModel::Type::Boolean.new.cast(params[:autoplay]) || current_user&.setting_auto_play_gif
|
||||||
end
|
end
|
||||||
|
@ -1,7 +0,0 @@
|
|||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
module WebfingerHelper
|
|
||||||
def webfinger!(uri)
|
|
||||||
Webfinger.new(uri).perform
|
|
||||||
end
|
|
||||||
end
|
|
@ -230,62 +230,6 @@ function loaded() {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
Rails.delegate(
|
|
||||||
document,
|
|
||||||
'button.status__content__spoiler-link',
|
|
||||||
'click',
|
|
||||||
function () {
|
|
||||||
if (!(this instanceof HTMLButtonElement)) return;
|
|
||||||
|
|
||||||
const statusEl = this.parentNode?.parentNode;
|
|
||||||
|
|
||||||
if (
|
|
||||||
!(
|
|
||||||
statusEl instanceof HTMLDivElement &&
|
|
||||||
statusEl.classList.contains('.status__content')
|
|
||||||
)
|
|
||||||
)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (statusEl.dataset.spoiler === 'expanded') {
|
|
||||||
statusEl.dataset.spoiler = 'folded';
|
|
||||||
this.textContent = new IntlMessageFormat(
|
|
||||||
localeData['status.show_more'] ?? 'Show more',
|
|
||||||
locale,
|
|
||||||
).format() as string;
|
|
||||||
} else {
|
|
||||||
statusEl.dataset.spoiler = 'expanded';
|
|
||||||
this.textContent = new IntlMessageFormat(
|
|
||||||
localeData['status.show_less'] ?? 'Show less',
|
|
||||||
locale,
|
|
||||||
).format() as string;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
document
|
|
||||||
.querySelectorAll<HTMLButtonElement>('button.status__content__spoiler-link')
|
|
||||||
.forEach((spoilerLink) => {
|
|
||||||
const statusEl = spoilerLink.parentNode?.parentNode;
|
|
||||||
|
|
||||||
if (
|
|
||||||
!(
|
|
||||||
statusEl instanceof HTMLDivElement &&
|
|
||||||
statusEl.classList.contains('.status__content')
|
|
||||||
)
|
|
||||||
)
|
|
||||||
return;
|
|
||||||
|
|
||||||
const message =
|
|
||||||
statusEl.dataset.spoiler === 'expanded'
|
|
||||||
? (localeData['status.show_less'] ?? 'Show less')
|
|
||||||
: (localeData['status.show_more'] ?? 'Show more');
|
|
||||||
spoilerLink.textContent = new IntlMessageFormat(
|
|
||||||
message,
|
|
||||||
locale,
|
|
||||||
).format() as string;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Rails.delegate(
|
Rails.delegate(
|
||||||
@ -327,31 +271,24 @@ Rails.delegate(document, '.input-copy button', 'click', ({ target }) => {
|
|||||||
|
|
||||||
if (!input) return;
|
if (!input) return;
|
||||||
|
|
||||||
const oldReadOnly = input.readOnly;
|
navigator.clipboard
|
||||||
|
.writeText(input.value)
|
||||||
input.readOnly = false;
|
.then(() => {
|
||||||
input.focus();
|
|
||||||
input.select();
|
|
||||||
input.setSelectionRange(0, input.value.length);
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (document.execCommand('copy')) {
|
|
||||||
input.blur();
|
|
||||||
|
|
||||||
const parent = target.parentElement;
|
const parent = target.parentElement;
|
||||||
|
|
||||||
if (!parent) return;
|
if (parent) {
|
||||||
parent.classList.add('copied');
|
parent.classList.add('copied');
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
parent.classList.remove('copied');
|
parent.classList.remove('copied');
|
||||||
}, 700);
|
}, 700);
|
||||||
}
|
}
|
||||||
} catch (err) {
|
|
||||||
console.error(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
input.readOnly = oldReadOnly;
|
return true;
|
||||||
|
})
|
||||||
|
.catch((error: unknown) => {
|
||||||
|
console.error(error);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const toggleSidebar = () => {
|
const toggleSidebar = () => {
|
||||||
@ -446,6 +383,24 @@ Rails.delegate(document, '#registration_new_user,#new_user', 'submit', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Rails.delegate(document, '.rules-list button', 'click', ({ target }) => {
|
||||||
|
if (!(target instanceof HTMLElement)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const button = target.closest('button');
|
||||||
|
|
||||||
|
if (!button) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (button.ariaExpanded === 'true') {
|
||||||
|
button.ariaExpanded = 'false';
|
||||||
|
} else {
|
||||||
|
button.ariaExpanded = 'true';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
function main() {
|
function main() {
|
||||||
ready(loaded).catch((error: unknown) => {
|
ready(loaded).catch((error: unknown) => {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
|
BIN
app/javascript/images/archetypes/booster.png
Executable file
After Width: | Height: | Size: 620 KiB |
BIN
app/javascript/images/archetypes/lurker.png
Executable file
After Width: | Height: | Size: 1.0 MiB |
BIN
app/javascript/images/archetypes/oracle.png
Executable file
After Width: | Height: | Size: 1.2 MiB |
BIN
app/javascript/images/archetypes/pollster.png
Executable file
After Width: | Height: | Size: 710 KiB |
BIN
app/javascript/images/archetypes/replier.png
Executable file
After Width: | Height: | Size: 786 KiB |
Before Width: | Height: | Size: 5.6 KiB |
@ -1 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg"><symbol id="mastodon-svg-logo" viewBox="0 0 216.4144 232.00976"><path d="M107.86523 0C78.203984.2425 49.672422 3.4535937 33.044922 11.089844c0 0-32.97656262 14.752031-32.97656262 65.082031 0 11.525-.224375 25.306175.140625 39.919925 1.19750002 49.22 9.02375002 97.72843 54.53124962 109.77343 20.9825 5.55375 38.99711 6.71547 53.505856 5.91797 26.31125-1.45875 41.08203-9.38867 41.08203-9.38867l-.86914-19.08984s-18.80171 5.92758-39.91796 5.20508c-20.921254-.7175-43.006879-2.25516-46.390629-27.94141-.3125-2.25625-.46875-4.66938-.46875-7.20313 0 0 20.536953 5.0204 46.564449 6.21289 15.915.73001 30.8393-.93343 45.99805-2.74218 29.07-3.47125 54.38125-21.3818 57.5625-37.74805 5.0125-25.78125 4.59961-62.916015 4.59961-62.916015 0-50.33-32.97461-65.082031-32.97461-65.082031C166.80539 3.4535938 138.255.2425 108.59375 0h-.72852zM74.296875 39.326172c12.355 0 21.710234 4.749297 27.896485 14.248047l6.01367 10.080078 6.01563-10.080078c6.185-9.49875 15.54023-14.248047 27.89648-14.248047 10.6775 0 19.28156 3.753672 25.85156 11.076172 6.36875 7.3225 9.53907 17.218828 9.53907 29.673828v60.941408h-24.14454V81.869141c0-12.46875-5.24453-18.798829-15.73828-18.798829-11.6025 0-17.41797 7.508516-17.41797 22.353516v32.375002H96.207031V85.423828c0-14.845-5.815468-22.353515-17.417969-22.353516-10.49375 0-15.740234 6.330079-15.740234 18.798829v59.148439H38.904297V80.076172c0-12.455 3.171016-22.351328 9.541015-29.673828 6.568751-7.3225 15.172813-11.076172 25.851563-11.076172z" /></symbol></svg>
|
|
Before Width: | Height: | Size: 1.5 KiB |
3
app/javascript/images/quote.svg
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<svg width="24" height="20" viewBox="0 0 24 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M23.933 2.82414C22.324 4.07931 21.0726 5.3569 20.1788 6.6569C19.3296 7.91207 18.905 9.1 18.905 10.2207C19.0838 10.131 19.3073 10.0862 19.5754 10.0862C19.8883 10.0414 20.1564 10.019 20.3799 10.019C21.4078 10.019 22.257 10.4448 22.9274 11.2966C23.6425 12.1034 24 13.1121 24 14.3224C24 15.8017 23.5084 17.0345 22.5251 18.0207C21.5419 19.0069 20.3129 19.5 18.838 19.5C17.2737 19.5 16.0447 18.9397 15.1508 17.819C14.257 16.6535 13.8101 15.1069 13.8101 13.1793C13.8101 10.8931 14.5028 8.62931 15.8883 6.38793C17.2737 4.14655 19.3073 2.01724 21.9888 0L23.933 2.82414ZM10.1229 2.82414C8.51397 4.07931 7.26257 5.3569 6.36872 6.6569C5.51955 7.91207 5.09497 9.1 5.09497 10.2207C5.27374 10.131 5.49721 10.0862 5.76536 10.0862C6.07821 10.0414 6.34637 10.019 6.56983 10.019C7.59777 10.019 8.44693 10.4448 9.11732 11.2966C9.8324 12.1034 10.1899 13.1121 10.1899 14.3224C10.1899 15.8017 9.69832 17.0345 8.71508 18.0207C7.73184 19.0069 6.50279 19.5 5.02793 19.5C3.46369 19.5 2.23464 18.9397 1.34078 17.819C0.446927 16.6535 0 15.1069 0 13.1793C0 10.8931 0.692738 8.62931 2.07821 6.38793C3.46369 4.14655 5.49721 2.01724 8.17877 0L10.1229 2.82414Z" fill="currentColor" />
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.2 KiB |
@ -1,66 +0,0 @@
|
|||||||
import { defineMessages } from 'react-intl';
|
|
||||||
|
|
||||||
import { AxiosError } from 'axios';
|
|
||||||
|
|
||||||
const messages = defineMessages({
|
|
||||||
unexpectedTitle: { id: 'alert.unexpected.title', defaultMessage: 'Oops!' },
|
|
||||||
unexpectedMessage: { id: 'alert.unexpected.message', defaultMessage: 'An unexpected error occurred.' },
|
|
||||||
rateLimitedTitle: { id: 'alert.rate_limited.title', defaultMessage: 'Rate limited' },
|
|
||||||
rateLimitedMessage: { id: 'alert.rate_limited.message', defaultMessage: 'Please retry after {retry_time, time, medium}.' },
|
|
||||||
});
|
|
||||||
|
|
||||||
export const ALERT_SHOW = 'ALERT_SHOW';
|
|
||||||
export const ALERT_DISMISS = 'ALERT_DISMISS';
|
|
||||||
export const ALERT_CLEAR = 'ALERT_CLEAR';
|
|
||||||
export const ALERT_NOOP = 'ALERT_NOOP';
|
|
||||||
|
|
||||||
export const dismissAlert = alert => ({
|
|
||||||
type: ALERT_DISMISS,
|
|
||||||
alert,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const clearAlert = () => ({
|
|
||||||
type: ALERT_CLEAR,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const showAlert = alert => ({
|
|
||||||
type: ALERT_SHOW,
|
|
||||||
alert,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const showAlertForError = (error, skipNotFound = false) => {
|
|
||||||
if (error.response) {
|
|
||||||
const { data, status, statusText, headers } = error.response;
|
|
||||||
|
|
||||||
// Skip these errors as they are reflected in the UI
|
|
||||||
if (skipNotFound && (status === 404 || status === 410)) {
|
|
||||||
return { type: ALERT_NOOP };
|
|
||||||
}
|
|
||||||
|
|
||||||
// Rate limit errors
|
|
||||||
if (status === 429 && headers['x-ratelimit-reset']) {
|
|
||||||
return showAlert({
|
|
||||||
title: messages.rateLimitedTitle,
|
|
||||||
message: messages.rateLimitedMessage,
|
|
||||||
values: { 'retry_time': new Date(headers['x-ratelimit-reset']) },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return showAlert({
|
|
||||||
title: `${status}`,
|
|
||||||
message: data.error || statusText,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// An aborted request, e.g. due to reloading the browser window, it not really error
|
|
||||||
if (error.code === AxiosError.ECONNABORTED) {
|
|
||||||
return { type: ALERT_NOOP };
|
|
||||||
}
|
|
||||||
|
|
||||||
console.error(error);
|
|
||||||
|
|
||||||
return showAlert({
|
|
||||||
title: messages.unexpectedTitle,
|
|
||||||
message: messages.unexpectedMessage,
|
|
||||||
});
|
|
||||||
};
|
|
90
app/javascript/mastodon/actions/alerts.ts
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
import { defineMessages } from 'react-intl';
|
||||||
|
import type { MessageDescriptor } from 'react-intl';
|
||||||
|
|
||||||
|
import { AxiosError } from 'axios';
|
||||||
|
import type { AxiosResponse } from 'axios';
|
||||||
|
|
||||||
|
interface Alert {
|
||||||
|
title: string | MessageDescriptor;
|
||||||
|
message: string | MessageDescriptor;
|
||||||
|
values?: Record<string, string | number | Date>;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ApiErrorResponse {
|
||||||
|
error?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const messages = defineMessages({
|
||||||
|
unexpectedTitle: { id: 'alert.unexpected.title', defaultMessage: 'Oops!' },
|
||||||
|
unexpectedMessage: {
|
||||||
|
id: 'alert.unexpected.message',
|
||||||
|
defaultMessage: 'An unexpected error occurred.',
|
||||||
|
},
|
||||||
|
rateLimitedTitle: {
|
||||||
|
id: 'alert.rate_limited.title',
|
||||||
|
defaultMessage: 'Rate limited',
|
||||||
|
},
|
||||||
|
rateLimitedMessage: {
|
||||||
|
id: 'alert.rate_limited.message',
|
||||||
|
defaultMessage: 'Please retry after {retry_time, time, medium}.',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const ALERT_SHOW = 'ALERT_SHOW';
|
||||||
|
export const ALERT_DISMISS = 'ALERT_DISMISS';
|
||||||
|
export const ALERT_CLEAR = 'ALERT_CLEAR';
|
||||||
|
export const ALERT_NOOP = 'ALERT_NOOP';
|
||||||
|
|
||||||
|
export const dismissAlert = (alert: Alert) => ({
|
||||||
|
type: ALERT_DISMISS,
|
||||||
|
alert,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const clearAlert = () => ({
|
||||||
|
type: ALERT_CLEAR,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const showAlert = (alert: Alert) => ({
|
||||||
|
type: ALERT_SHOW,
|
||||||
|
alert,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const showAlertForError = (error: unknown, skipNotFound = false) => {
|
||||||
|
if (error instanceof AxiosError && error.response) {
|
||||||
|
const { status, statusText, headers } = error.response;
|
||||||
|
const { data } = error.response as AxiosResponse<ApiErrorResponse>;
|
||||||
|
|
||||||
|
// Skip these errors as they are reflected in the UI
|
||||||
|
if (skipNotFound && (status === 404 || status === 410)) {
|
||||||
|
return { type: ALERT_NOOP };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rate limit errors
|
||||||
|
if (status === 429 && headers['x-ratelimit-reset']) {
|
||||||
|
return showAlert({
|
||||||
|
title: messages.rateLimitedTitle,
|
||||||
|
message: messages.rateLimitedMessage,
|
||||||
|
values: {
|
||||||
|
retry_time: new Date(headers['x-ratelimit-reset'] as string),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return showAlert({
|
||||||
|
title: `${status}`,
|
||||||
|
message: data.error ?? statusText,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// An aborted request, e.g. due to reloading the browser window, it not really error
|
||||||
|
if (error instanceof AxiosError && error.code === AxiosError.ECONNABORTED) {
|
||||||
|
return { type: ALERT_NOOP };
|
||||||
|
}
|
||||||
|
|
||||||
|
console.error(error);
|
||||||
|
|
||||||
|
return showAlert({
|
||||||
|
title: messages.unexpectedTitle,
|
||||||
|
message: messages.unexpectedMessage,
|
||||||
|
});
|
||||||
|
};
|
@ -1,8 +1,5 @@
|
|||||||
import api from '../api';
|
import api from '../api';
|
||||||
|
|
||||||
import { showAlertForError } from './alerts';
|
|
||||||
import { importFetchedAccounts } from './importer';
|
|
||||||
|
|
||||||
export const LIST_FETCH_REQUEST = 'LIST_FETCH_REQUEST';
|
export const LIST_FETCH_REQUEST = 'LIST_FETCH_REQUEST';
|
||||||
export const LIST_FETCH_SUCCESS = 'LIST_FETCH_SUCCESS';
|
export const LIST_FETCH_SUCCESS = 'LIST_FETCH_SUCCESS';
|
||||||
export const LIST_FETCH_FAIL = 'LIST_FETCH_FAIL';
|
export const LIST_FETCH_FAIL = 'LIST_FETCH_FAIL';
|
||||||
@ -11,45 +8,10 @@ export const LISTS_FETCH_REQUEST = 'LISTS_FETCH_REQUEST';
|
|||||||
export const LISTS_FETCH_SUCCESS = 'LISTS_FETCH_SUCCESS';
|
export const LISTS_FETCH_SUCCESS = 'LISTS_FETCH_SUCCESS';
|
||||||
export const LISTS_FETCH_FAIL = 'LISTS_FETCH_FAIL';
|
export const LISTS_FETCH_FAIL = 'LISTS_FETCH_FAIL';
|
||||||
|
|
||||||
export const LIST_EDITOR_TITLE_CHANGE = 'LIST_EDITOR_TITLE_CHANGE';
|
|
||||||
export const LIST_EDITOR_RESET = 'LIST_EDITOR_RESET';
|
|
||||||
export const LIST_EDITOR_SETUP = 'LIST_EDITOR_SETUP';
|
|
||||||
|
|
||||||
export const LIST_CREATE_REQUEST = 'LIST_CREATE_REQUEST';
|
|
||||||
export const LIST_CREATE_SUCCESS = 'LIST_CREATE_SUCCESS';
|
|
||||||
export const LIST_CREATE_FAIL = 'LIST_CREATE_FAIL';
|
|
||||||
|
|
||||||
export const LIST_UPDATE_REQUEST = 'LIST_UPDATE_REQUEST';
|
|
||||||
export const LIST_UPDATE_SUCCESS = 'LIST_UPDATE_SUCCESS';
|
|
||||||
export const LIST_UPDATE_FAIL = 'LIST_UPDATE_FAIL';
|
|
||||||
|
|
||||||
export const LIST_DELETE_REQUEST = 'LIST_DELETE_REQUEST';
|
export const LIST_DELETE_REQUEST = 'LIST_DELETE_REQUEST';
|
||||||
export const LIST_DELETE_SUCCESS = 'LIST_DELETE_SUCCESS';
|
export const LIST_DELETE_SUCCESS = 'LIST_DELETE_SUCCESS';
|
||||||
export const LIST_DELETE_FAIL = 'LIST_DELETE_FAIL';
|
export const LIST_DELETE_FAIL = 'LIST_DELETE_FAIL';
|
||||||
|
|
||||||
export const LIST_ACCOUNTS_FETCH_REQUEST = 'LIST_ACCOUNTS_FETCH_REQUEST';
|
|
||||||
export const LIST_ACCOUNTS_FETCH_SUCCESS = 'LIST_ACCOUNTS_FETCH_SUCCESS';
|
|
||||||
export const LIST_ACCOUNTS_FETCH_FAIL = 'LIST_ACCOUNTS_FETCH_FAIL';
|
|
||||||
|
|
||||||
export const LIST_EDITOR_SUGGESTIONS_CHANGE = 'LIST_EDITOR_SUGGESTIONS_CHANGE';
|
|
||||||
export const LIST_EDITOR_SUGGESTIONS_READY = 'LIST_EDITOR_SUGGESTIONS_READY';
|
|
||||||
export const LIST_EDITOR_SUGGESTIONS_CLEAR = 'LIST_EDITOR_SUGGESTIONS_CLEAR';
|
|
||||||
|
|
||||||
export const LIST_EDITOR_ADD_REQUEST = 'LIST_EDITOR_ADD_REQUEST';
|
|
||||||
export const LIST_EDITOR_ADD_SUCCESS = 'LIST_EDITOR_ADD_SUCCESS';
|
|
||||||
export const LIST_EDITOR_ADD_FAIL = 'LIST_EDITOR_ADD_FAIL';
|
|
||||||
|
|
||||||
export const LIST_EDITOR_REMOVE_REQUEST = 'LIST_EDITOR_REMOVE_REQUEST';
|
|
||||||
export const LIST_EDITOR_REMOVE_SUCCESS = 'LIST_EDITOR_REMOVE_SUCCESS';
|
|
||||||
export const LIST_EDITOR_REMOVE_FAIL = 'LIST_EDITOR_REMOVE_FAIL';
|
|
||||||
|
|
||||||
export const LIST_ADDER_RESET = 'LIST_ADDER_RESET';
|
|
||||||
export const LIST_ADDER_SETUP = 'LIST_ADDER_SETUP';
|
|
||||||
|
|
||||||
export const LIST_ADDER_LISTS_FETCH_REQUEST = 'LIST_ADDER_LISTS_FETCH_REQUEST';
|
|
||||||
export const LIST_ADDER_LISTS_FETCH_SUCCESS = 'LIST_ADDER_LISTS_FETCH_SUCCESS';
|
|
||||||
export const LIST_ADDER_LISTS_FETCH_FAIL = 'LIST_ADDER_LISTS_FETCH_FAIL';
|
|
||||||
|
|
||||||
export const fetchList = id => (dispatch, getState) => {
|
export const fetchList = id => (dispatch, getState) => {
|
||||||
if (getState().getIn(['lists', id])) {
|
if (getState().getIn(['lists', id])) {
|
||||||
return;
|
return;
|
||||||
@ -100,89 +62,6 @@ export const fetchListsFail = error => ({
|
|||||||
error,
|
error,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const submitListEditor = shouldReset => (dispatch, getState) => {
|
|
||||||
const listId = getState().getIn(['listEditor', 'listId']);
|
|
||||||
const title = getState().getIn(['listEditor', 'title']);
|
|
||||||
|
|
||||||
if (listId === null) {
|
|
||||||
dispatch(createList(title, shouldReset));
|
|
||||||
} else {
|
|
||||||
dispatch(updateList(listId, title, shouldReset));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const setupListEditor = listId => (dispatch, getState) => {
|
|
||||||
dispatch({
|
|
||||||
type: LIST_EDITOR_SETUP,
|
|
||||||
list: getState().getIn(['lists', listId]),
|
|
||||||
});
|
|
||||||
|
|
||||||
dispatch(fetchListAccounts(listId));
|
|
||||||
};
|
|
||||||
|
|
||||||
export const changeListEditorTitle = value => ({
|
|
||||||
type: LIST_EDITOR_TITLE_CHANGE,
|
|
||||||
value,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const createList = (title, shouldReset) => (dispatch) => {
|
|
||||||
dispatch(createListRequest());
|
|
||||||
|
|
||||||
api().post('/api/v1/lists', { title }).then(({ data }) => {
|
|
||||||
dispatch(createListSuccess(data));
|
|
||||||
|
|
||||||
if (shouldReset) {
|
|
||||||
dispatch(resetListEditor());
|
|
||||||
}
|
|
||||||
}).catch(err => dispatch(createListFail(err)));
|
|
||||||
};
|
|
||||||
|
|
||||||
export const createListRequest = () => ({
|
|
||||||
type: LIST_CREATE_REQUEST,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const createListSuccess = list => ({
|
|
||||||
type: LIST_CREATE_SUCCESS,
|
|
||||||
list,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const createListFail = error => ({
|
|
||||||
type: LIST_CREATE_FAIL,
|
|
||||||
error,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const updateList = (id, title, shouldReset, isExclusive, replies_policy) => (dispatch) => {
|
|
||||||
dispatch(updateListRequest(id));
|
|
||||||
|
|
||||||
api().put(`/api/v1/lists/${id}`, { title, replies_policy, exclusive: typeof isExclusive === 'undefined' ? undefined : !!isExclusive }).then(({ data }) => {
|
|
||||||
dispatch(updateListSuccess(data));
|
|
||||||
|
|
||||||
if (shouldReset) {
|
|
||||||
dispatch(resetListEditor());
|
|
||||||
}
|
|
||||||
}).catch(err => dispatch(updateListFail(id, err)));
|
|
||||||
};
|
|
||||||
|
|
||||||
export const updateListRequest = id => ({
|
|
||||||
type: LIST_UPDATE_REQUEST,
|
|
||||||
id,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const updateListSuccess = list => ({
|
|
||||||
type: LIST_UPDATE_SUCCESS,
|
|
||||||
list,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const updateListFail = (id, error) => ({
|
|
||||||
type: LIST_UPDATE_FAIL,
|
|
||||||
id,
|
|
||||||
error,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const resetListEditor = () => ({
|
|
||||||
type: LIST_EDITOR_RESET,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const deleteList = id => (dispatch) => {
|
export const deleteList = id => (dispatch) => {
|
||||||
dispatch(deleteListRequest(id));
|
dispatch(deleteListRequest(id));
|
||||||
|
|
||||||
@ -206,167 +85,3 @@ export const deleteListFail = (id, error) => ({
|
|||||||
id,
|
id,
|
||||||
error,
|
error,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const fetchListAccounts = listId => (dispatch) => {
|
|
||||||
dispatch(fetchListAccountsRequest(listId));
|
|
||||||
|
|
||||||
api().get(`/api/v1/lists/${listId}/accounts`, { params: { limit: 0 } }).then(({ data }) => {
|
|
||||||
dispatch(importFetchedAccounts(data));
|
|
||||||
dispatch(fetchListAccountsSuccess(listId, data));
|
|
||||||
}).catch(err => dispatch(fetchListAccountsFail(listId, err)));
|
|
||||||
};
|
|
||||||
|
|
||||||
export const fetchListAccountsRequest = id => ({
|
|
||||||
type: LIST_ACCOUNTS_FETCH_REQUEST,
|
|
||||||
id,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const fetchListAccountsSuccess = (id, accounts, next) => ({
|
|
||||||
type: LIST_ACCOUNTS_FETCH_SUCCESS,
|
|
||||||
id,
|
|
||||||
accounts,
|
|
||||||
next,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const fetchListAccountsFail = (id, error) => ({
|
|
||||||
type: LIST_ACCOUNTS_FETCH_FAIL,
|
|
||||||
id,
|
|
||||||
error,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const fetchListSuggestions = q => (dispatch) => {
|
|
||||||
const params = {
|
|
||||||
q,
|
|
||||||
resolve: false,
|
|
||||||
limit: 4,
|
|
||||||
following: true,
|
|
||||||
};
|
|
||||||
|
|
||||||
api().get('/api/v1/accounts/search', { params }).then(({ data }) => {
|
|
||||||
dispatch(importFetchedAccounts(data));
|
|
||||||
dispatch(fetchListSuggestionsReady(q, data));
|
|
||||||
}).catch(error => dispatch(showAlertForError(error)));
|
|
||||||
};
|
|
||||||
|
|
||||||
export const fetchListSuggestionsReady = (query, accounts) => ({
|
|
||||||
type: LIST_EDITOR_SUGGESTIONS_READY,
|
|
||||||
query,
|
|
||||||
accounts,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const clearListSuggestions = () => ({
|
|
||||||
type: LIST_EDITOR_SUGGESTIONS_CLEAR,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const changeListSuggestions = value => ({
|
|
||||||
type: LIST_EDITOR_SUGGESTIONS_CHANGE,
|
|
||||||
value,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const addToListEditor = accountId => (dispatch, getState) => {
|
|
||||||
dispatch(addToList(getState().getIn(['listEditor', 'listId']), accountId));
|
|
||||||
};
|
|
||||||
|
|
||||||
export const addToList = (listId, accountId) => (dispatch) => {
|
|
||||||
dispatch(addToListRequest(listId, accountId));
|
|
||||||
|
|
||||||
api().post(`/api/v1/lists/${listId}/accounts`, { account_ids: [accountId] })
|
|
||||||
.then(() => dispatch(addToListSuccess(listId, accountId)))
|
|
||||||
.catch(err => dispatch(addToListFail(listId, accountId, err)));
|
|
||||||
};
|
|
||||||
|
|
||||||
export const addToListRequest = (listId, accountId) => ({
|
|
||||||
type: LIST_EDITOR_ADD_REQUEST,
|
|
||||||
listId,
|
|
||||||
accountId,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const addToListSuccess = (listId, accountId) => ({
|
|
||||||
type: LIST_EDITOR_ADD_SUCCESS,
|
|
||||||
listId,
|
|
||||||
accountId,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const addToListFail = (listId, accountId, error) => ({
|
|
||||||
type: LIST_EDITOR_ADD_FAIL,
|
|
||||||
listId,
|
|
||||||
accountId,
|
|
||||||
error,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const removeFromListEditor = accountId => (dispatch, getState) => {
|
|
||||||
dispatch(removeFromList(getState().getIn(['listEditor', 'listId']), accountId));
|
|
||||||
};
|
|
||||||
|
|
||||||
export const removeFromList = (listId, accountId) => (dispatch) => {
|
|
||||||
dispatch(removeFromListRequest(listId, accountId));
|
|
||||||
|
|
||||||
api().delete(`/api/v1/lists/${listId}/accounts`, { params: { account_ids: [accountId] } })
|
|
||||||
.then(() => dispatch(removeFromListSuccess(listId, accountId)))
|
|
||||||
.catch(err => dispatch(removeFromListFail(listId, accountId, err)));
|
|
||||||
};
|
|
||||||
|
|
||||||
export const removeFromListRequest = (listId, accountId) => ({
|
|
||||||
type: LIST_EDITOR_REMOVE_REQUEST,
|
|
||||||
listId,
|
|
||||||
accountId,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const removeFromListSuccess = (listId, accountId) => ({
|
|
||||||
type: LIST_EDITOR_REMOVE_SUCCESS,
|
|
||||||
listId,
|
|
||||||
accountId,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const removeFromListFail = (listId, accountId, error) => ({
|
|
||||||
type: LIST_EDITOR_REMOVE_FAIL,
|
|
||||||
listId,
|
|
||||||
accountId,
|
|
||||||
error,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const resetListAdder = () => ({
|
|
||||||
type: LIST_ADDER_RESET,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const setupListAdder = accountId => (dispatch, getState) => {
|
|
||||||
dispatch({
|
|
||||||
type: LIST_ADDER_SETUP,
|
|
||||||
account: getState().getIn(['accounts', accountId]),
|
|
||||||
});
|
|
||||||
dispatch(fetchLists());
|
|
||||||
dispatch(fetchAccountLists(accountId));
|
|
||||||
};
|
|
||||||
|
|
||||||
export const fetchAccountLists = accountId => (dispatch) => {
|
|
||||||
dispatch(fetchAccountListsRequest(accountId));
|
|
||||||
|
|
||||||
api().get(`/api/v1/accounts/${accountId}/lists`)
|
|
||||||
.then(({ data }) => dispatch(fetchAccountListsSuccess(accountId, data)))
|
|
||||||
.catch(err => dispatch(fetchAccountListsFail(accountId, err)));
|
|
||||||
};
|
|
||||||
|
|
||||||
export const fetchAccountListsRequest = id => ({
|
|
||||||
type:LIST_ADDER_LISTS_FETCH_REQUEST,
|
|
||||||
id,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const fetchAccountListsSuccess = (id, lists) => ({
|
|
||||||
type: LIST_ADDER_LISTS_FETCH_SUCCESS,
|
|
||||||
id,
|
|
||||||
lists,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const fetchAccountListsFail = (id, err) => ({
|
|
||||||
type: LIST_ADDER_LISTS_FETCH_FAIL,
|
|
||||||
id,
|
|
||||||
err,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const addToListAdder = listId => (dispatch, getState) => {
|
|
||||||
dispatch(addToList(listId, getState().getIn(['listAdder', 'accountId'])));
|
|
||||||
};
|
|
||||||
|
|
||||||
export const removeFromListAdder = listId => (dispatch, getState) => {
|
|
||||||
dispatch(removeFromList(listId, getState().getIn(['listAdder', 'accountId'])));
|
|
||||||
};
|
|
||||||
|
13
app/javascript/mastodon/actions/lists_typed.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import { apiCreate, apiUpdate } from 'mastodon/api/lists';
|
||||||
|
import type { List } from 'mastodon/models/list';
|
||||||
|
import { createDataLoadingThunk } from 'mastodon/store/typed_functions';
|
||||||
|
|
||||||
|
export const createList = createDataLoadingThunk(
|
||||||
|
'list/create',
|
||||||
|
(list: Partial<List>) => apiCreate(list),
|
||||||
|
);
|
||||||
|
|
||||||
|
export const updateList = createDataLoadingThunk(
|
||||||
|
'list/update',
|
||||||
|
(list: Partial<List>) => apiUpdate(list),
|
||||||
|
);
|
@ -37,8 +37,7 @@ export const synchronouslySubmitMarkers = createAppAsyncThunk(
|
|||||||
});
|
});
|
||||||
|
|
||||||
return;
|
return;
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
} else if ('sendBeacon' in navigator) {
|
||||||
} else if ('navigator' && 'sendBeacon' in navigator) {
|
|
||||||
// Failing that, we can use sendBeacon, but we have to encode the data as
|
// Failing that, we can use sendBeacon, but we have to encode the data as
|
||||||
// FormData for DoorKeeper to recognize the token.
|
// FormData for DoorKeeper to recognize the token.
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
|
@ -8,6 +8,7 @@ import type { ApiAccountJSON } from 'mastodon/api_types/accounts';
|
|||||||
import type {
|
import type {
|
||||||
ApiNotificationGroupJSON,
|
ApiNotificationGroupJSON,
|
||||||
ApiNotificationJSON,
|
ApiNotificationJSON,
|
||||||
|
NotificationType,
|
||||||
} from 'mastodon/api_types/notifications';
|
} from 'mastodon/api_types/notifications';
|
||||||
import { allNotificationTypes } from 'mastodon/api_types/notifications';
|
import { allNotificationTypes } from 'mastodon/api_types/notifications';
|
||||||
import type { ApiStatusJSON } from 'mastodon/api_types/statuses';
|
import type { ApiStatusJSON } from 'mastodon/api_types/statuses';
|
||||||
@ -15,6 +16,7 @@ import { usePendingItems } from 'mastodon/initial_state';
|
|||||||
import type { NotificationGap } from 'mastodon/reducers/notification_groups';
|
import type { NotificationGap } from 'mastodon/reducers/notification_groups';
|
||||||
import {
|
import {
|
||||||
selectSettingsNotificationsExcludedTypes,
|
selectSettingsNotificationsExcludedTypes,
|
||||||
|
selectSettingsNotificationsGroupFollows,
|
||||||
selectSettingsNotificationsQuickFilterActive,
|
selectSettingsNotificationsQuickFilterActive,
|
||||||
selectSettingsNotificationsShows,
|
selectSettingsNotificationsShows,
|
||||||
} from 'mastodon/selectors/settings';
|
} from 'mastodon/selectors/settings';
|
||||||
@ -68,13 +70,19 @@ function dispatchAssociatedRecords(
|
|||||||
dispatch(importFetchedStatuses(fetchedStatuses));
|
dispatch(importFetchedStatuses(fetchedStatuses));
|
||||||
}
|
}
|
||||||
|
|
||||||
const supportedGroupedNotificationTypes = ['favourite', 'reblog'];
|
function selectNotificationGroupedTypes(state: RootState) {
|
||||||
|
const types: NotificationType[] = ['favourite', 'reblog'];
|
||||||
|
|
||||||
|
if (selectSettingsNotificationsGroupFollows(state)) types.push('follow');
|
||||||
|
|
||||||
|
return types;
|
||||||
|
}
|
||||||
|
|
||||||
export const fetchNotifications = createDataLoadingThunk(
|
export const fetchNotifications = createDataLoadingThunk(
|
||||||
'notificationGroups/fetch',
|
'notificationGroups/fetch',
|
||||||
async (_params, { getState }) =>
|
async (_params, { getState }) =>
|
||||||
apiFetchNotificationGroups({
|
apiFetchNotificationGroups({
|
||||||
grouped_types: supportedGroupedNotificationTypes,
|
grouped_types: selectNotificationGroupedTypes(getState()),
|
||||||
exclude_types: getExcludedTypes(getState()),
|
exclude_types: getExcludedTypes(getState()),
|
||||||
}),
|
}),
|
||||||
({ notifications, accounts, statuses }, { dispatch }) => {
|
({ notifications, accounts, statuses }, { dispatch }) => {
|
||||||
@ -98,7 +106,7 @@ export const fetchNotificationsGap = createDataLoadingThunk(
|
|||||||
'notificationGroups/fetchGap',
|
'notificationGroups/fetchGap',
|
||||||
async (params: { gap: NotificationGap }, { getState }) =>
|
async (params: { gap: NotificationGap }, { getState }) =>
|
||||||
apiFetchNotificationGroups({
|
apiFetchNotificationGroups({
|
||||||
grouped_types: supportedGroupedNotificationTypes,
|
grouped_types: selectNotificationGroupedTypes(getState()),
|
||||||
max_id: params.gap.maxId,
|
max_id: params.gap.maxId,
|
||||||
exclude_types: getExcludedTypes(getState()),
|
exclude_types: getExcludedTypes(getState()),
|
||||||
}),
|
}),
|
||||||
@ -115,7 +123,7 @@ export const pollRecentNotifications = createDataLoadingThunk(
|
|||||||
'notificationGroups/pollRecentNotifications',
|
'notificationGroups/pollRecentNotifications',
|
||||||
async (_params, { getState }) => {
|
async (_params, { getState }) => {
|
||||||
return apiFetchNotificationGroups({
|
return apiFetchNotificationGroups({
|
||||||
grouped_types: supportedGroupedNotificationTypes,
|
grouped_types: selectNotificationGroupedTypes(getState()),
|
||||||
max_id: undefined,
|
max_id: undefined,
|
||||||
exclude_types: getExcludedTypes(getState()),
|
exclude_types: getExcludedTypes(getState()),
|
||||||
// In slow mode, we don't want to include notifications that duplicate the already-displayed ones
|
// In slow mode, we don't want to include notifications that duplicate the already-displayed ones
|
||||||
@ -133,6 +141,9 @@ export const pollRecentNotifications = createDataLoadingThunk(
|
|||||||
|
|
||||||
return { notifications };
|
return { notifications };
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
useLoadingBar: false,
|
||||||
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
export const processNewNotificationForGroups = createAppAsyncThunk(
|
export const processNewNotificationForGroups = createAppAsyncThunk(
|
||||||
@ -164,7 +175,10 @@ export const processNewNotificationForGroups = createAppAsyncThunk(
|
|||||||
|
|
||||||
dispatchAssociatedRecords(dispatch, [notification]);
|
dispatchAssociatedRecords(dispatch, [notification]);
|
||||||
|
|
||||||
return notification;
|
return {
|
||||||
|
notification,
|
||||||
|
groupedTypes: selectNotificationGroupedTypes(state),
|
||||||
|
};
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -106,12 +106,13 @@ export const connectTimelineStream = (timelineId, channelName, params = {}, opti
|
|||||||
dispatch(processNewNotificationForGroups(notificationJSON));
|
dispatch(processNewNotificationForGroups(notificationJSON));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'notifications_merged':
|
case 'notifications_merged': {
|
||||||
const state = getState();
|
const state = getState();
|
||||||
if (state.notifications.top || !state.notifications.mounted)
|
if (state.notifications.top || !state.notifications.mounted)
|
||||||
dispatch(expandNotifications({ forceLoad: true, maxId: undefined }));
|
dispatch(expandNotifications({ forceLoad: true, maxId: undefined }));
|
||||||
dispatch(refreshStaleNotificationGroups());
|
dispatch(refreshStaleNotificationGroups());
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
case 'conversation':
|
case 'conversation':
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
dispatch(updateConversations(JSON.parse(data.payload)));
|
dispatch(updateConversations(JSON.parse(data.payload)));
|
||||||
|
@ -1,58 +0,0 @@
|
|||||||
import api from '../api';
|
|
||||||
|
|
||||||
import { fetchRelationships } from './accounts';
|
|
||||||
import { importFetchedAccounts } from './importer';
|
|
||||||
|
|
||||||
export const SUGGESTIONS_FETCH_REQUEST = 'SUGGESTIONS_FETCH_REQUEST';
|
|
||||||
export const SUGGESTIONS_FETCH_SUCCESS = 'SUGGESTIONS_FETCH_SUCCESS';
|
|
||||||
export const SUGGESTIONS_FETCH_FAIL = 'SUGGESTIONS_FETCH_FAIL';
|
|
||||||
|
|
||||||
export const SUGGESTIONS_DISMISS = 'SUGGESTIONS_DISMISS';
|
|
||||||
|
|
||||||
export function fetchSuggestions(withRelationships = false) {
|
|
||||||
return (dispatch) => {
|
|
||||||
dispatch(fetchSuggestionsRequest());
|
|
||||||
|
|
||||||
api().get('/api/v2/suggestions', { params: { limit: 20 } }).then(response => {
|
|
||||||
dispatch(importFetchedAccounts(response.data.map(x => x.account)));
|
|
||||||
dispatch(fetchSuggestionsSuccess(response.data));
|
|
||||||
|
|
||||||
if (withRelationships) {
|
|
||||||
dispatch(fetchRelationships(response.data.map(item => item.account.id)));
|
|
||||||
}
|
|
||||||
}).catch(error => dispatch(fetchSuggestionsFail(error)));
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function fetchSuggestionsRequest() {
|
|
||||||
return {
|
|
||||||
type: SUGGESTIONS_FETCH_REQUEST,
|
|
||||||
skipLoading: true,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function fetchSuggestionsSuccess(suggestions) {
|
|
||||||
return {
|
|
||||||
type: SUGGESTIONS_FETCH_SUCCESS,
|
|
||||||
suggestions,
|
|
||||||
skipLoading: true,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function fetchSuggestionsFail(error) {
|
|
||||||
return {
|
|
||||||
type: SUGGESTIONS_FETCH_FAIL,
|
|
||||||
error,
|
|
||||||
skipLoading: true,
|
|
||||||
skipAlert: true,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export const dismissSuggestion = accountId => (dispatch) => {
|
|
||||||
dispatch({
|
|
||||||
type: SUGGESTIONS_DISMISS,
|
|
||||||
id: accountId,
|
|
||||||
});
|
|
||||||
|
|
||||||
api().delete(`/api/v1/suggestions/${accountId}`).catch(() => {});
|
|
||||||
};
|
|
24
app/javascript/mastodon/actions/suggestions.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import {
|
||||||
|
apiGetSuggestions,
|
||||||
|
apiDeleteSuggestion,
|
||||||
|
} from 'mastodon/api/suggestions';
|
||||||
|
import { createDataLoadingThunk } from 'mastodon/store/typed_functions';
|
||||||
|
|
||||||
|
import { fetchRelationships } from './accounts';
|
||||||
|
import { importFetchedAccounts } from './importer';
|
||||||
|
|
||||||
|
export const fetchSuggestions = createDataLoadingThunk(
|
||||||
|
'suggestions/fetch',
|
||||||
|
() => apiGetSuggestions(20),
|
||||||
|
(data, { dispatch }) => {
|
||||||
|
dispatch(importFetchedAccounts(data.map((x) => x.account)));
|
||||||
|
dispatch(fetchRelationships(data.map((x) => x.account.id)));
|
||||||
|
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
export const dismissSuggestion = createDataLoadingThunk(
|
||||||
|
'suggestions/dismiss',
|
||||||
|
({ accountId }: { accountId: string }) => apiDeleteSuggestion(accountId),
|
||||||
|
);
|
@ -1,9 +1,5 @@
|
|||||||
import api, { getLinks } from '../api';
|
import api, { getLinks } from '../api';
|
||||||
|
|
||||||
export const HASHTAG_FETCH_REQUEST = 'HASHTAG_FETCH_REQUEST';
|
|
||||||
export const HASHTAG_FETCH_SUCCESS = 'HASHTAG_FETCH_SUCCESS';
|
|
||||||
export const HASHTAG_FETCH_FAIL = 'HASHTAG_FETCH_FAIL';
|
|
||||||
|
|
||||||
export const FOLLOWED_HASHTAGS_FETCH_REQUEST = 'FOLLOWED_HASHTAGS_FETCH_REQUEST';
|
export const FOLLOWED_HASHTAGS_FETCH_REQUEST = 'FOLLOWED_HASHTAGS_FETCH_REQUEST';
|
||||||
export const FOLLOWED_HASHTAGS_FETCH_SUCCESS = 'FOLLOWED_HASHTAGS_FETCH_SUCCESS';
|
export const FOLLOWED_HASHTAGS_FETCH_SUCCESS = 'FOLLOWED_HASHTAGS_FETCH_SUCCESS';
|
||||||
export const FOLLOWED_HASHTAGS_FETCH_FAIL = 'FOLLOWED_HASHTAGS_FETCH_FAIL';
|
export const FOLLOWED_HASHTAGS_FETCH_FAIL = 'FOLLOWED_HASHTAGS_FETCH_FAIL';
|
||||||
@ -12,39 +8,6 @@ export const FOLLOWED_HASHTAGS_EXPAND_REQUEST = 'FOLLOWED_HASHTAGS_EXPAND_REQUES
|
|||||||
export const FOLLOWED_HASHTAGS_EXPAND_SUCCESS = 'FOLLOWED_HASHTAGS_EXPAND_SUCCESS';
|
export const FOLLOWED_HASHTAGS_EXPAND_SUCCESS = 'FOLLOWED_HASHTAGS_EXPAND_SUCCESS';
|
||||||
export const FOLLOWED_HASHTAGS_EXPAND_FAIL = 'FOLLOWED_HASHTAGS_EXPAND_FAIL';
|
export const FOLLOWED_HASHTAGS_EXPAND_FAIL = 'FOLLOWED_HASHTAGS_EXPAND_FAIL';
|
||||||
|
|
||||||
export const HASHTAG_FOLLOW_REQUEST = 'HASHTAG_FOLLOW_REQUEST';
|
|
||||||
export const HASHTAG_FOLLOW_SUCCESS = 'HASHTAG_FOLLOW_SUCCESS';
|
|
||||||
export const HASHTAG_FOLLOW_FAIL = 'HASHTAG_FOLLOW_FAIL';
|
|
||||||
|
|
||||||
export const HASHTAG_UNFOLLOW_REQUEST = 'HASHTAG_UNFOLLOW_REQUEST';
|
|
||||||
export const HASHTAG_UNFOLLOW_SUCCESS = 'HASHTAG_UNFOLLOW_SUCCESS';
|
|
||||||
export const HASHTAG_UNFOLLOW_FAIL = 'HASHTAG_UNFOLLOW_FAIL';
|
|
||||||
|
|
||||||
export const fetchHashtag = name => (dispatch) => {
|
|
||||||
dispatch(fetchHashtagRequest());
|
|
||||||
|
|
||||||
api().get(`/api/v1/tags/${name}`).then(({ data }) => {
|
|
||||||
dispatch(fetchHashtagSuccess(name, data));
|
|
||||||
}).catch(err => {
|
|
||||||
dispatch(fetchHashtagFail(err));
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const fetchHashtagRequest = () => ({
|
|
||||||
type: HASHTAG_FETCH_REQUEST,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const fetchHashtagSuccess = (name, tag) => ({
|
|
||||||
type: HASHTAG_FETCH_SUCCESS,
|
|
||||||
name,
|
|
||||||
tag,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const fetchHashtagFail = error => ({
|
|
||||||
type: HASHTAG_FETCH_FAIL,
|
|
||||||
error,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const fetchFollowedHashtags = () => (dispatch) => {
|
export const fetchFollowedHashtags = () => (dispatch) => {
|
||||||
dispatch(fetchFollowedHashtagsRequest());
|
dispatch(fetchFollowedHashtagsRequest());
|
||||||
|
|
||||||
@ -116,57 +79,3 @@ export function expandFollowedHashtagsFail(error) {
|
|||||||
error,
|
error,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export const followHashtag = name => (dispatch) => {
|
|
||||||
dispatch(followHashtagRequest(name));
|
|
||||||
|
|
||||||
api().post(`/api/v1/tags/${name}/follow`).then(({ data }) => {
|
|
||||||
dispatch(followHashtagSuccess(name, data));
|
|
||||||
}).catch(err => {
|
|
||||||
dispatch(followHashtagFail(name, err));
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const followHashtagRequest = name => ({
|
|
||||||
type: HASHTAG_FOLLOW_REQUEST,
|
|
||||||
name,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const followHashtagSuccess = (name, tag) => ({
|
|
||||||
type: HASHTAG_FOLLOW_SUCCESS,
|
|
||||||
name,
|
|
||||||
tag,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const followHashtagFail = (name, error) => ({
|
|
||||||
type: HASHTAG_FOLLOW_FAIL,
|
|
||||||
name,
|
|
||||||
error,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const unfollowHashtag = name => (dispatch) => {
|
|
||||||
dispatch(unfollowHashtagRequest(name));
|
|
||||||
|
|
||||||
api().post(`/api/v1/tags/${name}/unfollow`).then(({ data }) => {
|
|
||||||
dispatch(unfollowHashtagSuccess(name, data));
|
|
||||||
}).catch(err => {
|
|
||||||
dispatch(unfollowHashtagFail(name, err));
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const unfollowHashtagRequest = name => ({
|
|
||||||
type: HASHTAG_UNFOLLOW_REQUEST,
|
|
||||||
name,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const unfollowHashtagSuccess = (name, tag) => ({
|
|
||||||
type: HASHTAG_UNFOLLOW_SUCCESS,
|
|
||||||
name,
|
|
||||||
tag,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const unfollowHashtagFail = (name, error) => ({
|
|
||||||
type: HASHTAG_UNFOLLOW_FAIL,
|
|
||||||
name,
|
|
||||||
error,
|
|
||||||
});
|
|
||||||
|
17
app/javascript/mastodon/actions/tags_typed.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import { apiGetTag, apiFollowTag, apiUnfollowTag } from 'mastodon/api/tags';
|
||||||
|
import { createDataLoadingThunk } from 'mastodon/store/typed_functions';
|
||||||
|
|
||||||
|
export const fetchHashtag = createDataLoadingThunk(
|
||||||
|
'tags/fetch',
|
||||||
|
({ tagId }: { tagId: string }) => apiGetTag(tagId),
|
||||||
|
);
|
||||||
|
|
||||||
|
export const followHashtag = createDataLoadingThunk(
|
||||||
|
'tags/follow',
|
||||||
|
({ tagId }: { tagId: string }) => apiFollowTag(tagId),
|
||||||
|
);
|
||||||
|
|
||||||
|
export const unfollowHashtag = createDataLoadingThunk(
|
||||||
|
'tags/unfollow',
|
||||||
|
({ tagId }: { tagId: string }) => apiUnfollowTag(tagId),
|
||||||
|
);
|
@ -68,6 +68,7 @@ export async function apiRequest<ApiResponse = unknown>(
|
|||||||
method: Method,
|
method: Method,
|
||||||
url: string,
|
url: string,
|
||||||
args: {
|
args: {
|
||||||
|
signal?: AbortSignal;
|
||||||
params?: RequestParamsOrData;
|
params?: RequestParamsOrData;
|
||||||
data?: RequestParamsOrData;
|
data?: RequestParamsOrData;
|
||||||
timeout?: number;
|
timeout?: number;
|
||||||
|