mirror of
https://github.com/mastodon/mastodon.git
synced 2026-04-27 02:17:27 -05:00
Compare commits
127 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8965e1bfa9 | ||
|
|
1e27ab0885 | ||
|
|
cef2c50a71 | ||
|
|
d7f4eca801 | ||
|
|
adf291631e | ||
|
|
cbef4c9e65 | ||
|
|
1631fb80e8 | ||
|
|
8477bec2f2 | ||
|
|
6796765363 | ||
|
|
044a20f12d | ||
|
|
e4bdbccba8 | ||
|
|
d7d6407d41 | ||
|
|
a186bad399 | ||
|
|
67575e59e6 | ||
|
|
d9113976c8 | ||
|
|
670316499f | ||
|
|
3c725240fd | ||
|
|
d8ddf95485 | ||
|
|
4c12c2ed60 | ||
|
|
636ecd1d03 | ||
|
|
cb0065cfe9 | ||
|
|
6ae1b4fae9 | ||
|
|
dc6d8f8825 | ||
|
|
dd0647ca45 | ||
|
|
70e2eb49df | ||
|
|
bef28b2e51 | ||
|
|
0b66bd591f | ||
|
|
a94d7bf520 | ||
|
|
c8551a3eca | ||
|
|
06c2393805 | ||
|
|
4e85b9073b | ||
|
|
c966d75600 | ||
|
|
6f1fd0c2a7 | ||
|
|
68c219e753 | ||
|
|
a795743c3f | ||
|
|
1ab5ea9bfb | ||
|
|
48f55e3224 | ||
|
|
6044270d69 | ||
|
|
be1bd91e6d | ||
|
|
34cd5a716f | ||
|
|
ec5128bc1f | ||
|
|
c0f9e7f4c3 | ||
|
|
1137a0ca3a | ||
|
|
1faf520ce4 | ||
|
|
8777443c9b | ||
|
|
bd6d1f0e3f | ||
|
|
1a1a23f6f0 | ||
|
|
a48567784c | ||
|
|
b71216a08a | ||
|
|
36974aaa99 | ||
|
|
567f337db3 | ||
|
|
97f118013a | ||
|
|
ea5d1f0297 | ||
|
|
7a862d3308 | ||
|
|
1675eab561 | ||
|
|
5f4116a311 | ||
|
|
0741381670 | ||
|
|
e61900cadc | ||
|
|
cbb9a4dbe3 | ||
|
|
4ef0ce033e | ||
|
|
5478ef9b32 | ||
|
|
e2592419d9 | ||
|
|
e330447b0e | ||
|
|
b15861528c | ||
|
|
83dc7dc16e | ||
|
|
7d3cc51148 | ||
|
|
cabb33bc49 | ||
|
|
208cb8276a | ||
|
|
4ae47f4263 | ||
|
|
08b2f255fc | ||
|
|
8242f06eca | ||
|
|
5429351889 | ||
|
|
6ff4e83937 | ||
|
|
77d2cdb302 | ||
|
|
c727197760 | ||
|
|
d6859c9658 | ||
|
|
7a9e98f4d6 | ||
|
|
7924a27ae7 | ||
|
|
d664b9d8ff | ||
|
|
4558cfadd8 | ||
|
|
713965467d | ||
|
|
aec6d0f807 | ||
|
|
e103815d2d | ||
|
|
d73b9fba90 | ||
|
|
a89d11bc08 | ||
|
|
a250928934 | ||
|
|
1d1b17b04b | ||
|
|
2aff51013c | ||
|
|
8c3c1faaec | ||
|
|
a2888f1bb2 | ||
|
|
77fe044f03 | ||
|
|
da0cc0f5b9 | ||
|
|
ee83f3a8b9 | ||
|
|
7ae78b1032 | ||
|
|
c4b7c3bdda | ||
|
|
a79dbf8334 | ||
|
|
ef6f5f9357 | ||
|
|
f65f6ad6f1 | ||
|
|
c0e242cb73 | ||
|
|
609a40181e | ||
|
|
93ce44d21d | ||
|
|
fb3ff194b5 | ||
|
|
81b363b338 | ||
|
|
1151b05c2d | ||
|
|
f96743fcfb | ||
|
|
69e14246b8 | ||
|
|
c1794fb948 | ||
|
|
333a17a478 | ||
|
|
388e09e1a3 | ||
|
|
2dcededcf0 | ||
|
|
2db8a328cd | ||
|
|
b4a950c2fc | ||
|
|
194645aada | ||
|
|
0c5ce23ae4 | ||
|
|
cb937a920e | ||
|
|
7051458467 | ||
|
|
025abf7325 | ||
|
|
28373a9c88 | ||
|
|
42884d8727 | ||
|
|
000ff9c05f | ||
|
|
921af5d27d | ||
|
|
878e1e65eb | ||
|
|
06f5f270cc | ||
|
|
961c22a6fd | ||
|
|
07b4fa55c8 | ||
|
|
041bce9ed6 | ||
|
|
d7a08d81b6 |
|
|
@ -1,5 +1,5 @@
|
|||
# For details, see https://github.com/devcontainers/images/tree/main/src/ruby
|
||||
FROM mcr.microsoft.com/devcontainers/ruby:4.0-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
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@
|
|||
.gitattributes
|
||||
.gitignore
|
||||
.github
|
||||
.vscode
|
||||
public/system
|
||||
public/assets
|
||||
public/packs
|
||||
|
|
@ -21,7 +20,6 @@ postgres14
|
|||
redis
|
||||
elasticsearch
|
||||
chart
|
||||
storybook-static
|
||||
.yarn/
|
||||
!.yarn/patches
|
||||
!.yarn/plugins
|
||||
|
|
|
|||
|
|
@ -88,3 +88,24 @@ S3_ALIAS_HOST=files.example.com
|
|||
# -----------------------
|
||||
IP_RETENTION_PERIOD=31556952
|
||||
SESSION_RETENTION_PERIOD=31556952
|
||||
|
||||
# Fetch All Replies Behavior
|
||||
# --------------------------
|
||||
# When a user expands a post (DetailedStatus view), fetch all of its replies
|
||||
# (default: false)
|
||||
FETCH_REPLIES_ENABLED=false
|
||||
|
||||
# Period to wait between fetching replies (in minutes)
|
||||
FETCH_REPLIES_COOLDOWN_MINUTES=15
|
||||
|
||||
# Period to wait after a post is first created before fetching its replies (in minutes)
|
||||
FETCH_REPLIES_INITIAL_WAIT_MINUTES=5
|
||||
|
||||
# Max number of replies to fetch - total, recursively through a whole reply tree
|
||||
FETCH_REPLIES_MAX_GLOBAL=1000
|
||||
|
||||
# Max number of replies to fetch - for a single post
|
||||
FETCH_REPLIES_MAX_SINGLE=500
|
||||
|
||||
# Max number of replies Collection pages to fetch - total
|
||||
FETCH_REPLIES_MAX_PAGES=500
|
||||
|
|
|
|||
2
.github/ISSUE_TEMPLATE/1.web_bug_report.yml
vendored
2
.github/ISSUE_TEMPLATE/1.web_bug_report.yml
vendored
|
|
@ -1,6 +1,6 @@
|
|||
name: Bug Report (Web Interface)
|
||||
description: There is a problem using Mastodon's web interface.
|
||||
labels: ['area/web interface']
|
||||
labels: ['status/to triage', 'area/web interface']
|
||||
type: Bug
|
||||
body:
|
||||
- type: markdown
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
name: Bug Report (server / API)
|
||||
description: |
|
||||
There is a problem with the HTTP server, REST API, ActivityPub interaction, etc.
|
||||
labels: ['status/to triage']
|
||||
type: 'Bug'
|
||||
body:
|
||||
- type: markdown
|
||||
|
|
@ -59,7 +60,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. v4.0.2)
|
||||
- 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. v4.0.2)
|
||||
- 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@668228422ae6a00e4ad889ee87cd7109ec5666a7 # 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 }}
|
||||
|
|
|
|||
6
.github/actions/setup-ruby/action.yml
vendored
6
.github/actions/setup-ruby/action.yml
vendored
|
|
@ -1,4 +1,4 @@
|
|||
name: 'Setup Ruby'
|
||||
name: 'Setup RUby'
|
||||
description: 'Setup a Ruby environment ready to run the Mastodon code'
|
||||
inputs:
|
||||
ruby-version:
|
||||
|
|
@ -14,10 +14,10 @@ runs:
|
|||
shell: bash
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install --no-install-recommends -y libicu-dev libidn11-dev libvips42 ${{ inputs.additional-system-dependencies }}
|
||||
sudo apt-get install -y libicu-dev libidn11-dev libvips42 ${{ inputs.additional-system-dependencies }}
|
||||
|
||||
- name: Set up Ruby
|
||||
uses: ruby/setup-ruby@c984c1a20bb35a1cbda04477c816cea024418be9 # v1
|
||||
uses: ruby/setup-ruby@v1
|
||||
with:
|
||||
ruby-version: ${{ inputs.ruby-version }}
|
||||
bundler-cache: true
|
||||
|
|
|
|||
31
.github/renovate.json5
vendored
31
.github/renovate.json5
vendored
|
|
@ -5,7 +5,7 @@
|
|||
'customManagers:dockerfileVersions',
|
||||
':labels(dependencies)',
|
||||
':prConcurrentLimitNone', // Remove limit for open PRs at any time.
|
||||
':enableVulnerabilityAlertsWithLabel(security)',
|
||||
':prHourlyLimit2', // Rate limit PR creation to a maximum of two per hour.
|
||||
],
|
||||
rebaseWhen: 'conflicted',
|
||||
minimumReleaseAge: '3', // Wait 3 days after the package has been published before upgrading it
|
||||
|
|
@ -22,6 +22,9 @@
|
|||
// Require Dependency Dashboard Approval for major version bumps of these node packages
|
||||
matchManagers: ['npm'],
|
||||
matchPackageNames: [
|
||||
'tesseract.js', // Requires code changes
|
||||
'react-hotkeys', // Requires code changes
|
||||
|
||||
// react-router: Requires manual upgrade
|
||||
'history',
|
||||
'react-router-dom',
|
||||
|
|
@ -91,19 +94,6 @@
|
|||
matchUpdateTypes: ['patch', 'minor'],
|
||||
groupName: 'eslint (non-major)',
|
||||
},
|
||||
{
|
||||
// Group all Storybook-related packages in the same PR
|
||||
matchManagers: ['npm'],
|
||||
matchPackageNames: [
|
||||
'chromatic',
|
||||
'storybook',
|
||||
'@storybook/*',
|
||||
'msw',
|
||||
'msw-storybook-addon',
|
||||
],
|
||||
matchUpdateTypes: ['patch', 'minor'],
|
||||
groupName: 'storybook (non-major)',
|
||||
},
|
||||
{
|
||||
// Group actions/*-artifact in the same PR
|
||||
matchManagers: ['github-actions'],
|
||||
|
|
@ -113,7 +103,6 @@
|
|||
],
|
||||
matchUpdateTypes: ['major'],
|
||||
groupName: 'artifact actions (major)',
|
||||
extends: ['helpers:pinGitHubActionDigests'],
|
||||
},
|
||||
{
|
||||
// Update @types/* packages every week, with one grouped PR
|
||||
|
|
@ -153,18 +142,6 @@
|
|||
matchUpdateTypes: ['patch', 'minor'],
|
||||
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'],
|
||||
groupName: 'Playwright',
|
||||
},
|
||||
// Add labels depending on package manager
|
||||
{ matchManagers: ['npm', 'nvm'], addLabels: ['javascript'] },
|
||||
{ matchManagers: ['bundler', 'ruby-version'], addLabels: ['ruby'] },
|
||||
|
|
|
|||
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
|
||||
|
|
|
|||
42
.github/workflows/build-releases.yml
vendored
42
.github/workflows/build-releases.yml
vendored
|
|
@ -9,44 +9,7 @@ permissions:
|
|||
packages: write
|
||||
|
||||
jobs:
|
||||
check-latest-stable:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
latest: ${{ steps.check.outputs.is_latest_stable }}
|
||||
steps:
|
||||
# Repository needs to be cloned to list branches
|
||||
- name: Clone repository
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Check latest stable
|
||||
shell: bash
|
||||
id: check
|
||||
run: |
|
||||
ref="${GITHUB_REF#refs/tags/}"
|
||||
|
||||
if [[ "$ref" =~ ^v([0-9]+)\.([0-9]+)(\.[0-9]+)?$ ]]; then
|
||||
current="${BASH_REMATCH[1]}.${BASH_REMATCH[2]}"
|
||||
else
|
||||
echo "tag $ref is not semver"
|
||||
echo "is_latest_stable=false" >> "$GITHUB_OUTPUT"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
latest=$(git for-each-ref --format='%(refname:short)' "refs/remotes/origin/stable-*.*" \
|
||||
| sed -E 's#^origin/stable-##' \
|
||||
| sort -Vr \
|
||||
| head -n1)
|
||||
|
||||
if [[ "$current" == "$latest" ]]; then
|
||||
echo "is_latest_stable=true" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "is_latest_stable=false" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
build-image:
|
||||
needs: check-latest-stable
|
||||
uses: ./.github/workflows/build-container-image.yml
|
||||
with:
|
||||
file_to_build: Dockerfile
|
||||
|
|
@ -58,14 +21,13 @@ jobs:
|
|||
# Only tag with latest when ran against the latest stable branch
|
||||
# This needs to be updated after each minor version release
|
||||
flavor: |
|
||||
latest=${{ needs.check-latest-stable.outputs.latest }}
|
||||
latest=${{ startsWith(github.ref, 'refs/tags/v4.4.') }}
|
||||
tags: |
|
||||
type=pep440,pattern={{raw}}
|
||||
type=pep440,pattern=v{{major}}.{{minor}}
|
||||
secrets: inherit
|
||||
|
||||
build-image-streaming:
|
||||
needs: check-latest-stable
|
||||
uses: ./.github/workflows/build-container-image.yml
|
||||
with:
|
||||
file_to_build: streaming/Dockerfile
|
||||
|
|
@ -77,7 +39,7 @@ jobs:
|
|||
# Only tag with latest when ran against the latest stable branch
|
||||
# This needs to be updated after each minor version release
|
||||
flavor: |
|
||||
latest=${{ needs.check-latest-stable.outputs.latest }}
|
||||
latest=${{ startsWith(github.ref, 'refs/tags/v4.4.') }}
|
||||
tags: |
|
||||
type=pep440,pattern={{raw}}
|
||||
type=pep440,pattern=v{{major}}.{{minor}}
|
||||
|
|
|
|||
1
.github/workflows/build-security.yml
vendored
1
.github/workflows/build-security.yml
vendored
|
|
@ -9,6 +9,7 @@ permissions:
|
|||
jobs:
|
||||
compute-suffix:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'mastodon/mastodon'
|
||||
steps:
|
||||
- id: version_vars
|
||||
env:
|
||||
|
|
|
|||
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@c984c1a20bb35a1cbda04477c816cea024418be9 # 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
|
||||
|
|
|
|||
33
.github/workflows/chromatic.yml
vendored
33
.github/workflows/chromatic.yml
vendored
|
|
@ -1,30 +1,11 @@
|
|||
name: 'Chromatic'
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
on:
|
||||
push:
|
||||
branches-ignore:
|
||||
- renovate/*
|
||||
- stable-*
|
||||
|
||||
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:
|
||||
paths:
|
||||
- 'package.json'
|
||||
- 'yarn.lock'
|
||||
- '**/*.js'
|
||||
|
|
@ -35,17 +16,16 @@ jobs:
|
|||
- '**/*.scss'
|
||||
- '.github/workflows/chromatic.yml'
|
||||
|
||||
jobs:
|
||||
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
|
||||
|
|
|
|||
12
.github/workflows/codeql.yml
vendored
12
.github/workflows/codeql.yml
vendored
|
|
@ -25,17 +25,17 @@ jobs:
|
|||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: ['actions', 'javascript', 'ruby']
|
||||
# CodeQL supports [ 'actions', 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
|
||||
language: ['javascript', 'ruby']
|
||||
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
|
||||
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
|
||||
|
||||
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.6
|
||||
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@c984c1a20bb35a1cbda04477c816cea024418be9 # 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
|
||||
|
|
|
|||
9
.github/workflows/lint-ruby.yml
vendored
9
.github/workflows/lint-ruby.yml
vendored
|
|
@ -35,15 +35,18 @@ jobs:
|
|||
|
||||
steps:
|
||||
- name: Clone repository
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Ruby
|
||||
uses: ruby/setup-ruby@c984c1a20bb35a1cbda04477c816cea024418be9 # v1
|
||||
uses: ruby/setup-ruby@v1
|
||||
with:
|
||||
bundler-cache: true
|
||||
|
||||
- name: Set-up RuboCop Problem Matcher
|
||||
uses: r7kamura/rubocop-problem-matchers-action@v1
|
||||
|
||||
- name: Run rubocop
|
||||
run: bin/rubocop --format github
|
||||
run: bin/rubocop
|
||||
|
||||
- name: Run brakeman
|
||||
if: always() # Run both checks, even if the first failed
|
||||
|
|
|
|||
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
|
||||
|
|
|
|||
134
.github/workflows/test-ruby.yml
vendored
134
.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@668228422ae6a00e4ad889ee87cd7109ec5666a7 # 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: |-
|
||||
|
|
@ -124,13 +124,13 @@ jobs:
|
|||
fail-fast: false
|
||||
matrix:
|
||||
ruby-version:
|
||||
- '3.2'
|
||||
- '3.3'
|
||||
- '3.4'
|
||||
- '.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@668228422ae6a00e4ad889ee87cd7109ec5666a7 # 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
|
||||
|
|
@ -217,14 +304,14 @@ jobs:
|
|||
fail-fast: false
|
||||
matrix:
|
||||
ruby-version:
|
||||
- '3.2'
|
||||
- '3.3'
|
||||
- '3.4'
|
||||
- '.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@668228422ae6a00e4ad889ee87cd7109ec5666a7 # 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 }}
|
||||
|
|
@ -348,21 +435,21 @@ jobs:
|
|||
fail-fast: false
|
||||
matrix:
|
||||
ruby-version:
|
||||
- '3.2'
|
||||
- '3.3'
|
||||
- '3.4'
|
||||
- '.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,8 +469,15 @@ 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@v4
|
||||
if: failure()
|
||||
with:
|
||||
name: test-search-screenshots
|
||||
path: tmp/capybara/
|
||||
|
|
|
|||
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -23,7 +23,6 @@
|
|||
/public/packs
|
||||
/public/packs-dev
|
||||
/public/packs-test
|
||||
stats.html
|
||||
.env
|
||||
.env.production
|
||||
node_modules/
|
||||
|
|
|
|||
|
|
@ -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
|
||||
};
|
||||
|
|
@ -8,7 +8,7 @@ AllCops:
|
|||
- lib/mastodon/migration_helpers.rb
|
||||
ExtraDetails: true
|
||||
NewCops: enable
|
||||
TargetRubyVersion: 3.3 # Oldest supported ruby version
|
||||
TargetRubyVersion: 3.2 # Oldest supported ruby version
|
||||
|
||||
inherit_from:
|
||||
- .rubocop/layout.yml
|
||||
|
|
|
|||
|
|
@ -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,21 +1,17 @@
|
|||
---
|
||||
Metrics/AbcSize:
|
||||
Enabled: false
|
||||
Exclude:
|
||||
- lib/mastodon/cli/*.rb
|
||||
|
||||
Metrics/BlockLength:
|
||||
Enabled: false
|
||||
|
||||
Metrics/BlockNesting:
|
||||
Enabled: false
|
||||
|
||||
Metrics/ClassLength:
|
||||
Enabled: false
|
||||
|
||||
Metrics/CollectionLiteralLength:
|
||||
Enabled: false
|
||||
|
||||
Metrics/CyclomaticComplexity:
|
||||
Enabled: false
|
||||
Exclude:
|
||||
- lib/mastodon/cli/*.rb
|
||||
|
||||
Metrics/MethodLength:
|
||||
Enabled: false
|
||||
|
|
@ -24,7 +20,4 @@ Metrics/ModuleLength:
|
|||
Enabled: false
|
||||
|
||||
Metrics/ParameterLists:
|
||||
Enabled: false
|
||||
|
||||
Metrics/PerceivedComplexity:
|
||||
Enabled: false
|
||||
CountKeywordArgs: false
|
||||
|
|
|
|||
|
|
@ -1,11 +1,32 @@
|
|||
# This configuration was generated by
|
||||
# `rubocop --auto-gen-config --auto-gen-only-exclude --no-offense-counts --no-auto-gen-timestamp`
|
||||
# using RuboCop version 1.80.2.
|
||||
# using RuboCop version 1.77.0.
|
||||
# The point is for the user to remove these configuration records
|
||||
# one by one as the offenses are removed from the code base.
|
||||
# Note that changes in the inspected code, or installation of new
|
||||
# versions of RuboCop, may require this file to be generated again.
|
||||
|
||||
Lint/NonLocalExitFromIterator:
|
||||
Exclude:
|
||||
- 'app/helpers/json_ld_helper.rb'
|
||||
|
||||
# Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes.
|
||||
Metrics/AbcSize:
|
||||
Max: 82
|
||||
|
||||
# Configuration parameters: CountBlocks, CountModifierForms, Max.
|
||||
Metrics/BlockNesting:
|
||||
Exclude:
|
||||
- 'lib/tasks/mastodon.rake'
|
||||
|
||||
# Configuration parameters: AllowedMethods, AllowedPatterns.
|
||||
Metrics/CyclomaticComplexity:
|
||||
Max: 25
|
||||
|
||||
# Configuration parameters: AllowedMethods, AllowedPatterns.
|
||||
Metrics/PerceivedComplexity:
|
||||
Max: 27
|
||||
|
||||
# This cop supports safe autocorrection (--autocorrect).
|
||||
# Configuration parameters: AllowedVars, DefaultToNil.
|
||||
Style/FetchEnvVar:
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
4.0.2
|
||||
3.4.4
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
import { resolve } from 'node:path';
|
||||
|
||||
import type { StorybookConfig } from '@storybook/react-vite';
|
||||
|
||||
const config: StorybookConfig = {
|
||||
|
|
@ -27,14 +25,7 @@ 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');
|
||||
return config;
|
||||
},
|
||||
};
|
||||
|
||||
export default config;
|
||||
|
|
|
|||
|
|
@ -1,8 +0,0 @@
|
|||
export const modes = {
|
||||
darkTheme: {
|
||||
theme: 'dark',
|
||||
},
|
||||
lightTheme: {
|
||||
theme: 'light',
|
||||
},
|
||||
} as const;
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
<html class="no-reduce-motion" data-color-scheme="light">
|
||||
</html>
|
||||
|
|
@ -11,25 +11,18 @@ 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 { reducerWithInitialState, rootReducer } from '@/mastodon/reducers';
|
||||
import { defaultMiddleware } from '@/mastodon/store/store';
|
||||
import { mockHandlers, unhandledRequestHandler } from '@/testing/api';
|
||||
|
||||
import { modes } from './modes';
|
||||
// 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 '../app/javascript/styles/application.scss';
|
||||
import './styles.css';
|
||||
|
||||
// Disabling locales in Storybook as it's breaking with Vite 8.
|
||||
// const localeFiles = import.meta.glob('@/mastodon/locales/*.json', {
|
||||
// query: { as: 'json' },
|
||||
// });
|
||||
const localeFiles = import.meta.glob('@/mastodon/locales/*.json', {
|
||||
query: { as: 'json' },
|
||||
});
|
||||
|
||||
// Initialize MSW
|
||||
initialize({
|
||||
|
|
@ -40,68 +33,28 @@ const preview: Preview = {
|
|||
// Auto-generate docs: https://storybook.js.org/docs/writing-docs/autodocs
|
||||
tags: ['autodocs'],
|
||||
globalTypes: {
|
||||
// locale: {
|
||||
// description: 'Locale for the story',
|
||||
// toolbar: {
|
||||
// title: 'Locale',
|
||||
// icon: 'globe',
|
||||
// items: Object.keys(localeFiles).map((path) =>
|
||||
// path.replace('/mastodon/locales/', '').replace('.json', ''),
|
||||
// ),
|
||||
// dynamicTitle: true,
|
||||
// },
|
||||
// },
|
||||
theme: {
|
||||
description: 'Theme for the story',
|
||||
locale: {
|
||||
description: 'Locale for the story',
|
||||
toolbar: {
|
||||
title: 'Theme',
|
||||
icon: 'circlehollow',
|
||||
items: [{ value: 'light' }, { value: 'dark' }],
|
||||
title: 'Locale',
|
||||
icon: 'globe',
|
||||
items: Object.keys(localeFiles).map((path) =>
|
||||
path.replace('/mastodon/locales/', '').replace('.json', ''),
|
||||
),
|
||||
dynamicTitle: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
initialGlobals: {
|
||||
locale: 'en',
|
||||
theme: 'light',
|
||||
},
|
||||
decorators: [
|
||||
(Story, { parameters, globals, args, argTypes }) => {
|
||||
// Get the locale from the global toolbar
|
||||
// and merge it with any parameters or args state.
|
||||
const { locale } = globals as { locale: string };
|
||||
(Story, { parameters }) => {
|
||||
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] = {};
|
||||
let reducer = rootReducer;
|
||||
if (typeof state === 'object' && state) {
|
||||
reducer = reducerWithInitialState(state as Record<string, unknown>);
|
||||
}
|
||||
if (i === reduxPath.length - 1) {
|
||||
acc[key] = value;
|
||||
}
|
||||
return acc[key] as Record<string, unknown>;
|
||||
}, argsState);
|
||||
}
|
||||
}
|
||||
|
||||
const reducer = reducerWithInitialState(
|
||||
{
|
||||
meta: {
|
||||
locale,
|
||||
},
|
||||
},
|
||||
state as Record<string, unknown>,
|
||||
argsState,
|
||||
);
|
||||
|
||||
const store = configureStore({
|
||||
reducer,
|
||||
middleware(getDefaultMiddleware) {
|
||||
|
|
@ -137,18 +90,15 @@ const preview: Preview = {
|
|||
}, [currentLocale, currentLocaleData]);
|
||||
|
||||
return (
|
||||
<IntlProvider locale={currentLocale} messages={currentLocaleData}>
|
||||
<IntlProvider
|
||||
locale={currentLocale}
|
||||
messages={currentLocaleData}
|
||||
textComponent='span'
|
||||
>
|
||||
<Story />
|
||||
</IntlProvider>
|
||||
);
|
||||
},
|
||||
(Story, { globals }) => {
|
||||
const theme = (globals.theme as string) || 'light';
|
||||
useEffect(() => {
|
||||
document.body.setAttribute('data-color-scheme', theme);
|
||||
}, [theme]);
|
||||
return <Story />;
|
||||
},
|
||||
(Story) => (
|
||||
<MemoryRouter>
|
||||
<Story />
|
||||
|
|
@ -165,12 +115,7 @@ const preview: Preview = {
|
|||
</MemoryRouter>
|
||||
),
|
||||
],
|
||||
loaders: [
|
||||
mswLoader,
|
||||
importCustomEmojiData,
|
||||
importLegacyShortcodes,
|
||||
({ globals: { locale } }) => importEmojiData(locale as string),
|
||||
],
|
||||
loaders: [mswLoader],
|
||||
parameters: {
|
||||
layout: 'centered',
|
||||
|
||||
|
|
@ -195,13 +140,6 @@ const preview: Preview = {
|
|||
msw: {
|
||||
handlers: mockHandlers,
|
||||
},
|
||||
|
||||
chromatic: {
|
||||
modes: {
|
||||
dark: modes.darkTheme,
|
||||
light: modes.lightTheme,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -7,8 +7,8 @@
|
|||
* - Please do NOT modify this file.
|
||||
*/
|
||||
|
||||
const PACKAGE_VERSION = '2.12.14'
|
||||
const INTEGRITY_CHECKSUM = '4db4a41e972cec1b64cc569c66952d82'
|
||||
const PACKAGE_VERSION = '2.10.2'
|
||||
const INTEGRITY_CHECKSUM = 'f5825c521429caf22a4dd13b66e243af'
|
||||
const IS_MOCKED_RESPONSE = Symbol('isMockedResponse')
|
||||
const activeClientIds = new Set()
|
||||
|
||||
|
|
@ -71,6 +71,11 @@ addEventListener('message', async function (event) {
|
|||
break
|
||||
}
|
||||
|
||||
case 'MOCK_DEACTIVATE': {
|
||||
activeClientIds.delete(clientId)
|
||||
break
|
||||
}
|
||||
|
||||
case 'CLIENT_CLOSED': {
|
||||
activeClientIds.delete(clientId)
|
||||
|
||||
|
|
@ -89,8 +94,6 @@ addEventListener('message', async function (event) {
|
|||
})
|
||||
|
||||
addEventListener('fetch', function (event) {
|
||||
const requestInterceptedAt = Date.now()
|
||||
|
||||
// Bypass navigation requests.
|
||||
if (event.request.mode === 'navigate') {
|
||||
return
|
||||
|
|
@ -107,29 +110,23 @@ addEventListener('fetch', function (event) {
|
|||
|
||||
// Bypass all requests when there are no active clients.
|
||||
// Prevents the self-unregistered worked from handling requests
|
||||
// after it's been terminated (still remains active until the next reload).
|
||||
// after it's been deleted (still remains active until the next reload).
|
||||
if (activeClientIds.size === 0) {
|
||||
return
|
||||
}
|
||||
|
||||
const requestId = crypto.randomUUID()
|
||||
event.respondWith(handleRequest(event, requestId, requestInterceptedAt))
|
||||
event.respondWith(handleRequest(event, requestId))
|
||||
})
|
||||
|
||||
/**
|
||||
* @param {FetchEvent} event
|
||||
* @param {string} requestId
|
||||
* @param {number} requestInterceptedAt
|
||||
*/
|
||||
async function handleRequest(event, requestId, requestInterceptedAt) {
|
||||
async function handleRequest(event, requestId) {
|
||||
const client = await resolveMainClient(event)
|
||||
const requestCloneForEvents = event.request.clone()
|
||||
const response = await getResponse(
|
||||
event,
|
||||
client,
|
||||
requestId,
|
||||
requestInterceptedAt,
|
||||
)
|
||||
const response = await getResponse(event, client, requestId)
|
||||
|
||||
// Send back the response clone for the "response:*" life-cycle events.
|
||||
// Ensure MSW is active and ready to handle the message, otherwise
|
||||
|
|
@ -205,10 +202,9 @@ 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) {
|
||||
async function getResponse(event, client, requestId) {
|
||||
// Clone the request because it might've been already used
|
||||
// (i.e. its body has been read and sent to the client).
|
||||
const requestClone = event.request.clone()
|
||||
|
|
@ -259,7 +255,6 @@ async function getResponse(event, client, requestId, requestInterceptedAt) {
|
|||
type: 'REQUEST',
|
||||
payload: {
|
||||
id: requestId,
|
||||
interceptedAt: requestInterceptedAt,
|
||||
...serializedRequest,
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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 {};
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
a {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
8
.storybook/vitest.setup.ts
Normal file
8
.storybook/vitest.setup.ts
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
import * as a11yAddonAnnotations from '@storybook/addon-a11y/preview';
|
||||
import { setProjectAnnotations } from '@storybook/react-vite';
|
||||
|
||||
import * as projectAnnotations from './preview';
|
||||
|
||||
// This is an important step to apply the right configuration when testing your stories.
|
||||
// More info at: https://storybook.js.org/docs/api/portable-stories/portable-stories-vitest#setprojectannotations
|
||||
setProjectAnnotations([a11yAddonAnnotations, projectAnnotations]);
|
||||
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)
|
||||
|
|
|
|||
1463
CHANGELOG.md
1463
CHANGELOG.md
File diff suppressed because it is too large
Load Diff
|
|
@ -1,83 +1,132 @@
|
|||
# Contributor Covenant 3.0 Code of Conduct
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
We pledge to make our community welcoming, safe, and equitable for all.
|
||||
We as members, contributors, and leaders pledge to make participation in our
|
||||
community a harassment-free experience for everyone, regardless of age, body
|
||||
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||
identity and expression, level of experience, education, socio-economic status,
|
||||
nationality, personal appearance, race, caste, color, religion, or sexual
|
||||
identity and orientation.
|
||||
|
||||
We are committed to fostering an environment that respects and promotes the dignity, rights, and contributions of all individuals, regardless of characteristics including race, ethnicity, caste, color, age, physical characteristics, neurodiversity, disability, sex or gender, gender identity or expression, sexual orientation, language, philosophy or religion, national or social origin, socio-economic position, level of education, or other status. The same privileges of participation are extended to everyone who participates in good faith and in accordance with this Covenant.
|
||||
We pledge to act and interact in ways that contribute to an open, welcoming,
|
||||
diverse, inclusive, and healthy community.
|
||||
|
||||
## Encouraged Behaviors
|
||||
## Our Standards
|
||||
|
||||
While acknowledging differences in social norms, we all strive to meet our community's expectations for positive behavior. We also understand that our words and actions may be interpreted differently than we intend based on culture, background, or native language.
|
||||
Examples of behavior that contributes to a positive environment for our
|
||||
community include:
|
||||
|
||||
With these considerations in mind, we agree to behave mindfully toward each other and act in ways that center our shared values, including:
|
||||
- Demonstrating empathy and kindness toward other people
|
||||
- Being respectful of differing opinions, viewpoints, and experiences
|
||||
- Giving and gracefully accepting constructive feedback
|
||||
- Accepting responsibility and apologizing to those affected by our mistakes,
|
||||
and learning from the experience
|
||||
- Focusing on what is best not just for us as individuals, but for the overall
|
||||
community
|
||||
|
||||
1. Respecting the **purpose of our community**, our activities, and our ways of gathering.
|
||||
2. Engaging **kindly and honestly** with others.
|
||||
3. Respecting **different viewpoints** and experiences.
|
||||
4. **Taking responsibility** for our actions and contributions.
|
||||
5. Gracefully giving and accepting **constructive feedback**.
|
||||
6. Committing to **repairing harm** when it occurs.
|
||||
7. Behaving in other ways that promote and sustain the **well-being of our community**.
|
||||
Examples of unacceptable behavior include:
|
||||
|
||||
## Restricted Behaviors
|
||||
- The use of sexualized language or imagery, and sexual attention or advances of
|
||||
any kind
|
||||
- Trolling, insulting or derogatory comments, and personal or political attacks
|
||||
- Public or private harassment
|
||||
- Publishing others' private information, such as a physical or email address,
|
||||
without their explicit permission
|
||||
- Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
We agree to restrict the following behaviors in our community. Instances, threats, and promotion of these behaviors are violations of this Code of Conduct.
|
||||
## Enforcement Responsibilities
|
||||
|
||||
1. **Harassment.** Violating explicitly expressed boundaries or engaging in unnecessary personal attention after any clear request to stop.
|
||||
2. **Character attacks.** Making insulting, demeaning, or pejorative comments directed at a community member or group of people.
|
||||
3. **Stereotyping or discrimination.** Characterizing anyone’s personality or behavior on the basis of immutable identities or traits.
|
||||
4. **Sexualization.** Behaving in a way that would generally be considered inappropriately intimate in the context or purpose of the community.
|
||||
5. **Violating confidentiality**. Sharing or acting on someone's personal or private information without their permission.
|
||||
6. **Endangerment.** Causing, encouraging, or threatening violence or other harm toward any person or group.
|
||||
7. Behaving in other ways that **threaten the well-being** of our community.
|
||||
Community leaders are responsible for clarifying and enforcing our standards of
|
||||
acceptable behavior and will take appropriate and fair corrective action in
|
||||
response to any behavior that they deem inappropriate, threatening, offensive,
|
||||
or harmful.
|
||||
|
||||
### Other Restrictions
|
||||
|
||||
1. **Misleading identity.** Impersonating someone else for any reason, or pretending to be someone else to evade enforcement actions.
|
||||
2. **Failing to credit sources.** Not properly crediting the sources of content you contribute.
|
||||
3. **Promotional materials**. Sharing marketing or other commercial content in a way that is outside the norms of the community.
|
||||
4. **Irresponsible communication.** Failing to responsibly present content which includes, links or describes any other restricted behaviors.
|
||||
|
||||
## Reporting an Issue
|
||||
|
||||
Tensions can occur between community members even when they are trying their best to collaborate. Not every conflict represents a code of conduct violation, and this Code of Conduct reinforces encouraged behaviors and norms that can help avoid conflicts and minimize harm.
|
||||
|
||||
When an incident does occur, it is important to report it promptly. To report a possible violation, send an email describing the situation to hello@joinmastodon.org.
|
||||
|
||||
Community Moderators take reports of violations seriously and will make every effort to respond in a timely manner. They will investigate all reports of code of conduct violations, reviewing messages, logs, and recordings, or interviewing witnesses and other participants. Community Moderators will keep investigation and enforcement actions as transparent as possible while prioritizing safety and confidentiality. In order to honor these values, enforcement actions are carried out in private with the involved parties, but communicating to the whole community may be part of a mutually agreed upon resolution.
|
||||
|
||||
## Addressing and Repairing Harm
|
||||
|
||||
If an investigation by the Community Moderators finds that this Code of Conduct has been violated, the following enforcement ladder may be used to determine how best to repair harm, based on the incident's impact on the individuals involved and the community as a whole. Depending on the severity of a violation, lower rungs on the ladder may be skipped.
|
||||
|
||||
1. Warning
|
||||
1. Event: A violation involving a single incident or series of incidents.
|
||||
2. Consequence: A private, written warning from the Community Moderators.
|
||||
3. Repair: Examples of repair include a private written apology, acknowledgement of responsibility, and seeking clarification on expectations.
|
||||
2. Temporarily Limited Activities
|
||||
1. Event: A repeated incidence of a violation that previously resulted in a warning, or the first incidence of a more serious violation.
|
||||
2. Consequence: A private, written warning with a time-limited cooldown period designed to underscore the seriousness of the situation and give the community members involved time to process the incident. The cooldown period may be limited to particular communication channels or interactions with particular community members.
|
||||
3. Repair: Examples of repair may include making an apology, using the cooldown period to reflect on actions and impact, and being thoughtful about re-entering community spaces after the period is over.
|
||||
3. Temporary Suspension
|
||||
1. Event: A pattern of repeated violation which the Community Moderators have tried to address with warnings, or a single serious violation.
|
||||
2. Consequence: A private written warning with conditions for return from suspension. In general, temporary suspensions give the person being suspended time to reflect upon their behavior and possible corrective actions.
|
||||
3. Repair: Examples of repair include respecting the spirit of the suspension, meeting the specified conditions for return, and being thoughtful about how to reintegrate with the community when the suspension is lifted.
|
||||
4. Permanent Ban
|
||||
1. Event: A pattern of repeated code of conduct violations that other steps on the ladder have failed to resolve, or a violation so serious that the Community Moderators determine there is no way to keep the community safe with this person as a member.
|
||||
2. Consequence: Access to all community spaces, tools, and communication channels is removed. In general, permanent bans should be rarely used, should have strong reasoning behind them, and should only be resorted to if working through other remedies has failed to change the behavior.
|
||||
3. Repair: There is no possible repair in cases of this severity.
|
||||
|
||||
This enforcement ladder is intended as a guideline. It does not limit the ability of Community Managers to use their discretion and judgment, in keeping with the best interests of our community.
|
||||
Community leaders have the right and responsibility to remove, edit, or reject
|
||||
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
||||
decisions when appropriate.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public or other spaces. Examples of representing our community include using an official email address, posting via an official social media account, or acting as an appointed representative at an online or offline event.
|
||||
This Code of Conduct applies within all community spaces, and also applies when
|
||||
an individual is officially representing the community in public spaces.
|
||||
Examples of representing our community include using an official e-mail address,
|
||||
posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported to the community leaders responsible for enforcement at
|
||||
[hello@joinmastodon.org](mailto:hello@joinmastodon.org).
|
||||
All complaints will be reviewed and investigated promptly and fairly.
|
||||
|
||||
All community leaders are obligated to respect the privacy and security of the
|
||||
reporter of any incident.
|
||||
|
||||
## Enforcement Guidelines
|
||||
|
||||
Community leaders will follow these Community Impact Guidelines in determining
|
||||
the consequences for any action they deem in violation of this Code of Conduct:
|
||||
|
||||
### 1. Correction
|
||||
|
||||
**Community Impact**: Use of inappropriate language or other behavior deemed
|
||||
unprofessional or unwelcome in the community.
|
||||
|
||||
**Consequence**: A private, written warning from community leaders, providing
|
||||
clarity around the nature of the violation and an explanation of why the
|
||||
behavior was inappropriate. A public apology may be requested.
|
||||
|
||||
### 2. Warning
|
||||
|
||||
**Community Impact**: A violation through a single incident or series of
|
||||
actions.
|
||||
|
||||
**Consequence**: A warning with consequences for continued behavior. No
|
||||
interaction with the people involved, including unsolicited interaction with
|
||||
those enforcing the Code of Conduct, for a specified period of time. This
|
||||
includes avoiding interactions in community spaces as well as external channels
|
||||
like social media. Violating these terms may lead to a temporary or permanent
|
||||
ban.
|
||||
|
||||
### 3. Temporary Ban
|
||||
|
||||
**Community Impact**: A serious violation of community standards, including
|
||||
sustained inappropriate behavior.
|
||||
|
||||
**Consequence**: A temporary ban from any sort of interaction or public
|
||||
communication with the community for a specified period of time. No public or
|
||||
private interaction with the people involved, including unsolicited interaction
|
||||
with those enforcing the Code of Conduct, is allowed during this period.
|
||||
Violating these terms may lead to a permanent ban.
|
||||
|
||||
### 4. Permanent Ban
|
||||
|
||||
**Community Impact**: Demonstrating a pattern of violation of community
|
||||
standards, including sustained inappropriate behavior, harassment of an
|
||||
individual, or aggression toward or disparagement of classes of individuals.
|
||||
|
||||
**Consequence**: A permanent ban from any sort of public interaction within the
|
||||
community.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the Contributor Covenant, version 3.0, permanently available at [https://www.contributor-covenant.org/version/3/0/](https://www.contributor-covenant.org/version/3/0/).
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||
version 2.1, available at
|
||||
[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
|
||||
|
||||
Contributor Covenant is stewarded by the Organization for Ethical Source and licensed under CC BY-SA 4.0. To view a copy of this license, visit [https://creativecommons.org/licenses/by-sa/4.0/](https://creativecommons.org/licenses/by-sa/4.0/)
|
||||
Community Impact Guidelines were inspired by
|
||||
[Mozilla's code of conduct enforcement ladder][Mozilla CoC].
|
||||
|
||||
For answers to common questions about Contributor Covenant, see the FAQ at [https://www.contributor-covenant.org/faq](https://www.contributor-covenant.org/faq). Translations are provided at [https://www.contributor-covenant.org/translations](https://www.contributor-covenant.org/translations). Additional enforcement and community guideline resources can be found at [https://www.contributor-covenant.org/resources](https://www.contributor-covenant.org/resources). The enforcement ladder was inspired by the work of [Mozilla’s code of conduct team](https://github.com/mozilla/inclusion).
|
||||
For answers to common questions about this code of conduct, see the FAQ at
|
||||
[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at
|
||||
[https://www.contributor-covenant.org/translations][translations].
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
|
||||
[Mozilla CoC]: https://github.com/mozilla/diversity
|
||||
[FAQ]: https://www.contributor-covenant.org/faq
|
||||
[translations]: https://www.contributor-covenant.org/translations
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
239
Dockerfile
239
Dockerfile
|
|
@ -1,4 +1,4 @@
|
|||
# syntax=docker/dockerfile:1.18
|
||||
# syntax=docker/dockerfile:1.12
|
||||
|
||||
# This file is designed for production server deployment, not local development work
|
||||
# For a containerized local dev environment, see: https://github.com/mastodon/mastodon/blob/main/docs/DEVELOPMENT.md#docker
|
||||
|
|
@ -11,22 +11,22 @@ ARG TARGETPLATFORM=${TARGETPLATFORM}
|
|||
ARG BUILDPLATFORM=${BUILDPLATFORM}
|
||||
ARG BASE_REGISTRY="docker.io"
|
||||
|
||||
# Ruby image to use for base image, change with [--build-arg RUBY_VERSION="4.0.x"]
|
||||
# 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="4.0.2"
|
||||
# # Node.js version to use in base image, change with [--build-arg NODE_MAJOR_VERSION="22"]
|
||||
ARG RUBY_VERSION="3.4.4"
|
||||
# # Node.js version to use in base image, change with [--build-arg NODE_MAJOR_VERSION="20"]
|
||||
# renovate: datasource=node-version depName=node
|
||||
ARG NODE_MAJOR_VERSION="24"
|
||||
# Debian image to use for base image, change with [--build-arg DEBIAN_VERSION="trixie"]
|
||||
ARG DEBIAN_VERSION="trixie"
|
||||
# Node.js image to use for base image based on combined variables (ex: 20-trixie-slim)
|
||||
ARG NODE_MAJOR_VERSION="22"
|
||||
# Debian image to use for base image, change with [--build-arg DEBIAN_VERSION="bookworm"]
|
||||
ARG DEBIAN_VERSION="bookworm"
|
||||
# Node.js image to use for base image based on combined variables (ex: 20-bookworm-slim)
|
||||
FROM ${BASE_REGISTRY}/node:${NODE_MAJOR_VERSION}-${DEBIAN_VERSION}-slim AS node
|
||||
# Ruby image to use for base image based on combined variables (ex: 3.4.x-slim-trixie)
|
||||
# Ruby image to use for base image based on combined variables (ex: 3.4.x-slim-bookworm)
|
||||
FROM ${BASE_REGISTRY}/ruby:${RUBY_VERSION}-slim-${DEBIAN_VERSION} AS ruby
|
||||
|
||||
# Resulting version string is vX.X.X-MASTODON_VERSION_PRERELEASE+MASTODON_VERSION_METADATA
|
||||
# Example: v4.3.0-nightly.2023-11-09+pr-123456
|
||||
# Overwrite existence of 'alpha.X' in version.rb [--build-arg MASTODON_VERSION_PRERELEASE="nightly.2023-11-09"]
|
||||
# Example: v4.3.0-nightly.2023.11.09+pr-123456
|
||||
# Overwrite existence of 'alpha.X' in version.rb [--build-arg MASTODON_VERSION_PRERELEASE="nightly.2023.11.09"]
|
||||
ARG MASTODON_VERSION_PRERELEASE=""
|
||||
# Append build metadata or fork information to version.rb [--build-arg MASTODON_VERSION_METADATA="pr-123456"]
|
||||
ARG MASTODON_VERSION_METADATA=""
|
||||
|
|
@ -48,27 +48,31 @@ ARG GID="991"
|
|||
|
||||
# Apply Mastodon build options based on options above
|
||||
ENV \
|
||||
# Apply Mastodon version information
|
||||
MASTODON_VERSION_PRERELEASE="${MASTODON_VERSION_PRERELEASE}" \
|
||||
MASTODON_VERSION_METADATA="${MASTODON_VERSION_METADATA}" \
|
||||
SOURCE_COMMIT="${SOURCE_COMMIT}" \
|
||||
RAILS_SERVE_STATIC_FILES="${RAILS_SERVE_STATIC_FILES}" \
|
||||
RUBY_YJIT_ENABLE="${RUBY_YJIT_ENABLE}" \
|
||||
TZ="${TZ}"
|
||||
# Apply Mastodon static files and YJIT options
|
||||
RAILS_SERVE_STATIC_FILES=${RAILS_SERVE_STATIC_FILES} \
|
||||
RUBY_YJIT_ENABLE=${RUBY_YJIT_ENABLE} \
|
||||
# Apply timezone
|
||||
TZ=${TZ}
|
||||
|
||||
# Configure runtime environment
|
||||
# BIND: IP to bind Mastodon to when serving traffic
|
||||
# NODE_ENV/RAILS_ENV: production settings for Node.js and Ruby on Rails
|
||||
# DEBIAN_FRONTEND: suppress interactive prompts
|
||||
# PATH: add Ruby and Mastodon installation directories
|
||||
# MALLOC_CONF: optimize jemalloc 5.x performance
|
||||
# MASTODON_SIDEKIQ_READY_FILENAME: Sidekiq readiness check filename for Kubernetes
|
||||
ENV \
|
||||
# Configure the IP to bind Mastodon to when serving traffic
|
||||
BIND="0.0.0.0" \
|
||||
# Use production settings for Yarn, Node.js and related tools
|
||||
NODE_ENV="production" \
|
||||
# Use production settings for Ruby on Rails
|
||||
RAILS_ENV="production" \
|
||||
# Add Ruby and Mastodon installation to the PATH
|
||||
DEBIAN_FRONTEND="noninteractive" \
|
||||
PATH="${PATH}:/opt/ruby/bin:/opt/mastodon/bin" \
|
||||
# Optimize jemalloc 5.x performance
|
||||
MALLOC_CONF="narenas:2,background_thread:true,thp:never,dirty_decay_ms:1000,muzzy_decay_ms:0" \
|
||||
# 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
|
||||
|
||||
# Set default shell used for running commands
|
||||
|
|
@ -92,15 +96,18 @@ RUN \
|
|||
# Set /opt/mastodon as working directory
|
||||
WORKDIR /opt/mastodon
|
||||
|
||||
# Add backport repository for some specific packages where we need the latest version
|
||||
RUN echo 'deb http://deb.debian.org/debian bookworm-backports main' >> /etc/apt/sources.list
|
||||
|
||||
# hadolint ignore=DL3008,DL3005
|
||||
RUN \
|
||||
# Mount Apt cache and lib directories from Docker buildx caches
|
||||
--mount=type=cache,id=apt-cache-${TARGETPLATFORM},target=/var/cache/apt,sharing=locked \
|
||||
--mount=type=cache,id=apt-lib-${TARGETPLATFORM},target=/var/lib/apt,sharing=locked \
|
||||
# Update package list and upgrade system packages
|
||||
# Apt update & upgrade to check for security updates to Debian image
|
||||
apt-get update; \
|
||||
apt-get dist-upgrade -yq; \
|
||||
# Install jemalloc and other necessary components
|
||||
# Install jemalloc, curl and other necessary components
|
||||
apt-get install -y --no-install-recommends \
|
||||
curl \
|
||||
file \
|
||||
|
|
@ -110,42 +117,6 @@ RUN \
|
|||
tini \
|
||||
tzdata \
|
||||
wget \
|
||||
# Mastodon components
|
||||
libexpat1 \
|
||||
libglib2.0-0t64 \
|
||||
libicu76 \
|
||||
libidn12 \
|
||||
libpq5 \
|
||||
libreadline8t64 \
|
||||
libssl3t64 \
|
||||
libyaml-0-2 \
|
||||
# libvips components
|
||||
libcgif0 \
|
||||
libexif12 \
|
||||
libheif1 \
|
||||
libhwy1t64 \
|
||||
libimagequant0 \
|
||||
libjpeg62-turbo \
|
||||
liblcms2-2 \
|
||||
libspng0 \
|
||||
libtiff6 \
|
||||
libwebp7 \
|
||||
libwebpdemux2 \
|
||||
libwebpmux3 \
|
||||
# ffmpeg components
|
||||
libdav1d7 \
|
||||
libmp3lame0 \
|
||||
libopencore-amrnb0 \
|
||||
libopencore-amrwb0 \
|
||||
libopus0 \
|
||||
libsnappy1v5 \
|
||||
libtheora0 \
|
||||
libvorbis0a \
|
||||
libvorbisenc2 \
|
||||
libvorbisfile3 \
|
||||
libvpx9 \
|
||||
libx264-164 \
|
||||
libx265-215 \
|
||||
; \
|
||||
# Patch Ruby to use jemalloc
|
||||
patchelf --add-needed libjemalloc.so.2 /usr/local/bin/ruby; \
|
||||
|
|
@ -154,42 +125,47 @@ RUN \
|
|||
patchelf \
|
||||
;
|
||||
|
||||
# Build stage for media libraries (libvips, ffmpeg)
|
||||
FROM ${BASE_REGISTRY}/ruby:${RUBY_VERSION}-slim-${DEBIAN_VERSION} AS media-build
|
||||
# Create temporary build layer from base image
|
||||
FROM ruby AS build
|
||||
|
||||
ARG TARGETPLATFORM
|
||||
|
||||
# Set default shell used for running commands
|
||||
SHELL ["/bin/bash", "-o", "pipefail", "-o", "errexit", "-c"]
|
||||
|
||||
# hadolint ignore=DL3008
|
||||
RUN \
|
||||
--mount=type=cache,id=apt-native-cache-${TARGETPLATFORM},target=/var/cache/apt,sharing=locked \
|
||||
--mount=type=cache,id=apt-native-lib-${TARGETPLATFORM},target=/var/lib/apt,sharing=locked \
|
||||
# Remove automatic apt cache Docker cleanup scripts
|
||||
rm -f /etc/apt/apt.conf.d/docker-clean; \
|
||||
# Install build tools for native libraries
|
||||
apt-get update; \
|
||||
# Mount Apt cache and lib directories from Docker buildx caches
|
||||
--mount=type=cache,id=apt-cache-${TARGETPLATFORM},target=/var/cache/apt,sharing=locked \
|
||||
--mount=type=cache,id=apt-lib-${TARGETPLATFORM},target=/var/lib/apt,sharing=locked \
|
||||
# Install build tools and bundler dependencies from APT
|
||||
apt-get install -y --no-install-recommends \
|
||||
autoconf \
|
||||
automake \
|
||||
build-essential \
|
||||
cmake \
|
||||
git \
|
||||
libgdbm-dev \
|
||||
libglib2.0-dev \
|
||||
libgmp-dev \
|
||||
libicu-dev \
|
||||
libidn-dev \
|
||||
libpq-dev \
|
||||
libssl-dev \
|
||||
libtool \
|
||||
libyaml-dev \
|
||||
meson \
|
||||
nasm \
|
||||
pkg-config \
|
||||
shared-mime-info \
|
||||
xz-utils \
|
||||
# libvips components
|
||||
libcgif-dev \
|
||||
libexif-dev \
|
||||
libexpat1-dev \
|
||||
libgirepository1.0-dev \
|
||||
libglib2.0-dev \
|
||||
libheif-dev \
|
||||
libhwy-dev \
|
||||
libheif-dev/bookworm-backports \
|
||||
libimagequant-dev \
|
||||
libjpeg62-turbo-dev \
|
||||
liblcms2-dev \
|
||||
liborc-dev \
|
||||
libspng-dev \
|
||||
libtiff-dev \
|
||||
libwebp-dev \
|
||||
|
|
@ -205,12 +181,12 @@ RUN \
|
|||
libx265-dev \
|
||||
;
|
||||
|
||||
# Create temporary libvips specific build layer
|
||||
FROM media-build AS libvips
|
||||
# Create temporary libvips specific build layer from build layer
|
||||
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.2
|
||||
ARG VIPS_VERSION=8.17.0
|
||||
# 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
|
||||
|
||||
|
|
@ -221,27 +197,26 @@ RUN tar xf vips-${VIPS_VERSION}.tar.xz;
|
|||
|
||||
WORKDIR /usr/local/libvips/src/vips-${VIPS_VERSION}
|
||||
|
||||
# Configure libvips
|
||||
RUN meson setup build --prefix /usr/local/libvips --libdir=lib -Ddeprecated=false -Dintrospection=disabled -Dmodules=disabled -Dexamples=false
|
||||
# Configure and compile libvips
|
||||
RUN \
|
||||
meson setup build --prefix /usr/local/libvips --libdir=lib -Ddeprecated=false -Dintrospection=disabled -Dmodules=disabled -Dexamples=false; \
|
||||
cd build; \
|
||||
ninja; \
|
||||
ninja install;
|
||||
|
||||
WORKDIR /usr/local/libvips/src/vips-${VIPS_VERSION}/build
|
||||
|
||||
# Compile and install libvips
|
||||
RUN ninja && ninja install
|
||||
|
||||
# Create temporary ffmpeg specific build layer
|
||||
FROM media-build AS ffmpeg
|
||||
# Create temporary ffmpeg specific build layer from build layer
|
||||
FROM build AS ffmpeg
|
||||
|
||||
# ffmpeg version to compile, change with [--build-arg FFMPEG_VERSION="7.0.x"]
|
||||
# renovate: datasource=github-tags depName=FFmpeg/FFmpeg extractVersion=^n(?<version>\d+\.\d+(\.\d+)?)$
|
||||
ARG FFMPEG_VERSION=8.1
|
||||
# renovate: datasource=repology depName=ffmpeg packageName=openpkg_current/ffmpeg
|
||||
ARG FFMPEG_VERSION=7.1
|
||||
# ffmpeg download URL, change with [--build-arg FFMPEG_URL="https://ffmpeg.org/releases"]
|
||||
ARG FFMPEG_URL=https://github.com/FFmpeg/FFmpeg/archive/refs/tags
|
||||
ARG FFMPEG_URL=https://ffmpeg.org/releases
|
||||
|
||||
WORKDIR /usr/local/ffmpeg/src
|
||||
# Download and extract ffmpeg source code
|
||||
ADD ${FFMPEG_URL}/n${FFMPEG_VERSION}.tar.gz /usr/local/ffmpeg/src/
|
||||
RUN tar xf n${FFMPEG_VERSION}.tar.gz && mv FFmpeg-n${FFMPEG_VERSION} ffmpeg-${FFMPEG_VERSION};
|
||||
ADD ${FFMPEG_URL}/ffmpeg-${FFMPEG_VERSION}.tar.xz /usr/local/ffmpeg/src/
|
||||
RUN tar xf ffmpeg-${FFMPEG_VERSION}.tar.xz;
|
||||
|
||||
WORKDIR /usr/local/ffmpeg/src/ffmpeg-${FFMPEG_VERSION}
|
||||
|
||||
|
|
@ -271,48 +246,17 @@ RUN \
|
|||
--enable-shared \
|
||||
--enable-version3 \
|
||||
; \
|
||||
make -j"$(nproc)"; \
|
||||
make -j$(nproc); \
|
||||
make install;
|
||||
|
||||
# Create temporary build layer from base image for Ruby dependencies
|
||||
FROM ruby AS ruby-build
|
||||
|
||||
ARG TARGETPLATFORM
|
||||
|
||||
# hadolint ignore=DL3008
|
||||
RUN \
|
||||
# Mount Apt cache and lib directories from Docker buildx caches
|
||||
--mount=type=cache,id=apt-cache-${TARGETPLATFORM},target=/var/cache/apt,sharing=locked \
|
||||
--mount=type=cache,id=apt-lib-${TARGETPLATFORM},target=/var/lib/apt,sharing=locked \
|
||||
# Install build tools and bundler dependencies from APT
|
||||
apt-get install -y --no-install-recommends \
|
||||
build-essential \
|
||||
git \
|
||||
libgdbm-dev \
|
||||
libgmp-dev \
|
||||
libicu-dev \
|
||||
libidn-dev \
|
||||
libpq-dev \
|
||||
libssl-dev \
|
||||
libyaml-dev \
|
||||
shared-mime-info \
|
||||
zlib1g-dev \
|
||||
;
|
||||
|
||||
# Create temporary bundler specific build layer from build layer
|
||||
FROM ruby-build AS bundler
|
||||
FROM build AS bundler
|
||||
|
||||
ARG TARGETPLATFORM
|
||||
|
||||
# Copy Gemfile config into working directory
|
||||
COPY Gemfile* /opt/mastodon/
|
||||
|
||||
# Copy libvips for gems that need it during install
|
||||
COPY --from=libvips /usr/local/libvips/lib /usr/local/lib
|
||||
COPY --from=libvips /usr/local/libvips/include /usr/local/include
|
||||
|
||||
RUN ldconfig
|
||||
|
||||
RUN \
|
||||
# Mount Ruby Gem caches
|
||||
--mount=type=cache,id=gem-cache-${TARGETPLATFORM},target=/usr/local/bundle/cache/,sharing=locked \
|
||||
|
|
@ -328,7 +272,7 @@ RUN \
|
|||
bundle install -j"$(nproc)";
|
||||
|
||||
# Create temporary assets build layer from build layer
|
||||
FROM ruby-build AS precompiler
|
||||
FROM build AS precompiler
|
||||
|
||||
ARG TARGETPLATFORM
|
||||
|
||||
|
|
@ -372,6 +316,53 @@ FROM ruby AS mastodon
|
|||
|
||||
ARG TARGETPLATFORM
|
||||
|
||||
# hadolint ignore=DL3008
|
||||
RUN \
|
||||
# Mount Apt cache and lib directories from Docker buildx caches
|
||||
--mount=type=cache,id=apt-cache-${TARGETPLATFORM},target=/var/cache/apt,sharing=locked \
|
||||
--mount=type=cache,id=apt-lib-${TARGETPLATFORM},target=/var/lib/apt,sharing=locked \
|
||||
# Mount Corepack and Yarn caches from Docker buildx caches
|
||||
--mount=type=cache,id=corepack-cache-${TARGETPLATFORM},target=/usr/local/share/.cache/corepack,sharing=locked \
|
||||
--mount=type=cache,id=yarn-cache-${TARGETPLATFORM},target=/usr/local/share/.cache/yarn,sharing=locked \
|
||||
# Apt update install non-dev versions of necessary components
|
||||
apt-get install -y --no-install-recommends \
|
||||
libexpat1 \
|
||||
libglib2.0-0 \
|
||||
libicu72 \
|
||||
libidn12 \
|
||||
libpq5 \
|
||||
libreadline8 \
|
||||
libssl3 \
|
||||
libyaml-0-2 \
|
||||
# libvips components
|
||||
libcgif0 \
|
||||
libexif12 \
|
||||
libheif1/bookworm-backports \
|
||||
libimagequant0 \
|
||||
libjpeg62-turbo \
|
||||
liblcms2-2 \
|
||||
liborc-0.4-0 \
|
||||
libspng0 \
|
||||
libtiff6 \
|
||||
libwebp7 \
|
||||
libwebpdemux2 \
|
||||
libwebpmux3 \
|
||||
# ffmpeg components
|
||||
libdav1d6 \
|
||||
libmp3lame0 \
|
||||
libopencore-amrnb0 \
|
||||
libopencore-amrwb0 \
|
||||
libopus0 \
|
||||
libsnappy1v5 \
|
||||
libtheora0 \
|
||||
libvorbis0a \
|
||||
libvorbisenc2 \
|
||||
libvorbisfile3 \
|
||||
libvpx7 \
|
||||
libx264-164 \
|
||||
libx265-199 \
|
||||
;
|
||||
|
||||
# Copy Mastodon sources into final layer
|
||||
COPY . /opt/mastodon/
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
@ -49,25 +48,3 @@ Mastodon requires all `POST` requests to be signed, and MAY require `GET` reques
|
|||
### Additional documentation
|
||||
|
||||
- [Mastodon documentation](https://docs.joinmastodon.org/)
|
||||
|
||||
## 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.
|
||||
|
||||
| Limited property | Size limit | Consequence of exceeding the limit |
|
||||
| ------------------------------------------------------------- | ---------- | ---------------------------------- |
|
||||
| Serialized JSON-LD | 1MB | **Activity is rejected/dropped** |
|
||||
| Profile fields (actor `PropertyValue` attachments) name/value | 2047 | Field name/value is truncated |
|
||||
| Number of profile fields (actor `PropertyValue` attachments) | 50 | Fields list is truncated |
|
||||
| Poll options (number of `anyOf`/`oneOf` in a `Question`) | 500 | Items list is truncated |
|
||||
| Account username (actor `preferredUsername`) length | 2048 | **Actor will be rejected** |
|
||||
| Account display name (actor `name`) length | 2048 | Display name will be truncated |
|
||||
| Account note (actor `summary`) length | 20kB | Account note will be truncated |
|
||||
| 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`) | 10000 | Description will be truncated |
|
||||
| Collection name (`FeaturedCollection` `name`) | 256 | Name will be truncated |
|
||||
| Collection description (`FeaturedCollection` `summary`) | 2048 | Description will be truncated |
|
||||
|
|
|
|||
77
Gemfile
77
Gemfile
|
|
@ -1,19 +1,19 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
source 'https://rubygems.org'
|
||||
ruby '>= 3.3.0', '< 4.1.0'
|
||||
ruby '>= 3.2.0', '< 3.5.0'
|
||||
|
||||
gem 'propshaft'
|
||||
gem 'puma', '~> 7.0'
|
||||
gem 'rails', '~> 8.1.0'
|
||||
gem 'puma', '~> 6.3'
|
||||
gem 'rails', '~> 8.0'
|
||||
gem 'thor', '~> 1.2'
|
||||
|
||||
gem 'dotenv'
|
||||
gem 'haml-rails', '~>3.0'
|
||||
gem 'haml-rails', '~>2.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'
|
||||
|
|
@ -50,43 +50,45 @@ gem 'doorkeeper', '~> 5.6'
|
|||
gem 'faraday-httpclient'
|
||||
gem 'fast_blank', '~> 1.0'
|
||||
gem 'fastimage'
|
||||
gem 'hiredis', '~> 0.6'
|
||||
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'
|
||||
gem 'irb', '~> 1.8'
|
||||
gem 'kaminari', '~> 1.2'
|
||||
gem 'link_header', '~> 0.0'
|
||||
gem 'linzer', '~> 0.7.7'
|
||||
gem 'linzer', '~> 0.7.2'
|
||||
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'
|
||||
gem 'rails-i18n', '~> 8.0'
|
||||
gem 'redcarpet', '~> 3.6'
|
||||
gem 'redis', '~> 5'
|
||||
gem 'redis', '~> 4.5', require: ['redis', 'redis/connection/hiredis']
|
||||
gem 'rqrcode', '~> 3.0'
|
||||
gem 'ruby-progressbar', '~> 1.13'
|
||||
gem 'sanitize', '~> 7.0'
|
||||
gem 'scenic', '~> 1.7'
|
||||
gem 'sidekiq', '< 9'
|
||||
gem 'sidekiq', '< 8'
|
||||
gem 'sidekiq-bulk', '~> 0.2.0'
|
||||
gem 'sidekiq-scheduler', '~> 6.0'
|
||||
gem 'sidekiq-scheduler', '~> 5.0'
|
||||
gem 'sidekiq-unique-jobs', '> 8'
|
||||
gem 'simple_form', '~> 5.2'
|
||||
gem 'simple-navigation', '~> 4.4'
|
||||
gem 'stoplight'
|
||||
gem 'stoplight', '~> 4.1'
|
||||
gem 'strong_migrations'
|
||||
gem 'tty-prompt', '~> 0.23', require: false
|
||||
gem 'twitter-text', '~> 3.1.0'
|
||||
|
|
@ -94,30 +96,29 @@ 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.9.0'
|
||||
gem 'opentelemetry-api', '~> 1.5.0'
|
||||
|
||||
group :opentelemetry do
|
||||
gem 'opentelemetry-exporter-otlp', '~> 0.33.0', require: false
|
||||
gem 'opentelemetry-instrumentation-active_job', '~> 0.11.0', require: false
|
||||
gem 'opentelemetry-instrumentation-active_model_serializers', '~> 0.25.0', require: false
|
||||
gem 'opentelemetry-instrumentation-concurrent_ruby', '~> 0.25.0', require: false
|
||||
gem 'opentelemetry-instrumentation-excon', '~> 0.29.0', require: false
|
||||
gem 'opentelemetry-instrumentation-faraday', '~> 0.33.0', require: false
|
||||
gem 'opentelemetry-instrumentation-http', '~> 0.30.0', require: false
|
||||
gem 'opentelemetry-instrumentation-http_client', '~> 0.29.0', require: false
|
||||
gem 'opentelemetry-instrumentation-net_http', '~> 0.29.0', require: false
|
||||
gem 'opentelemetry-instrumentation-pg', '~> 0.36.0', require: false
|
||||
gem 'opentelemetry-instrumentation-rack', '~> 0.31.0', require: false
|
||||
gem 'opentelemetry-instrumentation-rails', '~> 0.41.0', require: false
|
||||
gem 'opentelemetry-instrumentation-redis', '~> 0.29.0', require: false
|
||||
gem 'opentelemetry-instrumentation-sidekiq', '~> 0.29.0', require: false
|
||||
gem 'opentelemetry-exporter-otlp', '~> 0.30.0', require: false
|
||||
gem 'opentelemetry-instrumentation-active_job', '~> 0.8.0', require: false
|
||||
gem 'opentelemetry-instrumentation-active_model_serializers', '~> 0.22.0', require: false
|
||||
gem 'opentelemetry-instrumentation-concurrent_ruby', '~> 0.22.0', require: false
|
||||
gem 'opentelemetry-instrumentation-excon', '~> 0.23.0', require: false
|
||||
gem 'opentelemetry-instrumentation-faraday', '~> 0.27.0', require: false
|
||||
gem 'opentelemetry-instrumentation-http', '~> 0.25.0', require: false
|
||||
gem 'opentelemetry-instrumentation-http_client', '~> 0.23.0', require: false
|
||||
gem 'opentelemetry-instrumentation-net_http', '~> 0.23.0', require: false
|
||||
gem 'opentelemetry-instrumentation-pg', '~> 0.30.0', require: false
|
||||
gem 'opentelemetry-instrumentation-rack', '~> 0.26.0', require: false
|
||||
gem 'opentelemetry-instrumentation-rails', '~> 0.36.0', require: false
|
||||
gem 'opentelemetry-instrumentation-redis', '~> 0.26.0', require: false
|
||||
gem 'opentelemetry-instrumentation-sidekiq', '~> 0.26.0', require: false
|
||||
gem 'opentelemetry-sdk', '~> 1.4', require: false
|
||||
end
|
||||
|
||||
|
|
@ -128,13 +129,15 @@ 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.59.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'
|
||||
|
|
@ -143,7 +146,7 @@ group :test do
|
|||
gem 'climate_control'
|
||||
|
||||
# Validate schemas in specs
|
||||
gem 'json-schema', '~> 6.0'
|
||||
gem 'json-schema', '~> 5.0'
|
||||
|
||||
# Test harness fo rack components
|
||||
gem 'rack-test', '~> 2.1'
|
||||
|
|
@ -176,14 +179,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
|
||||
|
|
@ -223,7 +226,7 @@ gem 'connection_pool', require: false
|
|||
gem 'xorcist', '~> 1.1'
|
||||
|
||||
gem 'net-http', '~> 0.6.0'
|
||||
gem 'rubyzip', '~> 3.0'
|
||||
gem 'rubyzip', '~> 2.3'
|
||||
|
||||
gem 'hcaptcha', '~> 7.1'
|
||||
|
||||
|
|
|
|||
825
Gemfile.lock
825
Gemfile.lock
File diff suppressed because it is too large
Load Diff
71
README.md
71
README.md
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
<p align="center">
|
||||
<a style="text-decoration:none" href="https://www.youtube.com/watch?v=IPSbNdBmWKE">
|
||||
<img alt="Mastodon hero image" src="./docs/hero-nodes.gif" />
|
||||
<img alt="Mastodon hero image" src="https://github.com/user-attachments/assets/ef53f5e9-c0d8-484d-9f53-00efdebb92c3" />
|
||||
</a>
|
||||
</p>
|
||||
|
||||
|
|
@ -17,72 +17,71 @@
|
|||
<img src="https://d322cqt584bo4o.cloudfront.net/mastodon/localized.svg" alt="Crowdin" /></a>
|
||||
</p>
|
||||
|
||||
Mastodon is a **free, open-source social network server** based on [ActivityPub](https://www.w3.org/TR/activitypub/) where users can follow friends and discover new ones. On Mastodon, users can publish anything they want: links, pictures, text, and video. All Mastodon servers are interoperable as a federated network (users on one server can seamlessly communicate with users from another one, including non-Mastodon software that implements ActivityPub!)
|
||||
Mastodon is a **free, open-source social network server** based on ActivityPub where users can follow friends and discover new ones. On Mastodon, users can publish anything they want: links, pictures, text, and video. All Mastodon servers are interoperable as a federated network (users on one server can seamlessly communicate with users from another one, including non-Mastodon software that implements ActivityPub!)
|
||||
|
||||
## Navigation
|
||||
|
||||
- [Project homepage 🐘](https://joinmastodon.org)
|
||||
- [Donate to support development 🎁](https://joinmastodon.org/sponsors#donate)
|
||||
- [View sponsors](https://joinmastodon.org/sponsors)
|
||||
- [Blog 📰](https://blog.joinmastodon.org)
|
||||
- [Documentation 📚](https://docs.joinmastodon.org)
|
||||
- [Official container image 🚢](https://github.com/mastodon/mastodon/pkgs/container/mastodon)
|
||||
- [Support the development via Patreon][patreon]
|
||||
- [View sponsors](https://joinmastodon.org/sponsors)
|
||||
- [Blog](https://blog.joinmastodon.org)
|
||||
- [Documentation](https://docs.joinmastodon.org)
|
||||
- [Roadmap](https://joinmastodon.org/roadmap)
|
||||
- [Official Docker image](https://github.com/mastodon/mastodon/pkgs/container/mastodon)
|
||||
- [Browse Mastodon servers](https://joinmastodon.org/communities)
|
||||
- [Browse Mastodon apps](https://joinmastodon.org/apps)
|
||||
|
||||
[patreon]: https://www.patreon.com/mastodon
|
||||
|
||||
## Features
|
||||
|
||||
<img src="./app/javascript/images/elephant_ui_working.svg?raw=true" align="right" width="30%" />
|
||||
<img src="/app/javascript/images/elephant_ui_working.svg?raw=true" align="right" width="30%" />
|
||||
|
||||
**Part of the Fediverse. Based on open standards, with no vendor lock-in.** - the network goes beyond just Mastodon; anything that implements ActivityPub is part of a broader social network known as [the Fediverse](https://jointhefediverse.net/). You can follow and interact with users on other servers (including those running different software), and they can follow you back.
|
||||
**No vendor lock-in: Fully interoperable with any conforming platform** - It doesn't have to be Mastodon; whatever implements ActivityPub is part of the social network! [Learn more](https://blog.joinmastodon.org/2018/06/why-activitypub-is-the-future/)
|
||||
|
||||
**Real-time, chronological timeline updates** - updates of people you're following appear in real-time in the UI.
|
||||
**Real-time, chronological timeline updates** - updates of people you're following appear in real-time in the UI via WebSockets. There's a firehose view as well!
|
||||
|
||||
**Media attachments** - upload and view images and videos attached to the updates. Videos with no audio track are treated like animated GIFs; normal videos loop continuously.
|
||||
**Media attachments like images and short videos** - upload and view images and WebM/MP4 videos attached to the updates. Videos with no audio track are treated like GIFs; normal videos loop continuously!
|
||||
|
||||
**Safety and moderation tools** - Mastodon includes private posts, locked accounts, phrase filtering, muting, blocking, and many other features, along with a reporting and moderation system.
|
||||
**Safety and moderation tools** - Mastodon includes private posts, locked accounts, phrase filtering, muting, blocking, and all sorts of other features, along with a reporting and moderation system. [Learn more](https://blog.joinmastodon.org/2018/07/cage-the-mastodon/)
|
||||
|
||||
**OAuth2 and a straightforward REST API** - Mastodon acts as an OAuth2 provider, and third party apps can use the REST and Streaming APIs. This results in a [rich app ecosystem](https://joinmastodon.org/apps) with a variety of choices!
|
||||
**OAuth2 and a straightforward REST API** - Mastodon acts as an OAuth2 provider, so 3rd party apps can use the REST and Streaming APIs. This results in a rich app ecosystem with a lot of choices!
|
||||
|
||||
## Deployment
|
||||
|
||||
### Tech stack
|
||||
|
||||
- [Ruby on Rails](https://github.com/rails/rails) powers the REST API and other web pages.
|
||||
- [PostgreSQL](https://www.postgresql.org/) is the main database.
|
||||
- [Redis](https://redis.io/) and [Sidekiq](https://sidekiq.org/) are used for caching and queueing.
|
||||
- [Node.js](https://nodejs.org/) powers the streaming API.
|
||||
- [React.js](https://reactjs.org/) and [Redux](https://redux.js.org/) are used for the dynamic parts of the interface.
|
||||
- [BrowserStack](https://www.browserstack.com/) supports testing on real devices and browsers. (This project is tested with BrowserStack)
|
||||
- [Chromatic](https://www.chromatic.com/) provides visual regression testing. (This project is tested with Chromatic)
|
||||
- **Ruby on Rails** powers the REST API and other web pages
|
||||
- **React.js** and **Redux** are used for the dynamic parts of the interface
|
||||
- **Node.js** powers the streaming API
|
||||
|
||||
### Requirements
|
||||
|
||||
- **Ruby** 3.3+
|
||||
- **PostgreSQL** 14+
|
||||
- **Redis** 7.0+
|
||||
- **PostgreSQL** 13+
|
||||
- **Redis** 6.2+
|
||||
- **Ruby** 3.2+
|
||||
- **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.
|
||||
The repository includes deployment configurations for **Docker and docker-compose** as well as specific platforms like **Heroku**, and **Scalingo**. For Helm charts, reference the [mastodon/chart repository](https://github.com/mastodon/chart). The [**standalone** installation guide](https://docs.joinmastodon.org/admin/install/) is available in the documentation.
|
||||
|
||||
## Contributing
|
||||
|
||||
Mastodon is **free, open-source software** licensed under **AGPLv3**. We welcome contributions and help from anyone who wants to improve the project.
|
||||
Mastodon is **free, open-source software** licensed under **AGPLv3**.
|
||||
|
||||
You should read the overall [CONTRIBUTING](https://github.com/mastodon/.github/blob/main/CONTRIBUTING.md) guide, which covers our development processes.
|
||||
You can open issues for bugs you've found or features you think are missing. You
|
||||
can also submit pull requests to this repository or translations via Crowdin. To
|
||||
get started, look at the [CONTRIBUTING] and [DEVELOPMENT] guides. For changes
|
||||
accepted into Mastodon, you can request to be paid through our [OpenCollective].
|
||||
|
||||
You should also read and understand the [CODE OF CONDUCT](https://github.com/mastodon/.github/blob/main/CODE_OF_CONDUCT.md) that enables us to maintain a welcoming and inclusive community. Collaboration begins with mutual respect and understanding.
|
||||
**IRC channel**: #mastodon on [`irc.libera.chat`](https://libera.chat)
|
||||
|
||||
You can learn about setting up a development environment in the [DEVELOPMENT](docs/DEVELOPMENT.md) documentation.
|
||||
|
||||
If you would like to help with translations 🌐 you can do so on [Crowdin](https://crowdin.com/project/mastodon).
|
||||
|
||||
## LICENSE
|
||||
## License
|
||||
|
||||
Copyright (c) 2016-2025 Eugen Rochko (+ [`mastodon authors`](AUTHORS.md))
|
||||
|
||||
Licensed under GNU Affero General Public License as stated in the [LICENSE](LICENSE):
|
||||
|
||||
```text
|
||||
```
|
||||
Copyright (c) 2016-2025 Eugen Rochko & other Mastodon contributors
|
||||
|
||||
This program is free software: you can redistribute it and/or modify it under
|
||||
|
|
@ -98,3 +97,7 @@ details.
|
|||
You should have received a copy of the GNU Affero General Public License along
|
||||
with this program. If not, see https://www.gnu.org/licenses/
|
||||
```
|
||||
|
||||
[CONTRIBUTING]: CONTRIBUTING.md
|
||||
[DEVELOPMENT]: docs/DEVELOPMENT.md
|
||||
[OpenCollective]: https://opencollective.com/mastodon
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ A "vulnerability in Mastodon" is a vulnerability in the code distributed through
|
|||
|
||||
| Version | Supported |
|
||||
| ------- | ---------------- |
|
||||
| 4.5.x | Yes |
|
||||
| 4.4.x | Yes |
|
||||
| 4.3.x | Until 2026-05-06 |
|
||||
| < 4.3 | No |
|
||||
| 4.3.x | Yes |
|
||||
| 4.2.x | Until 2026-01-08 |
|
||||
| < 4.2 | No |
|
||||
|
|
|
|||
4
Vagrantfile
vendored
4
Vagrantfile
vendored
|
|
@ -29,6 +29,7 @@ sudo apt-get install \
|
|||
libpq-dev \
|
||||
libxml2-dev \
|
||||
libxslt1-dev \
|
||||
imagemagick \
|
||||
nodejs \
|
||||
redis-server \
|
||||
redis-tools \
|
||||
|
|
@ -53,7 +54,6 @@ sudo apt-get install \
|
|||
pkg-config \
|
||||
protobuf-compiler \
|
||||
zlib1g-dev \
|
||||
libvips42t64 \
|
||||
-y
|
||||
|
||||
# Install rvm
|
||||
|
|
@ -134,7 +134,7 @@ VAGRANTFILE_API_VERSION = "2"
|
|||
|
||||
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
|
||||
|
||||
config.vm.box = "bento/ubuntu-24.04"
|
||||
config.vm.box = "ubuntu/focal64"
|
||||
|
||||
config.vm.provider :virtualbox do |vb|
|
||||
vb.name = "mastodon"
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -73,10 +71,6 @@ class AccountsController < ApplicationController
|
|||
params[:username]
|
||||
end
|
||||
|
||||
def account_id_param
|
||||
params[:id]
|
||||
end
|
||||
|
||||
def skip_temporary_suspension_response?
|
||||
request.format == :json
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,36 +1,20 @@
|
|||
# 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?
|
||||
before_action :check_authorization
|
||||
before_action :set_items
|
||||
before_action :set_size
|
||||
before_action :set_type
|
||||
|
||||
def show
|
||||
expires_in 3.minutes, public: public_fetch_mode?
|
||||
|
||||
if @unauthorized
|
||||
render json: collection_presenter, content_type: 'application/activity+json', serializer: ActivityPub::CollectionSerializer, adapter: ActivityPub::Adapter
|
||||
else
|
||||
render_with_cache json: collection_presenter, content_type: 'application/activity+json', serializer: ActivityPub::CollectionSerializer, adapter: ActivityPub::Adapter
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def check_authorization
|
||||
# Because in public fetch mode we cache the response, there would be no
|
||||
# benefit from performing the check below, since a blocked account or domain
|
||||
# would likely be served the cache from the reverse proxy anyway
|
||||
|
||||
@unauthorized = authorized_fetch_mode? && !signed_request_account.nil? && (@account.blocking?(signed_request_account) || (!signed_request_account.domain.nil? && @account.domain_blocking?(signed_request_account.domain)))
|
||||
end
|
||||
|
||||
def set_items
|
||||
case params[:id]
|
||||
when 'featured'
|
||||
|
|
@ -73,7 +57,11 @@ class ActivityPub::CollectionsController < ActivityPub::BaseController
|
|||
end
|
||||
|
||||
def for_signed_account
|
||||
if @unauthorized
|
||||
# Because in public fetch mode we cache the response, there would be no
|
||||
# benefit from performing the check below, since a blocked account or domain
|
||||
# would likely be served the cache from the reverse proxy anyway
|
||||
|
||||
if authorized_fetch_mode? && !signed_request_account.nil? && (@account.blocking?(signed_request_account) || (!signed_request_account.domain.nil? && @account.domain_blocking?(signed_request_account.domain)))
|
||||
[]
|
||||
else
|
||||
yield
|
||||
|
|
|
|||
|
|
@ -1,81 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class ActivityPub::ContextsController < ActivityPub::BaseController
|
||||
vary_by -> { 'Signature' if authorized_fetch_mode? }
|
||||
|
||||
before_action :require_account_signature!, if: :authorized_fetch_mode?
|
||||
before_action :set_conversation
|
||||
before_action :set_items
|
||||
|
||||
DESCENDANTS_LIMIT = 60
|
||||
|
||||
def show
|
||||
expires_in 3.minutes, public: public_fetch_mode?
|
||||
render_with_cache json: context_presenter, serializer: ActivityPub::ContextSerializer, adapter: ActivityPub::Adapter, content_type: 'application/activity+json'
|
||||
end
|
||||
|
||||
def items
|
||||
expires_in 3.minutes, public: public_fetch_mode?
|
||||
render_with_cache json: items_collection_presenter, serializer: ActivityPub::CollectionSerializer, adapter: ActivityPub::Adapter, content_type: 'application/activity+json'
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def account_required?
|
||||
false
|
||||
end
|
||||
|
||||
def set_conversation
|
||||
account_id, status_id = params[:id].split('-')
|
||||
@conversation = Conversation.local.find_by(parent_account_id: account_id, parent_status_id: status_id)
|
||||
end
|
||||
|
||||
def set_items
|
||||
@items = @conversation.statuses.distributable_visibility.paginate_by_min_id(DESCENDANTS_LIMIT, params[:min_id])
|
||||
end
|
||||
|
||||
def context_presenter
|
||||
first_page = ActivityPub::CollectionPresenter.new(
|
||||
type: :unordered,
|
||||
part_of: context_url(@conversation),
|
||||
next: next_page,
|
||||
items: @items.map { |status| status.local? ? ActivityPub::TagManager.instance.uri_for(status) : status.uri }
|
||||
)
|
||||
|
||||
ActivityPub::ContextPresenter.from_conversation(@conversation).tap do |presenter|
|
||||
presenter.first = first_page
|
||||
end
|
||||
end
|
||||
|
||||
def items_collection_presenter
|
||||
page = ActivityPub::CollectionPresenter.new(
|
||||
id: items_context_url(@conversation, page_params),
|
||||
type: :unordered,
|
||||
part_of: context_url(@conversation),
|
||||
next: next_page,
|
||||
items: @items.map { |status| status.local? ? ActivityPub::TagManager.instance.uri_for(status) : status.uri }
|
||||
)
|
||||
|
||||
return page if page_requested?
|
||||
|
||||
ActivityPub::CollectionPresenter.new(
|
||||
id: items_context_url(@conversation),
|
||||
type: :unordered,
|
||||
first: page
|
||||
)
|
||||
end
|
||||
|
||||
def page_requested?
|
||||
truthy_param?(:page)
|
||||
end
|
||||
|
||||
def next_page
|
||||
return nil if @items.size < DESCENDANTS_LIMIT
|
||||
|
||||
items_context_url(@conversation, page: true, min_id: @items.last.id)
|
||||
end
|
||||
|
||||
def page_params
|
||||
params.permit(:page, :min_id)
|
||||
end
|
||||
end
|
||||
|
|
@ -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,79 +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
|
||||
.includes(:accepted_collection_items)
|
||||
.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
|
||||
|
|
@ -3,7 +3,6 @@
|
|||
class ActivityPub::InboxesController < ActivityPub::BaseController
|
||||
include JsonLdHelper
|
||||
|
||||
before_action :skip_large_payload
|
||||
before_action :skip_unknown_actor_activity
|
||||
before_action :require_actor_signature!
|
||||
skip_before_action :authenticate_user!
|
||||
|
|
@ -17,18 +16,14 @@ class ActivityPub::InboxesController < ActivityPub::BaseController
|
|||
|
||||
private
|
||||
|
||||
def skip_large_payload
|
||||
head 413 if request.content_length > ActivityPub::Activity::MAX_JSON_SIZE
|
||||
end
|
||||
|
||||
def skip_unknown_actor_activity
|
||||
head 202 if unknown_affected_account?
|
||||
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 +39,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)
|
||||
|
||||
|
|
|
|||
|
|
@ -22,13 +22,13 @@ class ActivityPub::LikesController < ActivityPub::BaseController
|
|||
def set_status
|
||||
@status = @account.statuses.find(params[:status_id])
|
||||
authorize @status, :show?
|
||||
rescue ActiveRecord::RecordNotFound, Mastodon::NotPermittedError
|
||||
rescue Mastodon::NotPermittedError
|
||||
not_found
|
||||
end
|
||||
|
||||
def likes_collection_presenter
|
||||
ActivityPub::CollectionPresenter.new(
|
||||
id: ActivityPub::TagManager.instance.likes_uri_for(@status),
|
||||
id: account_status_likes_url(@account, @status),
|
||||
type: :unordered,
|
||||
size: @status.favourites_count
|
||||
)
|
||||
|
|
|
|||
|
|
@ -73,8 +73,6 @@ class ActivityPub::OutboxesController < ActivityPub::BaseController
|
|||
end
|
||||
|
||||
def set_account
|
||||
return super if params[:account_username].present? || params[:account_id].present?
|
||||
|
||||
@account = Account.representative
|
||||
@account = params[:account_username].present? ? Account.find_local!(username_param) : Account.representative
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,30 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class ActivityPub::QuoteAuthorizationsController < ActivityPub::BaseController
|
||||
include Authorization
|
||||
|
||||
vary_by -> { 'Signature' if authorized_fetch_mode? }
|
||||
|
||||
before_action :require_account_signature!, if: :authorized_fetch_mode?
|
||||
before_action :set_quote_authorization
|
||||
|
||||
def show
|
||||
expires_in 30.seconds, public: true if @quote.quoted_status.distributable? && public_fetch_mode?
|
||||
render json: @quote, serializer: ActivityPub::QuoteAuthorizationSerializer, adapter: ActivityPub::Adapter, content_type: 'application/activity+json'
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def pundit_user
|
||||
signed_request_account
|
||||
end
|
||||
|
||||
def set_quote_authorization
|
||||
@quote = Quote.accepted.where(quoted_account: @account).find(params[:id])
|
||||
return not_found unless @quote.status.present? && @quote.quoted_status.present?
|
||||
|
||||
authorize @quote.quoted_status, :show?
|
||||
rescue ActiveRecord::RecordNotFound, Mastodon::NotPermittedError
|
||||
not_found
|
||||
end
|
||||
end
|
||||
|
|
@ -25,7 +25,7 @@ class ActivityPub::RepliesController < ActivityPub::BaseController
|
|||
def set_status
|
||||
@status = @account.statuses.find(params[:status_id])
|
||||
authorize @status, :show?
|
||||
rescue ActiveRecord::RecordNotFound, Mastodon::NotPermittedError
|
||||
rescue Mastodon::NotPermittedError
|
||||
not_found
|
||||
end
|
||||
|
||||
|
|
@ -37,7 +37,7 @@ class ActivityPub::RepliesController < ActivityPub::BaseController
|
|||
|
||||
def replies_collection_presenter
|
||||
page = ActivityPub::CollectionPresenter.new(
|
||||
id: ActivityPub::TagManager.instance.replies_uri_for(@status, page_params),
|
||||
id: account_status_replies_url(@account, @status, page_params),
|
||||
type: :unordered,
|
||||
part_of: account_status_replies_url(@account, @status),
|
||||
next: next_page,
|
||||
|
|
@ -47,7 +47,7 @@ class ActivityPub::RepliesController < ActivityPub::BaseController
|
|||
return page if page_requested?
|
||||
|
||||
ActivityPub::CollectionPresenter.new(
|
||||
id: ActivityPub::TagManager.instance.replies_uri_for(@status),
|
||||
id: account_status_replies_url(@account, @status),
|
||||
type: :unordered,
|
||||
first: page
|
||||
)
|
||||
|
|
@ -66,7 +66,8 @@ class ActivityPub::RepliesController < ActivityPub::BaseController
|
|||
# Only consider remote accounts
|
||||
return nil if @replies.size < DESCENDANTS_LIMIT
|
||||
|
||||
ActivityPub::TagManager.instance.replies_uri_for(
|
||||
account_status_replies_url(
|
||||
@account,
|
||||
@status,
|
||||
page: true,
|
||||
min_id: @replies&.last&.id,
|
||||
|
|
@ -76,7 +77,8 @@ class ActivityPub::RepliesController < ActivityPub::BaseController
|
|||
# For now, we're serving only self-replies, but next page might be other accounts
|
||||
next_only_other_accounts = @replies&.last&.account_id != @account.id || @replies.size < DESCENDANTS_LIMIT
|
||||
|
||||
ActivityPub::TagManager.instance.replies_uri_for(
|
||||
account_status_replies_url(
|
||||
@account,
|
||||
@status,
|
||||
page: true,
|
||||
min_id: next_only_other_accounts ? nil : @replies&.last&.id,
|
||||
|
|
|
|||
|
|
@ -22,13 +22,13 @@ class ActivityPub::SharesController < ActivityPub::BaseController
|
|||
def set_status
|
||||
@status = @account.statuses.find(params[:status_id])
|
||||
authorize @status, :show?
|
||||
rescue ActiveRecord::RecordNotFound, Mastodon::NotPermittedError
|
||||
rescue Mastodon::NotPermittedError
|
||||
not_found
|
||||
end
|
||||
|
||||
def shares_collection_presenter
|
||||
ActivityPub::CollectionPresenter.new(
|
||||
id: ActivityPub::TagManager.instance.shares_uri_for(@status),
|
||||
id: account_status_shares_url(@account, @status),
|
||||
type: :unordered,
|
||||
size: @status.reblogs_count
|
||||
)
|
||||
|
|
|
|||
|
|
@ -16,14 +16,11 @@ module Admin
|
|||
def batch
|
||||
authorize :account, :index?
|
||||
|
||||
@form = Form::AccountBatch.new(
|
||||
form_account_batch_params.merge(
|
||||
action: action_from_button,
|
||||
current_account:,
|
||||
query: filtered_accounts,
|
||||
select_all_matching: params[:select_all_matching]
|
||||
)
|
||||
)
|
||||
@form = Form::AccountBatch.new(form_account_batch_params)
|
||||
@form.current_account = current_account
|
||||
@form.action = action_from_button
|
||||
@form.select_all_matching = params[:select_all_matching]
|
||||
@form.query = filtered_accounts
|
||||
@form.save
|
||||
rescue ActionController::ParameterMissing
|
||||
flash[:alert] = I18n.t('admin.accounts.no_account_selected')
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ module Admin
|
|||
|
||||
def index
|
||||
authorize :audit_log, :index?
|
||||
@auditable_accounts = Account.auditable.select(:id, :username).order(username: :asc)
|
||||
@auditable_accounts = Account.auditable.select(:id, :username)
|
||||
end
|
||||
|
||||
private
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -19,13 +19,15 @@ module Admin
|
|||
|
||||
log_action :resend, @user
|
||||
|
||||
redirect_to admin_accounts_path, notice: t('admin.accounts.resend_confirmation.success')
|
||||
flash[:notice] = I18n.t('admin.accounts.resend_confirmation.success')
|
||||
redirect_to admin_accounts_path
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def redirect_confirmed_user
|
||||
redirect_to admin_accounts_path, flash: { error: t('admin.accounts.resend_confirmation.already_confirmed') }
|
||||
flash[:error] = I18n.t('admin.accounts.resend_confirmation.already_confirmed')
|
||||
redirect_to admin_accounts_path
|
||||
end
|
||||
|
||||
def user_confirmed?
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ class Admin::Disputes::AppealsController < Admin::BaseController
|
|||
end
|
||||
|
||||
def reject
|
||||
authorize @appeal, :reject?
|
||||
authorize @appeal, :approve?
|
||||
log_action :reject, @appeal
|
||||
@appeal.reject!(current_account)
|
||||
UserMailer.appeal_rejected(@appeal.account.user, @appeal).deliver_later
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ module Admin
|
|||
end
|
||||
|
||||
def edit
|
||||
authorize :domain_block, :update?
|
||||
authorize :domain_block, :create?
|
||||
end
|
||||
|
||||
def create
|
||||
|
|
@ -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
|
||||
|
|
@ -129,7 +129,7 @@ module Admin
|
|||
end
|
||||
|
||||
def requires_confirmation?
|
||||
@domain_block.valid? && (@domain_block.new_record? || @domain_block.severity_changed?) && @domain_block.suspend? && !params[:confirm]
|
||||
@domain_block.valid? && (@domain_block.new_record? || @domain_block.severity_changed?) && @domain_block.severity.to_s == 'suspend' && !params[:confirm]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -49,8 +49,8 @@ module Admin
|
|||
|
||||
def export_data
|
||||
CSV.generate(headers: export_headers, write_headers: true) do |content|
|
||||
DomainAllow.allowed_domains.each do |domain|
|
||||
content << [domain]
|
||||
DomainAllow.allowed_domains.each do |instance|
|
||||
content << [instance.domain]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -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,9 +13,27 @@ class Admin::Reports::ActionsController < Admin::BaseController
|
|||
|
||||
case action_from_button
|
||||
when 'delete', 'mark_as_sensitive'
|
||||
Admin::ModerationAction.new(moderation_action_params).save!
|
||||
status_batch_action = Admin::StatusBatchAction.new(
|
||||
type: action_from_button,
|
||||
status_ids: @report.status_ids,
|
||||
current_account: current_account,
|
||||
report_id: @report.id,
|
||||
send_email_notification: !@report.spam?,
|
||||
text: params[:text]
|
||||
)
|
||||
|
||||
status_batch_action.save!
|
||||
when 'silence', 'suspend'
|
||||
Admin::AccountAction.new(account_action_params).save!
|
||||
account_action = Admin::AccountAction.new(
|
||||
type: action_from_button,
|
||||
report_id: @report.id,
|
||||
target_account: @report.target_account,
|
||||
current_account: current_account,
|
||||
send_email_notification: !@report.spam?,
|
||||
text: params[:text]
|
||||
)
|
||||
|
||||
account_action.save!
|
||||
else
|
||||
return redirect_to admin_report_path(@report), alert: I18n.t('admin.reports.unknown_action_msg', action: action_from_button)
|
||||
end
|
||||
|
|
@ -25,25 +43,6 @@ class Admin::Reports::ActionsController < Admin::BaseController
|
|||
|
||||
private
|
||||
|
||||
def moderation_action_params
|
||||
shared_params
|
||||
end
|
||||
|
||||
def account_action_params
|
||||
shared_params
|
||||
.merge(target_account: @report.target_account)
|
||||
end
|
||||
|
||||
def shared_params
|
||||
{
|
||||
current_account: current_account,
|
||||
report_id: @report.id,
|
||||
send_email_notification: !@report.spam?,
|
||||
text: params[:text],
|
||||
type: action_from_button,
|
||||
}
|
||||
end
|
||||
|
||||
def set_report
|
||||
@report = Report.find(params[:report_id])
|
||||
end
|
||||
|
|
|
|||
|
|
@ -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, :collection_limit, permissions_as_keys: []])
|
||||
.expect(user_role: [:name, :color, :highlighted, :position, permissions_as_keys: []])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -14,7 +14,8 @@ module Admin
|
|||
@admin_settings = Form::AdminSettings.new(settings_params)
|
||||
|
||||
if @admin_settings.save
|
||||
redirect_to after_update_redirect_path, notice: t('generic.changes_saved_msg')
|
||||
flash[:notice] = I18n.t('generic.changes_saved_msg')
|
||||
redirect_to after_update_redirect_path
|
||||
else
|
||||
render :show
|
||||
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
|
||||
|
|
|
|||
|
|
@ -62,11 +62,7 @@ module Admin
|
|||
end
|
||||
|
||||
def set_statuses
|
||||
@statuses = Admin::StatusFilter.new(@account, filter_params).results.preload(*preload_columns, reblog: [:account, *preload_columns]).page(params[:page]).per(PER_PAGE)
|
||||
end
|
||||
|
||||
def preload_columns
|
||||
[:application, :preloadable_poll, :media_attachments, active_mentions: :account]
|
||||
@statuses = Admin::StatusFilter.new(@account, filter_params).results.preload(:application, :preloadable_poll, :media_attachments, active_mentions: :account, reblog: [:account, :application, :preloadable_poll, :media_attachments, active_mentions: :account]).page(params[:page]).per(PER_PAGE)
|
||||
end
|
||||
|
||||
def filter_params
|
||||
|
|
@ -82,6 +78,8 @@ module Admin
|
|||
'report'
|
||||
elsif params[:remove_from_report]
|
||||
'remove_from_report'
|
||||
elsif params[:delete]
|
||||
'delete'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ module Admin
|
|||
before_action :set_tag, except: [:index]
|
||||
|
||||
PER_PAGE = 20
|
||||
PERIOD_DAYS = 6.days
|
||||
|
||||
def index
|
||||
authorize :tag, :index?
|
||||
|
|
@ -16,7 +15,7 @@ module Admin
|
|||
def show
|
||||
authorize @tag, :show?
|
||||
|
||||
@time_period = report_range
|
||||
@time_period = (6.days.ago.to_date...Time.now.utc.to_date)
|
||||
end
|
||||
|
||||
def update
|
||||
|
|
@ -25,7 +24,7 @@ module Admin
|
|||
if @tag.update(tag_params.merge(reviewed_at: Time.now.utc))
|
||||
redirect_to admin_tag_path(@tag.id), notice: I18n.t('admin.tags.updated_msg')
|
||||
else
|
||||
@time_period = report_range
|
||||
@time_period = (6.days.ago.to_date...Time.now.utc.to_date)
|
||||
|
||||
render :show
|
||||
end
|
||||
|
|
@ -37,10 +36,6 @@ module Admin
|
|||
@tag = Tag.find(params[:id])
|
||||
end
|
||||
|
||||
def report_range
|
||||
(PERIOD_DAYS.ago.to_date...Time.now.utc.to_date)
|
||||
end
|
||||
|
||||
def tag_params
|
||||
params
|
||||
.expect(tag: [:name, :display_name, :trendable, :usable, :listable])
|
||||
|
|
|
|||
|
|
@ -1,77 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Admin::UsernameBlocksController < Admin::BaseController
|
||||
before_action :set_username_block, only: [:edit, :update]
|
||||
|
||||
def index
|
||||
authorize :username_block, :index?
|
||||
@username_blocks = UsernameBlock.order(username: :asc).page(params[:page])
|
||||
@form = Form::UsernameBlockBatch.new
|
||||
end
|
||||
|
||||
def batch
|
||||
authorize :username_block, :index?
|
||||
|
||||
@form = Form::UsernameBlockBatch.new(form_username_block_batch_params.merge(current_account: current_account, action: action_from_button))
|
||||
@form.save
|
||||
rescue ActionController::ParameterMissing
|
||||
flash[:alert] = I18n.t('admin.username_blocks.no_username_block_selected')
|
||||
rescue Mastodon::NotPermittedError
|
||||
flash[:alert] = I18n.t('admin.username_blocks.not_permitted')
|
||||
ensure
|
||||
redirect_to admin_username_blocks_path
|
||||
end
|
||||
|
||||
def new
|
||||
authorize :username_block, :create?
|
||||
@username_block = UsernameBlock.new(exact: true)
|
||||
end
|
||||
|
||||
def edit
|
||||
authorize @username_block, :update?
|
||||
end
|
||||
|
||||
def create
|
||||
authorize :username_block, :create?
|
||||
|
||||
@username_block = UsernameBlock.new(resource_params)
|
||||
|
||||
if @username_block.save
|
||||
log_action :create, @username_block
|
||||
redirect_to admin_username_blocks_path, notice: I18n.t('admin.username_blocks.created_msg')
|
||||
else
|
||||
render :new
|
||||
end
|
||||
end
|
||||
|
||||
def update
|
||||
authorize @username_block, :update?
|
||||
|
||||
if @username_block.update(resource_params)
|
||||
log_action :update, @username_block
|
||||
redirect_to admin_username_blocks_path, notice: I18n.t('admin.username_blocks.updated_msg')
|
||||
else
|
||||
render :new
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_username_block
|
||||
@username_block = UsernameBlock.find(params[:id])
|
||||
end
|
||||
|
||||
def form_username_block_batch_params
|
||||
params
|
||||
.expect(form_username_block_batch: [username_block_ids: []])
|
||||
end
|
||||
|
||||
def resource_params
|
||||
params
|
||||
.expect(username_block: [:username, :comparison, :allow_with_approval])
|
||||
end
|
||||
|
||||
def action_from_button
|
||||
'delete' if params[:delete]
|
||||
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
|
||||
|
||||
|
|
|
|||
|
|
@ -48,7 +48,6 @@ class Api::V1::Accounts::CredentialsController < Api::BaseController
|
|||
default_privacy: source_params.fetch(:privacy, @account.user.setting_default_privacy),
|
||||
default_sensitive: source_params.fetch(:sensitive, @account.user.setting_default_sensitive),
|
||||
default_language: source_params.fetch(:language, @account.user.setting_default_language),
|
||||
default_quote_policy: source_params.fetch(:quote_policy, @account.user.setting_default_quote_policy),
|
||||
},
|
||||
}
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,28 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::Accounts::EmailSubscriptionsController < Api::BaseController
|
||||
before_action :set_account
|
||||
before_action :require_feature_enabled!
|
||||
before_action :require_account_permissions!
|
||||
|
||||
def create
|
||||
@account.email_subscriptions.create!(email: params[:email], locale: I18n.locale)
|
||||
render_empty
|
||||
rescue ActiveRecord::RecordInvalid => e
|
||||
render json: ValidationErrorFormatter.new(e).as_json, status: 422
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_account
|
||||
@account = Account.local.find(params[:account_id])
|
||||
end
|
||||
|
||||
def require_feature_enabled!
|
||||
head 404 unless Mastodon::Feature.email_subscriptions_enabled?
|
||||
end
|
||||
|
||||
def require_account_permissions!
|
||||
head 404 if @account.unavailable? || !@account.user_can?(:manage_email_subscriptions) || !@account.user_email_subscriptions_enabled?
|
||||
end
|
||||
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
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
class Api::V1::Admin::TagsController < Api::BaseController
|
||||
include Authorization
|
||||
|
||||
before_action -> { authorize_if_got_token! :'admin:read' }, only: [:index, :show]
|
||||
before_action -> { authorize_if_got_token! :'admin:write' }, only: :update
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::Instances::TermsOfServiceController < Api::V1::Instances::BaseController
|
||||
before_action :cache_even_if_authenticated!
|
||||
|
||||
def index
|
||||
@terms_of_service = TermsOfService.current || raise(ActiveRecord::RecordNotFound)
|
||||
render json: @terms_of_service, serializer: REST::TermsOfServiceSerializer
|
||||
end
|
||||
|
||||
def show
|
||||
@terms_of_service = TermsOfService.published.find_by!(effective_date: params[:date])
|
||||
render json: @terms_of_service, serializer: REST::TermsOfServiceSerializer
|
||||
end
|
||||
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