mirror of
https://github.com/mastodon/mastodon.git
synced 2026-03-22 02:16:16 -05:00
Compare commits
127 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e8045de79b | ||
|
|
5f30206c5e | ||
|
|
68a26ce7c6 | ||
|
|
ff20ce9acf | ||
|
|
1ba2b1cdc1 | ||
|
|
4c1fbe4e2e | ||
|
|
569ff6c8ad | ||
|
|
81716f7e27 | ||
|
|
8935137526 | ||
|
|
dcc5c2b6f6 | ||
|
|
f1c32f6a11 | ||
|
|
db943c43c8 | ||
|
|
1a74b74a40 | ||
|
|
9a25b12f0c | ||
|
|
6f9b32b137 | ||
|
|
1b3ef035b9 | ||
|
|
6698901d57 | ||
|
|
ba0609bbaf | ||
|
|
ded7f50f2c | ||
|
|
85eda5b46f | ||
|
|
f1c9c89c39 | ||
|
|
57e0c6562f | ||
|
|
f7b6e57151 | ||
|
|
57f658dc5c | ||
|
|
0cda068918 | ||
|
|
deeaf50472 | ||
|
|
adea0b7b31 | ||
|
|
1eb8d1b967 | ||
|
|
f354bbe8aa | ||
|
|
53437c4653 | ||
|
|
617926742c | ||
|
|
55a7b1ea58 | ||
|
|
c1fb6893c5 | ||
|
|
71ae4cf2cf | ||
|
|
a846ed17ff | ||
|
|
3013039720 | ||
|
|
ad4ba5aa00 | ||
|
|
1c5461fffe | ||
|
|
3de59a9344 | ||
|
|
32c3376d84 | ||
|
|
962ae88caf | ||
|
|
7d9d3de972 | ||
|
|
546a95349e | ||
|
|
df1ab0ab90 | ||
|
|
8d1ea4c531 | ||
|
|
8233295e3b | ||
|
|
4eb0a506d3 | ||
|
|
75739a5a9b | ||
|
|
86cff1abca | ||
|
|
e6d2fc869b | ||
|
|
a9f8268a75 | ||
|
|
dfe269439a | ||
|
|
9bc9ebc59e | ||
|
|
a6d31c0ccf | ||
|
|
1e2cf6c964 | ||
|
|
c42c71c90a | ||
|
|
782e410719 | ||
|
|
b0c141e658 | ||
|
|
1ef4bbd88d | ||
|
|
240d38b7c0 | ||
|
|
770d1212bb | ||
|
|
86e463c0e8 | ||
|
|
a04a210e14 | ||
|
|
19588756ef | ||
|
|
e398ff40b2 | ||
|
|
96eb687524 | ||
|
|
05c624cfa7 | ||
|
|
5fe316b2e9 | ||
|
|
1dbf10198d | ||
|
|
c6ccacdf7b | ||
|
|
6ccd9c2f1f | ||
|
|
261d9b33fe | ||
|
|
4ee21c2e29 | ||
|
|
c08cd6d62a | ||
|
|
44d45e5705 | ||
|
|
27c67f1750 | ||
|
|
bb28552859 | ||
|
|
6486c092f6 | ||
|
|
a7b45682a6 | ||
|
|
5a57c0844a | ||
|
|
1d081250f4 | ||
|
|
bb6093c315 | ||
|
|
058f704c21 | ||
|
|
6baa8f2466 | ||
|
|
e742eff044 | ||
|
|
55b9d21537 | ||
|
|
59f0134578 | ||
|
|
28b9e9087a | ||
|
|
fa2cc409ce | ||
|
|
8a100d84c5 | ||
|
|
9ae0464e8f | ||
|
|
9eea4479e1 | ||
|
|
30103fd2c8 | ||
|
|
a9a7ad62f1 | ||
|
|
ea663cf7c7 | ||
|
|
fbe05d42fb | ||
|
|
29ae9c9c4b | ||
|
|
26c78392f8 | ||
|
|
048430f4e8 | ||
|
|
d45b4db1d7 | ||
|
|
ef3a95affc | ||
|
|
3e6a9371b0 | ||
|
|
e91c764590 | ||
|
|
cfdd9396c0 | ||
|
|
ba498ae779 | ||
|
|
5bae08d1ff | ||
|
|
5253527ec4 | ||
|
|
0b50789c5b | ||
|
|
a978e37f4c | ||
|
|
dd708298a8 | ||
|
|
449eb03f11 | ||
|
|
1baede0a7c | ||
|
|
a7ecfc1ca5 | ||
|
|
e62baacfc1 | ||
|
|
b5a6feb3bf | ||
|
|
05964f571b | ||
|
|
16a54f7158 | ||
|
|
6d53ca63d6 | ||
|
|
93acfdd7d3 | ||
|
|
a209b8e544 | ||
|
|
af4c372ab2 | ||
|
|
aa579ce286 | ||
|
|
adfabf8c80 | ||
|
|
ea710df180 | ||
|
|
e1b6e28829 | ||
|
|
214d59bd37 | ||
|
|
e4291e9b05 |
|
|
@ -1,5 +1,5 @@
|
|||
# For details, see https://github.com/devcontainers/images/tree/main/src/ruby
|
||||
FROM mcr.microsoft.com/devcontainers/ruby:3.4-trixie
|
||||
FROM mcr.microsoft.com/devcontainers/ruby:1-3.3-bookworm
|
||||
|
||||
# Install node version from .nvmrc
|
||||
WORKDIR /app
|
||||
|
|
@ -9,7 +9,7 @@ RUN /bin/bash --login -i -c "nvm install"
|
|||
# Install additional OS packages
|
||||
RUN apt-get update && \
|
||||
export DEBIAN_FRONTEND=noninteractive && \
|
||||
apt-get -y install --no-install-recommends libicu-dev libidn11-dev ffmpeg libvips42 libpam-dev
|
||||
apt-get -y install --no-install-recommends libicu-dev libidn11-dev ffmpeg imagemagick libvips42 libpam-dev
|
||||
|
||||
# Disable download prompt for Corepack
|
||||
ENV COREPACK_ENABLE_DOWNLOAD_PROMPT=0
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ services:
|
|||
- internal_network
|
||||
|
||||
es:
|
||||
image: docker.elastic.co/elasticsearch/elasticsearch:7.17.29
|
||||
image: docker.elastic.co/elasticsearch/elasticsearch-oss:7.10.2
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
ES_JAVA_OPTS: -Xms512m -Xmx512m
|
||||
|
|
@ -73,7 +73,7 @@ services:
|
|||
hard: -1
|
||||
|
||||
libretranslate:
|
||||
image: libretranslate/libretranslate:v1.7.3
|
||||
image: libretranslate/libretranslate:v1.6.2
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- lt-data:/home/libretranslate/.local
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ body:
|
|||
Any additional technical details you may have, like logs or error traces
|
||||
value: |
|
||||
If this is happening on your own Mastodon server, please fill out those:
|
||||
- Ruby version: (from `ruby --version`, eg. v3.4.9)
|
||||
- Ruby version: (from `ruby --version`, eg. v3.4.4)
|
||||
- Node.js version: (from `node --version`, eg. v22.16.0)
|
||||
validations:
|
||||
required: false
|
||||
|
|
|
|||
2
.github/ISSUE_TEMPLATE/3.troubleshooting.yml
vendored
2
.github/ISSUE_TEMPLATE/3.troubleshooting.yml
vendored
|
|
@ -61,7 +61,7 @@ body:
|
|||
value: |
|
||||
Please at least include those informations:
|
||||
- Operating system: (eg. Ubuntu 24.04.2)
|
||||
- Ruby version: (from `ruby --version`, eg. v3.4.9)
|
||||
- Ruby version: (from `ruby --version`, eg. v3.4.4)
|
||||
- Node.js version: (from `node --version`, eg. v22.16.0)
|
||||
validations:
|
||||
required: false
|
||||
|
|
|
|||
4
.github/actions/setup-javascript/action.yml
vendored
4
.github/actions/setup-javascript/action.yml
vendored
|
|
@ -9,7 +9,7 @@ runs:
|
|||
using: 'composite'
|
||||
steps:
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
|
||||
|
|
@ -23,7 +23,7 @@ runs:
|
|||
shell: bash
|
||||
run: echo "dir=$(yarn config get cacheFolder)" >> $GITHUB_OUTPUT
|
||||
|
||||
- uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5
|
||||
- uses: actions/cache@v4
|
||||
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
|
||||
with:
|
||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
|
|
|
|||
2
.github/actions/setup-ruby/action.yml
vendored
2
.github/actions/setup-ruby/action.yml
vendored
|
|
@ -17,7 +17,7 @@ runs:
|
|||
sudo apt-get install -y libicu-dev libidn11-dev libvips42 ${{ inputs.additional-system-dependencies }}
|
||||
|
||||
- name: Set up Ruby
|
||||
uses: ruby/setup-ruby@4eb9f110bac952a8b68ecf92e3b5c7a987594ba6 # v1
|
||||
uses: ruby/setup-ruby@v1
|
||||
with:
|
||||
ruby-version: ${{ inputs.ruby-version }}
|
||||
bundler-cache: true
|
||||
|
|
|
|||
16
.github/renovate.json5
vendored
16
.github/renovate.json5
vendored
|
|
@ -5,6 +5,7 @@
|
|||
'customManagers:dockerfileVersions',
|
||||
':labels(dependencies)',
|
||||
':prConcurrentLimitNone', // Remove limit for open PRs at any time.
|
||||
':prHourlyLimit2', // Rate limit PR creation to a maximum of two per hour.
|
||||
':enableVulnerabilityAlertsWithLabel(security)',
|
||||
],
|
||||
rebaseWhen: 'conflicted',
|
||||
|
|
@ -22,6 +23,8 @@
|
|||
// Require Dependency Dashboard Approval for major version bumps of these node packages
|
||||
matchManagers: ['npm'],
|
||||
matchPackageNames: [
|
||||
'tesseract.js', // Requires code changes
|
||||
|
||||
// react-router: Requires manual upgrade
|
||||
'history',
|
||||
'react-router-dom',
|
||||
|
|
@ -113,7 +116,6 @@
|
|||
],
|
||||
matchUpdateTypes: ['major'],
|
||||
groupName: 'artifact actions (major)',
|
||||
extends: ['helpers:pinGitHubActionDigests'],
|
||||
},
|
||||
{
|
||||
// Update @types/* packages every week, with one grouped PR
|
||||
|
|
@ -154,15 +156,9 @@
|
|||
groupName: 'opentelemetry-ruby (non-major)',
|
||||
},
|
||||
{
|
||||
// The ruby portion of the Playwright group
|
||||
matchManagers: ['bundler'],
|
||||
matchPackageNames: ['playwright-ruby-client'],
|
||||
groupName: 'Playwright',
|
||||
},
|
||||
{
|
||||
// The node portion of the Playwright group
|
||||
matchManagers: ['npm'],
|
||||
matchPackageNames: ['playwright'],
|
||||
// Group Playwright Ruby & JS deps in the same PR, as they need to be in sync
|
||||
matchManagers: ['bundler', 'npm'],
|
||||
matchPackageNames: ['playwright-ruby-client', 'playwright'],
|
||||
groupName: 'Playwright',
|
||||
},
|
||||
// Add labels depending on package manager
|
||||
|
|
|
|||
26
.github/workflows/build-container-image.yml
vendored
26
.github/workflows/build-container-image.yml
vendored
|
|
@ -35,7 +35,7 @@ jobs:
|
|||
- linux/arm64
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Prepare
|
||||
env:
|
||||
|
|
@ -47,19 +47,19 @@ jobs:
|
|||
image_names=${PUSH_TO_IMAGES//$'\n'/,}
|
||||
echo "IMAGE_NAMES=${image_names%,}" >> $GITHUB_ENV
|
||||
|
||||
- uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3
|
||||
- uses: docker/setup-buildx-action@v3
|
||||
id: buildx
|
||||
|
||||
- name: Log in to Docker Hub
|
||||
if: contains(inputs.push_to_images, 'tootsuite')
|
||||
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Log in to the GitHub Container registry
|
||||
if: contains(inputs.push_to_images, 'ghcr.io')
|
||||
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
|
|
@ -67,7 +67,7 @@ jobs:
|
|||
|
||||
- name: Docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5
|
||||
uses: docker/metadata-action@v5
|
||||
if: ${{ inputs.push_to_images != '' }}
|
||||
with:
|
||||
images: ${{ inputs.push_to_images }}
|
||||
|
|
@ -76,7 +76,7 @@ jobs:
|
|||
|
||||
- name: Build and push by digest
|
||||
id: build
|
||||
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
file: ${{ inputs.file_to_build }}
|
||||
|
|
@ -100,7 +100,7 @@ jobs:
|
|||
|
||||
- name: Upload digest
|
||||
if: ${{ inputs.push_to_images != '' }}
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
# `hashFiles` is used to disambiguate between streaming and non-streaming images
|
||||
name: digests-${{ hashFiles(inputs.file_to_build) }}-${{ env.PLATFORM_PAIR }}
|
||||
|
|
@ -119,10 +119,10 @@ jobs:
|
|||
PUSH_TO_IMAGES: ${{ inputs.push_to_images }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Download digests
|
||||
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: ${{ runner.temp }}/digests
|
||||
# `hashFiles` is used to disambiguate between streaming and non-streaming images
|
||||
|
|
@ -131,25 +131,25 @@ jobs:
|
|||
|
||||
- name: Log in to Docker Hub
|
||||
if: contains(inputs.push_to_images, 'tootsuite')
|
||||
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Log in to the GitHub Container registry
|
||||
if: contains(inputs.push_to_images, 'ghcr.io')
|
||||
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5
|
||||
uses: docker/metadata-action@v5
|
||||
if: ${{ inputs.push_to_images != '' }}
|
||||
with:
|
||||
images: ${{ inputs.push_to_images }}
|
||||
|
|
|
|||
2
.github/workflows/build-push-pr.yml
vendored
2
.github/workflows/build-push-pr.yml
vendored
|
|
@ -18,7 +18,7 @@ jobs:
|
|||
steps:
|
||||
# Repository needs to be cloned so `git rev-parse` below works
|
||||
- name: Clone repository
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
uses: actions/checkout@v4
|
||||
- id: version_vars
|
||||
run: |
|
||||
echo mastodon_version_metadata=pr-${{ github.event.pull_request.number }}-$(git rev-parse --short ${{github.event.pull_request.head.sha}}) >> $GITHUB_OUTPUT
|
||||
|
|
|
|||
2
.github/workflows/build-releases.yml
vendored
2
.github/workflows/build-releases.yml
vendored
|
|
@ -16,7 +16,7 @@ jobs:
|
|||
steps:
|
||||
# Repository needs to be cloned to list branches
|
||||
- name: Clone repository
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
|
|
|
|||
4
.github/workflows/bundler-audit.yml
vendored
4
.github/workflows/bundler-audit.yml
vendored
|
|
@ -28,10 +28,10 @@ jobs:
|
|||
|
||||
steps:
|
||||
- name: Clone repository
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Ruby
|
||||
uses: ruby/setup-ruby@4eb9f110bac952a8b68ecf92e3b5c7a987594ba6 # v1
|
||||
uses: ruby/setup-ruby@v1
|
||||
with:
|
||||
bundler-cache: true
|
||||
|
||||
|
|
|
|||
5
.github/workflows/check-i18n.yml
vendored
5
.github/workflows/check-i18n.yml
vendored
|
|
@ -21,7 +21,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Ruby environment
|
||||
uses: ./.github/actions/setup-ruby
|
||||
|
|
@ -42,7 +42,8 @@ jobs:
|
|||
|
||||
- name: Check for missing strings in English YML
|
||||
run: |
|
||||
bin/i18n-tasks missing -t used -l en
|
||||
bin/i18n-tasks add-missing -l en
|
||||
git diff --exit-code
|
||||
|
||||
- name: Check for wrong string interpolations
|
||||
run: bin/i18n-tasks check-consistent-interpolations
|
||||
|
|
|
|||
49
.github/workflows/chromatic.yml
vendored
49
.github/workflows/chromatic.yml
vendored
|
|
@ -1,51 +1,31 @@
|
|||
name: 'Chromatic'
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
on:
|
||||
push:
|
||||
branches-ignore:
|
||||
- renovate/*
|
||||
- stable-*
|
||||
paths:
|
||||
- 'package.json'
|
||||
- 'yarn.lock'
|
||||
- '**/*.js'
|
||||
- '**/*.jsx'
|
||||
- '**/*.ts'
|
||||
- '**/*.tsx'
|
||||
- '**/*.css'
|
||||
- '**/*.scss'
|
||||
- '.github/workflows/chromatic.yml'
|
||||
|
||||
jobs:
|
||||
pathcheck:
|
||||
name: Check for relevant changes
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
changed: ${{ steps.filter.outputs.src }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3
|
||||
id: filter
|
||||
with:
|
||||
filters: |
|
||||
src:
|
||||
- 'package.json'
|
||||
- 'yarn.lock'
|
||||
- '**/*.js'
|
||||
- '**/*.jsx'
|
||||
- '**/*.ts'
|
||||
- '**/*.tsx'
|
||||
- '**/*.css'
|
||||
- '**/*.scss'
|
||||
- '.github/workflows/chromatic.yml'
|
||||
|
||||
chromatic:
|
||||
name: Run Chromatic
|
||||
runs-on: ubuntu-latest
|
||||
needs: pathcheck
|
||||
if: github.repository == 'mastodon/mastodon' && needs.pathcheck.outputs.changed == 'true'
|
||||
if: github.repository == 'mastodon/mastodon'
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up Javascript environment
|
||||
uses: ./.github/actions/setup-javascript
|
||||
|
||||
|
|
@ -53,10 +33,9 @@ jobs:
|
|||
run: yarn build-storybook
|
||||
|
||||
- name: Run Chromatic
|
||||
uses: chromaui/action@07791f8243f4cb2698bf4d00426baf4b2d1cb7e0 # v13
|
||||
uses: chromaui/action@v12
|
||||
with:
|
||||
# ⚠️ Make sure to configure a `CHROMATIC_PROJECT_TOKEN` repository secret
|
||||
projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
|
||||
zip: true
|
||||
storybookBuildDir: 'storybook-static'
|
||||
exitOnceUploaded: true # Exit immediately after upload
|
||||
autoAcceptChanges: 'main' # Auto-accept changes on main branch only
|
||||
|
|
|
|||
8
.github/workflows/codeql.yml
vendored
8
.github/workflows/codeql.yml
vendored
|
|
@ -31,11 +31,11 @@ jobs:
|
|||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
uses: actions/checkout@v4
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@45cbd0c69e560cd9e7cd7f8c32362050c9b7ded2 # v4
|
||||
uses: github/codeql-action/init@v3
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
|
|
@ -48,7 +48,7 @@ jobs:
|
|||
# Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@45cbd0c69e560cd9e7cd7f8c32362050c9b7ded2 # v4
|
||||
uses: github/codeql-action/autobuild@v3
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
||||
|
|
@ -61,6 +61,6 @@ jobs:
|
|||
# ./location_of_script_within_repo/buildscript.sh
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@45cbd0c69e560cd9e7cd7f8c32362050c9b7ded2 # v4
|
||||
uses: github/codeql-action/analyze@v3
|
||||
with:
|
||||
category: '/language:${{matrix.language}}'
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ jobs:
|
|||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Increase Git http.postBuffer
|
||||
# This is needed due to a bug in Ubuntu's cURL version?
|
||||
|
|
@ -24,7 +24,7 @@ jobs:
|
|||
|
||||
# Download the translation files from Crowdin
|
||||
- name: crowdin action
|
||||
uses: crowdin/github-action@b4b468cffefb50bdd99dd83e5d2eaeb63c880380 # v2
|
||||
uses: crowdin/github-action@v2
|
||||
with:
|
||||
upload_sources: false
|
||||
upload_translations: false
|
||||
|
|
@ -50,7 +50,7 @@ jobs:
|
|||
|
||||
# Create or update the pull request
|
||||
- name: Create Pull Request
|
||||
uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 # v8.1.0
|
||||
uses: peter-evans/create-pull-request@v7.0.8
|
||||
with:
|
||||
commit-message: 'New Crowdin translations'
|
||||
title: 'New Crowdin Translations for ${{ github.base_ref || github.ref_name }} (automated)'
|
||||
|
|
|
|||
6
.github/workflows/crowdin-download.yml
vendored
6
.github/workflows/crowdin-download.yml
vendored
|
|
@ -15,7 +15,7 @@ jobs:
|
|||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Increase Git http.postBuffer
|
||||
# This is needed due to a bug in Ubuntu's cURL version?
|
||||
|
|
@ -26,7 +26,7 @@ jobs:
|
|||
|
||||
# Download the translation files from Crowdin
|
||||
- name: crowdin action
|
||||
uses: crowdin/github-action@b4b468cffefb50bdd99dd83e5d2eaeb63c880380 # v2
|
||||
uses: crowdin/github-action@v2
|
||||
with:
|
||||
upload_sources: false
|
||||
upload_translations: false
|
||||
|
|
@ -52,7 +52,7 @@ jobs:
|
|||
|
||||
# Create or update the pull request
|
||||
- name: Create Pull Request
|
||||
uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 # v8
|
||||
uses: peter-evans/create-pull-request@v7
|
||||
with:
|
||||
commit-message: 'New Crowdin translations'
|
||||
title: 'New Crowdin Translations (automated)'
|
||||
|
|
|
|||
4
.github/workflows/crowdin-upload.yml
vendored
4
.github/workflows/crowdin-upload.yml
vendored
|
|
@ -23,10 +23,10 @@ jobs:
|
|||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: crowdin action
|
||||
uses: crowdin/github-action@b4b468cffefb50bdd99dd83e5d2eaeb63c880380 # v2
|
||||
uses: crowdin/github-action@v2
|
||||
with:
|
||||
upload_sources: true
|
||||
upload_translations: false
|
||||
|
|
|
|||
4
.github/workflows/format-check.yml
vendored
4
.github/workflows/format-check.yml
vendored
|
|
@ -13,10 +13,10 @@ jobs:
|
|||
|
||||
steps:
|
||||
- name: Clone repository
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Javascript environment
|
||||
uses: ./.github/actions/setup-javascript
|
||||
|
||||
- name: Check formatting
|
||||
- name: Check formatting with Prettier
|
||||
run: yarn format:check
|
||||
|
|
|
|||
17
.github/workflows/haml-lint-problem-matcher.json
vendored
Normal file
17
.github/workflows/haml-lint-problem-matcher.json
vendored
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"problemMatcher": [
|
||||
{
|
||||
"owner": "haml-lint",
|
||||
"severity": "warning",
|
||||
"pattern": [
|
||||
{
|
||||
"regexp": "^(.*):(\\d+)\\s\\[W]\\s(.*):\\s(.*)$",
|
||||
"file": 1,
|
||||
"line": 2,
|
||||
"code": 3,
|
||||
"message": 4
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
4
.github/workflows/lint-css.yml
vendored
4
.github/workflows/lint-css.yml
vendored
|
|
@ -9,6 +9,7 @@ on:
|
|||
- 'package.json'
|
||||
- 'yarn.lock'
|
||||
- '.nvmrc'
|
||||
- '.prettier*'
|
||||
- 'stylelint.config.js'
|
||||
- '**/*.css'
|
||||
- '**/*.scss'
|
||||
|
|
@ -20,6 +21,7 @@ on:
|
|||
- 'package.json'
|
||||
- 'yarn.lock'
|
||||
- '.nvmrc'
|
||||
- '.prettier*'
|
||||
- 'stylelint.config.js'
|
||||
- '**/*.css'
|
||||
- '**/*.scss'
|
||||
|
|
@ -32,7 +34,7 @@ jobs:
|
|||
|
||||
steps:
|
||||
- name: Clone repository
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Javascript environment
|
||||
uses: ./.github/actions/setup-javascript
|
||||
|
|
|
|||
5
.github/workflows/lint-haml.yml
vendored
5
.github/workflows/lint-haml.yml
vendored
|
|
@ -33,13 +33,14 @@ jobs:
|
|||
|
||||
steps:
|
||||
- name: Clone repository
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Ruby
|
||||
uses: ruby/setup-ruby@4eb9f110bac952a8b68ecf92e3b5c7a987594ba6 # v1
|
||||
uses: ruby/setup-ruby@v1
|
||||
with:
|
||||
bundler-cache: true
|
||||
|
||||
- name: Run haml-lint
|
||||
run: |
|
||||
echo "::add-matcher::.github/workflows/haml-lint-problem-matcher.json"
|
||||
bin/haml-lint --reporter github
|
||||
|
|
|
|||
4
.github/workflows/lint-js.yml
vendored
4
.github/workflows/lint-js.yml
vendored
|
|
@ -10,6 +10,7 @@ on:
|
|||
- 'yarn.lock'
|
||||
- 'tsconfig.json'
|
||||
- '.nvmrc'
|
||||
- '.prettier*'
|
||||
- 'eslint.config.mjs'
|
||||
- '**/*.js'
|
||||
- '**/*.jsx'
|
||||
|
|
@ -23,6 +24,7 @@ on:
|
|||
- 'yarn.lock'
|
||||
- 'tsconfig.json'
|
||||
- '.nvmrc'
|
||||
- '.prettier*'
|
||||
- 'eslint.config.mjs'
|
||||
- '**/*.js'
|
||||
- '**/*.jsx'
|
||||
|
|
@ -36,7 +38,7 @@ jobs:
|
|||
|
||||
steps:
|
||||
- name: Clone repository
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Javascript environment
|
||||
uses: ./.github/actions/setup-javascript
|
||||
|
|
|
|||
6
.github/workflows/lint-ruby.yml
vendored
6
.github/workflows/lint-ruby.yml
vendored
|
|
@ -35,15 +35,15 @@ jobs:
|
|||
|
||||
steps:
|
||||
- name: Clone repository
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Ruby
|
||||
uses: ruby/setup-ruby@4eb9f110bac952a8b68ecf92e3b5c7a987594ba6 # v1
|
||||
uses: ruby/setup-ruby@v1
|
||||
with:
|
||||
bundler-cache: true
|
||||
|
||||
- name: Set-up RuboCop Problem Matcher
|
||||
uses: r7kamura/rubocop-problem-matchers-action@59f1a0759f50cc2649849fd850b8487594bb5a81 # v1.2.2
|
||||
uses: r7kamura/rubocop-problem-matchers-action@v1
|
||||
|
||||
- name: Run rubocop
|
||||
run: bin/rubocop
|
||||
|
|
|
|||
2
.github/workflows/rebase-needed.yml
vendored
2
.github/workflows/rebase-needed.yml
vendored
|
|
@ -18,7 +18,7 @@ jobs:
|
|||
|
||||
steps:
|
||||
- name: Check for merge conflicts
|
||||
uses: eps1lon/actions-label-merge-conflict@1df065ebe6e3310545d4f4c4e862e43bdca146f0 # v3
|
||||
uses: eps1lon/actions-label-merge-conflict@v3
|
||||
with:
|
||||
dirtyLabel: 'rebase needed :construction:'
|
||||
repoToken: '${{ secrets.GITHUB_TOKEN }}'
|
||||
|
|
|
|||
2
.github/workflows/test-js.yml
vendored
2
.github/workflows/test-js.yml
vendored
|
|
@ -34,7 +34,7 @@ jobs:
|
|||
|
||||
steps:
|
||||
- name: Clone repository
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Javascript environment
|
||||
uses: ./.github/actions/setup-javascript
|
||||
|
|
|
|||
2
.github/workflows/test-migrations.yml
vendored
2
.github/workflows/test-migrations.yml
vendored
|
|
@ -72,7 +72,7 @@ jobs:
|
|||
BUNDLE_RETRY: 3
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Ruby environment
|
||||
uses: ./.github/actions/setup-ruby
|
||||
|
|
|
|||
123
.github/workflows/test-ruby.yml
vendored
123
.github/workflows/test-ruby.yml
vendored
|
|
@ -32,7 +32,7 @@ jobs:
|
|||
SECRET_KEY_BASE_DUMMY: 1
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Ruby environment
|
||||
uses: ./.github/actions/setup-ruby
|
||||
|
|
@ -43,7 +43,7 @@ jobs:
|
|||
onlyProduction: 'true'
|
||||
|
||||
- name: Cache assets from compilation
|
||||
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
public/assets
|
||||
|
|
@ -65,7 +65,7 @@ jobs:
|
|||
run: |
|
||||
tar --exclude={"*.br","*.gz"} -zcf artifacts.tar.gz public/assets public/packs* tmp/cache/vite/last-build*.json
|
||||
|
||||
- uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: matrix.mode == 'test'
|
||||
with:
|
||||
path: |-
|
||||
|
|
@ -128,9 +128,9 @@ jobs:
|
|||
- '3.3'
|
||||
- '.ruby-version'
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: './'
|
||||
name: ${{ github.sha }}
|
||||
|
|
@ -151,7 +151,7 @@ jobs:
|
|||
bin/flatware fan bin/rails db:test:prepare
|
||||
|
||||
- name: Cache RSpec persistence file
|
||||
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
tmp/rspec/examples.txt
|
||||
|
|
@ -167,12 +167,99 @@ jobs:
|
|||
|
||||
- name: Upload coverage reports to Codecov
|
||||
if: matrix.ruby-version == '.ruby-version'
|
||||
uses: codecov/codecov-action@671740ac38dd9b0130fbe1cec585b89eea48d3de # v5
|
||||
uses: codecov/codecov-action@v5
|
||||
with:
|
||||
files: coverage/lcov/*.lcov
|
||||
env:
|
||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||
|
||||
test-imagemagick:
|
||||
name: ImageMagick tests
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
needs:
|
||||
- build
|
||||
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:14-alpine
|
||||
env:
|
||||
POSTGRES_PASSWORD: postgres
|
||||
POSTGRES_USER: postgres
|
||||
options: >-
|
||||
--health-cmd pg_isready
|
||||
--health-interval 10ms
|
||||
--health-timeout 3s
|
||||
--health-retries 50
|
||||
ports:
|
||||
- 5432:5432
|
||||
|
||||
redis:
|
||||
image: redis:7-alpine
|
||||
options: >-
|
||||
--health-cmd "redis-cli ping"
|
||||
--health-interval 10ms
|
||||
--health-timeout 3s
|
||||
--health-retries 50
|
||||
ports:
|
||||
- 6379:6379
|
||||
|
||||
env:
|
||||
DB_HOST: localhost
|
||||
DB_USER: postgres
|
||||
DB_PASS: postgres
|
||||
COVERAGE: ${{ matrix.ruby-version == '.ruby-version' }}
|
||||
RAILS_ENV: test
|
||||
ALLOW_NOPAM: true
|
||||
PAM_ENABLED: true
|
||||
PAM_DEFAULT_SERVICE: pam_test
|
||||
PAM_CONTROLLED_SERVICE: pam_test_controlled
|
||||
OIDC_ENABLED: true
|
||||
OIDC_SCOPE: read
|
||||
SAML_ENABLED: true
|
||||
CAS_ENABLED: true
|
||||
BUNDLE_WITH: 'pam_authentication test'
|
||||
GITHUB_RSPEC: ${{ matrix.ruby-version == '.ruby-version' && github.event.pull_request && 'true' }}
|
||||
MASTODON_USE_LIBVIPS: false
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
ruby-version:
|
||||
- '3.2'
|
||||
- '3.3'
|
||||
- '.ruby-version'
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: './'
|
||||
name: ${{ github.sha }}
|
||||
|
||||
- name: Expand archived asset artifacts
|
||||
run: |
|
||||
tar xvzf artifacts.tar.gz
|
||||
|
||||
- name: Set up Ruby environment
|
||||
uses: ./.github/actions/setup-ruby
|
||||
with:
|
||||
ruby-version: ${{ matrix.ruby-version}}
|
||||
additional-system-dependencies: ffmpeg imagemagick libpam-dev
|
||||
|
||||
- name: Load database schema
|
||||
run: './bin/rails db:create db:schema:load db:seed'
|
||||
|
||||
- run: bin/rspec --tag attachment_processing
|
||||
|
||||
- name: Upload coverage reports to Codecov
|
||||
if: matrix.ruby-version == '.ruby-version'
|
||||
uses: codecov/codecov-action@v5
|
||||
with:
|
||||
files: coverage/lcov/mastodon.lcov
|
||||
env:
|
||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||
|
||||
test-e2e:
|
||||
name: End to End testing
|
||||
runs-on: ubuntu-latest
|
||||
|
|
@ -222,9 +309,9 @@ jobs:
|
|||
- '.ruby-version'
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: './'
|
||||
name: ${{ github.sha }}
|
||||
|
|
@ -247,7 +334,7 @@ jobs:
|
|||
|
||||
- name: Cache Playwright Chromium browser
|
||||
id: playwright-cache
|
||||
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.cache/ms-playwright
|
||||
key: playwright-browsers-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
|
||||
|
|
@ -263,14 +350,14 @@ jobs:
|
|||
- run: bin/rspec spec/system --tag streaming --tag js
|
||||
|
||||
- name: Archive logs
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
|
||||
uses: actions/upload-artifact@v4
|
||||
if: failure()
|
||||
with:
|
||||
name: e2e-logs-${{ matrix.ruby-version }}
|
||||
path: log/
|
||||
|
||||
- name: Archive test screenshots
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
|
||||
uses: actions/upload-artifact@v4
|
||||
if: failure()
|
||||
with:
|
||||
name: e2e-screenshots-${{ matrix.ruby-version }}
|
||||
|
|
@ -352,17 +439,17 @@ jobs:
|
|||
- '3.3'
|
||||
- '.ruby-version'
|
||||
search-image:
|
||||
- docker.elastic.co/elasticsearch/elasticsearch:7.17.29
|
||||
- docker.elastic.co/elasticsearch/elasticsearch:7.17.13
|
||||
include:
|
||||
- ruby-version: '.ruby-version'
|
||||
search-image: docker.elastic.co/elasticsearch/elasticsearch:8.19.2
|
||||
search-image: docker.elastic.co/elasticsearch/elasticsearch:8.10.2
|
||||
- ruby-version: '.ruby-version'
|
||||
search-image: opensearchproject/opensearch:2
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: './'
|
||||
name: ${{ github.sha }}
|
||||
|
|
@ -382,14 +469,14 @@ jobs:
|
|||
- run: bin/rspec --tag search
|
||||
|
||||
- name: Archive logs
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
|
||||
uses: actions/upload-artifact@v4
|
||||
if: failure()
|
||||
with:
|
||||
name: test-search-logs-${{ matrix.ruby-version }}
|
||||
path: log/
|
||||
|
||||
- name: Archive test screenshots
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
|
||||
uses: actions/upload-artifact@v4
|
||||
if: failure()
|
||||
with:
|
||||
name: test-search-screenshots
|
||||
|
|
|
|||
|
|
@ -10,6 +10,6 @@ linters:
|
|||
MiddleDot:
|
||||
enabled: true
|
||||
LineLength:
|
||||
max: 240 # Override default value of 80 inherited from rubocop
|
||||
max: 300
|
||||
ViewLength:
|
||||
max: 200 # Override default value of 100 inherited from rubocop
|
||||
|
|
|
|||
|
|
@ -1,92 +0,0 @@
|
|||
{
|
||||
"$schema": "./node_modules/oxfmt/configuration_schema.json",
|
||||
"singleQuote": true,
|
||||
"jsxSingleQuote": true,
|
||||
"printWidth": 80,
|
||||
"ignorePatterns": [
|
||||
"/tmp",
|
||||
"/coverage",
|
||||
"/public/assets",
|
||||
"/public/emoji",
|
||||
"/public/packs",
|
||||
"/public/packs-test",
|
||||
"/public/system",
|
||||
"/public/vite*",
|
||||
|
||||
"*.html",
|
||||
"docker-compose.override.yml",
|
||||
|
||||
// Ignore config YAML files that include ERB/ruby code
|
||||
"config/email.yml",
|
||||
|
||||
// Vendored CSS
|
||||
"app/javascript/styles/mastodon/reset.scss",
|
||||
|
||||
// Automatically generated
|
||||
"/app/javascript/mastodon/features/emoji/emoji_map.json",
|
||||
"/app/javascript/mastodon/features/emoji/emoji_data.json",
|
||||
"AUTHORS.md",
|
||||
"/app/javascript/mastodon/locales/*.json",
|
||||
"/config/locales",
|
||||
".storybook/static/mockServiceWorker.js",
|
||||
|
||||
// do not reformat JS files as this will change too many files and cause merge conflicts with open PRs and forks
|
||||
"app/javascript/**/*.js",
|
||||
"app/javascript/**/*.jsx",
|
||||
"streaming/**/*.js"
|
||||
],
|
||||
"experimentalSortPackageJson": false,
|
||||
"experimentalSortImports": {
|
||||
"groups": [
|
||||
["builtin"],
|
||||
["react"],
|
||||
["react-intl"],
|
||||
["react-utils"],
|
||||
["redux"],
|
||||
["external", "type-external"],
|
||||
["internal", "type-internal"],
|
||||
["mastodon-internals"],
|
||||
["parent", "type-parent"],
|
||||
["sibling", "type-sibling", "index", "type-index"],
|
||||
["side_effect"]
|
||||
],
|
||||
"customGroups": [
|
||||
{
|
||||
"groupName": "react",
|
||||
"elementNamePattern": [
|
||||
"react",
|
||||
"react-dom",
|
||||
"react-dom/client",
|
||||
"prop-types"
|
||||
]
|
||||
},
|
||||
{
|
||||
"groupName": "react-intl",
|
||||
"elementNamePattern": ["react-intl", "intl-messageformat"]
|
||||
},
|
||||
{
|
||||
"groupName": "react-utils",
|
||||
"elementNamePattern": [
|
||||
"classnames",
|
||||
"react-helmet",
|
||||
"react-router",
|
||||
"react-router-dom"
|
||||
]
|
||||
},
|
||||
{
|
||||
"groupName": "redux",
|
||||
"elementNamePattern": [
|
||||
"immutable",
|
||||
"@reduxjs/toolkit",
|
||||
"react-redux",
|
||||
"react-immutable-proptypes",
|
||||
"react-immutable-pure-component"
|
||||
]
|
||||
},
|
||||
{
|
||||
"groupName": "mastodon-internals",
|
||||
"elementNamePattern": ["mastodon/**", "@/**"]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
86
.prettierignore
Normal file
86
.prettierignore
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
# See https://help.github.com/articles/ignoring-files for more about ignoring files.
|
||||
#
|
||||
# If you find yourself ignoring temporary files generated by your text editor
|
||||
# or operating system, you probably want to add a global ignore instead:
|
||||
# git config --global core.excludesfile '~/.gitignore_global'
|
||||
|
||||
# Ignore bundler config and downloaded libraries.
|
||||
/.bundle
|
||||
/vendor/bundle
|
||||
|
||||
# Ignore the default SQLite database.
|
||||
/db/*.sqlite3
|
||||
/db/*.sqlite3-journal
|
||||
|
||||
# Ignore all logfiles and tempfiles.
|
||||
.eslintcache
|
||||
/log/*
|
||||
!/log/.keep
|
||||
/tmp
|
||||
/coverage
|
||||
.env
|
||||
.env.production
|
||||
.env.development
|
||||
/node_modules/
|
||||
/build/
|
||||
|
||||
# Ignore Vagrant files
|
||||
.vagrant/
|
||||
|
||||
# Ignore IDE files
|
||||
.vscode/
|
||||
.idea/
|
||||
|
||||
# Ignore postgres + redis + elasticsearch volume optionally created by docker-compose
|
||||
/postgres
|
||||
/postgres14
|
||||
/redis
|
||||
/elasticsearch
|
||||
|
||||
# Ignore Apple files
|
||||
.DS_Store
|
||||
|
||||
# Ignore vim files
|
||||
*~
|
||||
*.swp
|
||||
|
||||
# Ignore log files
|
||||
*.log
|
||||
|
||||
# Ignore Docker option files
|
||||
docker-compose.override.yml
|
||||
|
||||
# Ignore public
|
||||
/public/assets
|
||||
/public/emoji
|
||||
/public/packs
|
||||
/public/packs-test
|
||||
/public/system
|
||||
/public/vite*
|
||||
|
||||
# Ignore emoji map file
|
||||
/app/javascript/mastodon/features/emoji/emoji_map.json
|
||||
/app/javascript/mastodon/features/emoji/emoji_data.json
|
||||
|
||||
# Ignore locale files
|
||||
/app/javascript/mastodon/locales/*.json
|
||||
/config/locales
|
||||
|
||||
# Ignore vendored CSS reset
|
||||
app/javascript/styles/mastodon/reset.scss
|
||||
|
||||
# Ignore Javascript pending https://github.com/mastodon/mastodon/pull/23631
|
||||
*.js
|
||||
*.jsx
|
||||
|
||||
# Ignore HTML till cleaned and included in CI
|
||||
*.html
|
||||
|
||||
# Ignore the generated AUTHORS.md
|
||||
AUTHORS.md
|
||||
|
||||
# Process a few selected JS files
|
||||
!lint-staged.config.js
|
||||
|
||||
# Ignore config YAML files that include ERB/ruby code prettier does not understand
|
||||
/config/email.yml
|
||||
4
.prettierrc.js
Normal file
4
.prettierrc.js
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
module.exports = {
|
||||
singleQuote: true,
|
||||
jsxSingleQuote: true
|
||||
};
|
||||
|
|
@ -4,6 +4,3 @@ Layout/FirstHashElementIndentation:
|
|||
|
||||
Layout/LineLength:
|
||||
Max: 300 # Default of 120 causes a duplicate entry in generated todo file
|
||||
|
||||
Layout/MultilineMethodCallIndentation:
|
||||
EnforcedStyle: indented
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
3.4.9
|
||||
3.4.7
|
||||
|
|
|
|||
|
|
@ -27,12 +27,11 @@ const config: StorybookConfig = {
|
|||
'oops.gif',
|
||||
'oops.png',
|
||||
].map((path) => ({ from: `../public/${path}`, to: `/${path}` })),
|
||||
{ from: '../app/javascript/images/logo.svg', to: '/custom-emoji/logo.svg' },
|
||||
],
|
||||
viteFinal(config) {
|
||||
// For an unknown reason, Storybook does not use the root
|
||||
// from the Vite config so we need to set it manually.
|
||||
config.root = resolve(import.meta.dirname, '../app/javascript');
|
||||
config.root = resolve(__dirname, '../app/javascript');
|
||||
return config;
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,8 +0,0 @@
|
|||
export const modes = {
|
||||
darkTheme: {
|
||||
theme: 'dark',
|
||||
},
|
||||
lightTheme: {
|
||||
theme: 'light',
|
||||
},
|
||||
} as const;
|
||||
|
|
@ -1,2 +1,2 @@
|
|||
<html class="no-reduce-motion" data-color-scheme="light">
|
||||
<html class="no-reduce-motion">
|
||||
</html>
|
||||
|
|
@ -11,19 +11,14 @@ import type { Preview } from '@storybook/react-vite';
|
|||
import { initialize, mswLoader } from 'msw-storybook-addon';
|
||||
import { action } from 'storybook/actions';
|
||||
|
||||
import {
|
||||
importCustomEmojiData,
|
||||
importLegacyShortcodes,
|
||||
importEmojiData,
|
||||
} from '@/mastodon/features/emoji/loader';
|
||||
import type { LocaleData } from '@/mastodon/locales';
|
||||
import { reducerWithInitialState } from '@/mastodon/reducers';
|
||||
import { defaultMiddleware } from '@/mastodon/store/store';
|
||||
import { mockHandlers, unhandledRequestHandler } from '@/testing/api';
|
||||
|
||||
import { modes } from './modes';
|
||||
|
||||
import '../app/javascript/styles/application.scss';
|
||||
// If you want to run the dark theme during development,
|
||||
// you can change the below to `/application.scss`
|
||||
import '../app/javascript/styles/mastodon-light.scss';
|
||||
import './styles.css';
|
||||
|
||||
const localeFiles = import.meta.glob('@/mastodon/locales/*.json', {
|
||||
|
|
@ -50,46 +45,17 @@ const preview: Preview = {
|
|||
dynamicTitle: true,
|
||||
},
|
||||
},
|
||||
theme: {
|
||||
description: 'Theme for the story',
|
||||
toolbar: {
|
||||
title: 'Theme',
|
||||
icon: 'circlehollow',
|
||||
items: [{ value: 'light' }, { value: 'dark' }],
|
||||
dynamicTitle: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
initialGlobals: {
|
||||
locale: 'en',
|
||||
theme: 'light',
|
||||
},
|
||||
decorators: [
|
||||
(Story, { parameters, globals, args, argTypes }) => {
|
||||
(Story, { parameters, globals, args }) => {
|
||||
// Get the locale from the global toolbar
|
||||
// and merge it with any parameters or args state.
|
||||
const { locale } = globals as { locale: string };
|
||||
const { state = {} } = parameters;
|
||||
|
||||
const argsState: Record<string, unknown> = {};
|
||||
for (const [key, value] of Object.entries(args)) {
|
||||
const argType = argTypes[key];
|
||||
if (argType?.reduxPath) {
|
||||
const reduxPath = Array.isArray(argType.reduxPath)
|
||||
? argType.reduxPath.map((p) => p.toString())
|
||||
: argType.reduxPath.split('.');
|
||||
|
||||
reduxPath.reduce((acc, key, i) => {
|
||||
if (acc[key] === undefined) {
|
||||
acc[key] = {};
|
||||
}
|
||||
if (i === reduxPath.length - 1) {
|
||||
acc[key] = value;
|
||||
}
|
||||
return acc[key] as Record<string, unknown>;
|
||||
}, argsState);
|
||||
}
|
||||
}
|
||||
const { state: argsState = {} } = args;
|
||||
|
||||
const reducer = reducerWithInitialState(
|
||||
{
|
||||
|
|
@ -98,7 +64,7 @@ const preview: Preview = {
|
|||
},
|
||||
},
|
||||
state as Record<string, unknown>,
|
||||
argsState,
|
||||
argsState as Record<string, unknown>,
|
||||
);
|
||||
|
||||
const store = configureStore({
|
||||
|
|
@ -145,13 +111,6 @@ const preview: Preview = {
|
|||
</IntlProvider>
|
||||
);
|
||||
},
|
||||
(Story, { globals }) => {
|
||||
const theme = (globals.theme as string) || 'light';
|
||||
useEffect(() => {
|
||||
document.body.setAttribute('data-color-scheme', theme);
|
||||
}, [theme]);
|
||||
return <Story />;
|
||||
},
|
||||
(Story) => (
|
||||
<MemoryRouter>
|
||||
<Story />
|
||||
|
|
@ -168,12 +127,7 @@ const preview: Preview = {
|
|||
</MemoryRouter>
|
||||
),
|
||||
],
|
||||
loaders: [
|
||||
mswLoader,
|
||||
importCustomEmojiData,
|
||||
importLegacyShortcodes,
|
||||
({ globals: { locale } }) => importEmojiData(locale as string),
|
||||
],
|
||||
loaders: [mswLoader],
|
||||
parameters: {
|
||||
layout: 'centered',
|
||||
|
||||
|
|
@ -198,13 +152,6 @@ const preview: Preview = {
|
|||
msw: {
|
||||
handlers: mockHandlers,
|
||||
},
|
||||
|
||||
chromatic: {
|
||||
modes: {
|
||||
dark: modes.darkTheme,
|
||||
light: modes.lightTheme,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
* - Please do NOT modify this file.
|
||||
*/
|
||||
|
||||
const PACKAGE_VERSION = '2.12.1'
|
||||
const PACKAGE_VERSION = '2.11.3'
|
||||
const INTEGRITY_CHECKSUM = '4db4a41e972cec1b64cc569c66952d82'
|
||||
const IS_MOCKED_RESPONSE = Symbol('isMockedResponse')
|
||||
const activeClientIds = new Set()
|
||||
|
|
@ -205,7 +205,6 @@ async function resolveMainClient(event) {
|
|||
* @param {FetchEvent} event
|
||||
* @param {Client | undefined} client
|
||||
* @param {string} requestId
|
||||
* @param {number} requestInterceptedAt
|
||||
* @returns {Promise<Response>}
|
||||
*/
|
||||
async function getResponse(event, client, requestId, requestInterceptedAt) {
|
||||
|
|
|
|||
|
|
@ -1,20 +1,7 @@
|
|||
// The addon package.json incorrectly exports types, so we need to override them here.
|
||||
|
||||
import type { RootState } from '@/mastodon/store';
|
||||
|
||||
// See: https://github.com/storybookjs/storybook/blob/v9.0.4/code/addons/vitest/package.json#L70-L76
|
||||
declare module '@storybook/addon-vitest/vitest-plugin' {
|
||||
export * from '@storybook/addon-vitest/dist/vitest-plugin/index';
|
||||
}
|
||||
|
||||
type RootPathKeys = keyof RootState;
|
||||
|
||||
declare module 'storybook/internal/csf' {
|
||||
export interface InputType {
|
||||
reduxPath?:
|
||||
| `${RootPathKeys}.${string}`
|
||||
| [RootPathKeys, ...(string | number)[]];
|
||||
}
|
||||
}
|
||||
|
||||
export {};
|
||||
13
.yarn/patches/babel-plugin-lodash-npm-3.3.4-c7161075b6.patch
Normal file
13
.yarn/patches/babel-plugin-lodash-npm-3.3.4-c7161075b6.patch
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
diff --git a/lib/index.js b/lib/index.js
|
||||
index 16ed6be8be8f555cc99096c2ff60954b42dc313d..d009c069770d066ad0db7ad02de1ea473a29334e 100644
|
||||
--- a/lib/index.js
|
||||
+++ b/lib/index.js
|
||||
@@ -99,7 +99,7 @@ function lodash(_ref) {
|
||||
|
||||
var node = _ref3;
|
||||
|
||||
- if ((0, _types.isModuleDeclaration)(node)) {
|
||||
+ if ((0, _types.isImportDeclaration)(node) || (0, _types.isExportDeclaration)(node)) {
|
||||
isModule = true;
|
||||
break;
|
||||
}
|
||||
|
|
@ -538,7 +538,7 @@ and provided thanks to the work of the following contributors:
|
|||
* [Drew Schuster](mailto:dtschust@gmail.com)
|
||||
* [Dryusdan](mailto:dryusdan@dryusdan.fr)
|
||||
* [Eai](mailto:eai@mizle.net)
|
||||
* [Eashwar Ranganathan](mailto:eashwar@eashwar.com)
|
||||
* [Eashwar Ranganathan](mailto:eranganathan@lyft.com)
|
||||
* [Ed Knutson](mailto:knutsoned@gmail.com)
|
||||
* [Elizabeth Martín Campos](mailto:me@elizabeth.sh)
|
||||
* [Elizabeth Myers](mailto:elizabeth@interlinked.me)
|
||||
|
|
|
|||
1172
CHANGELOG.md
1172
CHANGELOG.md
File diff suppressed because it is too large
Load Diff
|
|
@ -11,8 +11,6 @@ You can contribute in the following ways:
|
|||
|
||||
Please review the org-level [contribution guidelines] for high-level acceptance
|
||||
criteria guidance and the [DEVELOPMENT] guide for environment-specific details.
|
||||
You should also read the project's [AI Contribution Policy] to understand how we approach
|
||||
AI-assisted contributions.
|
||||
|
||||
## API Changes and Additions
|
||||
|
||||
|
|
@ -43,7 +41,7 @@ reviewed and merged into the codebase.
|
|||
|
||||
Our time is limited and PRs making large, unsolicited changes are unlikely to
|
||||
get a response. Changes which link to an existing confirmed issue, or which come
|
||||
from a "help wanted" issue or other request, are more likely to be reviewed.
|
||||
from a "help wanted" issue or other request are more likely to be reviewed.
|
||||
|
||||
The smaller and more narrowly focused the changes in a PR are, the easier they
|
||||
are to review and potentially merge. If the change only makes sense in some
|
||||
|
|
@ -91,4 +89,3 @@ and API docs. Improvements are made via PRs to the [documentation repository].
|
|||
[keepachangelog]: https://keepachangelog.com/en/1.0.0/
|
||||
[Mastodon documentation]: https://docs.joinmastodon.org
|
||||
[SECURITY]: SECURITY.md
|
||||
[AI Contribution Policy]: https://github.com/mastodon/.github/blob/main/AI_POLICY.md
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ ARG BASE_REGISTRY="docker.io"
|
|||
|
||||
# Ruby image to use for base image, change with [--build-arg RUBY_VERSION="3.4.x"]
|
||||
# renovate: datasource=docker depName=docker.io/ruby
|
||||
ARG RUBY_VERSION="3.4.9"
|
||||
ARG RUBY_VERSION="3.4.7"
|
||||
# # Node.js version to use in base image, change with [--build-arg NODE_MAJOR_VERSION="22"]
|
||||
# renovate: datasource=node-version depName=node
|
||||
ARG NODE_MAJOR_VERSION="24"
|
||||
|
|
@ -70,6 +70,8 @@ ENV \
|
|||
PATH="${PATH}:/opt/ruby/bin:/opt/mastodon/bin" \
|
||||
# Optimize jemalloc 5.x performance
|
||||
MALLOC_CONF="narenas:2,background_thread:true,thp:never,dirty_decay_ms:1000,muzzy_decay_ms:0" \
|
||||
# Enable libvips, should not be changed
|
||||
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
|
||||
MASTODON_SIDEKIQ_READY_FILENAME=sidekiq_process_has_started_and_will_begin_processing_jobs
|
||||
|
||||
|
|
@ -181,7 +183,7 @@ FROM build AS libvips
|
|||
|
||||
# libvips version to compile, change with [--build-arg VIPS_VERSION="8.15.2"]
|
||||
# renovate: datasource=github-releases depName=libvips packageName=libvips/libvips
|
||||
ARG VIPS_VERSION=8.18.1
|
||||
ARG VIPS_VERSION=8.17.3
|
||||
# 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
|
||||
|
||||
|
|
|
|||
|
|
@ -13,8 +13,7 @@
|
|||
- [FEP-f1d5: NodeInfo in Fediverse Software](https://codeberg.org/fediverse/fep/src/branch/main/fep/f1d5/fep-f1d5.md)
|
||||
- [FEP-8fcf: Followers collection synchronization across servers](https://codeberg.org/fediverse/fep/src/branch/main/fep/8fcf/fep-8fcf.md)
|
||||
- [FEP-5feb: Search indexing consent for actors](https://codeberg.org/fediverse/fep/src/branch/main/fep/5feb/fep-5feb.md)
|
||||
- [FEP-044f: Consent-respecting quote posts](https://codeberg.org/fediverse/fep/src/branch/main/fep/044f/fep-044f.md)
|
||||
- [FEP-3b86: Activity Intents](https://codeberg.org/fediverse/fep/src/branch/main/fep/3b86/fep-3b86.md): offer handlers for `Object` and `Create` (with support for the `content` parameter only), has support for the `Follow`, `Announce`, `Like` and `Object` intents
|
||||
- [FEP-044f: Consent-respecting quote posts](https://codeberg.org/fediverse/fep/src/branch/main/fep/044f/fep-044f.md): partial support for incoming quote-posts
|
||||
|
||||
## ActivityPub in Mastodon
|
||||
|
||||
|
|
@ -53,8 +52,8 @@ Mastodon requires all `POST` requests to be signed, and MAY require `GET` reques
|
|||
## Size limits
|
||||
|
||||
Mastodon imposes a few hard limits on federated content.
|
||||
These limits are intended to be very generous and way above what the Mastodon user experience is optimized for, so as to accommodate future changes and unusual or unforeseen usage patterns, while still providing some limits for performance reasons.
|
||||
The following table summarizes those limits.
|
||||
These limits are intended to be very generous and way above what the Mastodon user experience is optimized for, so as to accomodate future changes and unusual or unforeseen usage patterns, while still providing some limits for performance reasons.
|
||||
The following table attempts to summary those limits.
|
||||
|
||||
| Limited property | Size limit | Consequence of exceeding the limit |
|
||||
| ------------------------------------------------------------- | ---------- | ---------------------------------- |
|
||||
|
|
@ -68,6 +67,3 @@ The following table summarizes those limits.
|
|||
| Account `attributionDomains` | 256 | List will be truncated |
|
||||
| Account aliases (actor `alsoKnownAs`) | 256 | List will be truncated |
|
||||
| Custom emoji shortcode (`Emoji` `name`) | 2048 | Emoji will be rejected |
|
||||
| Media and avatar/header descriptions (`name`/`summary`) | 1500 | Description will be truncated |
|
||||
| Collection name (`FeaturedCollection` `name`) | 256 | Name will be truncated |
|
||||
| Collection description (`FeaturedCollection` `summary`) | 2048 | Description will be truncated |
|
||||
|
|
|
|||
47
Gemfile
47
Gemfile
|
|
@ -5,7 +5,7 @@ ruby '>= 3.2.0', '< 3.5.0'
|
|||
|
||||
gem 'propshaft'
|
||||
gem 'puma', '~> 7.0'
|
||||
gem 'rails', '~> 8.1.0'
|
||||
gem 'rails', '~> 8.0'
|
||||
gem 'thor', '~> 1.2'
|
||||
|
||||
gem 'dotenv'
|
||||
|
|
@ -13,7 +13,7 @@ gem 'haml-rails', '~>3.0'
|
|||
gem 'pg', '~> 1.5'
|
||||
gem 'pghero'
|
||||
|
||||
gem 'aws-sdk-core', require: false
|
||||
gem 'aws-sdk-core', '< 3.216.0', require: false # TODO: https://github.com/mastodon/mastodon/pull/34173#issuecomment-2733378873
|
||||
gem 'aws-sdk-s3', '~> 1.123', require: false
|
||||
gem 'blurhash', '~> 0.1'
|
||||
gem 'fog-core', '<= 2.6.0'
|
||||
|
|
@ -24,11 +24,11 @@ gem 'ruby-vips', '~> 2.2', require: false
|
|||
|
||||
gem 'active_model_serializers', '~> 0.10'
|
||||
gem 'addressable', '~> 2.8'
|
||||
gem 'bootsnap', require: false
|
||||
gem 'bootsnap', '~> 1.18.0', require: false
|
||||
gem 'browser'
|
||||
gem 'charlock_holmes', '~> 0.7.7'
|
||||
gem 'chewy'
|
||||
gem 'devise'
|
||||
gem 'chewy', '~> 7.3'
|
||||
gem 'devise', '~> 4.9'
|
||||
gem 'devise-two-factor'
|
||||
|
||||
group :pam_authentication, optional: true do
|
||||
|
|
@ -40,7 +40,7 @@ gem 'net-ldap', '~> 0.18'
|
|||
gem 'omniauth', '~> 2.0'
|
||||
gem 'omniauth-cas', '~> 3.0.0.beta.1'
|
||||
gem 'omniauth_openid_connect', '~> 0.8.0'
|
||||
gem 'omniauth-rails_csrf_protection', '~> 2.0'
|
||||
gem 'omniauth-rails_csrf_protection', '~> 1.0'
|
||||
gem 'omniauth-saml', '~> 2.0'
|
||||
|
||||
gem 'color_diff', '~> 0.1'
|
||||
|
|
@ -55,7 +55,7 @@ gem 'hiredis-client'
|
|||
gem 'htmlentities', '~> 4.3'
|
||||
gem 'http', '~> 5.3.0'
|
||||
gem 'http_accept_language', '~> 2.1'
|
||||
gem 'httplog', '~> 1.8.0', require: false
|
||||
gem 'httplog', '~> 1.7.0', require: false
|
||||
gem 'i18n'
|
||||
gem 'idn-ruby', require: 'idn'
|
||||
gem 'inline_svg'
|
||||
|
|
@ -67,10 +67,11 @@ gem 'mario-redis-lock', '~> 1.2', require: 'redis_lock'
|
|||
gem 'mime-types', '~> 3.7.0', require: 'mime/types/columnar'
|
||||
gem 'mutex_m'
|
||||
gem 'nokogiri', '~> 1.15'
|
||||
gem 'oj', '~> 3.14'
|
||||
gem 'ox', '~> 2.14'
|
||||
gem 'parslet'
|
||||
gem 'premailer-rails'
|
||||
gem 'public_suffix', '~> 7.0'
|
||||
gem 'public_suffix', '~> 6.0'
|
||||
gem 'pundit', '~> 2.3'
|
||||
gem 'rack-attack', '~> 6.6'
|
||||
gem 'rack-cors', require: 'rack/cors'
|
||||
|
|
@ -95,28 +96,27 @@ gem 'tzinfo-data', '~> 1.2023'
|
|||
gem 'webauthn', '~> 3.0'
|
||||
gem 'webpush', github: 'mastodon/webpush', ref: '9631ac63045cfabddacc69fc06e919b4c13eb913'
|
||||
|
||||
gem 'json'
|
||||
gem 'json-ld'
|
||||
gem 'json-ld-preloaded', '~> 3.2'
|
||||
gem 'rdf-normalize', '~> 0.5'
|
||||
|
||||
gem 'prometheus_exporter', '~> 2.2', require: false
|
||||
|
||||
gem 'opentelemetry-api', '~> 1.8.0'
|
||||
gem 'opentelemetry-api', '~> 1.7.0'
|
||||
|
||||
group :opentelemetry do
|
||||
gem 'opentelemetry-exporter-otlp', '~> 0.32.0', require: false
|
||||
gem 'opentelemetry-exporter-otlp', '~> 0.31.0', require: false
|
||||
gem 'opentelemetry-instrumentation-active_job', '~> 0.10.0', require: false
|
||||
gem 'opentelemetry-instrumentation-active_model_serializers', '~> 0.24.0', require: false
|
||||
gem 'opentelemetry-instrumentation-concurrent_ruby', '~> 0.24.0', require: false
|
||||
gem 'opentelemetry-instrumentation-excon', '~> 0.28.0', require: false
|
||||
gem 'opentelemetry-instrumentation-faraday', '~> 0.32.0', require: false
|
||||
gem 'opentelemetry-instrumentation-http', '~> 0.29.0', require: false
|
||||
gem 'opentelemetry-instrumentation-http_client', '~> 0.28.0', require: false
|
||||
gem 'opentelemetry-instrumentation-net_http', '~> 0.28.0', require: false
|
||||
gem 'opentelemetry-instrumentation-pg', '~> 0.35.0', require: false
|
||||
gem 'opentelemetry-instrumentation-rack', '~> 0.30.0', require: false
|
||||
gem 'opentelemetry-instrumentation-rails', '~> 0.40.0', require: false
|
||||
gem 'opentelemetry-instrumentation-excon', '~> 0.26.0', require: false
|
||||
gem 'opentelemetry-instrumentation-faraday', '~> 0.30.0', require: false
|
||||
gem 'opentelemetry-instrumentation-http', '~> 0.27.0', require: false
|
||||
gem 'opentelemetry-instrumentation-http_client', '~> 0.26.0', require: false
|
||||
gem 'opentelemetry-instrumentation-net_http', '~> 0.26.0', require: false
|
||||
gem 'opentelemetry-instrumentation-pg', '~> 0.32.0', require: false
|
||||
gem 'opentelemetry-instrumentation-rack', '~> 0.29.0', require: false
|
||||
gem 'opentelemetry-instrumentation-rails', '~> 0.39.0', require: false
|
||||
gem 'opentelemetry-instrumentation-redis', '~> 0.28.0', require: false
|
||||
gem 'opentelemetry-instrumentation-sidekiq', '~> 0.28.0', require: false
|
||||
gem 'opentelemetry-sdk', '~> 1.4', require: false
|
||||
|
|
@ -129,13 +129,16 @@ group :test do
|
|||
# Adds RSpec Error/Warning annotations to GitHub PRs on the Files tab
|
||||
gem 'rspec-github', '~> 3.0', require: false
|
||||
|
||||
# RSpec helpers for email specs
|
||||
gem 'email_spec'
|
||||
|
||||
# Extra RSpec extension methods and helpers for sidekiq
|
||||
gem 'rspec-sidekiq', '~> 5.0'
|
||||
|
||||
# Browser integration testing
|
||||
gem 'capybara', '~> 3.39'
|
||||
gem 'capybara-playwright-driver'
|
||||
gem 'playwright-ruby-client', '1.57.1', require: false # Pinning the exact version as it needs to be kept in sync with the installed npm package
|
||||
gem 'playwright-ruby-client', '1.55.0', require: false # Pinning the exact version as it needs to be kept in sync with the installed npm package
|
||||
|
||||
# Used to reset the database between system tests
|
||||
gem 'database_cleaner-active_record'
|
||||
|
|
@ -177,14 +180,14 @@ group :development do
|
|||
|
||||
# Enhanced error message pages for development
|
||||
gem 'better_errors', '~> 2.9'
|
||||
gem 'binding_of_caller'
|
||||
gem 'binding_of_caller', '~> 1.0'
|
||||
|
||||
# Preview mail in the browser
|
||||
gem 'letter_opener', '~> 1.8'
|
||||
gem 'letter_opener_web', '~> 3.0'
|
||||
|
||||
# Security analysis CLI tools
|
||||
gem 'brakeman', '~> 8.0', require: false
|
||||
gem 'brakeman', '~> 7.0', require: false
|
||||
gem 'bundler-audit', '~> 0.9', require: false
|
||||
|
||||
# Linter CLI for HAML files
|
||||
|
|
|
|||
516
Gemfile.lock
516
Gemfile.lock
File diff suppressed because it is too large
Load Diff
|
|
@ -60,7 +60,6 @@ Mastodon is a **free, open-source social network server** based on [ActivityPub]
|
|||
- **PostgreSQL** 14+
|
||||
- **Redis** 7.0+
|
||||
- **Node.js** 20+
|
||||
- **FFmpeg** 5.1+
|
||||
|
||||
This repository includes deployment configurations for **Docker and docker-compose**, as well as for other environments like Heroku and Scalingo. For Helm charts, reference the [mastodon/chart repository](https://github.com/mastodon/chart). A [**standalone** installation guide](https://docs.joinmastodon.org/admin/install/) is available in the main documentation.
|
||||
|
||||
|
|
|
|||
1
Vagrantfile
vendored
1
Vagrantfile
vendored
|
|
@ -29,6 +29,7 @@ sudo apt-get install \
|
|||
libpq-dev \
|
||||
libxml2-dev \
|
||||
libxslt1-dev \
|
||||
imagemagick \
|
||||
nodejs \
|
||||
redis-server \
|
||||
redis-tools \
|
||||
|
|
|
|||
|
|
@ -53,9 +53,9 @@ class PublicStatusesIndex < Chewy::Index
|
|||
}
|
||||
|
||||
index_scope ::Status.unscoped
|
||||
.kept
|
||||
.indexable
|
||||
.includes(:media_attachments, :preloadable_poll, :tags, preview_cards_status: :preview_card)
|
||||
.kept
|
||||
.indexable
|
||||
.includes(:media_attachments, :preloadable_poll, :tags, preview_cards_status: :preview_card)
|
||||
|
||||
root date_detection: false do
|
||||
field(:id, type: 'long')
|
||||
|
|
|
|||
|
|
@ -18,8 +18,6 @@ class AccountsController < ApplicationController
|
|||
respond_to do |format|
|
||||
format.html do
|
||||
expires_in(15.seconds, public: true, stale_while_revalidate: 30.seconds, stale_if_error: 1.hour) unless user_signed_in?
|
||||
|
||||
redirect_to short_account_path(@account) if account_id_param.present? && username_param.blank?
|
||||
end
|
||||
|
||||
format.rss do
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class ActivityPub::CollectionsController < ActivityPub::BaseController
|
||||
SUPPORTED_COLLECTIONS = %w(featured tags).freeze
|
||||
|
||||
vary_by -> { 'Signature' if authorized_fetch_mode? }
|
||||
|
||||
before_action :require_account_signature!, if: :authorized_fetch_mode?
|
||||
|
|
|
|||
|
|
@ -1,29 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class ActivityPub::FeatureAuthorizationsController < ActivityPub::BaseController
|
||||
include Authorization
|
||||
|
||||
vary_by -> { 'Signature' if authorized_fetch_mode? }
|
||||
|
||||
before_action :require_account_signature!, if: :authorized_fetch_mode?
|
||||
before_action :set_collection_item
|
||||
|
||||
def show
|
||||
expires_in 30.seconds, public: true if public_fetch_mode?
|
||||
render json: @collection_item, serializer: ActivityPub::FeatureAuthorizationSerializer, adapter: ActivityPub::Adapter, content_type: 'application/activity+json'
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def pundit_user
|
||||
signed_request_account
|
||||
end
|
||||
|
||||
def set_collection_item
|
||||
@collection_item = @account.collection_items.accepted.find(params[:id])
|
||||
|
||||
authorize @collection_item.collection, :show?
|
||||
rescue ActiveRecord::RecordNotFound, Mastodon::NotPermittedError
|
||||
not_found
|
||||
end
|
||||
end
|
||||
|
|
@ -1,77 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class ActivityPub::FeaturedCollectionsController < ApplicationController
|
||||
include SignatureAuthentication
|
||||
include Authorization
|
||||
include AccountOwnedConcern
|
||||
|
||||
PER_PAGE = 5
|
||||
|
||||
vary_by -> { public_fetch_mode? ? 'Accept, Accept-Language, Cookie' : 'Accept, Accept-Language, Cookie, Signature' }
|
||||
|
||||
before_action :check_feature_enabled
|
||||
before_action :require_account_signature!, if: -> { authorized_fetch_mode? }
|
||||
before_action :set_collections
|
||||
|
||||
skip_around_action :set_locale
|
||||
skip_before_action :require_functional!, unless: :limited_federation_mode?
|
||||
|
||||
def index
|
||||
respond_to do |format|
|
||||
format.json do
|
||||
expires_in(page_requested? ? 0 : 3.minutes, public: public_fetch_mode?)
|
||||
|
||||
render json: collection_presenter,
|
||||
serializer: ActivityPub::CollectionSerializer,
|
||||
adapter: ActivityPub::Adapter,
|
||||
content_type: 'application/activity+json'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_collections
|
||||
authorize @account, :index_collections?
|
||||
@collections = @account.collections.page(params[:page]).per(PER_PAGE)
|
||||
rescue Mastodon::NotPermittedError
|
||||
not_found
|
||||
end
|
||||
|
||||
def page_requested?
|
||||
params[:page].present?
|
||||
end
|
||||
|
||||
def next_page_url
|
||||
ap_account_featured_collections_url(@account, page: @collections.next_page) if @collections.respond_to?(:next_page)
|
||||
end
|
||||
|
||||
def prev_page_url
|
||||
ap_account_featured_collections_url(@account, page: @collections.prev_page) if @collections.respond_to?(:prev_page)
|
||||
end
|
||||
|
||||
def collection_presenter
|
||||
if page_requested?
|
||||
ActivityPub::CollectionPresenter.new(
|
||||
id: ap_account_featured_collections_url(@account, page: params.fetch(:page, 1)),
|
||||
type: :unordered,
|
||||
size: @account.collections.count,
|
||||
items: @collections,
|
||||
part_of: ap_account_featured_collections_url(@account),
|
||||
next: next_page_url,
|
||||
prev: prev_page_url
|
||||
)
|
||||
else
|
||||
ActivityPub::CollectionPresenter.new(
|
||||
id: ap_account_featured_collections_url(@account),
|
||||
type: :unordered,
|
||||
size: @account.collections.count,
|
||||
first: ap_account_featured_collections_url(@account, page: 1)
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
def check_feature_enabled
|
||||
raise ActionController::RoutingError unless Mastodon::Feature.collections_enabled?
|
||||
end
|
||||
end
|
||||
|
|
@ -26,9 +26,9 @@ class ActivityPub::InboxesController < ActivityPub::BaseController
|
|||
end
|
||||
|
||||
def unknown_affected_account?
|
||||
json = JSON.parse(body)
|
||||
json = Oj.load(body, mode: :strict)
|
||||
json.is_a?(Hash) && %w(Delete Update).include?(json['type']) && json['actor'].present? && json['actor'] == value_or_id(json['object']) && !Account.exists?(uri: json['actor'])
|
||||
rescue JSON::ParserError
|
||||
rescue Oj::ParseError
|
||||
false
|
||||
end
|
||||
|
||||
|
|
@ -44,7 +44,7 @@ class ActivityPub::InboxesController < ActivityPub::BaseController
|
|||
return @body if defined?(@body)
|
||||
|
||||
@body = request.body.read
|
||||
@body.presence&.force_encoding('UTF-8')
|
||||
@body.force_encoding('UTF-8') if @body.present?
|
||||
|
||||
request.body.rewind if request.body.respond_to?(:rewind)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,22 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Admin
|
||||
class CollectionsController < BaseController
|
||||
before_action :set_account
|
||||
before_action :set_collection, only: :show
|
||||
|
||||
def show
|
||||
authorize @collection, :show?
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_account
|
||||
@account = Account.find(params[:account_id])
|
||||
end
|
||||
|
||||
def set_collection
|
||||
@collection = @account.collections.includes(accepted_collection_items: :account).find(params[:id])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -5,15 +5,6 @@ module Admin
|
|||
def index
|
||||
authorize :custom_emoji, :index?
|
||||
|
||||
# If filtering by local emojis, remove by_domain filter.
|
||||
params.delete(:by_domain) if params[:local].present?
|
||||
|
||||
# If filtering by domain, ensure remote filter is set.
|
||||
if params[:by_domain].present?
|
||||
params.delete(:local)
|
||||
params[:remote] = '1'
|
||||
end
|
||||
|
||||
@custom_emojis = filtered_custom_emojis.eager_load(:local_counterpart).page(params[:page])
|
||||
@form = Form::CustomEmojiBatch.new
|
||||
end
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ module Admin
|
|||
end
|
||||
|
||||
# Allow transparently upgrading a domain block
|
||||
if existing_domain_block.present? && existing_domain_block.domain == TagManager.instance.normalize_domain(@domain_block.domain)
|
||||
if existing_domain_block.present? && existing_domain_block.domain == TagManager.instance.normalize_domain(@domain_block.domain.strip)
|
||||
@domain_block = existing_domain_block
|
||||
@domain_block.assign_attributes(resource_params)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@ class Admin::Fasp::Debug::CallbacksController < Admin::BaseController
|
|||
authorize [:admin, :fasp, :provider], :update?
|
||||
|
||||
@callbacks = Fasp::DebugCallback
|
||||
.includes(:fasp_provider)
|
||||
.order(created_at: :desc)
|
||||
.includes(:fasp_provider)
|
||||
.order(created_at: :desc)
|
||||
end
|
||||
|
||||
def destroy
|
||||
|
|
|
|||
|
|
@ -34,11 +34,8 @@ class Admin::Instances::ModerationNotesController < Admin::BaseController
|
|||
end
|
||||
|
||||
def set_instance
|
||||
@instance = Instance.find_or_initialize_by(domain: normalized_domain)
|
||||
end
|
||||
|
||||
def normalized_domain
|
||||
TagManager.instance.normalize_domain(params[:instance_id])
|
||||
domain = params[:instance_id]&.strip
|
||||
@instance = Instance.find_or_initialize_by(domain: TagManager.instance.normalize_domain(domain))
|
||||
end
|
||||
|
||||
def set_instance_note
|
||||
|
|
|
|||
|
|
@ -55,11 +55,8 @@ module Admin
|
|||
private
|
||||
|
||||
def set_instance
|
||||
@instance = Instance.find_or_initialize_by(domain: normalized_domain)
|
||||
end
|
||||
|
||||
def normalized_domain
|
||||
TagManager.instance.normalize_domain(params[:id])
|
||||
domain = params[:id]&.strip
|
||||
@instance = Instance.find_or_initialize_by(domain: TagManager.instance.normalize_domain(domain))
|
||||
end
|
||||
|
||||
def set_instances
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ class Admin::Reports::ActionsController < Admin::BaseController
|
|||
|
||||
case action_from_button
|
||||
when 'delete', 'mark_as_sensitive'
|
||||
Admin::ModerationAction.new(moderation_action_params).save!
|
||||
Admin::StatusBatchAction.new(status_batch_action_params).save!
|
||||
when 'silence', 'suspend'
|
||||
Admin::AccountAction.new(account_action_params).save!
|
||||
else
|
||||
|
|
@ -25,8 +25,9 @@ class Admin::Reports::ActionsController < Admin::BaseController
|
|||
|
||||
private
|
||||
|
||||
def moderation_action_params
|
||||
def status_batch_action_params
|
||||
shared_params
|
||||
.merge(status_ids: @report.status_ids)
|
||||
end
|
||||
|
||||
def account_action_params
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ module Admin
|
|||
private
|
||||
|
||||
def filtered_reports
|
||||
ReportFilter.new(filter_params).results.order(id: :desc).includes(:account, :target_account, :collections)
|
||||
ReportFilter.new(filter_params).results.order(id: :desc).includes(:account, :target_account)
|
||||
end
|
||||
|
||||
def filter_params
|
||||
|
|
@ -58,7 +58,7 @@ module Admin
|
|||
end
|
||||
|
||||
def set_report
|
||||
@report = Report.includes(collections: :accepted_collection_items).find(params[:id])
|
||||
@report = Report.find(params[:id])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ module Admin
|
|||
|
||||
def resource_params
|
||||
params
|
||||
.expect(user_role: [:name, :color, :highlighted, :position, :require_2fa, permissions_as_keys: []])
|
||||
.expect(user_role: [:name, :color, :highlighted, :position, permissions_as_keys: []])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ module Admin
|
|||
|
||||
@site_upload.destroy!
|
||||
|
||||
redirect_back_or_to admin_settings_path, notice: I18n.t('admin.site_uploads.destroyed_msg')
|
||||
redirect_back fallback_location: admin_settings_path, notice: I18n.t('admin.site_uploads.destroyed_msg')
|
||||
end
|
||||
|
||||
private
|
||||
|
|
|
|||
|
|
@ -78,6 +78,8 @@ module Admin
|
|||
'report'
|
||||
elsif params[:remove_from_report]
|
||||
'remove_from_report'
|
||||
elsif params[:delete]
|
||||
'delete'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ class Api::Fasp::BaseController < ApplicationController
|
|||
provider = nil
|
||||
|
||||
Linzer.verify!(request.rack_request, no_older_than: 5.minutes) do |keyid|
|
||||
provider = Fasp::Provider.confirmed.find(keyid)
|
||||
provider = Fasp::Provider.find(keyid)
|
||||
Linzer.new_ed25519_public_key(provider.provider_public_key_pem, keyid)
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -9,9 +9,9 @@ class Api::V1::Accounts::NotesController < Api::BaseController
|
|||
|
||||
def create
|
||||
if params[:comment].blank?
|
||||
current_account.account_notes.find_by(target_account: @account)&.destroy
|
||||
AccountNote.find_by(account: current_account, target_account: @account)&.destroy
|
||||
else
|
||||
@note = current_account.account_notes.find_or_initialize_by(target_account: @account)
|
||||
@note = AccountNote.find_or_initialize_by(account: current_account, target_account: @account)
|
||||
@note.comment = params[:comment]
|
||||
@note.save! if @note.changed?
|
||||
end
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ class Api::V1::AccountsController < Api::BaseController
|
|||
|
||||
headers.merge!(response.headers)
|
||||
|
||||
self.response_body = response.body.to_json
|
||||
self.response_body = Oj.dump(response.body)
|
||||
self.status = response.status
|
||||
rescue ActiveRecord::RecordInvalid => e
|
||||
render json: ValidationErrorFormatter.new(e, 'account.username': :username, 'invite_request.text': :reason).as_json, status: 422
|
||||
|
|
|
|||
|
|
@ -1,12 +1,10 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::AnnualReportsController < Api::BaseController
|
||||
include AsyncRefreshesConcern
|
||||
|
||||
before_action -> { doorkeeper_authorize! :read, :'read:accounts' }, except: [:read, :generate]
|
||||
before_action -> { doorkeeper_authorize! :write, :'write:accounts' }, only: [:read, :generate]
|
||||
before_action -> { doorkeeper_authorize! :read, :'read:accounts' }, only: :index
|
||||
before_action -> { doorkeeper_authorize! :write, :'write:accounts' }, except: :index
|
||||
before_action :require_user!
|
||||
before_action :set_annual_report, only: [:show, :read]
|
||||
before_action :set_annual_report, except: :index
|
||||
|
||||
def index
|
||||
with_read_replica do
|
||||
|
|
@ -30,28 +28,6 @@ class Api::V1::AnnualReportsController < Api::BaseController
|
|||
relationships: @relationships
|
||||
end
|
||||
|
||||
def state
|
||||
render json: { state: report_state }
|
||||
end
|
||||
|
||||
def generate
|
||||
return render_empty unless year == AnnualReport.current_campaign
|
||||
return render_empty if GeneratedAnnualReport.exists?(account_id: current_account.id, year: year)
|
||||
|
||||
async_refresh = AsyncRefresh.new(refresh_key)
|
||||
|
||||
if async_refresh.running?
|
||||
add_async_refresh_header(async_refresh, retry_seconds: 2)
|
||||
return head 202
|
||||
end
|
||||
|
||||
add_async_refresh_header(AsyncRefresh.create(refresh_key), retry_seconds: 2)
|
||||
|
||||
GenerateAnnualReportWorker.perform_async(current_account.id, year)
|
||||
|
||||
head 202
|
||||
end
|
||||
|
||||
def read
|
||||
@annual_report.view!
|
||||
render_empty
|
||||
|
|
@ -59,21 +35,7 @@ class Api::V1::AnnualReportsController < Api::BaseController
|
|||
|
||||
private
|
||||
|
||||
def report_state
|
||||
AnnualReport.new(current_account, year).state do |async_refresh|
|
||||
add_async_refresh_header(async_refresh, retry_seconds: 2)
|
||||
end
|
||||
end
|
||||
|
||||
def refresh_key
|
||||
"wrapstodon:#{current_account.id}:#{year}"
|
||||
end
|
||||
|
||||
def year
|
||||
params[:id]&.to_i
|
||||
end
|
||||
|
||||
def set_annual_report
|
||||
@annual_report = GeneratedAnnualReport.find_by!(account_id: current_account.id, year: year)
|
||||
@annual_report = GeneratedAnnualReport.find_by!(account_id: current_account.id, year: params[:id])
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -18,14 +18,14 @@ class Api::V1::BlocksController < Api::BaseController
|
|||
|
||||
def paginated_blocks
|
||||
@paginated_blocks ||= Block.eager_load(target_account: [:account_stat, :user])
|
||||
.joins(:target_account)
|
||||
.merge(Account.without_suspended)
|
||||
.where(account: current_account)
|
||||
.paginate_by_max_id(
|
||||
limit_param(DEFAULT_ACCOUNTS_LIMIT),
|
||||
params[:max_id],
|
||||
params[:since_id]
|
||||
)
|
||||
.joins(:target_account)
|
||||
.merge(Account.without_suspended)
|
||||
.where(account: current_account)
|
||||
.paginate_by_max_id(
|
||||
limit_param(DEFAULT_ACCOUNTS_LIMIT),
|
||||
params[:max_id],
|
||||
params[:since_id]
|
||||
)
|
||||
end
|
||||
|
||||
def next_path
|
||||
|
|
|
|||
|
|
@ -37,20 +37,20 @@ class Api::V1::ConversationsController < Api::BaseController
|
|||
|
||||
def paginated_conversations
|
||||
AccountConversation.where(account: current_account)
|
||||
.includes(
|
||||
account: [:account_stat, user: :role],
|
||||
last_status: [
|
||||
:media_attachments,
|
||||
:status_stat,
|
||||
:tags,
|
||||
{
|
||||
preview_cards_status: { preview_card: { author_account: [:account_stat, user: :role] } },
|
||||
active_mentions: :account,
|
||||
account: [:account_stat, user: :role],
|
||||
},
|
||||
]
|
||||
)
|
||||
.to_a_paginated_by_id(limit_param(LIMIT), params_slice(:max_id, :since_id, :min_id))
|
||||
.includes(
|
||||
account: [:account_stat, user: :role],
|
||||
last_status: [
|
||||
:media_attachments,
|
||||
:status_stat,
|
||||
:tags,
|
||||
{
|
||||
preview_cards_status: { preview_card: { author_account: [:account_stat, user: :role] } },
|
||||
active_mentions: :account,
|
||||
account: [:account_stat, user: :role],
|
||||
},
|
||||
]
|
||||
)
|
||||
.to_a_paginated_by_id(limit_param(LIMIT), params_slice(:max_id, :since_id, :min_id))
|
||||
end
|
||||
|
||||
def next_path
|
||||
|
|
|
|||
|
|
@ -1,86 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::DonationCampaignsController < Api::BaseController
|
||||
before_action :require_user!
|
||||
|
||||
STOPLIGHT_COOL_OFF_TIME = 60
|
||||
STOPLIGHT_FAILURE_THRESHOLD = 10
|
||||
|
||||
def index
|
||||
return head 204 if api_url.blank?
|
||||
|
||||
json = from_cache
|
||||
return render json: json if json.present?
|
||||
|
||||
campaign = fetch_campaign
|
||||
return head 204 if campaign.nil?
|
||||
|
||||
save_to_cache!(campaign)
|
||||
|
||||
render json: campaign
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def api_url
|
||||
Rails.configuration.x.donation_campaigns.api_url
|
||||
end
|
||||
|
||||
def seed
|
||||
@seed ||= Random.new(current_account.id).rand(100)
|
||||
end
|
||||
|
||||
def from_cache
|
||||
key = Rails.cache.read(request_key, raw: true)
|
||||
return if key.blank?
|
||||
|
||||
campaign = Rails.cache.read("donation_campaign:#{key}", raw: true)
|
||||
JSON.parse(campaign) if campaign.present?
|
||||
end
|
||||
|
||||
def save_to_cache!(campaign)
|
||||
return if campaign.blank?
|
||||
|
||||
Rails.cache.write_multi(
|
||||
{
|
||||
request_key => campaign_key(campaign),
|
||||
"donation_campaign:#{campaign_key(campaign)}" => campaign.to_json,
|
||||
},
|
||||
expires_in: 1.hour,
|
||||
raw: true
|
||||
)
|
||||
end
|
||||
|
||||
def fetch_campaign
|
||||
stoplight_wrapper.run do
|
||||
url = Addressable::URI.parse(api_url)
|
||||
url.query_values = { platform: 'web', seed: seed, locale: locale, environment: Rails.configuration.x.donation_campaigns.environment }.compact
|
||||
|
||||
Request.new(:get, url.to_s).perform do |res|
|
||||
return JSON.parse(res.body_with_limit) if res.code == 200
|
||||
end
|
||||
end
|
||||
rescue *Mastodon::HTTP_CONNECTION_ERRORS, JSON::ParserError
|
||||
nil
|
||||
end
|
||||
|
||||
def stoplight_wrapper
|
||||
Stoplight(
|
||||
'donation_campaigns',
|
||||
cool_off_time: STOPLIGHT_COOL_OFF_TIME,
|
||||
threshold: STOPLIGHT_FAILURE_THRESHOLD
|
||||
)
|
||||
end
|
||||
|
||||
def request_key
|
||||
"donation_campaign_request:#{seed}:#{locale}"
|
||||
end
|
||||
|
||||
def campaign_key(campaign)
|
||||
"#{campaign['id']}:#{campaign['locale']}"
|
||||
end
|
||||
|
||||
def locale
|
||||
I18n.locale.to_s
|
||||
end
|
||||
end
|
||||
|
|
@ -32,7 +32,13 @@ class Api::V1::MarkersController < Api::BaseController
|
|||
private
|
||||
|
||||
def serialize_map(map)
|
||||
map.transform_values { |value| ActiveModelSerializers::SerializableResource.new(value, serializer: REST::MarkerSerializer) }
|
||||
serialized = {}
|
||||
|
||||
map.each_pair do |key, value|
|
||||
serialized[key] = ActiveModelSerializers::SerializableResource.new(value, serializer: REST::MarkerSerializer).as_json
|
||||
end
|
||||
|
||||
Oj.dump(serialized)
|
||||
end
|
||||
|
||||
def resource_params
|
||||
|
|
|
|||
|
|
@ -18,14 +18,14 @@ class Api::V1::MutesController < Api::BaseController
|
|||
|
||||
def paginated_mutes
|
||||
@paginated_mutes ||= Mute.eager_load(target_account: [:account_stat, :user])
|
||||
.joins(:target_account)
|
||||
.merge(Account.without_suspended)
|
||||
.where(account: current_account)
|
||||
.paginate_by_max_id(
|
||||
limit_param(DEFAULT_ACCOUNTS_LIMIT),
|
||||
params[:max_id],
|
||||
params[:since_id]
|
||||
)
|
||||
.joins(:target_account)
|
||||
.merge(Account.without_suspended)
|
||||
.where(account: current_account)
|
||||
.paginate_by_max_id(
|
||||
limit_param(DEFAULT_ACCOUNTS_LIMIT),
|
||||
params[:max_id],
|
||||
params[:since_id]
|
||||
)
|
||||
end
|
||||
|
||||
def next_path
|
||||
|
|
|
|||
|
|
@ -47,6 +47,10 @@ class Api::V1::Peers::SearchController < Api::BaseController
|
|||
end
|
||||
|
||||
def normalized_domain
|
||||
TagManager.instance.normalize_domain(params[:q])
|
||||
TagManager.instance.normalize_domain(query_value)
|
||||
end
|
||||
|
||||
def query_value
|
||||
params[:q].strip
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,43 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::ProfilesController < Api::BaseController
|
||||
before_action -> { doorkeeper_authorize! :profile, :read, :'read:accounts' }, except: [:update]
|
||||
before_action -> { doorkeeper_authorize! :write, :'write:accounts' }, only: [:update]
|
||||
before_action :require_user!
|
||||
|
||||
def show
|
||||
@account = current_account
|
||||
render json: @account, serializer: REST::ProfileSerializer
|
||||
end
|
||||
|
||||
def update
|
||||
@account = current_account
|
||||
UpdateAccountService.new.call(@account, account_params, raise_error: true)
|
||||
ActivityPub::UpdateDistributionWorker.perform_in(ActivityPub::UpdateDistributionWorker::DEBOUNCE_DELAY, @account.id)
|
||||
|
||||
render json: @account, serializer: REST::ProfileSerializer
|
||||
rescue ActiveRecord::RecordInvalid => e
|
||||
render json: ValidationErrorFormatter.new(e).as_json, status: 422
|
||||
end
|
||||
|
||||
def account_params
|
||||
params.permit(
|
||||
:display_name,
|
||||
:note,
|
||||
:avatar,
|
||||
:avatar_description,
|
||||
:header,
|
||||
:header_description,
|
||||
:locked,
|
||||
:bot,
|
||||
:discoverable,
|
||||
:hide_collections,
|
||||
:indexable,
|
||||
:show_media,
|
||||
:show_media_replies,
|
||||
:show_featured,
|
||||
attribution_domains: [],
|
||||
fields_attributes: [:name, :value]
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
@ -23,10 +23,6 @@ class Api::V1::ReportsController < Api::BaseController
|
|||
end
|
||||
|
||||
def report_params
|
||||
if Mastodon::Feature.collections_enabled?
|
||||
params.permit(:account_id, :comment, :category, :forward, forward_to_domains: [], status_ids: [], collection_ids: [], rule_ids: [])
|
||||
else
|
||||
params.permit(:account_id, :comment, :category, :forward, forward_to_domains: [], status_ids: [], rule_ids: [])
|
||||
end
|
||||
params.permit(:account_id, :comment, :category, :forward, forward_to_domains: [], status_ids: [], rule_ids: [])
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -26,20 +26,20 @@ class Api::V1::Statuses::PinsController < Api::V1::Statuses::BaseController
|
|||
def distribute_add_activity!
|
||||
json = ActiveModelSerializers::SerializableResource.new(
|
||||
@status,
|
||||
serializer: ActivityPub::AddNoteSerializer,
|
||||
serializer: ActivityPub::AddSerializer,
|
||||
adapter: ActivityPub::Adapter
|
||||
).as_json
|
||||
|
||||
ActivityPub::RawDistributionWorker.perform_async(json.to_json, current_account.id)
|
||||
ActivityPub::RawDistributionWorker.perform_async(Oj.dump(json), current_account.id)
|
||||
end
|
||||
|
||||
def distribute_remove_activity!
|
||||
json = ActiveModelSerializers::SerializableResource.new(
|
||||
@status,
|
||||
serializer: ActivityPub::RemoveNoteSerializer,
|
||||
serializer: ActivityPub::RemoveSerializer,
|
||||
adapter: ActivityPub::Adapter
|
||||
).as_json
|
||||
|
||||
ActivityPub::RawDistributionWorker.perform_async(json.to_json, current_account.id)
|
||||
ActivityPub::RawDistributionWorker.perform_async(Oj.dump(json), current_account.id)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -41,8 +41,8 @@ class Api::V1::Statuses::QuotesController < Api::V1::Statuses::BaseController
|
|||
if current_account&.id != @status.account_id
|
||||
domains = @statuses.filter_map(&:account_domain).uniq
|
||||
account_ids = @statuses.map(&:account_id).uniq
|
||||
current_account&.preload_relations!(account_ids, domains)
|
||||
@statuses.reject! { |status| StatusFilter.new(status, current_account).filtered? }
|
||||
relations = current_account&.relations_map(account_ids, domains) || {}
|
||||
@statuses.reject! { |status| StatusFilter.new(status, current_account, relations).filtered? }
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -127,13 +127,10 @@ class Api::V1::StatusesController < Api::BaseController
|
|||
@status = Status.where(account: current_account).find(params[:id])
|
||||
authorize @status, :destroy?
|
||||
|
||||
# JSON is generated before `discard_with_reblogs` in order to have the proper URL
|
||||
# for media attachments, as it would otherwise redirect to the media proxy
|
||||
json = render_to_body json: @status, serializer: REST::StatusSerializer, source_requested: true
|
||||
|
||||
@status.discard_with_reblogs
|
||||
StatusPin.find_by(status: @status)&.destroy
|
||||
@status.account.statuses_count = @status.account.statuses_count - 1
|
||||
json = render_to_body json: @status, serializer: REST::StatusSerializer, source_requested: true
|
||||
|
||||
RemovalWorker.perform_async(@status.id, { 'redraft' => !truthy_param?(:delete_media) })
|
||||
|
||||
|
|
|
|||
|
|
@ -39,6 +39,6 @@ class Api::V1::TagsController < Api::BaseController
|
|||
def set_or_create_tag
|
||||
return not_found unless Tag::HASHTAG_NAME_RE.match?(params[:id])
|
||||
|
||||
@tag = Tag.find_normalized(params[:id]) || Tag.new(name: params[:id], display_name: params[:id])
|
||||
@tag = Tag.find_normalized(params[:id]) || Tag.new(name: Tag.normalize(params[:id]), display_name: params[:id])
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,62 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1Alpha::CollectionItemsController < Api::BaseController
|
||||
include Authorization
|
||||
|
||||
before_action :check_feature_enabled
|
||||
|
||||
before_action -> { doorkeeper_authorize! :write, :'write:collections' }
|
||||
|
||||
before_action :require_user!
|
||||
|
||||
before_action :set_collection
|
||||
before_action :set_account, only: [:create]
|
||||
before_action :set_collection_item, only: [:destroy, :revoke]
|
||||
|
||||
after_action :verify_authorized
|
||||
|
||||
def create
|
||||
authorize @collection, :update?
|
||||
authorize @account, :feature?
|
||||
|
||||
@item = AddAccountToCollectionService.new.call(@collection, @account)
|
||||
|
||||
render json: @item, serializer: REST::CollectionItemSerializer, adapter: :json
|
||||
end
|
||||
|
||||
def destroy
|
||||
authorize @collection, :update?
|
||||
|
||||
DeleteCollectionItemService.new.call(@collection_item)
|
||||
|
||||
head 200
|
||||
end
|
||||
|
||||
def revoke
|
||||
authorize @collection_item, :revoke?
|
||||
|
||||
RevokeCollectionItemService.new.call(@collection_item)
|
||||
|
||||
head 200
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_collection
|
||||
@collection = Collection.find(params[:collection_id])
|
||||
end
|
||||
|
||||
def set_account
|
||||
return render(json: { error: '`account_id` parameter is missing' }, status: 422) if params[:account_id].blank?
|
||||
|
||||
@account = Account.find(params[:account_id])
|
||||
end
|
||||
|
||||
def set_collection_item
|
||||
@collection_item = @collection.collection_items.find(params[:id])
|
||||
end
|
||||
|
||||
def check_feature_enabled
|
||||
raise ActionController::RoutingError unless Mastodon::Feature.collections_enabled?
|
||||
end
|
||||
end
|
||||
|
|
@ -1,117 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1Alpha::CollectionsController < Api::BaseController
|
||||
include Authorization
|
||||
|
||||
DEFAULT_COLLECTIONS_LIMIT = 40
|
||||
|
||||
rescue_from ActiveRecord::RecordInvalid, Mastodon::ValidationError do |e|
|
||||
render json: { error: ValidationErrorFormatter.new(e).as_json }, status: 422
|
||||
end
|
||||
|
||||
before_action :check_feature_enabled
|
||||
|
||||
before_action -> { authorize_if_got_token! :read, :'read:collections' }, only: [:index, :show]
|
||||
before_action -> { doorkeeper_authorize! :write, :'write:collections' }, only: [:create, :update, :destroy]
|
||||
|
||||
before_action :require_user!, only: [:create, :update, :destroy]
|
||||
|
||||
before_action :set_account, only: [:index]
|
||||
before_action :set_collections, only: [:index]
|
||||
before_action :set_collection, only: [:show, :update, :destroy]
|
||||
|
||||
after_action :insert_pagination_headers, only: [:index]
|
||||
|
||||
after_action :verify_authorized
|
||||
|
||||
def index
|
||||
cache_if_unauthenticated!
|
||||
authorize @account, :index_collections?
|
||||
|
||||
render json: @collections, each_serializer: REST::CollectionSerializer, adapter: :json
|
||||
rescue Mastodon::NotPermittedError
|
||||
render json: { collections: [] }
|
||||
end
|
||||
|
||||
def show
|
||||
cache_if_unauthenticated!
|
||||
authorize @collection, :show?
|
||||
|
||||
render json: @collection, serializer: REST::CollectionWithAccountsSerializer
|
||||
end
|
||||
|
||||
def create
|
||||
authorize Collection, :create?
|
||||
|
||||
@collection = CreateCollectionService.new.call(collection_creation_params, current_user.account)
|
||||
|
||||
render json: @collection, serializer: REST::CollectionSerializer, adapter: :json
|
||||
end
|
||||
|
||||
def update
|
||||
authorize @collection, :update?
|
||||
|
||||
UpdateCollectionService.new.call(@collection, collection_update_params)
|
||||
|
||||
render json: @collection, serializer: REST::CollectionSerializer, adapter: :json
|
||||
end
|
||||
|
||||
def destroy
|
||||
authorize @collection, :destroy?
|
||||
|
||||
DeleteCollectionService.new.call(@collection)
|
||||
|
||||
head 200
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_account
|
||||
@account = Account.find(params[:account_id])
|
||||
end
|
||||
|
||||
def set_collections
|
||||
@collections = @account.collections
|
||||
.with_tag
|
||||
.order(created_at: :desc)
|
||||
.offset(offset_param)
|
||||
.limit(limit_param(DEFAULT_COLLECTIONS_LIMIT))
|
||||
@collections = @collections.discoverable unless @account == current_account
|
||||
end
|
||||
|
||||
def set_collection
|
||||
@collection = Collection.find(params[:id])
|
||||
end
|
||||
|
||||
def collection_creation_params
|
||||
params.permit(:name, :description, :language, :sensitive, :discoverable, :tag_name, account_ids: [])
|
||||
end
|
||||
|
||||
def collection_update_params
|
||||
params.permit(:name, :description, :language, :sensitive, :discoverable, :tag_name)
|
||||
end
|
||||
|
||||
def check_feature_enabled
|
||||
raise ActionController::RoutingError unless Mastodon::Feature.collections_enabled?
|
||||
end
|
||||
|
||||
def next_path
|
||||
return unless records_continue?
|
||||
|
||||
api_v1_alpha_account_collections_url(@account, pagination_params(offset: offset_param + limit_param(DEFAULT_COLLECTIONS_LIMIT)))
|
||||
end
|
||||
|
||||
def prev_path
|
||||
return if offset_param.zero?
|
||||
|
||||
api_v1_alpha_account_collections_url(@account, pagination_params(offset: offset_param - limit_param(DEFAULT_COLLECTIONS_LIMIT)))
|
||||
end
|
||||
|
||||
def records_continue?
|
||||
((offset_param * limit_param(DEFAULT_COLLECTIONS_LIMIT)) + @collections.size) < @account.collections.size
|
||||
end
|
||||
|
||||
def offset_param
|
||||
params[:offset].to_i
|
||||
end
|
||||
end
|
||||
|
|
@ -9,7 +9,6 @@ class ApplicationController < ActionController::Base
|
|||
include UserTrackingConcern
|
||||
include SessionTrackingConcern
|
||||
include CacheConcern
|
||||
include ErrorResponses
|
||||
include PreloadingConcern
|
||||
include DomainControlHelper
|
||||
include DatabaseHelper
|
||||
|
|
@ -18,12 +17,28 @@ class ApplicationController < ActionController::Base
|
|||
|
||||
helper_method :current_account
|
||||
helper_method :current_session
|
||||
helper_method :current_theme
|
||||
helper_method :single_user_mode?
|
||||
helper_method :use_seamless_external_login?
|
||||
helper_method :sso_account_settings
|
||||
helper_method :limited_federation_mode?
|
||||
helper_method :skip_csrf_meta_tags?
|
||||
|
||||
rescue_from ActionController::ParameterMissing, Paperclip::AdapterRegistry::NoHandlerError, with: :bad_request
|
||||
rescue_from Mastodon::NotPermittedError, with: :forbidden
|
||||
rescue_from ActionController::RoutingError, ActiveRecord::RecordNotFound, with: :not_found
|
||||
rescue_from ActionController::UnknownFormat, with: :not_acceptable
|
||||
rescue_from ActionController::InvalidAuthenticityToken, with: :unprocessable_content
|
||||
rescue_from Mastodon::RateLimitExceededError, with: :too_many_requests
|
||||
|
||||
rescue_from(*Mastodon::HTTP_CONNECTION_ERRORS, with: :internal_server_error)
|
||||
rescue_from Mastodon::RaceConditionError, Stoplight::Error::RedLight, ActiveRecord::SerializationFailure, with: :service_unavailable
|
||||
|
||||
rescue_from Seahorse::Client::NetworkingError do |e|
|
||||
Rails.logger.warn "Storage server error: #{e}"
|
||||
service_unavailable
|
||||
end
|
||||
|
||||
before_action :check_self_destruct!
|
||||
|
||||
before_action :store_referrer, except: :raise_not_found, if: :devise_controller?
|
||||
|
|
@ -47,25 +62,19 @@ class ApplicationController < ActionController::Base
|
|||
return if request.referer.blank?
|
||||
|
||||
redirect_uri = URI(request.referer)
|
||||
return if redirect_uri.path.start_with?('/auth', '/settings/two_factor_authentication', '/settings/otp_authentication')
|
||||
return if redirect_uri.path.start_with?('/auth')
|
||||
|
||||
stored_url = redirect_uri.to_s if redirect_uri.host == request.host && redirect_uri.port == request.port
|
||||
|
||||
store_location_for(:user, stored_url)
|
||||
end
|
||||
|
||||
def mfa_setup_path(path_params = {})
|
||||
settings_two_factor_authentication_methods_path(path_params)
|
||||
end
|
||||
|
||||
def require_functional!
|
||||
return if current_user.functional?
|
||||
|
||||
respond_to do |format|
|
||||
format.any do
|
||||
if current_user.missing_2fa?
|
||||
redirect_to mfa_setup_path
|
||||
elsif current_user.confirmed?
|
||||
if current_user.confirmed?
|
||||
redirect_to edit_user_registration_path
|
||||
else
|
||||
redirect_to auth_setup_path
|
||||
|
|
@ -77,8 +86,6 @@ class ApplicationController < ActionController::Base
|
|||
render json: { error: 'Your login is missing a confirmed e-mail address' }, status: 403
|
||||
elsif !current_user.approved?
|
||||
render json: { error: 'Your login is currently pending approval' }, status: 403
|
||||
elsif current_user.missing_2fa?
|
||||
render json: { error: 'Your account requires two-factor authentication' }, status: 403
|
||||
elsif !current_user.functional?
|
||||
render json: { error: 'Your login is currently disabled' }, status: 403
|
||||
end
|
||||
|
|
@ -104,6 +111,42 @@ class ApplicationController < ActionController::Base
|
|||
ActiveModel::Type::Boolean.new.cast(params[key])
|
||||
end
|
||||
|
||||
def forbidden
|
||||
respond_with_error(403)
|
||||
end
|
||||
|
||||
def not_found
|
||||
respond_with_error(404)
|
||||
end
|
||||
|
||||
def gone
|
||||
respond_with_error(410)
|
||||
end
|
||||
|
||||
def unprocessable_content
|
||||
respond_with_error(422)
|
||||
end
|
||||
|
||||
def not_acceptable
|
||||
respond_with_error(406)
|
||||
end
|
||||
|
||||
def bad_request
|
||||
respond_with_error(400)
|
||||
end
|
||||
|
||||
def internal_server_error
|
||||
respond_with_error(500)
|
||||
end
|
||||
|
||||
def service_unavailable
|
||||
respond_with_error(503)
|
||||
end
|
||||
|
||||
def too_many_requests
|
||||
respond_with_error(429)
|
||||
end
|
||||
|
||||
def single_user_mode?
|
||||
@single_user_mode ||= Rails.configuration.x.single_user_mode && Account.without_internal.exists?
|
||||
end
|
||||
|
|
@ -128,6 +171,19 @@ class ApplicationController < ActionController::Base
|
|||
@current_session = SessionActivation.find_by(session_id: cookies.signed['_session_id']) if cookies.signed['_session_id'].present?
|
||||
end
|
||||
|
||||
def current_theme
|
||||
return Setting.theme unless Themes.instance.names.include? current_user&.setting_theme
|
||||
|
||||
current_user.setting_theme
|
||||
end
|
||||
|
||||
def respond_with_error(code)
|
||||
respond_to do |format|
|
||||
format.any { render "errors/#{code}", layout: 'error', status: code, formats: [:html] }
|
||||
format.json { render json: { error: Rack::Utils::HTTP_STATUS_CODES[code] }, status: code }
|
||||
end
|
||||
end
|
||||
|
||||
def check_self_destruct!
|
||||
return unless self_destruct?
|
||||
|
||||
|
|
|
|||
|
|
@ -130,17 +130,12 @@ class Auth::RegistrationsController < Devise::RegistrationsController
|
|||
end
|
||||
|
||||
def require_rules_acceptance!
|
||||
return if @rules.empty? || validated_accept_token?
|
||||
return if @rules.empty? || (session[:accept_token].present? && params[:accept] == session[:accept_token])
|
||||
|
||||
@accept_token = session[:accept_token] = SecureRandom.hex
|
||||
@invite_code = invite_code
|
||||
@rule_translations = @rules.map { |rule| rule.translation_for(I18n.locale) }
|
||||
@invite_code = invite_code
|
||||
|
||||
render :rules
|
||||
end
|
||||
|
||||
def validated_accept_token?
|
||||
session[:accept_token].present? && params[:accept] == session[:accept_token]
|
||||
set_locale { render :rules }
|
||||
end
|
||||
|
||||
def is_flashing_format? # rubocop:disable Naming/PredicatePrefix
|
||||
|
|
|
|||
|
|
@ -197,14 +197,14 @@ class Auth::SessionsController < Devise::SessionsController
|
|||
"2fa_auth_attempts:#{user.id}:#{Time.now.utc.hour}"
|
||||
end
|
||||
|
||||
def respond_to_on_destroy(**)
|
||||
def respond_to_on_destroy
|
||||
respond_to do |format|
|
||||
format.json do
|
||||
render json: {
|
||||
redirect_to: after_sign_out_path_for(resource_name),
|
||||
}, status: 200
|
||||
end
|
||||
format.all { super(**) }
|
||||
format.all { super }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,42 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class CollectionItemsController < ApplicationController
|
||||
include SignatureAuthentication
|
||||
include Authorization
|
||||
include AccountOwnedConcern
|
||||
|
||||
vary_by -> { public_fetch_mode? ? 'Accept, Accept-Language, Cookie' : 'Accept, Accept-Language, Cookie, Signature' }
|
||||
|
||||
before_action :check_feature_enabled
|
||||
before_action :require_account_signature!, if: -> { authorized_fetch_mode? }
|
||||
before_action :set_collection_item
|
||||
|
||||
skip_around_action :set_locale
|
||||
skip_before_action :require_functional!, unless: :limited_federation_mode?
|
||||
|
||||
def show
|
||||
respond_to do |format|
|
||||
format.json do
|
||||
expires_in(3.minutes, public: public_fetch_mode?)
|
||||
|
||||
render json: @collection_item,
|
||||
serializer: ActivityPub::FeaturedItemSerializer,
|
||||
adapter: ActivityPub::Adapter,
|
||||
content_type: 'application/activity+json'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_collection_item
|
||||
@collection_item = @account.curated_collection_items.find(params[:id])
|
||||
authorize @collection_item.collection, :show?
|
||||
rescue ActiveRecord::RecordNotFound, Mastodon::NotPermittedError
|
||||
not_found
|
||||
end
|
||||
|
||||
def check_feature_enabled
|
||||
raise ActionController::RoutingError unless Mastodon::Feature.collections_enabled?
|
||||
end
|
||||
end
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class CollectionsController < ApplicationController
|
||||
include WebAppControllerConcern
|
||||
include SignatureAuthentication
|
||||
include Authorization
|
||||
include AccountOwnedConcern
|
||||
|
||||
vary_by -> { public_fetch_mode? ? 'Accept, Accept-Language, Cookie' : 'Accept, Accept-Language, Cookie, Signature' }
|
||||
|
||||
before_action :check_feature_enabled
|
||||
before_action :require_account_signature!, only: :show, if: -> { request.format == :json && authorized_fetch_mode? }
|
||||
before_action :set_collection
|
||||
|
||||
skip_around_action :set_locale, if: -> { request.format == :json }
|
||||
skip_before_action :require_functional!, only: :show, unless: :limited_federation_mode?
|
||||
|
||||
def show
|
||||
respond_to do |format|
|
||||
# TODO: format.html
|
||||
|
||||
format.json do
|
||||
expires_in expiration_duration, public: true if public_fetch_mode?
|
||||
render_with_cache json: @collection, content_type: 'application/activity+json', serializer: ActivityPub::FeaturedCollectionSerializer, adapter: ActivityPub::Adapter
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_collection
|
||||
@collection = @account.collections.find(params[:id])
|
||||
authorize @collection, :show?
|
||||
rescue ActiveRecord::RecordNotFound, Mastodon::NotPermittedError
|
||||
not_found
|
||||
end
|
||||
|
||||
def expiration_duration
|
||||
recently_updated = @collection.updated_at > 15.minutes.ago
|
||||
recently_updated ? 30.seconds : 5.minutes
|
||||
end
|
||||
|
||||
def check_feature_enabled
|
||||
raise ActionController::RoutingError unless Mastodon::Feature.collections_enabled?
|
||||
end
|
||||
end
|
||||
|
|
@ -4,8 +4,10 @@ module AccountableConcern
|
|||
extend ActiveSupport::Concern
|
||||
|
||||
def log_action(action, target)
|
||||
current_account
|
||||
.action_logs
|
||||
.create(action:, target:)
|
||||
Admin::ActionLog.create(
|
||||
account: current_account,
|
||||
action: action,
|
||||
target: target
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -6,9 +6,9 @@ module Api::InteractionPoliciesConcern
|
|||
def quote_approval_policy
|
||||
case status_params[:quote_approval_policy].presence || current_user.setting_default_quote_policy
|
||||
when 'public'
|
||||
InteractionPolicy::POLICY_FLAGS[:public] << 16
|
||||
Status::QUOTE_APPROVAL_POLICY_FLAGS[:public] << 16
|
||||
when 'followers'
|
||||
InteractionPolicy::POLICY_FLAGS[:followers] << 16
|
||||
Status::QUOTE_APPROVAL_POLICY_FLAGS[:followers] << 16
|
||||
when 'nobody'
|
||||
0
|
||||
else
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ module ChallengableConcern
|
|||
end
|
||||
|
||||
def render_challenge
|
||||
render 'auth/challenges/new', layout: params[:oauth] ? 'modal' : 'auth'
|
||||
render 'auth/challenges/new', layout: 'auth'
|
||||
end
|
||||
|
||||
def challenge_passed?
|
||||
|
|
|
|||
|
|
@ -1,68 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module ErrorResponses
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
rescue_from ActionController::InvalidAuthenticityToken, with: :unprocessable_content
|
||||
rescue_from ActionController::ParameterMissing, Paperclip::AdapterRegistry::NoHandlerError, with: :bad_request
|
||||
rescue_from ActionController::RoutingError, ActiveRecord::RecordNotFound, with: :not_found
|
||||
rescue_from ActionController::UnknownFormat, with: :not_acceptable
|
||||
rescue_from Mastodon::NotPermittedError, with: :forbidden
|
||||
rescue_from Mastodon::RaceConditionError, Stoplight::Error::RedLight, ActiveRecord::SerializationFailure, with: :service_unavailable
|
||||
rescue_from Mastodon::RateLimitExceededError, with: :too_many_requests
|
||||
rescue_from(*Mastodon::HTTP_CONNECTION_ERRORS, with: :internal_server_error)
|
||||
|
||||
rescue_from Seahorse::Client::NetworkingError do |e|
|
||||
Rails.logger.warn "Storage server error: #{e}"
|
||||
service_unavailable
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def bad_request
|
||||
respond_with_error(400)
|
||||
end
|
||||
|
||||
def forbidden
|
||||
respond_with_error(403)
|
||||
end
|
||||
|
||||
def gone
|
||||
respond_with_error(410)
|
||||
end
|
||||
|
||||
def internal_server_error
|
||||
respond_with_error(500)
|
||||
end
|
||||
|
||||
def not_acceptable
|
||||
respond_with_error(406)
|
||||
end
|
||||
|
||||
def not_found
|
||||
respond_with_error(404)
|
||||
end
|
||||
|
||||
def service_unavailable
|
||||
respond_with_error(503)
|
||||
end
|
||||
|
||||
def too_many_requests
|
||||
respond_with_error(429)
|
||||
end
|
||||
|
||||
def unprocessable_content
|
||||
respond_with_error(422)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def respond_with_error(code)
|
||||
respond_to do |format|
|
||||
format.any { render "errors/#{code}", layout: 'error', formats: [:html], status: code }
|
||||
format.json { render json: { error: Rack::Utils::HTTP_STATUS_CODES[code] }, status: code }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -7,7 +7,6 @@ class FollowerAccountsController < ApplicationController
|
|||
vary_by -> { public_fetch_mode? ? 'Accept, Accept-Language, Cookie' : 'Accept, Accept-Language, Cookie, Signature' }
|
||||
|
||||
before_action :require_account_signature!, if: -> { request.format == :json && authorized_fetch_mode? }
|
||||
before_action :protect_hidden_collections, if: -> { request.format.json? }
|
||||
|
||||
skip_around_action :set_locale, if: -> { request.format == :json }
|
||||
skip_before_action :require_functional!, unless: :limited_federation_mode?
|
||||
|
|
@ -19,6 +18,8 @@ class FollowerAccountsController < ApplicationController
|
|||
end
|
||||
|
||||
format.json do
|
||||
raise Mastodon::NotPermittedError if page_requested? && @account.hide_collections?
|
||||
|
||||
expires_in(page_requested? ? 0 : 3.minutes, public: public_fetch_mode?)
|
||||
|
||||
render json: collection_presenter,
|
||||
|
|
@ -40,10 +41,6 @@ class FollowerAccountsController < ApplicationController
|
|||
@follows = scope.recent.page(params[:page]).per(FOLLOW_PER_PAGE).preload(:account)
|
||||
end
|
||||
|
||||
def protect_hidden_collections
|
||||
raise Mastodon::NotPermittedError if page_requested? && @account.hide_collections?
|
||||
end
|
||||
|
||||
def page_requested?
|
||||
params[:page].present?
|
||||
end
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ class FollowingAccountsController < ApplicationController
|
|||
vary_by -> { public_fetch_mode? ? 'Accept, Accept-Language, Cookie' : 'Accept, Accept-Language, Cookie, Signature' }
|
||||
|
||||
before_action :require_account_signature!, if: -> { request.format == :json && authorized_fetch_mode? }
|
||||
before_action :protect_hidden_collections, if: -> { request.format.json? }
|
||||
|
||||
skip_around_action :set_locale, if: -> { request.format == :json }
|
||||
skip_before_action :require_functional!, unless: :limited_federation_mode?
|
||||
|
|
@ -19,6 +18,11 @@ class FollowingAccountsController < ApplicationController
|
|||
end
|
||||
|
||||
format.json do
|
||||
if page_requested? && @account.hide_collections?
|
||||
forbidden
|
||||
next
|
||||
end
|
||||
|
||||
expires_in(page_requested? ? 0 : 3.minutes, public: public_fetch_mode?)
|
||||
|
||||
render json: collection_presenter,
|
||||
|
|
@ -40,10 +44,6 @@ class FollowingAccountsController < ApplicationController
|
|||
@follows = scope.recent.page(params[:page]).per(FOLLOW_PER_PAGE).preload(:target_account)
|
||||
end
|
||||
|
||||
def protect_hidden_collections
|
||||
raise Mastodon::NotPermittedError if page_requested? && @account.hide_collections?
|
||||
end
|
||||
|
||||
def page_requested?
|
||||
params[:page].present?
|
||||
end
|
||||
|
|
|
|||
|
|
@ -11,7 +11,9 @@ class MediaProxyController < ApplicationController
|
|||
before_action :authenticate_user!, if: :limited_federation_mode?
|
||||
before_action :set_media_attachment
|
||||
|
||||
rescue_from ActiveRecord::RecordInvalid, Mastodon::NotPermittedError, Mastodon::UnexpectedResponseError, with: :not_found
|
||||
rescue_from ActiveRecord::RecordInvalid, with: :not_found
|
||||
rescue_from Mastodon::UnexpectedResponseError, with: :not_found
|
||||
rescue_from Mastodon::NotPermittedError, with: :not_found
|
||||
rescue_from(*Mastodon::HTTP_CONNECTION_ERRORS, with: :internal_server_error)
|
||||
|
||||
def show
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class OAuth::AuthorizationsController < Doorkeeper::AuthorizationsController
|
||||
prepend_before_action :store_current_location
|
||||
skip_before_action :authenticate_resource_owner!
|
||||
|
||||
before_action :store_current_location
|
||||
before_action :authenticate_resource_owner!
|
||||
|
||||
layout 'modal'
|
||||
|
||||
|
|
@ -17,15 +20,17 @@ class OAuth::AuthorizationsController < Doorkeeper::AuthorizationsController
|
|||
store_location_for(:user, request.url)
|
||||
end
|
||||
|
||||
def can_authorize_response?
|
||||
!truthy_param?('force_login') && super
|
||||
def render_success
|
||||
if skip_authorization? || (matching_token? && !truthy_param?('force_login'))
|
||||
redirect_or_render authorize_response
|
||||
elsif Doorkeeper.configuration.api_only
|
||||
render json: pre_auth
|
||||
else
|
||||
render :new
|
||||
end
|
||||
end
|
||||
|
||||
def truthy_param?(key)
|
||||
ActiveModel::Type::Boolean.new.cast(params[key])
|
||||
end
|
||||
|
||||
def mfa_setup_path
|
||||
super({ oauth: true })
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,9 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Settings
|
||||
module TwoFactorAuthentication
|
||||
class BaseController < ::Settings::BaseController
|
||||
layout -> { truthy_param?(:oauth) ? 'modal' : 'admin' }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -4,15 +4,12 @@ module Settings
|
|||
module TwoFactorAuthentication
|
||||
class ConfirmationsController < BaseController
|
||||
include ChallengableConcern
|
||||
include Devise::Controllers::StoreLocation
|
||||
|
||||
skip_before_action :require_functional!
|
||||
|
||||
before_action :require_challenge!
|
||||
before_action :ensure_otp_secret
|
||||
|
||||
helper_method :return_to_app_url
|
||||
|
||||
def new
|
||||
prepare_two_factor_form
|
||||
end
|
||||
|
|
@ -40,10 +37,6 @@ module Settings
|
|||
|
||||
private
|
||||
|
||||
def return_to_app_url
|
||||
stored_location_for(:user)
|
||||
end
|
||||
|
||||
def confirmation_params
|
||||
params.expect(form_two_factor_confirmation: [:otp_attempt])
|
||||
end
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user