Merge pull request #745 from kmycode/revert-743-upstream-20240517

Revert "Upstream 20240517"
This commit is contained in:
KMY(雪あすか) 2024-05-24 08:35:09 +09:00 committed by GitHub
commit 25bfa6208a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2347 changed files with 26470 additions and 87494 deletions

View file

@ -1,9 +1,7 @@
[production] [production]
defaults defaults
> 0.2% not IE 11
ios >= 15.6
not dead not dead
not OperaMini all
[development] [development]
supports es6-module supports es6-module

View file

@ -1,8 +0,0 @@
---
ignore:
# devise-two-factor advisory about brute-forcing TOTP
# We have rate-limits on authentication endpoints in place (including second
# factor verification) since Mastodon v3.2.0
- CVE-2024-0227
- CVE-2024-27456
- CVE-2023-51774

View file

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

View file

@ -1,4 +0,0 @@
# Required by ActiveRecord encryption feature
ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY=fkSxKD2bF396kdQbrP1EJ7WbU7ZgNokR
ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT=r0hvVmzBVsjxC7AMlwhOzmtc36ZCOS1E
ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY=PhdFyyfy5xJ7WVd2lWBpcPScRQHzRTNr

View file

@ -3,10 +3,3 @@ NODE_ENV=production
# Federation # Federation
LOCAL_DOMAIN=cb6e6126.ngrok.io LOCAL_DOMAIN=cb6e6126.ngrok.io
LOCAL_HTTPS=true LOCAL_HTTPS=true
# Elasticsearch
ES_PREFIX=test
# Required by ActiveRecord encryption feature
ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY=fkSxKD2bF396kdQbrP1EJ7WbU7ZgNokR
ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT=r0hvVmzBVsjxC7AMlwhOzmtc36ZCOS1E
ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY=PhdFyyfy5xJ7WVd2lWBpcPScRQHzRTNr

View file

@ -123,7 +123,7 @@ module.exports = defineConfig({
'react/react-in-jsx-scope': 'off', // not needed with new JSX transform 'react/react-in-jsx-scope': 'off', // not needed with new JSX transform
'react/self-closing-comp': 'error', 'react/self-closing-comp': 'error',
// recommended values found in https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/v6.8.0/src/index.js#L46 // recommended values found in https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/main/src/index.js
'jsx-a11y/accessible-emoji': 'warn', 'jsx-a11y/accessible-emoji': 'warn',
'jsx-a11y/click-events-have-key-events': 'off', 'jsx-a11y/click-events-have-key-events': 'off',
'jsx-a11y/label-has-associated-control': 'off', 'jsx-a11y/label-has-associated-control': 'off',
@ -165,7 +165,7 @@ module.exports = defineConfig({
// }, // },
// ], // ],
'jsx-a11y/no-noninteractive-tabindex': 'off', 'jsx-a11y/no-noninteractive-tabindex': 'off',
'jsx-a11y/no-onchange': 'off', 'jsx-a11y/no-onchange': 'warn',
// recommended is full 'error' // recommended is full 'error'
'jsx-a11y/no-static-element-interactions': [ 'jsx-a11y/no-static-element-interactions': [
'warn', 'warn',
@ -176,7 +176,7 @@ module.exports = defineConfig({
}, },
], ],
// See https://github.com/import-js/eslint-plugin-import/blob/v2.29.1/config/recommended.js // See https://github.com/import-js/eslint-plugin-import/blob/main/config/recommended.js
'import/extensions': [ 'import/extensions': [
'error', 'error',
'always', 'always',
@ -338,6 +338,7 @@ module.exports = defineConfig({
'plugin:import/typescript', 'plugin:import/typescript',
'plugin:promise/recommended', 'plugin:promise/recommended',
'plugin:jsdoc/recommended-typescript', 'plugin:jsdoc/recommended-typescript',
'plugin:prettier/recommended',
], ],
parserOptions: { parserOptions: {
@ -346,9 +347,6 @@ module.exports = defineConfig({
}, },
rules: { rules: {
// Disable formatting rules that have been enabled in the base config
'indent': 'off',
'import/consistent-type-specifier-style': ['error', 'prefer-top-level'], 'import/consistent-type-specifier-style': ['error', 'prefer-top-level'],
'@typescript-eslint/consistent-type-definitions': ['warn', 'interface'], '@typescript-eslint/consistent-type-definitions': ['warn', 'interface'],
@ -363,7 +361,6 @@ module.exports = defineConfig({
"message": "Use typed hooks `useAppDispatch` and `useAppSelector` instead." "message": "Use typed hooks `useAppDispatch` and `useAppSelector` instead."
} }
], ],
"@typescript-eslint/restrict-template-expressions": ['warn', { allowNumber: true }],
'jsdoc/require-jsdoc': 'off', 'jsdoc/require-jsdoc': 'off',
// Those rules set stricter rules for TS files // Those rules set stricter rules for TS files

1
.github/FUNDING.yml vendored
View file

@ -1 +0,0 @@
custom: https://fantia.jp/fanclubs/484677

View file

@ -1,74 +0,0 @@
name: バグ報告
description: kmyblueのバグ報告ただし情報改竄、秘密情報の漏洩、システムの破損などが発生するバグは、こちらではなく「Security」タブよりセキュリティインシデントとして報告してください
labels: [bug]
body:
- type: textarea
attributes:
label: バグの再現手順
description: どのように操作したらバグが発生したのか、バグが発生する直前までの手順を順番に詳しく教えてください
value: |
1.
2.
3.
...
validations:
required: true
- type: textarea
attributes:
label: 期待する動作
description: どのように動いてほしかったですか?
validations:
required: true
- type: textarea
attributes:
label: 実際の動作
description: どのようなバグが発生しましたか?
validations:
required: true
- type: textarea
attributes:
label: 詳しい情報
validations:
required: false
- type: input
attributes:
label: バグが発生したkmyblueサーバーのドメイン
description: サーバー固有の問題の可能性もありますので、プライバシー上可能な範囲内で、できるだけ書いてください
placeholder: kmy.blue
validations:
required: false
- type: input
attributes:
label: バグが発生したkmyblueのバージョン
description: |
Mastodonではなくkmyblueのバージョンを記述してください。例えばバージョン表記が `v4.2.0+kmyblue.5.1-LTS` の場合、バージョンは `5.1`になります
バージョンは、PCだと画面左下、スマホだと概要画面の一番下に書いてあります
placeholder: '5.1'
validations:
required: true
- type: input
attributes:
label: ブラウザの名前
description: |
ブラウザの名前を書いてください。可能であればバージョンも併記してください
placeholder: Firefox 105.0.3
validations:
required: false
- type: input
attributes:
label: OS
description: |
あなたのOSと、できればバージョンも教えてください。スマホの場合は、「Android」「iPhone」にバージョンをつけてください
placeholder: Windows11
validations:
required: false
- type: textarea
attributes:
label: その他の詳細情報
description: |
あなたの環境が特殊な場合、詳しいことを教えてください(例: VPS、tor、学内LANなど
サーバー管理者の場合は、Ruby、Node.jsのバージョン、Cloudflareの使用可否なども可能なら書いてください
validations:
required: false

View file

@ -0,0 +1,76 @@
name: Bug Report (Web Interface)
description: If you are using Mastodon's web interface and something is not working as expected
labels: [bug, 'status/to triage', 'area/web interface']
body:
- type: markdown
attributes:
value: |
Make sure that you are submitting a new bug that was not previously reported or already fixed.
Please use a concise and distinct title for the issue.
- type: textarea
attributes:
label: Steps to reproduce the problem
description: What were you trying to do?
value: |
1.
2.
3.
...
validations:
required: true
- type: input
attributes:
label: Expected behaviour
description: What should have happened?
validations:
required: true
- type: input
attributes:
label: Actual behaviour
description: What happened?
validations:
required: true
- type: textarea
attributes:
label: Detailed description
validations:
required: false
- type: input
attributes:
label: Mastodon instance
description: The address of the Mastodon instance where you experienced the issue
placeholder: mastodon.social
validations:
required: true
- type: input
attributes:
label: Mastodon version
description: |
This is displayed at the bottom of the About page, eg. `v4.1.2+nightly-20230627`
placeholder: v4.1.2
validations:
required: true
- type: input
attributes:
label: Browser name and version
description: |
What browser are you using when getting this bug? Please specify the version as well.
placeholder: Firefox 105.0.3
validations:
required: true
- type: input
attributes:
label: Operating system
description: |
What OS are you running? Please specify the version as well.
placeholder: macOS 13.4.1
validations:
required: true
- type: textarea
attributes:
label: Technical details
description: |
Any additional technical details you may have. This can include the full error log, inspector's output…
validations:
required: false

View file

@ -1,16 +0,0 @@
name: 機能要望
description: 機能の提案
labels: [enhancement]
body:
- type: textarea
attributes:
label: 欲しい機能
description: 欲しい機能の詳細を書いてください
validations:
required: true
- type: textarea
attributes:
label: 必要性
description: この機能はあなたにとってなぜ必要でしょうか?どういった状況で使われるものですか?
validations:
required: true

View file

@ -0,0 +1,65 @@
name: Bug Report (server / API)
description: |
If something is not working as expected, but is not from using the web interface.
labels: [bug, 'status/to triage']
body:
- type: markdown
attributes:
value: |
Make sure that you are submitting a new bug that was not previously reported or already fixed.
Please use a concise and distinct title for the issue.
- type: textarea
attributes:
label: Steps to reproduce the problem
description: What were you trying to do?
value: |
1.
2.
3.
...
validations:
required: true
- type: input
attributes:
label: Expected behaviour
description: What should have happened?
validations:
required: true
- type: input
attributes:
label: Actual behaviour
description: What happened?
validations:
required: true
- type: textarea
attributes:
label: Detailed description
validations:
required: false
- type: input
attributes:
label: Mastodon instance
description: The address of the Mastodon instance where you experienced the issue
placeholder: mastodon.social
validations:
required: false
- type: input
attributes:
label: Mastodon version
description: |
This is displayed at the bottom of the About page, eg. `v4.1.2+nightly-20230627`
placeholder: v4.1.2
validations:
required: false
- type: textarea
attributes:
label: Technical details
description: |
Any additional technical details you may have, like logs or error traces
value: |
If this is happening on your own Mastodon server, please fill out those:
- Ruby version: (from `ruby --version`, eg. v3.1.2)
- Node.js version: (from `node --version`, eg. v18.16.0)
validations:
required: false

View file

@ -0,0 +1,22 @@
name: Feature Request
description: I have a suggestion
labels: [suggestion]
body:
- type: markdown
attributes:
value: |
Please use a concise and distinct title for the issue.
Consider: Could it be implemented as a 3rd party app using the REST API instead?
- type: textarea
attributes:
label: Pitch
description: Describe your idea for a feature. Make sure it has not already been suggested/implemented/turned down before.
validations:
required: true
- type: textarea
attributes:
label: Motivation
description: Why do you think this feature is needed? Who would benefit from it?
validations:
required: true

View file

@ -1,28 +0,0 @@
name: 仕様変更・改善要望
description: 既存の仕様や挙動変更の要望
labels: [specchange]
body:
- type: markdown
attributes:
value: 意図したものとは明らかに異なる挙動をしているものはバグとして、もともと仕様として決められた動きをしているものを変更したいときはこちらでお願いします
- type: textarea
attributes:
label: 挙動を変更してほしい機能や動作
validations:
required: true
- type: textarea
attributes:
label: 現在の挙動
validations:
required: true
- type: textarea
attributes:
label: 変更してほしい新しい挙動
validations:
required: true
- type: textarea
attributes:
label: 必要性
description: この変更はあなたにとってなぜ必要でしょうか?どういった状況で使われるものですか?
validations:
required: true

View file

@ -1 +1,5 @@
blank_issues_enabled: true blank_issues_enabled: false
contact_links:
- name: GitHub Discussions
url: https://github.com/mastodon/mastodon/discussions
about: Please ask and answer questions here.

View file

@ -23,7 +23,7 @@ runs:
shell: bash shell: bash
run: echo "dir=$(yarn config get cacheFolder)" >> $GITHUB_OUTPUT run: echo "dir=$(yarn config get cacheFolder)" >> $GITHUB_OUTPUT
- uses: actions/cache@v4 - uses: actions/cache@v3
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`) id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
with: with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }} path: ${{ steps.yarn-cache-dir-path.outputs.dir }}

4
.github/codecov.yml vendored
View file

@ -1,4 +1,3 @@
comment: false # Do not leave PR comments
coverage: coverage:
status: status:
project: project:
@ -9,3 +8,6 @@ coverage:
default: default:
# Github status check is not blocking # Github status check is not blocking
informational: true informational: true
comment:
# Only write a comment in PR if there are changes
require_changes: true

View file

@ -125,29 +125,6 @@
], ],
groupName: null, // We dont want them to belong to any group groupName: null, // We dont want them to belong to any group
}, },
{
// Group all RuboCop packages with `rubocop` in the same PR
matchManagers: ['bundler'],
matchPackageNames: ['rubocop'],
matchPackagePrefixes: ['rubocop-'],
matchUpdateTypes: ['patch', 'minor'],
groupName: 'RuboCop (non-major)',
},
{
// Group all RSpec packages with `rspec` in the same PR
matchManagers: ['bundler'],
matchPackageNames: ['rspec'],
matchPackagePrefixes: ['rspec-'],
matchUpdateTypes: ['patch', 'minor'],
groupName: 'RSpec (non-major)',
},
{
// Group all opentelemetry-ruby packages in the same PR
matchManagers: ['bundler'],
matchPackagePrefixes: ['opentelemetry-'],
matchUpdateTypes: ['patch', 'minor'],
groupName: 'opentelemetry-ruby (non-major)',
},
// Add labels depending on package manager // Add labels depending on package manager
{ matchManagers: ['npm', 'nvm'], addLabels: ['javascript'] }, { matchManagers: ['npm', 'nvm'], addLabels: ['javascript'] },
{ matchManagers: ['bundler', 'ruby-version'], addLabels: ['ruby'] }, { matchManagers: ['bundler', 'ruby-version'], addLabels: ['ruby'] },

21
.github/stylelint-matcher.json vendored Normal file
View file

@ -0,0 +1,21 @@
{
"problemMatcher": [
{
"owner": "stylelint",
"pattern": [
{
"regexp": "^([^\\s].*)$",
"file": 1
},
{
"regexp": "^\\s+((\\d+):(\\d+))?\\s+(✖|×)\\s+(.*)\\s{2,}(.*)$",
"line": 2,
"column": 3,
"message": 5,
"code": 6,
"loop": true
}
]
}
]
}

View file

@ -0,0 +1,102 @@
on:
workflow_call:
inputs:
platforms:
required: true
type: string
cache:
type: boolean
default: true
use_native_arm64_builder:
type: boolean
push_to_images:
type: string
version_prerelease:
type: string
version_metadata:
type: string
flavor:
type: string
tags:
type: string
labels:
type: string
file_to_build:
type: string
jobs:
build-image:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: docker/setup-qemu-action@v3
if: contains(inputs.platforms, 'linux/arm64') && !inputs.use_native_arm64_builder
- uses: docker/setup-buildx-action@v3
id: buildx
if: ${{ !(inputs.use_native_arm64_builder && contains(inputs.platforms, 'linux/arm64')) }}
- name: Start a local Docker Builder
if: inputs.use_native_arm64_builder && contains(inputs.platforms, 'linux/arm64')
run: |
docker run --rm -d --name buildkitd -p 1234:1234 --privileged moby/buildkit:latest --addr tcp://0.0.0.0:1234
- uses: docker/setup-buildx-action@v3
id: buildx-native
if: inputs.use_native_arm64_builder && contains(inputs.platforms, 'linux/arm64')
with:
driver: remote
endpoint: tcp://localhost:1234
platforms: linux/amd64
append: |
- endpoint: tcp://${{ vars.DOCKER_BUILDER_HETZNER_ARM64_01_HOST }}:13865
platforms: linux/arm64
name: mastodon-docker-builder-arm64-01
driver-opts:
- servername=mastodon-docker-builder-arm64-01
env:
BUILDER_NODE_1_AUTH_TLS_CACERT: ${{ secrets.DOCKER_BUILDER_HETZNER_ARM64_01_CACERT }}
BUILDER_NODE_1_AUTH_TLS_CERT: ${{ secrets.DOCKER_BUILDER_HETZNER_ARM64_01_CERT }}
BUILDER_NODE_1_AUTH_TLS_KEY: ${{ secrets.DOCKER_BUILDER_HETZNER_ARM64_01_KEY }}
- name: Log in to Docker Hub
if: contains(inputs.push_to_images, 'tootsuite')
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Log in to the Github Container registry
if: contains(inputs.push_to_images, 'ghcr.io')
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- uses: docker/metadata-action@v5
id: meta
if: ${{ inputs.push_to_images != '' }}
with:
images: ${{ inputs.push_to_images }}
flavor: ${{ inputs.flavor }}
tags: ${{ inputs.tags }}
labels: ${{ inputs.labels }}
- uses: docker/build-push-action@v5
with:
context: .
file: ${{ inputs.file_to_build }}
build-args: |
MASTODON_VERSION_PRERELEASE=${{ inputs.version_prerelease }}
MASTODON_VERSION_METADATA=${{ inputs.version_metadata }}
platforms: ${{ inputs.platforms }}
provenance: false
builder: ${{ steps.buildx.outputs.name || steps.buildx-native.outputs.name }}
push: ${{ inputs.push_to_images != '' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: ${{ inputs.cache && 'type=gha' || '' }}
cache-to: ${{ inputs.cache && 'type=gha,mode=max' || '' }}

View file

@ -1,6 +1,8 @@
name: Build security nightly container image name: Build nightly container image
on: on:
workflow_dispatch: workflow_dispatch:
schedule:
- cron: '0 2 * * *' # run at 2 AM UTC
permissions: permissions:
contents: read contents: read
@ -15,7 +17,7 @@ jobs:
env: env:
TZ: Etc/UTC TZ: Etc/UTC
run: | run: |
echo mastodon_version_prerelease=nightly.$(date --date='next day' +'%Y-%m-%d')-security>> $GITHUB_OUTPUT echo mastodon_version_prerelease=nightly.$(date +'%Y-%m-%d')>> $GITHUB_OUTPUT
outputs: outputs:
prerelease: ${{ steps.version_vars.outputs.mastodon_version_prerelease }} prerelease: ${{ steps.version_vars.outputs.mastodon_version_prerelease }}
@ -38,7 +40,7 @@ jobs:
tags: | tags: |
type=raw,value=edge type=raw,value=edge
type=raw,value=nightly type=raw,value=nightly
type=raw,value=${{ needs.compute-suffix.outputs.prerelease }} type=schedule,pattern=${{ needs.compute-suffix.outputs.prerelease }}
secrets: inherit secrets: inherit
build-image-streaming: build-image-streaming:
@ -60,5 +62,5 @@ jobs:
tags: | tags: |
type=raw,value=edge type=raw,value=edge
type=raw,value=nightly type=raw,value=nightly
type=raw,value=${{ needs.compute-suffix.outputs.prerelease }} type=schedule,pattern=${{ needs.compute-suffix.outputs.prerelease }}
secrets: inherit secrets: inherit

58
.github/workflows/build-push-pr.yml vendored Normal file
View file

@ -0,0 +1,58 @@
name: Build container image for PR
on:
pull_request:
types: [labeled, synchronize, reopened, ready_for_review, opened]
permissions:
contents: read
packages: write
jobs:
compute-suffix:
runs-on: ubuntu-latest
# This is only allowed to run if:
# - the PR branch is in the `mastodon/mastodon` repository
# - the PR is not a draft
# - the PR has the "build-image" label
if: ${{ github.event.pull_request.head.repo.full_name == github.repository && !github.event.pull_request.draft && contains(github.event.pull_request.labels.*.name, 'build-image') }}
steps:
# Repository needs to be cloned so `git rev-parse` below works
- name: Clone repository
uses: actions/checkout@v4
- id: version_vars
run: |
echo mastodon_version_metadata=pr-${{ github.event.pull_request.number }}-$(git rev-parse --short HEAD) >> $GITHUB_OUTPUT
outputs:
metadata: ${{ steps.version_vars.outputs.mastodon_version_metadata }}
build-image:
needs: compute-suffix
uses: ./.github/workflows/build-container-image.yml
with:
file_to_build: Dockerfile
platforms: linux/amd64,linux/arm64
use_native_arm64_builder: true
push_to_images: |
ghcr.io/mastodon/mastodon
version_metadata: ${{ needs.compute-suffix.outputs.metadata }}
flavor: |
latest=auto
tags: |
type=ref,event=pr
secrets: inherit
build-image-streaming:
needs: compute-suffix
uses: ./.github/workflows/build-container-image.yml
with:
file_to_build: streaming/Dockerfile
platforms: linux/amd64,linux/arm64
use_native_arm64_builder: true
push_to_images: |
ghcr.io/mastodon/mastodon-streaming
version_metadata: ${{ needs.compute-suffix.outputs.metadata }}
flavor: |
latest=auto
tags: |
type=ref,event=pr
secrets: inherit

51
.github/workflows/build-releases.yml vendored Normal file
View file

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

71
.github/workflows/crowdin-download.yml vendored Normal file
View file

@ -0,0 +1,71 @@
name: Crowdin / Download translations
on:
schedule:
- cron: '17 4 * * *' # Every day
workflow_dispatch:
permissions:
contents: write
pull-requests: write
jobs:
download-translations:
runs-on: ubuntu-latest
if: github.repository == 'mastodon/mastodon'
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Increase Git http.postBuffer
# This is needed due to a bug in Ubuntu's cURL version?
# See https://github.com/orgs/community/discussions/55820
run: |
git config --global http.version HTTP/1.1
git config --global http.postBuffer 157286400
# Download the translation files from Crowdin
- name: crowdin action
uses: crowdin/github-action@v1
with:
upload_sources: false
upload_translations: false
download_translations: true
crowdin_branch_name: main
push_translations: false
create_pull_request: false
env:
CROWDIN_PROJECT_ID: ${{ vars.CROWDIN_PROJECT_ID }}
CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }}
# As the files are extracted from a Docker container, they belong to root:root
# We need to fix this before the next steps
- name: Fix file permissions
run: sudo chown -R runner:docker .
# This is needed to run the normalize step
- name: Set up Ruby environment
uses: ./.github/actions/setup-ruby
- name: Run i18n normalize task
run: bundle exec i18n-tasks normalize
# Create or update the pull request
- name: Create Pull Request
uses: peter-evans/create-pull-request@v5.0.2
with:
commit-message: 'New Crowdin translations'
title: 'New Crowdin Translations (automated)'
author: 'GitHub Actions <noreply@github.com>'
body: |
New Crowdin translations, automated with Github Actions
See `.github/workflows/crowdin-download.yml`
This PR will be updated every day with new translations.
Due to a limitation in Github Actions, checks are not running on this PR without manual action.
If you want to run the checks, then close and re-open it.
branch: i18n/crowdin/translations
base: main
labels: i18n

35
.github/workflows/crowdin-upload.yml vendored Normal file
View file

@ -0,0 +1,35 @@
name: Crowdin / Upload translations
on:
push:
branches:
- main
paths:
- crowdin.yml
- app/javascript/mastodon/locales/en.json
- config/locales/en.yml
- config/locales/simple_form.en.yml
- config/locales/activerecord.en.yml
- config/locales/devise.en.yml
- config/locales/doorkeeper.en.yml
- .github/workflows/crowdin-upload.yml
jobs:
upload-translations:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: crowdin action
uses: crowdin/github-action@v1
with:
upload_sources: true
upload_translations: false
download_translations: false
crowdin_branch_name: main
env:
CROWDIN_PROJECT_ID: ${{ vars.CROWDIN_PROJECT_ID }}
CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }}

View file

@ -1,18 +0,0 @@
name: Check formatting
on:
push:
pull_request:
jobs:
lint:
runs-on: ubuntu-latest
steps:
- name: Clone repository
uses: actions/checkout@v4
- name: Set up Javascript environment
uses: ./.github/actions/setup-javascript
- name: Check formatting with Prettier
run: yarn format:check

View file

@ -38,5 +38,9 @@ jobs:
- name: Set up Javascript environment - name: Set up Javascript environment
uses: ./.github/actions/setup-javascript uses: ./.github/actions/setup-javascript
- uses: xt0rted/stylelint-problem-matcher@v1
- run: echo "::add-matcher::.github/stylelint-matcher.json"
- name: Stylelint - name: Stylelint
run: yarn lint:css -f github run: yarn lint:sass

View file

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

38
.github/workflows/lint-json.yml vendored Normal file
View file

@ -0,0 +1,38 @@
name: JSON Linting
on:
push:
branches-ignore:
- 'dependabot/**'
- 'renovate/**'
paths:
- 'package.json'
- 'yarn.lock'
- '.nvmrc'
- '.prettier*'
- '**/*.json'
- '.github/workflows/lint-json.yml'
- '!app/javascript/mastodon/locales/*.json'
pull_request:
paths:
- 'package.json'
- 'yarn.lock'
- '.nvmrc'
- '.prettier*'
- '**/*.json'
- '.github/workflows/lint-json.yml'
- '!app/javascript/mastodon/locales/*.json'
jobs:
lint:
runs-on: ubuntu-latest
steps:
- name: Clone repository
uses: actions/checkout@v4
- name: Set up Javascript environment
uses: ./.github/actions/setup-javascript
- name: Prettier
run: yarn lint:json

38
.github/workflows/lint-md.yml vendored Normal file
View file

@ -0,0 +1,38 @@
name: Markdown Linting
on:
push:
branches-ignore:
- 'dependabot/**'
- 'renovate/**'
paths:
- '.github/workflows/lint-md.yml'
- '.nvmrc'
- '.prettier*'
- '**/*.md'
- '!AUTHORS.md'
- 'package.json'
- 'yarn.lock'
pull_request:
paths:
- '.github/workflows/lint-md.yml'
- '.nvmrc'
- '.prettier*'
- '**/*.md'
- '!AUTHORS.md'
- 'package.json'
- 'yarn.lock'
jobs:
lint:
runs-on: ubuntu-latest
steps:
- name: Clone repository
uses: actions/checkout@v4
- name: Set up Javascript environment
uses: ./.github/actions/setup-javascript
- name: Prettier
run: yarn lint:md

40
.github/workflows/lint-yml.yml vendored Normal file
View file

@ -0,0 +1,40 @@
name: YML Linting
on:
push:
branches-ignore:
- 'dependabot/**'
- 'renovate/**'
paths:
- 'package.json'
- 'yarn.lock'
- '.nvmrc'
- '.prettier*'
- '**/*.yaml'
- '**/*.yml'
- '.github/workflows/lint-yml.yml'
- '!config/locales/*.yml'
pull_request:
paths:
- 'package.json'
- 'yarn.lock'
- '.nvmrc'
- '.prettier*'
- '**/*.yaml'
- '**/*.yml'
- '.github/workflows/lint-yml.yml'
- '!config/locales/*.yml'
jobs:
lint:
runs-on: ubuntu-latest
steps:
- name: Clone repository
uses: actions/checkout@v4
- name: Set up Javascript environment
uses: ./.github/actions/setup-javascript
- name: Prettier
run: yarn lint:yml

35
.github/workflows/test-image-build.yml vendored Normal file
View file

@ -0,0 +1,35 @@
name: Test container image build
on:
pull_request:
paths:
- .github/workflows/build-nightly.yml
- .github/workflows/build-push-pr.yml
- .github/workflows/build-releases.yml
- .github/workflows/test-image-build.yml
- Dockerfile
- streaming/Dockerfile
permissions:
contents: read
jobs:
build-image:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
uses: ./.github/workflows/build-container-image.yml
with:
file_to_build: Dockerfile
platforms: linux/amd64 # Testing only on native platform so it is performant
cache: true
build-image-streaming:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}-streaming
cancel-in-progress: true
uses: ./.github/workflows/build-container-image.yml
with:
file_to_build: streaming/Dockerfile
platforms: linux/amd64 # Testing only on native platform so it is performant
cache: true

View file

@ -38,5 +38,5 @@ jobs:
- name: Set up Javascript environment - name: Set up Javascript environment
uses: ./.github/actions/setup-javascript uses: ./.github/actions/setup-javascript
- name: JavaScript testing - name: Jest testing
run: yarn jest --reporters github-actions summary run: yarn jest --reporters github-actions summary

View file

@ -78,8 +78,23 @@ jobs:
- name: Create database - name: Create database
run: './bin/rails db:create' run: './bin/rails db:create'
- name: Run historical migrations with data population - name: Run migrations up to v2.0.0
run: './bin/rails tests:migrations:prepare_database' run: './bin/rails db:migrate VERSION=20171010025614'
- name: Populate database with test data
run: './bin/rails tests:migrations:populate_v2'
- name: Run migrations up to v2.4.0
run: './bin/rails db:migrate VERSION=20180514140000'
- name: Populate database with test data
run: './bin/rails tests:migrations:populate_v2_4'
- name: Run migrations up to v2.4.3
run: './bin/rails db:migrate VERSION=20180707154237'
- name: Populate database with test data
run: './bin/rails tests:migrations:populate_v2_4_3'
- name: Run all remaining migrations - name: Run all remaining migrations
run: './bin/rails db:migrate' run: './bin/rails db:migrate'

View file

@ -45,7 +45,6 @@ jobs:
--health-retries 5 --health-retries 5
ports: ports:
- 5432:5432 - 5432:5432
redis: redis:
image: redis:7-alpine image: redis:7-alpine
options: >- options: >-
@ -78,11 +77,28 @@ jobs:
- name: Create database - name: Create database
run: './bin/rails db:create' run: './bin/rails db:create'
- name: Run historical migrations with data population - name: Run migrations up to v2.0.0
run: './bin/rails tests:migrations:prepare_database' run: './bin/rails db:migrate VERSION=20171010025614'
- name: Populate database with test data
run: './bin/rails tests:migrations:populate_v2'
- name: Run pre-deployment migrations up to v2.4.0
run: './bin/rails db:migrate VERSION=20180514140000'
env: env:
SKIP_POST_DEPLOYMENT_MIGRATIONS: true SKIP_POST_DEPLOYMENT_MIGRATIONS: true
- name: Populate database with test data
run: './bin/rails tests:migrations:populate_v2_4'
- name: Run migrations up to v2.4.3
run: './bin/rails db:migrate VERSION=20180707154237'
env:
SKIP_POST_DEPLOYMENT_MIGRATIONS: true
- name: Populate database with test data
run: './bin/rails tests:migrations:populate_v2_4_3'
- name: Run all remaining pre-deployment migrations - name: Run all remaining pre-deployment migrations
run: './bin/rails db:migrate' run: './bin/rails db:migrate'
env: env:

View file

@ -28,9 +28,6 @@ jobs:
env: env:
RAILS_ENV: ${{ matrix.mode }} RAILS_ENV: ${{ matrix.mode }}
BUNDLE_WITH: ${{ matrix.mode }} BUNDLE_WITH: ${{ matrix.mode }}
ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY: precompile_placeholder
ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT: precompile_placeholder
ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY: precompile_placeholder
OTP_SECRET: precompile_placeholder OTP_SECRET: precompile_placeholder
SECRET_KEY_BASE: precompile_placeholder SECRET_KEY_BASE: precompile_placeholder
@ -55,7 +52,7 @@ jobs:
run: | run: |
tar --exclude={"*.br","*.gz"} -zcf artifacts.tar.gz public/assets public/packs* tar --exclude={"*.br","*.gz"} -zcf artifacts.tar.gz public/assets public/packs*
- uses: actions/upload-artifact@v4 - uses: actions/upload-artifact@v3
if: matrix.mode == 'test' if: matrix.mode == 'test'
with: with:
path: |- path: |-
@ -109,19 +106,18 @@ jobs:
CAS_ENABLED: true CAS_ENABLED: true
BUNDLE_WITH: 'pam_authentication test' BUNDLE_WITH: 'pam_authentication test'
GITHUB_RSPEC: ${{ matrix.ruby-version == '.ruby-version' && github.event.pull_request && 'true' }} GITHUB_RSPEC: ${{ matrix.ruby-version == '.ruby-version' && github.event.pull_request && 'true' }}
ES_ENABLED: false
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
ruby-version: ruby-version:
- '3.0'
- '3.1' - '3.1'
- '3.2'
- '.ruby-version' - '.ruby-version'
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: actions/download-artifact@v4 - uses: actions/download-artifact@v3
with: with:
path: './' path: './'
name: ${{ github.sha }} name: ${{ github.sha }}
@ -143,11 +139,9 @@ jobs:
- name: Upload coverage reports to Codecov - name: Upload coverage reports to Codecov
if: matrix.ruby-version == '.ruby-version' if: matrix.ruby-version == '.ruby-version'
uses: codecov/codecov-action@v4 uses: codecov/codecov-action@v3
with: with:
files: coverage/lcov/mastodon.lcov files: coverage/lcov/mastodon.lcov
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
test-e2e: test-e2e:
name: End to End testing name: End to End testing
@ -187,22 +181,19 @@ jobs:
DISABLE_SIMPLECOV: true DISABLE_SIMPLECOV: true
RAILS_ENV: test RAILS_ENV: test
BUNDLE_WITH: test BUNDLE_WITH: test
ES_ENABLED: false
LOCAL_DOMAIN: localhost:3000
LOCAL_HTTPS: false
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
ruby-version: ruby-version:
- '3.0'
- '3.1' - '3.1'
- '3.2'
- '.ruby-version' - '.ruby-version'
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: actions/download-artifact@v4 - uses: actions/download-artifact@v3
with: with:
path: './public' path: './public'
name: ${{ github.sha }} name: ${{ github.sha }}
@ -219,21 +210,21 @@ jobs:
- name: Load database schema - name: Load database schema
run: './bin/rails db:create db:schema:load db:seed' run: './bin/rails db:create db:schema:load db:seed'
- run: bin/rspec spec/system --tag streaming --tag js - run: bundle exec rake spec:system
- name: Archive logs - name: Archive logs
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v3
if: failure() if: failure()
with: with:
name: e2e-logs-${{ matrix.ruby-version }} name: e2e-logs-${{ matrix.ruby-version }}
path: log/ path: log/
- name: Archive test screenshots - name: Archive test screenshots
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v3
if: failure() if: failure()
with: with:
name: e2e-screenshots name: e2e-screenshots
path: tmp/capybara/ path: tmp/screenshots/
test-search: test-search:
name: Elastic Search integration testing name: Elastic Search integration testing
@ -266,8 +257,8 @@ jobs:
ports: ports:
- 6379:6379 - 6379:6379
elasticsearch: search:
image: ${{ contains(matrix.search-image, 'elasticsearch') && matrix.search-image || '' }} image: ${{ matrix.search-image }}
env: env:
discovery.type: single-node discovery.type: single-node
xpack.security.enabled: false xpack.security.enabled: false
@ -279,20 +270,6 @@ jobs:
ports: ports:
- 9200:9200 - 9200:9200
opensearch:
image: ${{ contains(matrix.search-image, 'opensearch') && matrix.search-image || '' }}
env:
discovery.type: single-node
DISABLE_INSTALL_DEMO_CONFIG: true
DISABLE_SECURITY_PLUGIN: true
options: >-
--health-cmd "curl http://localhost:9200/_cluster/health"
--health-interval 10s
--health-timeout 5s
--health-retries 10
ports:
- 9200:9200
env: env:
DB_HOST: localhost DB_HOST: localhost
DB_USER: postgres DB_USER: postgres
@ -308,21 +285,19 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
ruby-version: ruby-version:
- '3.0'
- '3.1' - '3.1'
- '3.2'
- '.ruby-version' - '.ruby-version'
search-image: search-image:
- docker.elastic.co/elasticsearch/elasticsearch:7.17.13 - docker.elastic.co/elasticsearch/elasticsearch:7.17.13
include: include:
- ruby-version: '.ruby-version' - ruby-version: '.ruby-version'
search-image: docker.elastic.co/elasticsearch/elasticsearch:8.10.2 search-image: docker.elastic.co/elasticsearch/elasticsearch:8.10.2
- ruby-version: '.ruby-version'
search-image: opensearchproject/opensearch:2
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: actions/download-artifact@v4 - uses: actions/download-artifact@v3
with: with:
path: './public' path: './public'
name: ${{ github.sha }} name: ${{ github.sha }}
@ -342,105 +317,15 @@ jobs:
- run: bin/rspec --tag search - run: bin/rspec --tag search
- name: Archive logs - name: Archive logs
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v3
if: failure() if: failure()
with: with:
name: test-search-logs-${{ matrix.ruby-version }} name: test-search-logs-${{ matrix.ruby-version }}
path: log/ path: log/
- name: Archive test screenshots - name: Archive test screenshots
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v3
if: failure() if: failure()
with: with:
name: test-search-screenshots name: test-search-screenshots
path: tmp/capybara/ path: tmp/screenshots/
test-back-and-return:
name: Back to original and return test
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 10s
--health-timeout 5s
--health-retries 5
ports:
- 5432:5432
redis:
image: redis:7-alpine
options: >-
--health-cmd "redis-cli ping"
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 6379:6379
env:
DB_HOST: localhost
DB_USER: postgres
DB_PASS: postgres
DISABLE_SIMPLECOV: ${{ 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' }}
ES_ENABLED: false
BACK_UPSTREAM_FORCE: true
strategy:
fail-fast: false
matrix:
ruby-version:
- '.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'
- name: Back to upstream schema
run: 'bundle exec rake dangerous:back_upstream'
- name: Return to kmyblue
run: './bin/rails db:migrate'
- run: bin/rspec
- name: Upload coverage reports to Codecov
if: matrix.ruby-version == '.ruby-version'
uses: codecov/codecov-action@v3
with:
files: coverage/lcov/mastodon-back-ret.lcov

7
.gitignore vendored
View file

@ -24,12 +24,10 @@
/public/packs-test /public/packs-test
.env .env
.env.production .env.production
.env.development
/node_modules/ /node_modules/
/build/ /build/
# Ignore elasticsearch config
/.elasticsearch.yml
# Ignore Vagrant files # Ignore Vagrant files
.vagrant/ .vagrant/
@ -71,6 +69,3 @@ yarn-debug.log
# Ignore Docker option files # Ignore Docker option files
docker-compose.override.yml docker-compose.override.yml
# Ignore dotenv .local files
.env*.local

View file

@ -2,6 +2,7 @@ inherits_from: .haml-lint_todo.yml
exclude: exclude:
- 'vendor/**/*' - 'vendor/**/*'
- lib/templates/haml/scaffold/_form.html.haml
require: require:
- ./lib/linter/haml_middle_dot.rb - ./lib/linter/haml_middle_dot.rb
@ -12,6 +13,4 @@ linters:
MiddleDot: MiddleDot:
enabled: true enabled: true
LineLength: LineLength:
max: 300 max: 320
ViewLength:
max: 200 # Override default value of 100 inherited from rubocop

View file

@ -10,27 +10,4 @@ linters:
# Offense count: 1 # Offense count: 1
LineLength: LineLength:
exclude: exclude:
- 'app/views/admin/ng_rules/_ng_rule_fields.html.haml'
- 'app/views/admin/roles/_form.html.haml' - 'app/views/admin/roles/_form.html.haml'
# Offense count: 9
RuboCop:
exclude:
- 'app/views/home/index.html.haml'
ViewLength:
exclude:
- 'app/views/admin/accounts/index.html.haml'
- 'app/views/admin/instances/show.html.haml'
- 'app/views/admin/ng_rules/_ng_rule_fields.html.haml'
- 'app/views/admin/settings/discovery/show.html.haml'
- 'app/views/settings/preferences/appearance/show.html.haml'
- 'app/views/settings/preferences/other/show.html.haml'
InstanceVariables:
exclude:
- 'app/views/application/_sidebar.html.haml'
- 'app/views/admin/ng_rules/_ng_rule_fields.html.haml'
- 'app/views/admin/ng_words/keywords/_ng_word.html.haml'
- 'app/views/admin/ng_words/white_list/_specified_domain.html.haml'
- 'app/views/admin/sensitive_words/_sensitive_word.html.haml'

View file

@ -1 +1,4 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
yarn lint-staged yarn lint-staged

2
.nvmrc
View file

@ -1 +1 @@
20.13 20.11

View file

@ -54,13 +54,6 @@
# Ignore Docker option files # Ignore Docker option files
docker-compose.override.yml docker-compose.override.yml
# Ignore public
/public/assets
/public/emoji
/public/packs
/public/packs-test
/public/system
# Ignore emoji map file # Ignore emoji map file
/app/javascript/mastodon/features/emoji/emoji_map.json /app/javascript/mastodon/features/emoji/emoji_map.json
@ -81,5 +74,4 @@ app/javascript/styles/mastodon/reset.scss
# Ignore the generated AUTHORS.md # Ignore the generated AUTHORS.md
AUTHORS.md AUTHORS.md
# Process a few selected JS files
!lint-staged.config.js !lint-staged.config.js

View file

@ -9,13 +9,12 @@ inherit_mode:
require: require:
- rubocop-rails - rubocop-rails
- rubocop-rspec - rubocop-rspec
- rubocop-rspec_rails
- rubocop-performance - rubocop-performance
- rubocop-capybara - rubocop-capybara
- ./lib/linter/rubocop_middle_dot - ./lib/linter/rubocop_middle_dot
AllCops: AllCops:
TargetRubyVersion: 3.1 # Set to minimum supported version of CI TargetRubyVersion: 3.0 # Set to minimum supported version of CI
DisplayCopNames: true DisplayCopNames: true
DisplayStyleGuide: true DisplayStyleGuide: true
ExtraDetails: true ExtraDetails: true
@ -23,7 +22,7 @@ AllCops:
CacheRootDirectory: tmp CacheRootDirectory: tmp
NewCops: enable # Opt-in to newly added rules NewCops: enable # Opt-in to newly added rules
Exclude: Exclude:
- 'db/schema.rb' - db/schema.rb
- 'bin/*' - 'bin/*'
- 'node_modules/**/*' - 'node_modules/**/*'
- 'Vagrantfile' - 'Vagrantfile'
@ -40,7 +39,13 @@ Layout/FirstHashElementIndentation:
# Reason: Currently disabled in .rubocop_todo.yml # Reason: Currently disabled in .rubocop_todo.yml
# https://docs.rubocop.org/rubocop/cops_layout.html#layoutlinelength # https://docs.rubocop.org/rubocop/cops_layout.html#layoutlinelength
Layout/LineLength: Layout/LineLength:
Max: 300 # Default of 120 causes a duplicate entry in generated todo file Max: 320 # Default of 120 causes a duplicate entry in generated todo file
# Reason:
# https://docs.rubocop.org/rubocop/cops_lint.html#lintuselessaccessmodifier
Lint/UselessAccessModifier:
ContextCreatingMethods:
- class_methods
## Disable most Metrics/*Length cops ## Disable most Metrics/*Length cops
# Reason: those are often triggered and force significant refactors when this happend # Reason: those are often triggered and force significant refactors when this happend
@ -68,18 +73,12 @@ Metrics/ModuleLength:
# https://docs.rubocop.org/rubocop/cops_metrics.html#metricsabcsize # https://docs.rubocop.org/rubocop/cops_metrics.html#metricsabcsize
Metrics/AbcSize: Metrics/AbcSize:
Exclude: Exclude:
- 'app/serializers/initial_state_serializer.rb'
- 'lib/mastodon/cli/*.rb' - 'lib/mastodon/cli/*.rb'
# Reason: Currently disabled in .rubocop_todo.yml # Reason: Currently disabled in .rubocop_todo.yml
# https://docs.rubocop.org/rubocop/cops_metrics.html#metricscyclomaticcomplexity # https://docs.rubocop.org/rubocop/cops_metrics.html#metricscyclomaticcomplexity
Metrics/CyclomaticComplexity: Metrics/CyclomaticComplexity:
Exclude: Exclude:
- 'app/lib/feed_manager.rb'
- 'app/policies/status_policy.rb'
- 'app/services/activitypub/process_account_service.rb'
- 'app/services/delivery_antenna_service.rb'
- 'app/services/post_status_service.rb'
- lib/mastodon/cli/*.rb - lib/mastodon/cli/*.rb
# Reason: # Reason:
@ -87,17 +86,6 @@ Metrics/CyclomaticComplexity:
Metrics/ParameterLists: Metrics/ParameterLists:
CountKeywordArgs: false CountKeywordArgs: false
Metrics/PerceivedComplexity:
Exclude:
- 'app/policies/status_policy.rb'
- 'app/services/delivery_antenna_service.rb'
- 'app/services/post_status_service.rb'
# Reason: Prefer seeing a variable name
# https://docs.rubocop.org/rubocop/cops_naming.html#namingblockforwarding
Naming/BlockForwarding:
EnforcedStyle: explicit
# Reason: Prevailing style is argument file paths # Reason: Prevailing style is argument file paths
# https://docs.rubocop.org/rubocop-rails/cops_rails.html#railsfilepath # https://docs.rubocop.org/rubocop-rails/cops_rails.html#railsfilepath
Rails/FilePath: Rails/FilePath:
@ -108,26 +96,22 @@ Rails/FilePath:
Rails/HttpStatus: Rails/HttpStatus:
EnforcedStyle: numeric EnforcedStyle: numeric
# Reason: Allowed in `tootctl` CLI code and in boot ENV checker
# https://docs.rubocop.org/rubocop-rails/cops_rails.html#railsexit
Rails/Exit:
Exclude:
- 'config/boot.rb'
- 'lib/mastodon/cli/*.rb'
# Reason: Conflicts with `Lint/UselessMethodDefinition` for inherited controller actions # Reason: Conflicts with `Lint/UselessMethodDefinition` for inherited controller actions
# https://docs.rubocop.org/rubocop-rails/cops_rails.html#railslexicallyscopedactionfilter # https://docs.rubocop.org/rubocop-rails/cops_rails.html#railslexicallyscopedactionfilter
Rails/LexicallyScopedActionFilter: Rails/LexicallyScopedActionFilter:
Exclude: Exclude:
- 'app/controllers/auth/*' - 'app/controllers/auth/*'
# Reason: These tasks are doing local work which do not need full env loaded
# https://docs.rubocop.org/rubocop-rails/cops_rails.html#railsrakeenvironment
Rails/RakeEnvironment:
Exclude:
- 'lib/tasks/auto_annotate_models.rake'
- 'lib/tasks/emojis.rake'
- 'lib/tasks/mastodon.rake'
- 'lib/tasks/repo.rake'
- 'lib/tasks/statistics.rake'
# Reason: There are appropriate times to use these features
# https://docs.rubocop.org/rubocop-rails/cops_rails.html#railsskipsmodelvalidations
Rails/SkipsModelValidations: Rails/SkipsModelValidations:
Enabled: false Exclude:
- 'db/*migrate/**/*'
# Reason: We want to preserve the ability to migrate from arbitrary old versions, # Reason: We want to preserve the ability to migrate from arbitrary old versions,
# and cannot guarantee that every installation has run every migration as they upgrade. # and cannot guarantee that every installation has run every migration as they upgrade.
@ -140,11 +124,6 @@ Rails/UnusedIgnoredColumns:
Rails/NegateInclude: Rails/NegateInclude:
Enabled: false Enabled: false
# Reason: Enforce default limit, but allow some elements to span lines
# https://docs.rubocop.org/rubocop-rspec/cops_rspec.html#rspecexamplelength
RSpec/ExampleLength:
CountAsOne: ['array', 'heredoc', 'method_call']
# Reason: Deprecated cop, will be removed in 3.0, replaced by SpecFilePathFormat # Reason: Deprecated cop, will be removed in 3.0, replaced by SpecFilePathFormat
# https://docs.rubocop.org/rubocop-rspec/cops_rspec.html#rspecfilepath # https://docs.rubocop.org/rubocop-rspec/cops_rspec.html#rspecfilepath
RSpec/FilePath: RSpec/FilePath:
@ -160,6 +139,11 @@ RSpec/NamedSubject:
RSpec/NotToNot: RSpec/NotToNot:
EnforcedStyle: to_not EnforcedStyle: to_not
# Reason: Prevailing style uses numeric status codes, matches Rails/HttpStatus
# https://docs.rubocop.org/rubocop-rspec/cops_rspec_rails.html#rspecrailshttpstatus
RSpec/Rails/HttpStatus:
EnforcedStyle: numeric
# Reason: Match overrides from Rspec/FilePath rule above # Reason: Match overrides from Rspec/FilePath rule above
# https://docs.rubocop.org/rubocop-rspec/cops_rspec.html#rspecspecfilepathformat # https://docs.rubocop.org/rubocop-rspec/cops_rspec.html#rspecspecfilepathformat
RSpec/SpecFilePathFormat: RSpec/SpecFilePathFormat:
@ -170,11 +154,6 @@ RSpec/SpecFilePathFormat:
OEmbedController: oembed_controller OEmbedController: oembed_controller
OStatus: ostatus OStatus: ostatus
# Reason: Prevailing style uses numeric status codes, matches Rails/HttpStatus
# https://docs.rubocop.org/rubocop-rspec/cops_rspec_rails.html#rspecrailshttpstatus
RSpecRails/HttpStatus:
EnforcedStyle: numeric
# Reason: # Reason:
# https://docs.rubocop.org/rubocop/cops_style.html#styleclassandmodulechildren # https://docs.rubocop.org/rubocop/cops_style.html#styleclassandmodulechildren
Style/ClassAndModuleChildren: Style/ClassAndModuleChildren:
@ -185,25 +164,10 @@ Style/ClassAndModuleChildren:
Style/Documentation: Style/Documentation:
Enabled: false Enabled: false
# Reason: Route redirects are not token-formatted and must be skipped
# https://docs.rubocop.org/rubocop/cops_style.html#styleformatstringtoken
Style/FormatStringToken:
inherit_mode:
merge:
- AllowedMethods # The rubocop-rails config adds `redirect`
AllowedMethods:
- redirect_with_vary
# Reason: Prevailing style choice
# https://docs.rubocop.org/rubocop/cops_style.html#stylehashaslastarrayitem
Style/HashAsLastArrayItem:
Enabled: false
# Reason: Enforce modern Ruby style # Reason: Enforce modern Ruby style
# https://docs.rubocop.org/rubocop/cops_style.html#stylehashsyntax # https://docs.rubocop.org/rubocop/cops_style.html#stylehashsyntax
Style/HashSyntax: Style/HashSyntax:
EnforcedStyle: ruby19_no_mixed_keys EnforcedStyle: ruby19_no_mixed_keys
EnforcedShorthandSyntax: either
# Reason: # Reason:
# https://docs.rubocop.org/rubocop/cops_style.html#stylenumericliterals # https://docs.rubocop.org/rubocop/cops_style.html#stylenumericliterals
@ -223,16 +187,16 @@ Style/PercentLiteralDelimiters:
Style/RedundantBegin: Style/RedundantBegin:
Enabled: false Enabled: false
# Reason: Prevailing style choice
# https://docs.rubocop.org/rubocop/cops_style.html#styleredundantfetchblock
Style/RedundantFetchBlock:
Enabled: false
# Reason: Overridden to reduce implicit StandardError rescues # Reason: Overridden to reduce implicit StandardError rescues
# https://docs.rubocop.org/rubocop/cops_style.html#stylerescuestandarderror # https://docs.rubocop.org/rubocop/cops_style.html#stylerescuestandarderror
Style/RescueStandardError: Style/RescueStandardError:
EnforcedStyle: implicit EnforcedStyle: implicit
# Reason: Simplify some spec layouts
# https://docs.rubocop.org/rubocop/cops_style.html#stylesemicolon
Style/Semicolon:
AllowAsExpressionSeparator: true
# Reason: Originally disabled for CodeClimate, and no config consensus has been found # Reason: Originally disabled for CodeClimate, and no config consensus has been found
# https://docs.rubocop.org/rubocop/cops_style.html#stylesymbolarray # https://docs.rubocop.org/rubocop/cops_style.html#stylesymbolarray
Style/SymbolArray: Style/SymbolArray:

View file

@ -1,11 +1,25 @@
# This configuration was generated by # This configuration was generated by
# `rubocop --auto-gen-config --auto-gen-only-exclude --no-exclude-limit --no-offense-counts --no-auto-gen-timestamp` # `rubocop --auto-gen-config --auto-gen-only-exclude --no-exclude-limit --no-offense-counts --no-auto-gen-timestamp`
# using RuboCop version 1.63.5. # using RuboCop version 1.59.0.
# The point is for the user to remove these configuration records # The point is for the user to remove these configuration records
# one by one as the offenses are removed from the code base. # one by one as the offenses are removed from the code base.
# Note that changes in the inspected code, or installation of new # Note that changes in the inspected code, or installation of new
# versions of RuboCop, may require this file to be generated again. # versions of RuboCop, may require this file to be generated again.
# This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: TreatCommentsAsGroupSeparators, ConsiderPunctuation, Include.
# Include: **/*.gemfile, **/Gemfile, **/gems.rb
Bundler/OrderedGems:
Exclude:
- 'Gemfile'
# This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: Max, AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, AllowedPatterns.
# URISchemes: http, https
Layout/LineLength:
Exclude:
- 'app/models/account.rb'
Lint/NonLocalExitFromIterator: Lint/NonLocalExitFromIterator:
Exclude: Exclude:
- 'app/helpers/jsonld_helper.rb' - 'app/helpers/jsonld_helper.rb'
@ -29,27 +43,119 @@ Metrics/PerceivedComplexity:
# Configuration parameters: CountAsOne. # Configuration parameters: CountAsOne.
RSpec/ExampleLength: RSpec/ExampleLength:
Max: 18 Max: 22
RSpec/MultipleExpectations: RSpec/MultipleExpectations:
Max: 7 Max: 8
# Configuration parameters: AllowSubject. # Configuration parameters: AllowSubject.
RSpec/MultipleMemoizedHelpers: RSpec/MultipleMemoizedHelpers:
Max: 17 Max: 17
Exclude:
- 'spec/lib/activitypub/activity/create_spec.rb'
- 'spec/services/delete_account_service_spec.rb'
- 'spec/services/fan_out_on_write_service_spec.rb'
# Configuration parameters: AllowedGroups. # Configuration parameters: AllowedGroups.
RSpec/NestedGroups: RSpec/NestedGroups:
Max: 6 Max: 6
# Configuration parameters: Include.
# Include: app/models/**/*.rb
Rails/HasAndBelongsToMany:
Exclude:
- 'app/models/concerns/account/associations.rb'
- 'app/models/status.rb'
- 'app/models/tag.rb'
Rails/OutputSafety: Rails/OutputSafety:
Exclude: Exclude:
- 'config/initializers/simple_form.rb' - 'config/initializers/simple_form.rb'
# This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: Include.
# Include: **/Rakefile, **/*.rake
Rails/RakeEnvironment:
Exclude:
- 'lib/tasks/auto_annotate_models.rake'
- 'lib/tasks/db.rake'
- 'lib/tasks/emojis.rake'
- 'lib/tasks/mastodon.rake'
- 'lib/tasks/repo.rake'
- 'lib/tasks/statistics.rake'
# Configuration parameters: ForbiddenMethods, AllowedMethods.
# ForbiddenMethods: decrement!, decrement_counter, increment!, increment_counter, insert, insert!, insert_all, insert_all!, toggle!, touch, touch_all, update_all, update_attribute, update_column, update_columns, update_counters, upsert, upsert_all
Rails/SkipsModelValidations:
Exclude:
- 'app/controllers/admin/invites_controller.rb'
- 'app/controllers/concerns/session_tracking_concern.rb'
- 'app/models/concerns/account/merging.rb'
- 'app/models/concerns/expireable.rb'
- 'app/models/status.rb'
- 'app/models/trends/links.rb'
- 'app/models/trends/preview_card_batch.rb'
- 'app/models/trends/preview_card_provider_batch.rb'
- 'app/models/trends/status_batch.rb'
- 'app/models/trends/statuses.rb'
- 'app/models/trends/tag_batch.rb'
- 'app/models/trends/tags.rb'
- 'app/models/user.rb'
- 'app/services/activitypub/process_status_update_service.rb'
- 'app/services/approve_appeal_service.rb'
- 'app/services/block_domain_service.rb'
- 'app/services/delete_account_service.rb'
- 'app/services/process_mentions_service.rb'
- 'app/services/unallow_domain_service.rb'
- 'app/services/unblock_domain_service.rb'
- 'app/services/update_status_service.rb'
- 'app/workers/activitypub/post_upgrade_worker.rb'
- 'app/workers/move_worker.rb'
- 'app/workers/scheduler/ip_cleanup_scheduler.rb'
- 'app/workers/scheduler/scheduled_statuses_scheduler.rb'
- 'lib/mastodon/cli/accounts.rb'
- 'lib/mastodon/cli/maintenance.rb'
- 'spec/lib/activitypub/activity/follow_spec.rb'
- 'spec/services/follow_service_spec.rb'
- 'spec/services/update_account_service_spec.rb'
# Configuration parameters: Include.
# Include: app/models/**/*.rb
Rails/UniqueValidationWithoutIndex:
Exclude:
- 'app/models/account_alias.rb'
- 'app/models/custom_filter_status.rb'
- 'app/models/identity.rb'
- 'app/models/webauthn_credential.rb'
# This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: EnforcedStyle.
# SupportedStyles: exists, where
Rails/WhereExists:
Exclude:
- 'app/controllers/activitypub/inboxes_controller.rb'
- 'app/controllers/admin/email_domain_blocks_controller.rb'
- 'app/lib/activitypub/activity/create.rb'
- 'app/lib/delivery_failure_tracker.rb'
- 'app/lib/feed_manager.rb'
- 'app/lib/status_cache_hydrator.rb'
- 'app/lib/suspicious_sign_in_detector.rb'
- 'app/models/concerns/account/interactions.rb'
- 'app/models/featured_tag.rb'
- 'app/models/poll.rb'
- 'app/models/session_activation.rb'
- 'app/models/status.rb'
- 'app/models/user.rb'
- 'app/policies/status_policy.rb'
- 'app/serializers/rest/announcement_serializer.rb'
- 'app/serializers/rest/tag_serializer.rb'
- 'app/services/activitypub/fetch_remote_status_service.rb'
- 'app/services/vote_service.rb'
- 'app/validators/reaction_validator.rb'
- 'app/validators/vote_validator.rb'
- 'app/workers/move_worker.rb'
- 'lib/tasks/tests.rake'
- 'spec/models/account_spec.rb'
- 'spec/services/activitypub/process_collection_service_spec.rb'
- 'spec/services/purge_domain_service_spec.rb'
- 'spec/services/unallow_domain_service_spec.rb'
# This cop supports unsafe autocorrection (--autocorrect-all). # This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: AllowedMethods, AllowedPatterns. # Configuration parameters: AllowedMethods, AllowedPatterns.
# AllowedMethods: ==, equal?, eql? # AllowedMethods: ==, equal?, eql?
@ -58,12 +164,17 @@ Style/ClassEqualityComparison:
- 'app/helpers/jsonld_helper.rb' - 'app/helpers/jsonld_helper.rb'
- 'app/serializers/activitypub/outbox_serializer.rb' - 'app/serializers/activitypub/outbox_serializer.rb'
Style/ClassVars:
Exclude:
- 'config/initializers/devise.rb'
# This cop supports safe autocorrection (--autocorrect). # This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: AllowedVars. # Configuration parameters: AllowedVars.
Style/FetchEnvVar: Style/FetchEnvVar:
Exclude: Exclude:
- 'app/lib/redis_configuration.rb' - 'app/lib/redis_configuration.rb'
- 'app/lib/translation_service.rb' - 'app/lib/translation_service.rb'
- 'config/environments/development.rb'
- 'config/environments/production.rb' - 'config/environments/production.rb'
- 'config/initializers/2_limited_federation_mode.rb' - 'config/initializers/2_limited_federation_mode.rb'
- 'config/initializers/3_omniauth.rb' - 'config/initializers/3_omniauth.rb'
@ -73,8 +184,9 @@ Style/FetchEnvVar:
- 'config/initializers/paperclip.rb' - 'config/initializers/paperclip.rb'
- 'config/initializers/vapid.rb' - 'config/initializers/vapid.rb'
- 'lib/mastodon/redis_config.rb' - 'lib/mastodon/redis_config.rb'
- 'lib/premailer_webpack_strategy.rb'
- 'lib/tasks/repo.rake' - 'lib/tasks/repo.rake'
- 'spec/system/profile_spec.rb' - 'spec/features/profile_spec.rb'
# This cop supports safe autocorrection (--autocorrect). # This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: EnforcedStyle, MaxUnannotatedPlaceholdersAllowed, AllowedMethods, AllowedPatterns. # Configuration parameters: EnforcedStyle, MaxUnannotatedPlaceholdersAllowed, AllowedMethods, AllowedPatterns.
@ -82,6 +194,7 @@ Style/FetchEnvVar:
# AllowedMethods: redirect # AllowedMethods: redirect
Style/FormatStringToken: Style/FormatStringToken:
Exclude: Exclude:
- 'app/models/privacy_policy.rb'
- 'config/initializers/devise.rb' - 'config/initializers/devise.rb'
- 'lib/paperclip/color_extractor.rb' - 'lib/paperclip/color_extractor.rb'
@ -95,6 +208,10 @@ Style/GlobalStdStream:
# Configuration parameters: MinBodyLength, AllowConsecutiveConditionals. # Configuration parameters: MinBodyLength, AllowConsecutiveConditionals.
Style/GuardClause: Style/GuardClause:
Exclude: Exclude:
- 'app/controllers/admin/confirmations_controller.rb'
- 'app/controllers/auth/confirmations_controller.rb'
- 'app/controllers/auth/passwords_controller.rb'
- 'app/controllers/settings/two_factor_authentication/webauthn_credentials_controller.rb'
- 'app/lib/activitypub/activity/block.rb' - 'app/lib/activitypub/activity/block.rb'
- 'app/lib/request.rb' - 'app/lib/request.rb'
- 'app/lib/request_pool.rb' - 'app/lib/request_pool.rb'
@ -118,14 +235,35 @@ Style/GuardClause:
- 'lib/mastodon/cli/accounts.rb' - 'lib/mastodon/cli/accounts.rb'
- 'lib/mastodon/cli/maintenance.rb' - 'lib/mastodon/cli/maintenance.rb'
- 'lib/mastodon/cli/media.rb' - 'lib/mastodon/cli/media.rb'
- 'lib/paperclip/attachment_extensions.rb'
- 'lib/tasks/repo.rake' - 'lib/tasks/repo.rake'
# This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: EnforcedStyle.
# SupportedStyles: braces, no_braces
Style/HashAsLastArrayItem:
Exclude:
- 'app/controllers/admin/statuses_controller.rb'
- 'app/controllers/api/v1/statuses_controller.rb'
- 'app/models/concerns/account/counters.rb'
- 'app/models/concerns/status/threading_concern.rb'
- 'app/models/status.rb'
- 'app/services/batched_remove_status_service.rb'
- 'app/services/notify_service.rb'
# This cop supports unsafe autocorrection (--autocorrect-all). # This cop supports unsafe autocorrection (--autocorrect-all).
Style/HashTransformValues: Style/HashTransformValues:
Exclude: Exclude:
- 'app/serializers/rest/web_push_subscription_serializer.rb' - 'app/serializers/rest/web_push_subscription_serializer.rb'
- 'app/services/import_service.rb' - 'app/services/import_service.rb'
# This cop supports safe autocorrection (--autocorrect).
Style/IfUnlessModifier:
Exclude:
- 'config/environments/production.rb'
- 'config/initializers/devise.rb'
- 'config/initializers/ffmpeg.rb'
# This cop supports unsafe autocorrection (--autocorrect-all). # This cop supports unsafe autocorrection (--autocorrect-all).
Style/MapToHash: Style/MapToHash:
Exclude: Exclude:
@ -160,6 +298,13 @@ Style/OptionalBooleanParameter:
- 'app/workers/unfollow_follow_worker.rb' - 'app/workers/unfollow_follow_worker.rb'
- 'lib/mastodon/redis_config.rb' - 'lib/mastodon/redis_config.rb'
# This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: PreferredDelimiters.
Style/PercentLiteralDelimiters:
Exclude:
- 'config/deploy.rb'
- 'config/initializers/doorkeeper.rb'
# This cop supports unsafe autocorrection (--autocorrect-all). # This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: EnforcedStyle. # Configuration parameters: EnforcedStyle.
# SupportedStyles: short, verbose # SupportedStyles: short, verbose
@ -173,6 +318,16 @@ Style/RedundantConstantBase:
- 'config/environments/production.rb' - 'config/environments/production.rb'
- 'config/initializers/sidekiq.rb' - 'config/initializers/sidekiq.rb'
# This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: SafeForConstants.
Style/RedundantFetchBlock:
Exclude:
- 'config/initializers/1_hosts.rb'
- 'config/initializers/chewy.rb'
- 'config/initializers/devise.rb'
- 'config/initializers/paperclip.rb'
- 'config/puma.rb'
# This cop supports unsafe autocorrection (--autocorrect-all). # This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: ConvertCodeThatCanStartToReturnNil, AllowedMethods, MaxChainLength. # Configuration parameters: ConvertCodeThatCanStartToReturnNil, AllowedMethods, MaxChainLength.
# AllowedMethods: present?, blank?, presence, try, try! # AllowedMethods: present?, blank?, presence, try, try!
@ -180,12 +335,59 @@ Style/SafeNavigation:
Exclude: Exclude:
- 'app/models/concerns/account/finder_concern.rb' - 'app/models/concerns/account/finder_concern.rb'
# This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: EnforcedStyle.
# SupportedStyles: only_raise, only_fail, semantic
Style/SignalException:
Exclude:
- 'lib/devise/strategies/two_factor_ldap_authenticatable.rb'
- 'lib/devise/strategies/two_factor_pam_authenticatable.rb'
# This cop supports unsafe autocorrection (--autocorrect-all).
Style/SingleArgumentDig:
Exclude:
- 'lib/webpacker/manifest_extensions.rb'
# This cop supports unsafe autocorrection (--autocorrect-all). # This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: Mode. # Configuration parameters: Mode.
Style/StringConcatenation: Style/StringConcatenation:
Exclude: Exclude:
- 'config/initializers/paperclip.rb' - 'config/initializers/paperclip.rb'
# This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: EnforcedStyle, ConsistentQuotesInMultiline.
# SupportedStyles: single_quotes, double_quotes
Style/StringLiterals:
Exclude:
- 'config/environments/production.rb'
- 'config/initializers/backtrace_silencers.rb'
- 'config/initializers/http_client_proxy.rb'
- 'config/initializers/rack_attack.rb'
- 'config/initializers/webauthn.rb'
- 'config/routes.rb'
# This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: EnforcedStyle, AllowSafeAssignment.
# SupportedStyles: require_parentheses, require_no_parentheses, require_parentheses_when_complex
Style/TernaryParentheses:
Exclude:
- 'config/environments/development.rb'
# This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: EnforcedStyleForMultiline.
# SupportedStylesForMultiline: comma, consistent_comma, no_comma
Style/TrailingCommaInArguments:
Exclude:
- 'config/initializers/paperclip.rb'
# This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: EnforcedStyleForMultiline.
# SupportedStylesForMultiline: comma, consistent_comma, no_comma
Style/TrailingCommaInHashLiteral:
Exclude:
- 'config/environments/production.rb'
- 'config/environments/test.rb'
# This cop supports safe autocorrection (--autocorrect). # This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: WordRegex. # Configuration parameters: WordRegex.
# SupportedStyles: percent, brackets # SupportedStyles: percent, brackets

View file

@ -1 +1 @@
3.3.1 3.2.2

22
.simplecov Normal file
View file

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

View file

@ -1,18 +0,0 @@
# Authors for kmyblue fork
## 貢献者
kmyblueフォークは、以下の方の貢献によって成り立っています。
本家Mastodonの貢献者については、`AUTHORS.md`をご覧ください。
- [aoisensi](https://github.com/aoisensi)
- [KMY](https://github.com/kmycode)
- [S-H-GAMELINKS](https://github.com/S-H-GAMELINKS)
- [Yuicho](https://github.com/yuicho)
## 特記
kmyblueフォークの開発にあたって、API・Activity仕様の設計一部機能については内部仕様策定の過程で下記リポジトリのコードを参考にしました。
kmyblueフォークに直接貢献したわけではありませんが、以下のリポジトリにある絵文字リアクション機能・検索範囲機能のコードのうち一部にkmyblueへ転写した箇所がございますため、お名前記載させていただきます。
- [Fedibird](https://github.com/fedibird/mastodon)

View file

@ -2,101 +2,6 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
## [4.2.7] - 2024-02-16
### Fixed
- Fix OmniAuth tests and edge cases in error handling ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/29201), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/29207))
- Fix new installs by upgrading to the latest release of the `nsa` gem, instead of a no longer existing commit ([mjankowski](https://github.com/mastodon/mastodon/pull/29065))
### Security
- Fix insufficient checking of remote posts ([GHSA-jhrq-qvrm-qr36](https://github.com/mastodon/mastodon/security/advisories/GHSA-jhrq-qvrm-qr36))
## [4.2.6] - 2024-02-14
### Security
- Update the `sidekiq-unique-jobs` dependency (see [GHSA-cmh9-rx85-xj38](https://github.com/mhenrixon/sidekiq-unique-jobs/security/advisories/GHSA-cmh9-rx85-xj38))
In addition, we have disabled the web interface for `sidekiq-unique-jobs` out of caution.
If you need it, you can re-enable it by setting `ENABLE_SIDEKIQ_UNIQUE_JOBS_UI=true`.
If you only need to clear all locks, you can now use `bundle exec rake sidekiq_unique_jobs:delete_all_locks`.
- Update the `nokogiri` dependency (see [GHSA-xc9x-jj77-9p9j](https://github.com/sparklemotion/nokogiri/security/advisories/GHSA-xc9x-jj77-9p9j))
- Disable administrative Doorkeeper routes ([ThisIsMissEm](https://github.com/mastodon/mastodon/pull/29187))
- Fix ongoing streaming sessions not being invalidated when applications get deleted in some cases ([GHSA-7w3c-p9j8-mq3x](https://github.com/mastodon/mastodon/security/advisories/GHSA-7w3c-p9j8-mq3x))
In some rare cases, the streaming server was not notified of access tokens revocation on application deletion.
- Change external authentication behavior to never reattach a new identity to an existing user by default ([GHSA-vm39-j3vx-pch3](https://github.com/mastodon/mastodon/security/advisories/GHSA-vm39-j3vx-pch3))
Up until now, Mastodon has allowed new identities from external authentication providers to attach to an existing local user based on their verified e-mail address.
This allowed upgrading users from a database-stored password to an external authentication provider, or move from one authentication provider to another.
However, this behavior may be unexpected, and means that when multiple authentication providers are configured, the overall security would be that of the least secure authentication provider.
For these reasons, this behavior is now locked under the `ALLOW_UNSAFE_AUTH_PROVIDER_REATTACH` environment variable.
In addition, regardless of this environment variable, Mastodon will refuse to attach two identities from the same authentication provider to the same account.
## [4.2.5] - 2024-02-01
### Security
- Fix insufficient origin validation (CVE-2024-23832, [GHSA-3fjr-858r-92rw](https://github.com/mastodon/mastodon/security/advisories/GHSA-3fjr-858r-92rw))
## [4.2.4] - 2024-01-24
### Fixed
- Fix error when processing remote files with unusually long names ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/28823))
- Fix processing of compacted single-item JSON-LD collections ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/28816))
- Retry 401 errors on replies fetching ([ShadowJonathan](https://github.com/mastodon/mastodon/pull/28788))
- Fix `RecordNotUnique` errors in LinkCrawlWorker ([tribela](https://github.com/mastodon/mastodon/pull/28748))
- Fix Mastodon not correctly processing HTTP Signatures with query strings ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/28443), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/28476))
- Fix potential redirection loop of streaming endpoint ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/28665))
- Fix streaming API redirection ignoring the port of `streaming_api_base_url` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/28558))
- Fix error when processing link preview with an array as `inLanguage` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/28252))
- Fix unsupported time zone or locale preventing sign-up ([Gargron](https://github.com/mastodon/mastodon/pull/28035))
- Fix "Hide these posts from home" list setting not refreshing when switching lists ([brianholley](https://github.com/mastodon/mastodon/pull/27763))
- Fix missing background behind dismissable banner in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/27479))
- Fix line wrapping of language selection button with long locale codes ([gunchleoc](https://github.com/mastodon/mastodon/pull/27100), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/27127))
- Fix `Undo Announce` activity not being sent to non-follower authors ([MitarashiDango](https://github.com/mastodon/mastodon/pull/18482))
- Fix N+1s because of association preloaders not actually getting called ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/28339))
- Fix empty column explainer getting cropped under certain conditions ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/28337))
- Fix `LinkCrawlWorker` error when encountering empty OEmbed response ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/28268))
- Fix call to inefficient `delete_matched` cache method in domain blocks ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/28367))
### Security
- Add rate-limit of TOTP authentication attempts at controller level ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/28801))
## [4.2.3] - 2023-12-05
### Fixed
- Fix dependency on `json-canonicalization` version that has been made unavailable since last release
## [4.2.2] - 2023-12-04
### Changed
- Change dismissed banners to be stored server-side ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27055))
- Change GIF max matrix size error to explicitly mention GIF files ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27927))
- Change `Follow` activities delivery to bypass availability check ([ShadowJonathan](https://github.com/mastodon/mastodon/pull/27586))
- Change single-column navigation notice to be displayed outside of the logo container ([renchap](https://github.com/mastodon/mastodon/pull/27462), [renchap](https://github.com/mastodon/mastodon/pull/27476))
- Change Content-Security-Policy to be tighter on media paths ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/26889))
- Change post language code to include country code when relevant ([gunchleoc](https://github.com/mastodon/mastodon/pull/27099), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/27207))
### Fixed
- Fix upper border radius of onboarding columns ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27890))
- Fix incoming status creation date not being restricted to standard ISO8601 ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27655), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/28081))
- Fix some posts from threads received out-of-order sometimes not being inserted into timelines ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27653))
- Fix posts from force-sensitized accounts being able to trend ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27620))
- Fix error when trying to delete already-deleted file with OpenStack Swift ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27569))
- Fix batch attachment deletion when using OpenStack Swift ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27554))
- Fix processing LDSigned activities from actors with unknown public keys ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27474))
- Fix error and incorrect URLs in `/api/v1/accounts/:id/featured_tags` for remote accounts ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27459))
- Fix report processing notice not mentioning the report number when performing a custom action ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27442))
- Fix handling of `inLanguage` attribute in preview card processing ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27423))
- Fix own posts being removed from home timeline when unfollowing a used hashtag ([kmycode](https://github.com/mastodon/mastodon/pull/27391))
- Fix some link anchors being recognized as hashtags ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27271), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/27584))
- Fix format-dependent redirects being cached regardless of requested format ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27634))
## [4.2.1] - 2023-10-10 ## [4.2.1] - 2023-10-10
### Added ### Added

View file

@ -1,35 +1,50 @@
# CONTRIBUTING # Contributing
kmyblueは、コミュニティの意見も聞くには聞きますが導入する・しないは管理人が決定します。 Thank you for considering contributing to Mastodon 🐘
## バグ報告 You can contribute in the following ways:
バグについて、L最新よりも過去のバージョンへの対応は、LTSや特別な場合以外は行いません。 - Finding and reporting bugs
- Translating the Mastodon interface into various languages
- Contributing code to Mastodon by fixing bugs or implementing features
- Improving the documentation
以下のいずれかの方法で報告してください。 If your contributions are accepted into Mastodon, you can request to be paid through [our OpenCollective](https://opencollective.com/mastodon).
- [GitHub Issues](https://github.com/kmycode/mastodon/issues) (セキュリティインシデントはここの一番下から) ## API Changes and Additions
- [kmyblue開発者への連絡](https://kmy.blue/@askyq)
- [kmyblue開発者へのメール](https://kmy.blue/about)
## 翻訳、プルリクエスト Please note that any changes or additions made to the API should have an accompanying pull request on [our documentation repository](https://github.com/mastodon/documentation).
新しい機能や既存機能の修正については、プルリクエストのためにコードを作成する前に、まずGitHub Issuesで機能の提案を行いkmyblue開発者の考えを聞くことをおすすめします。バグ修正、翻訳、テストコードなどは基本受け入れますが、依存モジュールのバージョンアップについては特別な事情がなければ本家Mastodonよりも先に行かないようにしてください。 ## Bug reports
プルリクエストのタイトルには、プルリクエストの内容が明確になるようなものを設定してください。 Bug reports and feature suggestions must use descriptive and concise titles and be submitted to [GitHub Issues](https://github.com/mastodon/mastodon/issues). Please use the search function to make sure that you are not submitting duplicates, and that a similar report or request has not already been resolved or rejected.
### kmyblueの開発方針 ## Translations
下記のものに矛盾がなければ、あとは管理人の意向次第です。 You can submit translations via [Crowdin](https://crowdin.com/project/mastodon). They are periodically merged into the codebase.
- **自分の投稿を見せたくない人に見せない** [![Crowdin](https://d322cqt584bo4o.cloudfront.net/mastodon/localized.svg)](https://crowdin.com/project/mastodon)
- **他人の見たくない投稿を見ない**
- ただし本家Mastodonで上記原則に矛盾した機能が追加された場合は従う
- 画面を騒がしくするような機能(絵文字を大きく表示するなど)は追加しないか、控えめにする。ただし他のソフトウェアにも導入され利用者が多くいる場合などは別途判断して、オプトアウト可能な設定項目とともに追加する
- 負荷を著しく上げるような機能はできるだけ追加しない
kmyblueが意図的に実装していない機能は、例えば以下のものがあります。詳しい理由が知りたい場合は[この記事を参照するか](https://note.com/kmycode/n/n463410b5e03c)、別途お問い合わせください。もちろん明確な根拠がある場合、あなたはこれに抗議する権利を有しますが、あなたがこのkmyblueをフォークして新しいリポジトリを作るほうがより自由でしょう。 ## Pull requests
- お気に入り一覧の公開 **Please use clean, concise titles for your pull requests.** Unless the pull request is about refactoring code, updating dependencies or other internal tasks, assume that the person reading the pull request title is not a programmer or Mastodon developer, but instead a Mastodon user or server administrator, and **try to describe your change or fix from their perspective**. We use commit squashing, so the final commit in the main branch will carry the title of the pull request, and commits from the main branch are fed into the changelog. The changelog is separated into [keepachangelog.com categories](https://keepachangelog.com/en/1.0.0/), and while that spec does not prescribe how the entries ought to be named, for easier sorting, start your pull request titles using one of the verbs "Add", "Change", "Deprecate", "Remove", or "Fix" (present tense).
- ブックマーク分類の公開
- Fedibird、Misskeyにあるような詳細な画面表示オプション Example:
| Not ideal | Better |
| ------------------------------------ | ------------------------------------------------------------- |
| Fixed NoMethodError in RemovalWorker | Fix nil error when removing statuses caused by race condition |
It is not always possible to phrase every change in such a manner, but it is desired.
**The smaller the set of changes in the pull request is, the quicker it can be reviewed and merged.** Splitting tasks into multiple smaller pull requests is often preferable.
**Pull requests that do not pass automated checks may not be reviewed**. In particular, you need to keep in mind:
- Unit and integration tests (rspec, jest)
- Code style rules (rubocop, eslint)
- Normalization of locale files (i18n-tasks)
## Documentation
The [Mastodon documentation](https://docs.joinmastodon.org) is a statically generated site. You can [submit merge requests to mastodon/documentation](https://github.com/mastodon/documentation).

View file

@ -1,4 +1,4 @@
# syntax=docker/dockerfile:1.7 # syntax=docker/dockerfile:1.4
# Please see https://docs.docker.com/engine/reference/builder for information about # Please see https://docs.docker.com/engine/reference/builder for information about
# the extended buildx capabilities used in this file. # the extended buildx capabilities used in this file.
@ -7,20 +7,20 @@
ARG TARGETPLATFORM=${TARGETPLATFORM} ARG TARGETPLATFORM=${TARGETPLATFORM}
ARG BUILDPLATFORM=${BUILDPLATFORM} ARG BUILDPLATFORM=${BUILDPLATFORM}
# Ruby image to use for base image, change with [--build-arg RUBY_VERSION="3.3.1"] # Ruby image to use for base image, change with [--build-arg RUBY_VERSION="3.2.2"]
ARG RUBY_VERSION="3.3.1" ARG RUBY_VERSION="3.2.2"
# # Node version to use in base image, change with [--build-arg NODE_MAJOR_VERSION="20"] # # Node version to use in base image, change with [--build-arg NODE_MAJOR_VERSION="20"]
ARG NODE_MAJOR_VERSION="20" ARG NODE_MAJOR_VERSION="20"
# Debian image to use for base image, change with [--build-arg DEBIAN_VERSION="bookworm"] # Debian image to use for base image, change with [--build-arg DEBIAN_VERSION="bookworm"]
ARG DEBIAN_VERSION="bookworm" ARG DEBIAN_VERSION="bookworm"
# Node image to use for base image based on combined variables (ex: 20-bookworm-slim) # Node image to use for base image based on combined variables (ex: 20-bookworm-slim)
FROM docker.io/node:${NODE_MAJOR_VERSION}-${DEBIAN_VERSION}-slim as node FROM docker.io/node:${NODE_MAJOR_VERSION}-${DEBIAN_VERSION}-slim as node
# Ruby image to use for base image based on combined variables (ex: 3.3.1-slim-bookworm) # Ruby image to use for base image based on combined variables (ex: 3.2.2-slim-bookworm)
FROM docker.io/ruby:${RUBY_VERSION}-slim-${DEBIAN_VERSION} as ruby FROM docker.io/ruby:${RUBY_VERSION}-slim-${DEBIAN_VERSION} as ruby
# Resulting version string is vX.X.X-MASTODON_VERSION_PRERELEASE+MASTODON_VERSION_METADATA # Resulting version string is vX.X.X-MASTODON_VERSION_PRERELEASE+MASTODON_VERSION_METADATA
# Example: v4.2.0-nightly.2023.11.09+something # Example: v4.2.0-nightly.2023.11.09+something
# Overwrite existence of 'alpha.0' in version.rb [--build-arg MASTODON_VERSION_PRERELEASE="nightly.2023.11.09"] # Overwrite existance of 'alpha.0' in version.rb [--build-arg MASTODON_VERSION_PRERELEASE="nightly.2023.11.09"]
ARG MASTODON_VERSION_PRERELEASE="" ARG MASTODON_VERSION_PRERELEASE=""
# Append build metadata or fork information to version.rb [--build-arg MASTODON_VERSION_METADATA="something"] # Append build metadata or fork information to version.rb [--build-arg MASTODON_VERSION_METADATA="something"]
ARG MASTODON_VERSION_METADATA="" ARG MASTODON_VERSION_METADATA=""
@ -29,7 +29,7 @@ ARG MASTODON_VERSION_METADATA=""
# See: https://docs.joinmastodon.org/admin/config/#rails_serve_static_files # See: https://docs.joinmastodon.org/admin/config/#rails_serve_static_files
ARG RAILS_SERVE_STATIC_FILES="true" ARG RAILS_SERVE_STATIC_FILES="true"
# Allow to use YJIT compiler # Allow to use YJIT compiler
# See: https://github.com/ruby/ruby/blob/v3_2_4/doc/yjit/yjit.md # See: https://github.com/ruby/ruby/blob/master/doc/yjit/yjit.md
ARG RUBY_YJIT_ENABLE="1" ARG RUBY_YJIT_ENABLE="1"
# Timezone used by the Docker container and runtime, change with [--build-arg TZ=Europe/Berlin] # Timezone used by the Docker container and runtime, change with [--build-arg TZ=Europe/Berlin]
ARG TZ="Etc/UTC" ARG TZ="Etc/UTC"
@ -205,12 +205,7 @@ ARG TARGETPLATFORM
RUN \ RUN \
# Use Ruby on Rails to create Mastodon assets # Use Ruby on Rails to create Mastodon assets
ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY=precompile_placeholder \ OTP_SECRET=precompile_placeholder SECRET_KEY_BASE=precompile_placeholder bundle exec rails assets:precompile; \
ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT=precompile_placeholder \
ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY=precompile_placeholder \
OTP_SECRET=precompile_placeholder \
SECRET_KEY_BASE=precompile_placeholder \
bundle exec rails assets:precompile; \
# Cleanup temporary files # Cleanup temporary files
rm -fr /opt/mastodon/tmp; rm -fr /opt/mastodon/tmp;
@ -262,4 +257,4 @@ USER mastodon
# Expose default Puma ports # Expose default Puma ports
EXPOSE 3000 EXPOSE 3000
# Set container tini as default entry point # Set container tini as default entry point
ENTRYPOINT ["/usr/bin/tini", "--"] ENTRYPOINT ["/usr/bin/tini", "--"]

View file

@ -1,35 +1,19 @@
# Federation ## ActivityPub federation in Mastodon
## Supported federation protocols and standards
- [ActivityPub](https://www.w3.org/TR/activitypub/) (Server-to-Server)
- [WebFinger](https://webfinger.net/)
- [Http Signatures](https://datatracker.ietf.org/doc/html/draft-cavage-http-signatures)
- [NodeInfo](https://nodeinfo.diaspora.software/)
## Supported FEPs
- [FEP-67ff: FEDERATION.md](https://codeberg.org/fediverse/fep/src/branch/main/fep/67ff/fep-67ff.md)
- [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)
## ActivityPub in Mastodon
Mastodon largely follows the ActivityPub server-to-server specification but it makes uses of some non-standard extensions, some of which are required for interacting with Mastodon at all. Mastodon largely follows the ActivityPub server-to-server specification but it makes uses of some non-standard extensions, some of which are required for interacting with Mastodon at all.
- [Supported ActivityPub vocabulary](https://docs.joinmastodon.org/spec/activitypub/) Supported vocabulary: https://docs.joinmastodon.org/spec/activitypub/
### Required extensions ### Required extensions
#### WebFinger #### Webfinger
In Mastodon, users are identified by a `username` and `domain` pair (e.g., `Gargron@mastodon.social`). In Mastodon, users are identified by a `username` and `domain` pair (e.g., `Gargron@mastodon.social`).
This is used both for discovery and for unambiguously mentioning users across the fediverse. Furthermore, this is part of Mastodon's database design from its very beginnings. This is used both for discovery and for unambiguously mentioning users across the fediverse. Furthermore, this is part of Mastodon's database design from its very beginnings.
As a result, Mastodon requires that each ActivityPub actor uniquely maps back to an `acct:` URI that can be resolved via WebFinger. As a result, Mastodon requires that each ActivityPub actor uniquely maps back to an `acct:` URI that can be resolved via WebFinger.
- [WebFinger information and examples](https://docs.joinmastodon.org/spec/webfinger/) More information and examples are available at: https://docs.joinmastodon.org/spec/webfinger/
#### HTTP Signatures #### HTTP Signatures
@ -37,13 +21,11 @@ In order to authenticate activities, Mastodon relies on HTTP Signatures, signing
Mastodon requires all `POST` requests to be signed, and MAY require `GET` requests to be signed, depending on the configuration of the Mastodon server. Mastodon requires all `POST` requests to be signed, and MAY require `GET` requests to be signed, depending on the configuration of the Mastodon server.
- [HTTP Signatures information and examples](https://docs.joinmastodon.org/spec/security/#http) More information on HTTP Signatures, as well as examples, can be found here: https://docs.joinmastodon.org/spec/security/#http
### Optional extensions ### Optional extensions
- [Linked-Data Signatures](https://docs.joinmastodon.org/spec/security/#ld) - Linked-Data Signatures: https://docs.joinmastodon.org/spec/security/#ld
- [Bearcaps](https://docs.joinmastodon.org/spec/bearcaps/) - Bearcaps: https://docs.joinmastodon.org/spec/bearcaps/
- Followers collection synchronization: https://codeberg.org/fediverse/fep/src/branch/main/fep/8fcf/fep-8fcf.md
### Additional documentation - Search indexing consent for actors: https://codeberg.org/fediverse/fep/src/branch/main/fep/5feb/fep-5feb.md
- [Mastodon documentation](https://docs.joinmastodon.org/)

85
Gemfile
View file

@ -1,37 +1,37 @@
# frozen_string_literal: true # frozen_string_literal: true
source 'https://rubygems.org' source 'https://rubygems.org'
ruby '>= 3.1.0' ruby '>= 3.0.0'
gem 'propshaft'
gem 'puma', '~> 6.3' gem 'puma', '~> 6.3'
gem 'rack', '~> 2.2.7'
gem 'rails', '~> 7.1.1' gem 'rails', '~> 7.1.1'
gem 'propshaft'
gem 'thor', '~> 1.2' gem 'thor', '~> 1.2'
gem 'rack', '~> 2.2.7'
# For why irb is in the Gemfile, see: https://ruby.social/@st0012/111444685161478182 # For why irb is in the Gemfile, see: https://ruby.social/@st0012/111444685161478182
gem 'irb', '~> 1.8' gem 'irb', '~> 1.8'
gem 'dotenv'
gem 'haml-rails', '~>2.0' gem 'haml-rails', '~>2.0'
gem 'pg', '~> 1.5' gem 'pg', '~> 1.5'
gem 'pghero' gem 'pghero'
gem 'dotenv-rails', '~> 2.8'
gem 'aws-sdk-s3', '~> 1.123', require: false gem 'aws-sdk-s3', '~> 1.123', require: false
gem 'blurhash', '~> 0.1'
gem 'fog-core', '<= 2.4.0' gem 'fog-core', '<= 2.4.0'
gem 'fog-openstack', '~> 1.0', require: false gem 'fog-openstack', '~> 1.0', require: false
gem 'kt-paperclip', '~> 7.2' gem 'kt-paperclip', '~> 7.2'
gem 'md-paperclip-azure', '~> 2.2', require: false gem 'md-paperclip-azure', '~> 2.2', require: false
gem 'blurhash', '~> 0.1'
gem 'active_model_serializers', '~> 0.10' gem 'active_model_serializers', '~> 0.10'
gem 'addressable', '~> 2.8' gem 'addressable', '~> 2.8'
gem 'bootsnap', '~> 1.18.0', require: false gem 'bootsnap', '~> 1.17.0', require: false
gem 'browser' gem 'browser'
gem 'charlock_holmes', '~> 0.7.7' gem 'charlock_holmes', '~> 0.7.7'
gem 'chewy', '~> 7.3' gem 'chewy', '~> 7.3'
gem 'devise', '~> 4.9' gem 'devise', '~> 4.9'
gem 'devise-two-factor' gem 'devise-two-factor', '~> 4.1'
group :pam_authentication, optional: true do group :pam_authentication, optional: true do
gem 'devise_pam_authenticatable2', '~> 9.2' gem 'devise_pam_authenticatable2', '~> 9.2'
@ -39,11 +39,11 @@ end
gem 'net-ldap', '~> 0.18' gem 'net-ldap', '~> 0.18'
gem 'omniauth', '~> 2.0'
gem 'omniauth-cas', '~> 3.0.0.beta.1' gem 'omniauth-cas', '~> 3.0.0.beta.1'
gem 'omniauth_openid_connect', '~> 0.6.1'
gem 'omniauth-rails_csrf_protection', '~> 1.0'
gem 'omniauth-saml', '~> 2.0' gem 'omniauth-saml', '~> 2.0'
gem 'omniauth_openid_connect', '~> 0.6.1'
gem 'omniauth', '~> 2.0'
gem 'omniauth-rails_csrf_protection', '~> 1.0'
gem 'color_diff', '~> 0.1' gem 'color_diff', '~> 0.1'
gem 'csv', '~> 3.2' gem 'csv', '~> 3.2'
@ -53,49 +53,48 @@ gem 'ed25519', '~> 1.3'
gem 'fast_blank', '~> 1.0' gem 'fast_blank', '~> 1.0'
gem 'fastimage' gem 'fastimage'
gem 'hiredis', '~> 0.6' gem 'hiredis', '~> 0.6'
gem 'redis-namespace', '~> 1.10'
gem 'htmlentities', '~> 4.3' gem 'htmlentities', '~> 4.3'
gem 'http', '~> 5.2.0' gem 'http', '~> 5.1'
gem 'http_accept_language', '~> 2.1' gem 'http_accept_language', '~> 2.1'
gem 'httplog', '~> 1.6.2' gem 'httplog', '~> 1.6.2'
gem 'i18n'
gem 'idn-ruby', require: 'idn' gem 'idn-ruby', require: 'idn'
gem 'inline_svg'
gem 'kaminari', '~> 1.2' gem 'kaminari', '~> 1.2'
gem 'link_header', '~> 0.0' gem 'link_header', '~> 0.0'
gem 'mario-redis-lock', '~> 1.2', require: 'redis_lock'
gem 'mime-types', '~> 3.5.0', require: 'mime/types/columnar' gem 'mime-types', '~> 3.5.0', require: 'mime/types/columnar'
gem 'nokogiri', '~> 1.15' gem 'nokogiri', '~> 1.15'
gem 'nsa' gem 'nsa', github: 'jhawthorn/nsa', ref: 'e020fcc3a54d993ab45b7194d89ab720296c111b'
gem 'oj', '~> 3.14' gem 'oj', '~> 3.14'
gem 'ox', '~> 2.14' gem 'ox', '~> 2.14'
gem 'parslet' gem 'parslet'
gem 'premailer-rails' gem 'posix-spawn'
gem 'public_suffix', '~> 5.0' gem 'public_suffix', '~> 5.0'
gem 'pundit', '~> 2.3' gem 'pundit', '~> 2.3'
gem 'premailer-rails'
gem 'rack-attack', '~> 6.6' gem 'rack-attack', '~> 6.6'
gem 'rack-cors', '~> 2.0', require: 'rack/cors' gem 'rack-cors', '~> 2.0', require: 'rack/cors'
gem 'rails-i18n', '~> 7.0' gem 'rails-i18n', '~> 7.0'
gem 'redcarpet', '~> 3.6' gem 'redcarpet', '~> 3.6'
gem 'redis', '~> 4.5', require: ['redis', 'redis/connection/hiredis'] gem 'redis', '~> 4.5', require: ['redis', 'redis/connection/hiredis']
gem 'redis-namespace', '~> 1.10' gem 'mario-redis-lock', '~> 1.2', require: 'redis_lock'
gem 'rqrcode', '~> 2.2' gem 'rqrcode', '~> 2.2'
gem 'ruby-progressbar', '~> 1.13' gem 'ruby-progressbar', '~> 1.13'
gem 'sanitize', '~> 6.0' gem 'sanitize', '~> 6.0'
gem 'scenic', '~> 1.7' gem 'scenic', '~> 1.7'
gem 'sidekiq', '~> 6.5' gem 'sidekiq', '~> 6.5'
gem 'sidekiq-bulk', '~> 0.2.0'
gem 'sidekiq-scheduler', '~> 5.0' gem 'sidekiq-scheduler', '~> 5.0'
gem 'sidekiq-unique-jobs', '~> 7.1' gem 'sidekiq-unique-jobs', '~> 7.1'
gem 'simple_form', '~> 5.2' gem 'sidekiq-bulk', '~> 0.2.0'
gem 'simple-navigation', '~> 4.4' gem 'simple-navigation', '~> 4.4'
gem 'stoplight', '~> 4.1' gem 'simple_form', '~> 5.2'
gem 'strong_migrations', '1.8.0' gem 'stoplight', '~> 3.0.1'
gem 'strong_migrations', '1.7.0'
gem 'tty-prompt', '~> 0.23', require: false gem 'tty-prompt', '~> 0.23', require: false
gem 'twitter-text', '~> 3.1.0' gem 'twitter-text', '~> 3.1.0'
gem 'tzinfo-data', '~> 1.2023' gem 'tzinfo-data', '~> 1.2023'
gem 'webauthn', '~> 3.0'
gem 'webpacker', '~> 5.4' gem 'webpacker', '~> 5.4'
gem 'webpush', github: 'ClearlyClaire/webpush', ref: 'f14a4d52e201128b1b00245d11b6de80d6cfdcd9' gem 'webpush', github: 'ClearlyClaire/webpush', ref: 'f14a4d52e201128b1b00245d11b6de80d6cfdcd9'
gem 'webauthn', '~> 3.0'
gem 'json-ld' gem 'json-ld'
gem 'json-ld-preloaded', '~> 3.2' gem 'json-ld-preloaded', '~> 3.2'
@ -103,24 +102,6 @@ gem 'rdf-normalize', '~> 0.5'
gem 'private_address_check', '~> 0.5' gem 'private_address_check', '~> 0.5'
group :opentelemetry do
gem 'opentelemetry-exporter-otlp', '~> 0.26.3', require: false
gem 'opentelemetry-instrumentation-active_job', '~> 0.7.1', require: false
gem 'opentelemetry-instrumentation-active_model_serializers', '~> 0.20.1', require: false
gem 'opentelemetry-instrumentation-concurrent_ruby', '~> 0.21.2', require: false
gem 'opentelemetry-instrumentation-excon', '~> 0.22.0', require: false
gem 'opentelemetry-instrumentation-faraday', '~> 0.24.1', require: false
gem 'opentelemetry-instrumentation-http', '~> 0.23.2', require: false
gem 'opentelemetry-instrumentation-http_client', '~> 0.22.3', require: false
gem 'opentelemetry-instrumentation-net_http', '~> 0.22.4', require: false
gem 'opentelemetry-instrumentation-pg', '~> 0.27.1', require: false
gem 'opentelemetry-instrumentation-rack', '~> 0.24.1', require: false
gem 'opentelemetry-instrumentation-rails', '~> 0.30.0', require: false
gem 'opentelemetry-instrumentation-redis', '~> 0.25.3', require: false
gem 'opentelemetry-instrumentation-sidekiq', '~> 0.25.2', require: false
gem 'opentelemetry-sdk', '~> 1.4', require: false
end
group :test do group :test do
# Adds RSpec Error/Warning annotations to GitHub PRs on the Files tab # Adds RSpec Error/Warning annotations to GitHub PRs on the Files tab
gem 'rspec-github', '~> 2.4', require: false gem 'rspec-github', '~> 2.4', require: false
@ -131,8 +112,8 @@ group :test do
# RSpec helpers for email specs # RSpec helpers for email specs
gem 'email_spec' gem 'email_spec'
# Extra RSpec extension methods and helpers for sidekiq # Extra RSpec extenion methods and helpers for sidekiq
gem 'rspec-sidekiq', '~> 5.0' gem 'rspec-sidekiq', '~> 4.0'
# Browser integration testing # Browser integration testing
gem 'capybara', '~> 3.39' gem 'capybara', '~> 3.39'
@ -142,7 +123,13 @@ group :test do
gem 'database_cleaner-active_record' gem 'database_cleaner-active_record'
# Used to mock environment variables # Used to mock environment variables
gem 'climate_control' gem 'climate_control', '~> 0.2'
# Generating fake data for specs
gem 'faker', '~> 3.2'
# Generate test objects for specs
gem 'fabrication', '~> 2.30'
# Add back helpers functions removed in Rails 5.1 # Add back helpers functions removed in Rails 5.1
gem 'rails-controller-testing', '~> 1.0' gem 'rails-controller-testing', '~> 1.0'
@ -178,7 +165,7 @@ group :development do
# Preview mail in the browser # Preview mail in the browser
gem 'letter_opener', '~> 1.8' gem 'letter_opener', '~> 1.8'
gem 'letter_opener_web', '~> 3.0' gem 'letter_opener_web', '~> 2.0'
# Security analysis CLI tools # Security analysis CLI tools
gem 'brakeman', '~> 6.0', require: false gem 'brakeman', '~> 6.0', require: false
@ -195,12 +182,6 @@ group :development, :test do
# Interactive Debugging tools # Interactive Debugging tools
gem 'debug', '~> 1.8' gem 'debug', '~> 1.8'
# Generate fake data values
gem 'faker', '~> 3.2'
# Generate factory objects
gem 'fabrication', '~> 2.30'
# Profiling tools # Profiling tools
gem 'memory_profiler', require: false gem 'memory_profiler', require: false
gem 'ruby-prof', require: false gem 'ruby-prof', require: false
@ -215,14 +196,12 @@ group :production do
gem 'lograge', '~> 0.12' gem 'lograge', '~> 0.12'
end end
gem 'cocoon', '~> 1.2'
gem 'concurrent-ruby', require: false gem 'concurrent-ruby', require: false
gem 'connection_pool', require: false gem 'connection_pool', require: false
gem 'xorcist', '~> 1.1' gem 'xorcist', '~> 1.1'
gem 'cocoon', '~> 1.2'
gem 'net-http', '~> 0.4.0' gem 'net-http', '~> 0.4.0'
gem 'rubyzip', '~> 2.3' gem 'rubyzip', '~> 2.3'
gem 'hcaptcha', '~> 7.1' gem 'hcaptcha', '~> 7.1'
gem 'mail', '~> 2.8'

File diff suppressed because it is too large Load diff

164
README.md
View file

@ -1,112 +1,142 @@
# ![kmyblue icon](https://raw.githubusercontent.com/kmycode/mastodon/kb_development/app/javascript/icons/favicon-32x32.png) kmyblue <h1><picture>
<source media="(prefers-color-scheme: dark)" srcset="./lib/assets/wordmark.dark.png?raw=true">
<source media="(prefers-color-scheme: light)" srcset="./lib/assets/wordmark.light.png?raw=true">
<img alt="Mastodon" src="./lib/assets/wordmark.light.png?raw=true" height="34">
</picture></h1>
[![Ruby Testing](https://github.com/kmycode/mastodon/actions/workflows/test-ruby.yml/badge.svg)](https://github.com/kmycode/mastodon/actions/workflows/test-ruby.yml) [![GitHub release](https://img.shields.io/github/release/mastodon/mastodon.svg)][releases]
[![Ruby Testing](https://github.com/mastodon/mastodon/actions/workflows/test-ruby.yml/badge.svg)](https://github.com/mastodon/mastodon/actions/workflows/test-ruby.yml)
[![Crowdin](https://d322cqt584bo4o.cloudfront.net/mastodon/localized.svg)][crowdin]
kmyblueは[Mastodon](https://github.com/mastodon/mastodon)のフォークです。創作作家のためのMastodonを目指して開発しました。 [releases]: https://github.com/mastodon/mastodon/releases
[crowdin]: https://crowdin.com/project/mastodon
kmyblueはフォーク名であり、同時に[サーバー名](https://kmy.blue)でもあります。以下は特に記述がない限り、フォークとしてのkmyblueをさします。 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!)
kmyblueは AGPL ライセンスで公開されているため、どなたでも自由にフォークし、このソースコードを元に自分でサーバーを立てて公開することができます。確かにサーバーkmyblueは創作作家向けのものですが、フォークとしてのkmyblueはAGPLでライセンスつけられており、ルールは全くの別物です。創作活動の一部エロ関係含むまたは全体を否定するコミュニティなどにも平等にお使いいただけます。サーバーkmyblueのルールを適用する必要もなく、「Anyone But Kmyblue」なルールを設定することすら許容されます。 Click below to **learn more** in a video:
kmyblueは、特に非収載投稿の検索が強化されているため、ローカルタイムラインに掲載されていない投稿も検索・購読することが可能な場合があります。閉鎖的なコミュニティ、あまり目立ちたくないコミュニティには特に強力な機能を提供します。それ以外のコミュニティに対しても、kmyblueはプライバシーを考慮したうえで強力な検索・購読機能を提供するため、汎用サーバーとして利用するにもある程度十分な機能が揃っています。
ただしkmyblueにおいて**テストコードは飾り**でしかありません。これはkmyblueを利用する人が本家Mastodonより圧倒的に少なく、バグやセキュリティインシデントを発見するだけの人数が足りないことを意味します。kmyblueは対策として自動テストを拡充しています。独自機能のテストを記述するだけでなく、本家のテストコードの補強も行っておりますが、確認漏れは必ず発生するものです。不具合が発生しても自己責任になります。既知のバグもいくつかありますし、直す予定のないものも含まれます。 [![Screenshot](https://blog.joinmastodon.org/2018/06/why-activitypub-is-the-future/ezgif-2-60f1b00403.gif)][youtube_demo]
テストコード、Lint どちらも動いています。 [youtube_demo]: https://www.youtube.com/watch?v=IPSbNdBmWKE
## インストール方法 ## Navigation
[Wiki](https://github.com/kmycode/mastodon/wiki/Installation)を参照してください。 - [Project homepage 🐘](https://joinmastodon.org)
- [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
CONTRIBUTING.mdを参照してください。 ## Features
## テスト <img src="/app/javascript/images/elephant_ui_working.svg?raw=true" align="right" width="30%" />
``` ### No vendor lock-in: Fully interoperable with any conforming platform
# デバッグ実行(以下のいずれか)
foreman start
DB_USER=postgres DB_PASS=password foreman start
# 一部を除く全てのテストを行う 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/)
RAILS_ENV=test bundle exec rspec spec
# ElasticSearch連携テストを行う ### Real-time, chronological timeline updates
RAILS_ENV=test ES_ENABLED=true bundle exec rspec --tag search
RAILS_ENV=test ES_ENABLED=true RUN_SEARCH_SPECS=true bundle exec rspec spec/search
```
## kmyblueのブランチ Updates of people you're following appear in real-time in the UI via WebSockets. There's a firehose view as well!
- **main** - 管理者が本家MastodonにPRするときに使うことがあります ### Media attachments like images and short videos
- **kb_development** - 開発中の最新のソースコードです。メジャーバージョンアップデートは通常このブランチから公開されます
- **kb_lts** - LTSの管理に使います。LTSはこのブランチから公開されます
- **kb_patch** - 修正パッチの管理に使います。マイナーバージョンアップデートは通常このブランチから公開されます
## kmyblueの強み 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
https://note.com/kmycode/n/n5fd5e823ed40 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への積極的追従 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!
kmyblueは、追加機能を控えめにする代わりに本家Mastodonに積極的に追従を行います。kmyblueの追加機能そのままに、Mastodonの新機能も利用できるよう調整を行います。 ## Deployment
### ゆるやかな内輪での運用 ### Tech stack
kmyblueは同人向けサーバーとして出発したため、同人作家に需要のある「内輪リを外部にできるだけもらさない」という部分に特化しています。 - **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
「ローカル公開」という機能によって、「ローカルタイムラインに流すが他のサーバーの連合タイムラインに流さない」投稿が可能です。ただしMisskeyのローカル限定とは異なり、他のサーバーのフォロワーのタイムラインにも投稿は流れます。自分のサーバーの中で内輪で盛り上がって、他のサーバーの連合タイムラインには外面だけの投稿を流すことも可能です。 ### Requirements
「サークル」という機能によって、特定のフォロワーにだけ見える投稿を行うことも可能です。その投稿に返信することで、相手サークルの会話に参加することも可能です。ただしサークル投稿を正常に処理できるソフトウェアは現在、kmyblue・Fedibirdに限ります。 - **PostgreSQL** 12+
- **Redis** 4+
- **Ruby** 2.7+
- **Node.js** 16+
また、通常のMastodonでは公開投稿を他のサーバーの人に自由に検索できるようにすることも可能ですが、kmyblueでは非収載投稿に対して同様の設定が可能です。つまり、ローカルタイムラインにも連合タイムラインにも流れない、誰かの目に自然に触れることはない、でも特定キーワードを使った検索では引っかかりたい、そのような需要に対応できます。 The repository includes deployment configurations for **Docker and docker-compose** as well as specific platforms like **Heroku**, **Scalingo**, and **Nanobox**. For Helm charts, reference the [mastodon/chart repository](https://github.com/mastodon/chart). The [**standalone** installation guide](https://docs.joinmastodon.org/admin/install/) is available in the documentation.
内輪とは自分のサーバーに限ったものではありません。内輪同士で複数のサーバーを運営するとき、お互いが深く繋がれるフレンドサーバーというシステムも用意しています。 ## Development
ただしkmyblueは、同時に連合も重視しています。ローカル限定投稿など、連合を大きく制限させるような機能は作る予定はありません。 ### Vagrant
### 少人数サーバーでの運用 A **Vagrant** configuration is included for development purposes. To use it, complete the following steps:
kmyblueは、人の少ないサーバーでの運用を考慮して設計しています。そのため、他のサーバーのアカウントの購読機能はFedibirdほど発達していませんし、人の多いサーバー向けの独自改造もほとんど存在しません。 - Install Vagrant and Virtualbox
- Install the `vagrant-hostsupdater` plugin: `vagrant plugin install vagrant-hostsupdater`
- Run `vagrant up`
- Run `vagrant ssh -c "cd /vagrant && foreman start"`
- Open `http://mastodon.local` in your browser
ただしサーバーの負荷については一部度外視している部分があります。たとえば絵文字リアクション機能はサーバーへ著しい負荷をかける場合があります。絵文字リアクション機能そのものを無効にする管理者オプションも存在します。 ### MacOS
もちろん人の多いサーバーでの運用が不便になるような修正は行っていません。人の多いサーバーでもそのままお使いいただけます。 To set up **MacOS** for native development, complete the following steps:
### 比較的高い防御力 - Install the latest stable Ruby version (use a Ruby version manager for easy installation and management of Ruby versions)
- Run `brew install postgresql@14`
- Run `brew install redis`
- Run `brew install imagemagick`
- Run `brew install libidn`
- Install Foreman or a similar tool (such as [overmind](https://github.com/DarthSim/overmind)) to handle multiple process launching.
- Navigate to Mastodon's root directory and run `brew install nvm` then `nvm use` to use the version from .nvmrc
- Run `corepack enable && corepack prepare`
- Run `bundle exec rails db:setup` (optionally prepend `RAILS_ENV=development` to target the dev environment)
- Finally, run `overmind start -f Procfile.dev`
kmyblueでは、「Fediverseは将来的に荒むのではないか」「Fediverseは将来的にスパムに溢れるのではないか」を念頭に設計している部分があります。 ### Docker
個別ユーザー向けの設定項目が複数あります。 For development with **Docker**, complete the following steps:
- Misskeyは、たとえMastodonの投稿であっても非収載投稿を自由に検索できますが、kmyblueではそれをブロックできるユーザー設定が存在します - Install Docker Desktop
- 他の人からの絵文字リアクションの受け入れを制限する設定も可能であり、例えば他のサーバーから好ましくない絵文字リアクションを受け取ることを防止できます - Run `docker compose -f .devcontainer/docker-compose.yml up -d`
- 公開タイムラインの引用表示はデフォルトで無効になっています。不快な投稿を引用したものが公開タイムラインに流れても、ある程度は防止できます - Run `docker compose -f .devcontainer/docker-compose.yml exec app .devcontainer/post-create.sh`
- フィルター(ワードミュート)は、引用された投稿の内容にも適用されます。この場合、引用投稿そのものが表示されなくなります - Finally, run `docker compose -f .devcontainer/docker-compose.yml exec app foreman start -f Procfile.dev`
- 自分のフォローしている相手の投稿をフィルターから除外する設定が存在します。防御を上げすぎると不便な箇所が出てくるので、そちらも緩和できるよう可能な限り配慮しています
管理者向けには、スパムへの利用を前提とした正規表現可能なNGワード設定、細かい指定が可能な拡張ドメインブロック機能を用意しています。 If you are using an IDE with [support for the Development Container specification](https://containers.dev/supporting), it will run the above `docker compose` commands automatically. For **Visual Studio Code** this requires the [Dev Container extension](https://containers.dev/supporting#dev-containers).
ただし防御力の高さは自由を犠牲にします。例えばkmyblueは、絵文字リアクションの表示サイズ調整機能など、MisskeyやFedibirdには当たり前のようにある表示設定は存在しません。騒がしくなるようなものはあまり作りたいとは考えていません。 ### GitHub Codespaces
### その他の主な機能 To get you coding in just a few minutes, GitHub Codespaces provides a web-based version of Visual Studio Code and a cloud-hosted development environment fully configured with the software needed for this project..
- 絵文字リアクションによる手軽な交流 - Click this button to create a new codespace:<br>
- 絵文字デッキによる頻繁に使用する絵文字の登録・選択 [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://github.com/codespaces/new?hide_repo_select=true&ref=main&repo=52281283&devcontainer_path=.devcontainer%2Fcodespaces%2Fdevcontainer.json)
- 検索機能の強化(検索許可) - Wait for the environment to build. This will take a few minutes.
- 投稿の引用 - When the editor is ready, run `foreman start -f Procfile.dev` in the terminal.
- ブックマークの分類 - After a few seconds, a popup will appear with a button labeled _Open in Browser_. This will open Mastodon.
- On the _Ports_ tab, right click on the “stream” row and select _Port visibility__Public_.
## kmyblueは何でないか ## Contributing
kmyblueは、Misskeyではありません。絵文字リアクションなどMisskeyと同様の機能はありますが、根本的にUIの使い勝手が違う他にも、例えばブックマークを分類できてもそれを公開する機能を作っていません。Misskeyは「楽しむ」をコンセプトにしていますが、kmyblueはMastodonの思想を受け継ぎ、炎上や喧騒を避けることのできる落ち着いた場所を目指しています。そのため、思想に合わない機能は実装しないか、大幅に弱体化しています。 Mastodon is **free, open-source software** licensed under **AGPLv3**.
kmyblueは、Fedibirdではありません。確かにローカルタイムラインを無効にしFedibirdのような運営を可能にする設定は存在します。しかしkmyblueは本家追従を優先する観点からWebで対応する範囲をある程度絞り込んでいるため、Fedibirdにあるような豊富な表示設定は作っていません。絵文字の大きさすら調整することはできません。また、Fedibirdではアカウントの購読機能があります。kmyblueにも同様の機能はあるものの、Fedibirdのように一発ですぐできるようなUIではありません。購読機能は相手のフォローを伴わないため、特に利用者に擬似的なフォロー体験を与えるアカウント購読は、人の少ない小規模サーバーには向いていません。これは、小規模サーバーの運用を想定しているkmyblueがあえて作っていない部分です。 You can open issues for bugs you've found or features you think are missing. You can also submit pull requests to this repository or submit translations using Crowdin. To get started, take a look at [CONTRIBUTING.md](CONTRIBUTING.md). If your contributions are accepted into Mastodon, you can request to be paid through [our OpenCollective](https://opencollective.com/mastodon).
kmyblueは、企業・政府機関向けに開発されたものではありません。そもそも管理者はセキュリティに関する資格や専門知識を有しておらず、高度なセキュリティの求められる機関向けのソフトウェアを制作する能力はありません。kmyblueは確かに本家Mastodonに対して大幅に機能を追加していますが、そもそも個人によるフォークは、開発者が飽きたらそこで終わりというリスクも伴います。高い信頼性・安全性を保証することはできないので、導入の際はご自身で安全を十分に確認してからお使いになることを強くおすすめします。 **IRC channel**: #mastodon on irc.libera.chat
## License
Copyright (C) 2016-2024 Eugen Rochko & other Mastodon contributors (see [AUTHORS.md](AUTHORS.md))
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
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/>.

View file

@ -1,25 +1,20 @@
# セキュリティポリシー # Security Policy
kmyblueのプログラムにおいてセキュリティインシデントを発見した場合、kmyblueに報告してください。 If you believe you've identified a security vulnerability in Mastodon (a bug that allows something to happen that shouldn't be possible), you can either:
kmyblueにセキュリティインシデントを報告する場合、以下の手順を踏んでください。 - open a [Github security issue on the Mastodon project](https://github.com/mastodon/mastodon/security/advisories/new)
- reach us at <security@joinmastodon.org>
- [こちらのリンクから新規インシデントを起票してください](https://github.com/kmycode/mastodon/security/advisories/new) You should _not_ report such issues on public GitHub issues or in other public spaces to give us time to publish a fix for the issue without exposing Mastodon's users to increased risk.
- メール <tt@kmycode.net>、または[@askyq@kmy.blue](https://kmy.blue/@askyq)宛に、**セキュリティインシデントを起票したことだけ**を連絡してください。セキュリティインシデントの内容は、絶対に連絡に含めないでください(リンクくらいなら含めていいかな)
他のkmyblueフォークの利用者の安全のために少しでも時間稼ぎをしなければいけないので、この問題をIssueを含む公開された場所で記述しないでください。 ## Scope
## 範囲 A "vulnerability in Mastodon" is a vulnerability in the code distributed through our main source code repository on GitHub. Vulnerabilities that are specific to a given installation (e.g. misconfiguration) should be reported to the owner of that installation and not us.
こちらが対応できる範囲は、当リポジトリで公開しているソースコードのみとなります。当リポジトリの依存パッケージ内に問題がある場合は、そちらに報告してください。 ## Supported Versions
もしあなたに専門知識があり、それが本家Mastodon由来の問題であると信じるに足る根拠がある場合、kmyblueではなくMastodonのほうに報告してください。kmyblueに報告されても、Mastodonより先に修正してしまうことでMastodonにセキュリティリスクを発生させる可能性がありますし、本家Mastodonの対応を待つにしてもkmyblueのほうに来てしまったセキュリティインシデントの対応に困ります本家がなかなか対応してくれない可能性を考えると削除しづらい。もし間違ってkmyblueに来た場合、kmyblue開発者の責任で振り分けを行います。 | Version | Supported |
| ------- | --------- |
## サポートするバージョン | 4.2.x | Yes |
| 4.1.x | Yes |
下記以外のバージョンは、セキュリティインシデントを起票されても対応しません。 | < 4.1 | No |
- 最新メジャーバージョン、かつ、最新マイナーバージョン
- 最新メジャーバージョンのサポートは、次のメジャーバージョンが出た時点で終了します
- LTS
- LTSのサポートは、次のLTSが出た時点で終了しますただし移行期間があってもいいと思ってるので、ヶ月以内ならセキュリティインシデントの程度に応じて対応する可能性があります

3
Vagrantfile vendored
View file

@ -173,7 +173,6 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
# Otherwise, you can access the site at http://localhost:3000 and http://localhost:4000 , http://localhost:8080 # Otherwise, you can access the site at http://localhost:3000 and http://localhost:4000 , http://localhost:8080
config.vm.network :forwarded_port, guest: 3000, host: 3000 config.vm.network :forwarded_port, guest: 3000, host: 3000
config.vm.network :forwarded_port, guest: 3035, host: 3035
config.vm.network :forwarded_port, guest: 4000, host: 4000 config.vm.network :forwarded_port, guest: 4000, host: 4000
config.vm.network :forwarded_port, guest: 8080, host: 8080 config.vm.network :forwarded_port, guest: 8080, host: 8080
config.vm.network :forwarded_port, guest: 9200, host: 9200 config.vm.network :forwarded_port, guest: 9200, host: 9200
@ -189,7 +188,7 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
config.vm.post_up_message = <<MESSAGE config.vm.post_up_message = <<MESSAGE
To start server To start server
$ vagrant ssh -c "cd /vagrant && bin/dev" $ vagrant ssh -c "cd /vagrant && foreman start"
MESSAGE MESSAGE
end end

View file

@ -3,28 +3,68 @@
class AccountsIndex < Chewy::Index class AccountsIndex < Chewy::Index
include DatetimeClampingConcern include DatetimeClampingConcern
# ElasticSearch config is moved to "/config/elasticsearch.default.yml". settings index: index_preset(refresh_interval: '30s'), analysis: {
# Edit it when original Mastodon changed ElasticSearch config. filter: {
settings index: index_preset(refresh_interval: '30s'), analysis: ChewyConfig.instance.accounts english_stop: {
type: 'stop',
stopwords: '_english_',
},
english_stemmer: {
type: 'stemmer',
language: 'english',
},
english_possessive_stemmer: {
type: 'stemmer',
language: 'possessive_english',
},
},
analyzer: {
natural: {
tokenizer: 'standard',
filter: %w(
lowercase
asciifolding
cjk_width
elision
english_possessive_stemmer
english_stop
english_stemmer
),
},
verbatim: {
tokenizer: 'standard',
filter: %w(lowercase asciifolding cjk_width),
},
edge_ngram: {
tokenizer: 'edge_ngram',
filter: %w(lowercase asciifolding cjk_width),
},
},
tokenizer: {
edge_ngram: {
type: 'edge_ngram',
min_gram: 1,
max_gram: 15,
},
},
}
index_scope ::Account.searchable.includes(:account_stat) index_scope ::Account.searchable.includes(:account_stat)
root date_detection: false do root date_detection: false do
field(:id, type: 'long') field(:id, type: 'long')
field(:following_count, type: 'long', value: ->(account) { account.public_following_count }) field(:following_count, type: 'long')
field(:followers_count, type: 'long', value: ->(account) { account.public_followers_count }) field(:followers_count, type: 'long')
field(:properties, type: 'keyword', value: ->(account) { account.searchable_properties }) field(:properties, type: 'keyword', value: ->(account) { account.searchable_properties })
field(:last_status_at, type: 'date', value: ->(account) { clamp_date(account.last_status_at || account.created_at) }) field(:last_status_at, type: 'date', value: ->(account) { clamp_date(account.last_status_at || account.created_at) })
field(:domain, type: 'keyword', value: ->(account) { account.domain || '' }) field(:display_name, type: 'text', analyzer: 'verbatim') { field :edge_ngram, type: 'text', analyzer: 'edge_ngram', search_analyzer: 'verbatim' }
field(:display_name, type: 'text', analyzer: ChewyConfig.instance.accounts_analyzers.dig('display_name', 'analyzer')) do field(:username, type: 'text', analyzer: 'verbatim', value: ->(account) { [account.username, account.domain].compact.join('@') }) { field :edge_ngram, type: 'text', analyzer: 'edge_ngram', search_analyzer: 'verbatim' }
field :edge_ngram, type: 'text', analyzer: ChewyConfig.instance.accounts_analyzers.dig('display_name', 'edge_ngram', 'analyzer'), search_analyzer: ChewyConfig.instance.accounts_analyzers.dig('display_name', 'edge_ngram', 'search_analyzer') field(:text, type: 'text', analyzer: 'verbatim', value: ->(account) { account.searchable_text }) { field :stemmed, type: 'text', analyzer: 'natural' }
end
field(:username, type: 'text', analyzer: ChewyConfig.instance.accounts_analyzers.dig('username', 'analyzer'), value: lambda { |account|
[account.username, account.domain].compact.join('@')
}) do
field :edge_ngram, type: 'text', analyzer: ChewyConfig.instance.accounts_analyzers.dig('username', 'edge_ngram', 'analyzer'),
search_analyzer: ChewyConfig.instance.accounts_analyzers.dig('username', 'edge_ngram', 'search_analyzer')
end
field(:text, type: 'text', analyzer: ChewyConfig.instance.accounts_analyzers.dig('text', 'analyzer'), value: ->(account) { account.searchable_text }) { field(:stemmed, type: 'text', analyzer: ChewyConfig.instance.accounts_analyzers.dig('text', 'stemmed', 'analyzer')) }
end end
end end

View file

@ -3,22 +3,66 @@
class PublicStatusesIndex < Chewy::Index class PublicStatusesIndex < Chewy::Index
include DatetimeClampingConcern include DatetimeClampingConcern
# ElasticSearch config is moved to "/config/elasticsearch.default.yml". settings index: index_preset(refresh_interval: '30s', number_of_shards: 5), analysis: {
# Edit it when original Mastodon changed ElasticSearch config. filter: {
settings index: index_preset(refresh_interval: '30s', number_of_shards: 5), analysis: ChewyConfig.instance.public_statuses english_stop: {
type: 'stop',
stopwords: '_english_',
},
english_stemmer: {
type: 'stemmer',
language: 'english',
},
english_possessive_stemmer: {
type: 'stemmer',
language: 'possessive_english',
},
},
analyzer: {
verbatim: {
tokenizer: 'uax_url_email',
filter: %w(lowercase),
},
content: {
tokenizer: 'standard',
filter: %w(
lowercase
asciifolding
cjk_width
elision
english_possessive_stemmer
english_stop
english_stemmer
),
},
hashtag: {
tokenizer: 'keyword',
filter: %w(
word_delimiter_graph
lowercase
asciifolding
cjk_width
),
},
},
}
index_scope ::Status.unscoped index_scope ::Status.unscoped
.kept .kept
.indexable .indexable
.includes(:media_attachments, :preloadable_poll, :tags, :account, preview_cards_status: :preview_card) .includes(:media_attachments, :preloadable_poll, :tags, preview_cards_status: :preview_card)
root date_detection: false do root date_detection: false do
field(:id, type: 'long') field(:id, type: 'long')
field(:account_id, type: 'long') field(:account_id, type: 'long')
field(:text, type: 'text', analyzer: ChewyConfig.instance.public_statuses_analyzers.dig('text', 'analyzer'), value: ->(status) { status.searchable_text }) { field(:stemmed, type: 'text', analyzer: ChewyConfig.instance.public_statuses_analyzers.dig('text', 'stemmed', 'analyzer')) } field(:text, type: 'text', analyzer: 'verbatim', value: ->(status) { status.searchable_text }) { field(:stemmed, type: 'text', analyzer: 'content') }
field(:tags, type: 'text', analyzer: ChewyConfig.instance.public_statuses_analyzers.dig('tags', 'analyzer'), value: ->(status) { status.tags.map(&:display_name) }) field(:tags, type: 'text', analyzer: 'hashtag', value: ->(status) { status.tags.map(&:display_name) })
field(:language, type: 'keyword') field(:language, type: 'keyword')
field(:domain, type: 'keyword', value: ->(status) { status.account.domain || '' })
field(:properties, type: 'keyword', value: ->(status) { status.searchable_properties }) field(:properties, type: 'keyword', value: ->(status) { status.searchable_properties })
field(:created_at, type: 'date', value: ->(status) { clamp_date(status.created_at) }) field(:created_at, type: 'date', value: ->(status) { clamp_date(status.created_at) })
end end

View file

@ -3,49 +3,64 @@
class StatusesIndex < Chewy::Index class StatusesIndex < Chewy::Index
include DatetimeClampingConcern include DatetimeClampingConcern
# ElasticSearch config is moved to "/config/elasticsearch.default.yml". settings index: index_preset(refresh_interval: '30s', number_of_shards: 5), analysis: {
# Edit it when original Mastodon changed ElasticSearch config. filter: {
settings index: index_preset(refresh_interval: '30s', number_of_shards: 5), analysis: ChewyConfig.instance.statuses english_stop: {
type: 'stop',
stopwords: '_english_',
},
index_scope ::Status.unscoped.kept.without_reblogs.includes( english_stemmer: {
:account, type: 'stemmer',
:media_attachments, language: 'english',
:local_mentioned, },
:local_favorited,
:local_reblogged, english_possessive_stemmer: {
:local_bookmarked, type: 'stemmer',
:local_emoji_reacted, language: 'possessive_english',
:tags, },
:local_referenced, },
preview_cards_status: :preview_card,
preloadable_poll: :local_voters analyzer: {
), verbatim: {
delete_if: lambda { |status| tokenizer: 'uax_url_email',
if status.searchability == 'direct' filter: %w(lowercase),
status.searchable_by.empty? },
else
status.searchability == 'limited' ? !status.local? : false content: {
end tokenizer: 'standard',
} filter: %w(
lowercase
asciifolding
cjk_width
elision
english_possessive_stemmer
english_stop
english_stemmer
),
},
hashtag: {
tokenizer: 'keyword',
filter: %w(
word_delimiter_graph
lowercase
asciifolding
cjk_width
),
},
},
}
index_scope ::Status.unscoped.kept.without_reblogs.includes(:media_attachments, :local_mentioned, :local_favorited, :local_reblogged, :local_bookmarked, :tags, preview_cards_status: :preview_card, preloadable_poll: :local_voters), delete_if: ->(status) { status.searchable_by.empty? }
root date_detection: false do root date_detection: false do
field(:id, type: 'long') field(:id, type: 'long')
field(:account_id, type: 'long') field(:account_id, type: 'long')
field(:text, type: 'text', analyzer: ChewyConfig.instance.statuses_analyzers.dig('text', 'analyzer'), value: ->(status) { status.searchable_text }) { field(:stemmed, type: 'text', analyzer: ChewyConfig.instance.statuses_analyzers.dig('text', 'stemmed', 'analyzer')) } field(:text, type: 'text', analyzer: 'verbatim', value: ->(status) { status.searchable_text }) { field(:stemmed, type: 'text', analyzer: 'content') }
field(:tags, type: 'text', analyzer: ChewyConfig.instance.statuses_analyzers.dig('tags', 'analyzer'), value: ->(status) { status.tags.map(&:display_name) }) field(:tags, type: 'text', analyzer: 'hashtag', value: ->(status) { status.tags.map(&:display_name) })
field(:searchable_by, type: 'long', value: ->(status) { status.searchable_by }) field(:searchable_by, type: 'long', value: ->(status) { status.searchable_by })
field(:mentioned_by, type: 'long', value: ->(status) { status.mentioned_by })
field(:favourited_by, type: 'long', value: ->(status) { status.favourited_by })
field(:reblogged_by, type: 'long', value: ->(status) { status.reblogged_by })
field(:bookmarked_by, type: 'long', value: ->(status) { status.bookmarked_by })
field(:bookmark_categoried_by, type: 'long', value: ->(status) { status.bookmark_categoried_by })
field(:emoji_reacted_by, type: 'long', value: ->(status) { status.emoji_reacted_by })
field(:referenced_by, type: 'long', value: ->(status) { status.referenced_by })
field(:voted_by, type: 'long', value: ->(status) { status.voted_by })
field(:searchability, type: 'keyword', value: ->(status) { status.compute_searchability })
field(:visibility, type: 'keyword', value: ->(status) { status.searchable_visibility })
field(:language, type: 'keyword') field(:language, type: 'keyword')
field(:domain, type: 'keyword', value: ->(status) { status.account.domain || '' })
field(:properties, type: 'keyword', value: ->(status) { status.searchable_properties }) field(:properties, type: 'keyword', value: ->(status) { status.searchable_properties })
field(:created_at, type: 'date', value: ->(status) { clamp_date(status.created_at) }) field(:created_at, type: 'date', value: ->(status) { clamp_date(status.created_at) })
end end

View file

@ -3,9 +3,36 @@
class TagsIndex < Chewy::Index class TagsIndex < Chewy::Index
include DatetimeClampingConcern include DatetimeClampingConcern
# ElasticSearch config is moved to "/config/elasticsearch.default.yml". settings index: index_preset(refresh_interval: '30s'), analysis: {
# Edit it when original Mastodon changed ElasticSearch config. analyzer: {
settings index: index_preset(refresh_interval: '30s'), analysis: ChewyConfig.instance.tags content: {
tokenizer: 'keyword',
filter: %w(
word_delimiter_graph
lowercase
asciifolding
cjk_width
),
},
edge_ngram: {
tokenizer: 'edge_ngram',
filter: %w(
lowercase
asciifolding
cjk_width
),
},
},
tokenizer: {
edge_ngram: {
type: 'edge_ngram',
min_gram: 2,
max_gram: 15,
},
},
}
index_scope ::Tag.listable index_scope ::Tag.listable
@ -14,9 +41,7 @@ class TagsIndex < Chewy::Index
end end
root date_detection: false do root date_detection: false do
field(:name, type: 'text', analyzer: ChewyConfig.instance.tags_analyzers.dig('name', 'analyzer'), value: :display_name) do field(:name, type: 'text', analyzer: 'content', value: :display_name) { field(:edge_ngram, type: 'text', analyzer: 'edge_ngram', search_analyzer: 'content') }
field(:edge_ngram, type: 'text', analyzer: ChewyConfig.instance.tags_analyzers.dig('name', 'edge_ngram', 'analyzer'), search_analyzer: ChewyConfig.instance.tags_analyzers.dig('name', 'edge_ngram', 'search_analyzer'))
end
field(:reviewed, type: 'boolean', value: ->(tag) { tag.reviewed? }) field(:reviewed, type: 'boolean', value: ->(tag) { tag.reviewed? })
field(:usage, type: 'long', value: ->(tag, crutches) { tag.history.aggregate(crutches.time_period).accounts }) field(:usage, type: 'long', value: ->(tag, crutches) { tag.history.aggregate(crutches.time_period).accounts })
field(:last_status_at, type: 'date', value: ->(tag) { clamp_date(tag.last_status_at || tag.created_at) }) field(:last_status_at, type: 'date', value: ->(tag) { clamp_date(tag.last_status_at || tag.created_at) })

View file

@ -25,7 +25,7 @@ class AccountsController < ApplicationController
limit = params[:limit].present? ? [params[:limit].to_i, PAGE_SIZE_MAX].min : PAGE_SIZE limit = params[:limit].present? ? [params[:limit].to_i, PAGE_SIZE_MAX].min : PAGE_SIZE
@statuses = filtered_statuses.without_reblogs.limit(limit) @statuses = filtered_statuses.without_reblogs.limit(limit)
@statuses = preload_collection(@statuses, Status) @statuses = cache_collection(@statuses, Status)
end end
format.json do format.json do
@ -46,11 +46,7 @@ class AccountsController < ApplicationController
end end
def default_statuses def default_statuses
if current_account.present? @account.statuses.where(visibility: [:public, :unlisted])
@account.statuses.distributable_visibility
else
@account.statuses.distributable_visibility_for_anonymous
end
end end
def only_media_scope def only_media_scope

View file

@ -1,9 +1,6 @@
# frozen_string_literal: true # frozen_string_literal: true
class ActivityPub::BaseController < Api::BaseController class ActivityPub::BaseController < Api::BaseController
include SignatureVerification
include AccountOwnedConcern
skip_before_action :require_authenticated_user! skip_before_action :require_authenticated_user!
skip_before_action :require_not_suspended! skip_before_action :require_not_suspended!
skip_around_action :set_locale skip_around_action :set_locale

View file

@ -1,6 +1,9 @@
# frozen_string_literal: true # frozen_string_literal: true
class ActivityPub::ClaimsController < ActivityPub::BaseController class ActivityPub::ClaimsController < ActivityPub::BaseController
include SignatureVerification
include AccountOwnedConcern
skip_before_action :authenticate_user! skip_before_action :authenticate_user!
before_action :require_account_signature! before_action :require_account_signature!

View file

@ -1,6 +1,9 @@
# frozen_string_literal: true # frozen_string_literal: true
class ActivityPub::CollectionsController < ActivityPub::BaseController class ActivityPub::CollectionsController < ActivityPub::BaseController
include SignatureVerification
include AccountOwnedConcern
vary_by -> { 'Signature' if authorized_fetch_mode? } vary_by -> { 'Signature' if authorized_fetch_mode? }
before_action :require_account_signature!, if: :authorized_fetch_mode? before_action :require_account_signature!, if: :authorized_fetch_mode?
@ -18,7 +21,7 @@ class ActivityPub::CollectionsController < ActivityPub::BaseController
def set_items def set_items
case params[:id] case params[:id]
when 'featured' when 'featured'
@items = for_signed_account { preload_collection(@account.pinned_statuses, Status) } @items = for_signed_account { cache_collection(@account.pinned_statuses, Status) }
@items = @items.map { |item| item.distributable? ? item : ActivityPub::TagManager.instance.uri_for(item) } @items = @items.map { |item| item.distributable? ? item : ActivityPub::TagManager.instance.uri_for(item) }
when 'tags' when 'tags'
@items = for_signed_account { @account.featured_tags } @items = for_signed_account { @account.featured_tags }

View file

@ -1,23 +0,0 @@
# frozen_string_literal: true
class ActivityPub::ContextsController < ActivityPub::BaseController
include SignatureVerification
vary_by -> { 'Signature' if authorized_fetch_mode? }
before_action :set_context
def show
expires_in 3.minutes, public: true
render json: @context,
serializer: ActivityPub::ContextSerializer,
adapter: ActivityPub::Adapter,
content_type: 'application/activity+json'
end
private
def set_context
@context = Conversation.find(params[:id])
end
end

View file

@ -1,6 +1,9 @@
# frozen_string_literal: true # frozen_string_literal: true
class ActivityPub::FollowersSynchronizationsController < ActivityPub::BaseController class ActivityPub::FollowersSynchronizationsController < ActivityPub::BaseController
include SignatureVerification
include AccountOwnedConcern
vary_by -> { 'Signature' if authorized_fetch_mode? } vary_by -> { 'Signature' if authorized_fetch_mode? }
before_action :require_account_signature! before_action :require_account_signature!
@ -21,7 +24,7 @@ class ActivityPub::FollowersSynchronizationsController < ActivityPub::BaseContro
end end
def set_items def set_items
@items = @account.followers.matches_uri_prefix(uri_prefix).pluck(:uri) @items = @account.followers.where(Account.arel_table[:uri].matches("#{Account.sanitize_sql_like(uri_prefix)}/%", false, true)).or(@account.followers.where(uri: uri_prefix)).pluck(:uri)
end end
def collection_presenter def collection_presenter

View file

@ -1,7 +1,9 @@
# frozen_string_literal: true # frozen_string_literal: true
class ActivityPub::InboxesController < ActivityPub::BaseController class ActivityPub::InboxesController < ActivityPub::BaseController
include SignatureVerification
include JsonLdHelper include JsonLdHelper
include AccountOwnedConcern
before_action :skip_unknown_actor_activity before_action :skip_unknown_actor_activity
before_action :require_actor_signature! before_action :require_actor_signature!
@ -22,7 +24,7 @@ class ActivityPub::InboxesController < ActivityPub::BaseController
def unknown_affected_account? def unknown_affected_account?
json = Oj.load(body, mode: :strict) 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']) json.is_a?(Hash) && %w(Delete Update).include?(json['type']) && json['actor'].present? && json['actor'] == value_or_id(json['object']) && !Account.where(uri: json['actor']).exists?
rescue Oj::ParseError rescue Oj::ParseError
false false
end end
@ -60,10 +62,11 @@ class ActivityPub::InboxesController < ActivityPub::BaseController
return if raw_params.blank? || ENV['DISABLE_FOLLOWERS_SYNCHRONIZATION'] == 'true' || signed_request_account.nil? return if raw_params.blank? || ENV['DISABLE_FOLLOWERS_SYNCHRONIZATION'] == 'true' || signed_request_account.nil?
# Re-using the syntax for signature parameters # Re-using the syntax for signature parameters
params = SignatureParser.parse(raw_params) tree = SignatureParamsParser.new.parse(raw_params)
params = SignatureParamsTransformer.new.apply(tree)
ActivityPub::PrepareFollowersSynchronizationService.new.call(signed_request_account, params) ActivityPub::PrepareFollowersSynchronizationService.new.call(signed_request_account, params)
rescue SignatureParser::ParsingError rescue Parslet::ParseFailed
Rails.logger.warn 'Error parsing Collection-Synchronization header' Rails.logger.warn 'Error parsing Collection-Synchronization header'
end end

View file

@ -3,6 +3,9 @@
class ActivityPub::OutboxesController < ActivityPub::BaseController class ActivityPub::OutboxesController < ActivityPub::BaseController
LIMIT = 20 LIMIT = 20
include SignatureVerification
include AccountOwnedConcern
vary_by -> { 'Signature' if authorized_fetch_mode? || page_requested? } vary_by -> { 'Signature' if authorized_fetch_mode? || page_requested? }
before_action :require_account_signature!, if: :authorized_fetch_mode? before_action :require_account_signature!, if: :authorized_fetch_mode?
@ -34,7 +37,7 @@ class ActivityPub::OutboxesController < ActivityPub::BaseController
ActivityPub::CollectionPresenter.new( ActivityPub::CollectionPresenter.new(
id: outbox_url, id: outbox_url,
type: :ordered, type: :ordered,
size: @account.user&.setting_hide_statuses_count ? 0 : @account.statuses_count, size: @account.statuses_count,
first: outbox_url(page: true), first: outbox_url(page: true),
last: outbox_url(page: true, min_id: 0) last: outbox_url(page: true, min_id: 0)
) )
@ -60,7 +63,7 @@ class ActivityPub::OutboxesController < ActivityPub::BaseController
def set_statuses def set_statuses
return unless page_requested? return unless page_requested?
@statuses = preload_collection_paginated_by_id( @statuses = cache_collection_paginated_by_id(
AccountStatusesFilter.new(@account, signed_request_account).results, AccountStatusesFilter.new(@account, signed_request_account).results,
Status, Status,
LIMIT, LIMIT,

View file

@ -1,89 +0,0 @@
# frozen_string_literal: true
class ActivityPub::ReferencesController < ActivityPub::BaseController
include SignatureVerification
include Authorization
include AccountOwnedConcern
before_action :require_signature!, if: :authorized_fetch_mode?
before_action :set_status
def index
expires_in 0, public: public_fetch_mode?
render json: references_collection_presenter, serializer: ActivityPub::CollectionSerializer, adapter: ActivityPub::Adapter, content_type: 'application/activity+json', skip_activities: true
end
private
def pundit_user
signed_request_account
end
def set_status
@status = @account.statuses.find(params[:status_id])
authorize @status, :show?
rescue Mastodon::NotPermittedError
not_found
end
def load_statuses
cached_references
end
def cached_references
preload_collection(Status.where(id: results).reorder(:id), Status)
end
def results
@results ||= begin
references = @status.reference_objects.order(target_status_id: :asc)
references = references.where('target_status_id > ?', page_params[:min_id]) if page_params[:min_id].present?
references = references.limit(limit_param(references_limit))
references.pluck(:target_status_id)
end
end
def references_limit
StatusReference::REFERENCES_LIMIT
end
def pagination_min_id
results.last
end
def records_continue?
results.size == limit_param(references_limit)
end
def references_collection_presenter
page = ActivityPub::CollectionPresenter.new(
id: ActivityPub::TagManager.instance.references_uri_for(@status, page_params),
type: :unordered,
part_of: ActivityPub::TagManager.instance.references_uri_for(@status),
items: load_statuses.map(&:uri),
next: next_page
)
return page if page_requested?
ActivityPub::CollectionPresenter.new(
type: :unordered,
id: ActivityPub::TagManager.instance.references_uri_for(@status),
first: page
)
end
def page_requested?
truthy_param?(:page)
end
def next_page
return unless records_continue?
ActivityPub::TagManager.instance.references_uri_for(@status, page_params.merge(min_id: pagination_min_id))
end
def page_params
params_slice(:min_id, :limit).merge(page: true)
end
end

View file

@ -1,7 +1,9 @@
# frozen_string_literal: true # frozen_string_literal: true
class ActivityPub::RepliesController < ActivityPub::BaseController class ActivityPub::RepliesController < ActivityPub::BaseController
include SignatureVerification
include Authorization include Authorization
include AccountOwnedConcern
DESCENDANTS_LIMIT = 60 DESCENDANTS_LIMIT = 60
@ -31,7 +33,7 @@ class ActivityPub::RepliesController < ActivityPub::BaseController
def set_replies def set_replies
@replies = only_other_accounts? ? Status.where.not(account_id: @account.id).joins(:account).merge(Account.without_suspended) : @account.statuses @replies = only_other_accounts? ? Status.where.not(account_id: @account.id).joins(:account).merge(Account.without_suspended) : @account.statuses
@replies = @replies.distributable_visibility.where(in_reply_to_id: @status.id) @replies = @replies.where(in_reply_to_id: @status.id, visibility: [:public, :unlisted])
@replies = @replies.paginate_by_min_id(DESCENDANTS_LIMIT, params[:min_id]) @replies = @replies.paginate_by_min_id(DESCENDANTS_LIMIT, params[:min_id])
end end

View file

@ -3,13 +3,13 @@
module Admin module Admin
class AccountsController < BaseController class AccountsController < BaseController
before_action :set_account, except: [:index, :batch] before_action :set_account, except: [:index, :batch]
before_action :require_remote_account!, only: [:redownload, :approve_remote, :reject_remote] before_action :require_remote_account!, only: [:redownload]
before_action :require_local_account!, only: [:enable, :memorialize, :approve, :reject] before_action :require_local_account!, only: [:enable, :memorialize, :approve, :reject]
def index def index
authorize :account, :index? authorize :account, :index?
@accounts = filtered_accounts.page(params[:page]).without_count @accounts = filtered_accounts.page(params[:page])
@form = Form::AccountBatch.new @form = Form::AccountBatch.new
end end
@ -66,20 +66,6 @@ module Admin
redirect_to admin_accounts_path(status: 'pending'), notice: I18n.t('admin.accounts.rejected_msg', username: @account.acct) redirect_to admin_accounts_path(status: 'pending'), notice: I18n.t('admin.accounts.rejected_msg', username: @account.acct)
end end
def approve_remote
authorize @account, :approve_remote?
@account.approve_remote!
log_action :approve_remote, @account
redirect_to admin_account_path(@account.id), notice: I18n.t('admin.accounts.approved_msg', username: @account.acct)
end
def reject_remote
authorize @account, :reject_remote?
@account.reject_remote!
log_action :reject_remote, @account
redirect_to admin_account_path(@account.id), notice: I18n.t('admin.accounts.rejected_msg', username: @account.acct)
end
def destroy def destroy
authorize @account, :destroy? authorize @account, :destroy?
Admin::AccountDeletionWorker.perform_async(@account.id) Admin::AccountDeletionWorker.perform_async(@account.id)
@ -142,7 +128,7 @@ module Admin
def unblock_email def unblock_email
authorize @account, :unblock_email? authorize @account, :unblock_email?
CanonicalEmailBlock.matching_account(@account).delete_all CanonicalEmailBlock.where(reference_account: @account).delete_all
log_action :unblock_email, @account log_action :unblock_email, @account
@ -182,12 +168,6 @@ module Admin
'approve' 'approve'
elsif params[:reject] elsif params[:reject]
'reject' 'reject'
elsif params[:approve_remote]
'approve_remote'
elsif params[:approve_remote_domain]
'approve_remote_domain'
elsif params[:reject_remote]
'reject_remote'
end end
end end
end end

View file

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

View file

@ -3,11 +3,11 @@
module Admin module Admin
class ConfirmationsController < BaseController class ConfirmationsController < BaseController
before_action :set_user before_action :set_user
before_action :redirect_confirmed_user, only: [:resend], if: :user_confirmed? before_action :check_confirmation, only: [:resend]
def create def create
authorize @user, :confirm? authorize @user, :confirm?
@user.mark_email_as_confirmed! @user.confirm!
log_action :confirm, @user log_action :confirm, @user
redirect_to admin_accounts_path redirect_to admin_accounts_path
end end
@ -25,13 +25,11 @@ module Admin
private private
def redirect_confirmed_user def check_confirmation
flash[:error] = I18n.t('admin.accounts.resend_confirmation.already_confirmed') if @user.confirmed?
redirect_to admin_accounts_path flash[:error] = I18n.t('admin.accounts.resend_confirmation.already_confirmed')
end redirect_to admin_accounts_path
end
def user_confirmed?
@user.confirmed?
end end
end end
end end

View file

@ -2,12 +2,10 @@
module Admin module Admin
class CustomEmojisController < BaseController class CustomEmojisController < BaseController
before_action :set_custom_emoji, only: [:edit, :update]
def index def index
authorize :custom_emoji, :index? authorize :custom_emoji, :index?
@custom_emojis = filtered_custom_emojis.eager_load(:local_counterpart).page(params[:page]).without_count @custom_emojis = filtered_custom_emojis.eager_load(:local_counterpart).page(params[:page])
@form = Form::CustomEmojiBatch.new @form = Form::CustomEmojiBatch.new
end end
@ -17,10 +15,6 @@ module Admin
@custom_emoji = CustomEmoji.new @custom_emoji = CustomEmoji.new
end end
def edit
authorize :custom_emoji, :create?
end
def create def create
authorize :custom_emoji, :create? authorize :custom_emoji, :create?
@ -34,19 +28,6 @@ module Admin
end end
end end
def update
authorize :custom_emoji, :create?
@custom_emoji.assign_attributes(update_params)
if @custom_emoji.save
log_action :update, @custom_emoji
redirect_to admin_custom_emojis_path(filter_params), notice: I18n.t('admin.custom_emojis.updated_msg')
else
render :new
end
end
def batch def batch
authorize :custom_emoji, :index? authorize :custom_emoji, :index?
@ -62,16 +43,8 @@ module Admin
private private
def set_custom_emoji
@custom_emoji = CustomEmoji.find(params[:id])
end
def resource_params def resource_params
params.require(:custom_emoji).permit(:shortcode, :image, :category_id, :visible_in_picker, :aliases_raw, :license) params.require(:custom_emoji).permit(:shortcode, :image, :visible_in_picker)
end
def update_params
params.require(:custom_emoji).permit(:category_id, :visible_in_picker, :aliases_raw, :license)
end end
def filtered_custom_emojis def filtered_custom_emojis

View file

@ -25,8 +25,6 @@ class Admin::DomainAllowsController < Admin::BaseController
def destroy def destroy
authorize @domain_allow, :destroy? authorize @domain_allow, :destroy?
UnallowDomainService.new.call(@domain_allow) UnallowDomainService.new.call(@domain_allow)
log_action :destroy, @domain_allow
redirect_to admin_instances_path, notice: I18n.t('admin.domain_allows.destroyed_msg') redirect_to admin_instances_path, notice: I18n.t('admin.domain_allows.destroyed_msg')
end end

View file

@ -88,19 +88,15 @@ module Admin
end end
def update_params def update_params
params.require(:domain_block).permit(:severity, :reject_media, :reject_favourite, :reject_reply_exclude_followers, :reject_send_sensitive, :reject_hashtag, params.require(:domain_block).permit(:severity, :reject_media, :reject_reports, :private_comment, :public_comment, :obfuscate)
:reject_straight_follow, :reject_new_follow, :reject_friend, :block_trends, :detect_invalid_subscription, :reject_reports, :private_comment, :public_comment, :obfuscate, :hidden)
end end
def resource_params def resource_params
params.require(:domain_block).permit(:domain, :severity, :reject_media, :reject_favourite, :reject_reply_exclude_followers, :reject_send_sensitive, :reject_hashtag, params.require(:domain_block).permit(:domain, :severity, :reject_media, :reject_reports, :private_comment, :public_comment, :obfuscate)
:reject_straight_follow, :reject_new_follow, :reject_friend, :block_trends, :detect_invalid_subscription, :reject_reports, :private_comment, :public_comment, :obfuscate, :hidden)
end end
def form_domain_block_batch_params def form_domain_block_batch_params
params.require(:form_domain_block_batch).permit(domain_blocks_attributes: [:enabled, :domain, :severity, :reject_media, :reject_favourite, :reject_reply_exclude_followers, params.require(:form_domain_block_batch).permit(domain_blocks_attributes: [:enabled, :domain, :severity, :reject_media, :reject_reports, :private_comment, :public_comment, :obfuscate])
:reject_send_sensitive, :reject_hashtag, :reject_straight_follow, :reject_new_follow, :reject_friend, :block_trends, :detect_invalid_subscription,
:reject_reports, :private_comment, :public_comment, :obfuscate, :hidden])
end end
def action_from_button def action_from_button

View file

@ -38,7 +38,7 @@ module Admin
log_action :create, @email_domain_block log_action :create, @email_domain_block
(@email_domain_block.other_domains || []).uniq.each do |domain| (@email_domain_block.other_domains || []).uniq.each do |domain|
next if EmailDomainBlock.exists?(domain: domain) next if EmailDomainBlock.where(domain: domain).exists?
other_email_domain_block = EmailDomainBlock.create!(domain: domain, allow_with_approval: @email_domain_block.allow_with_approval, parent: @email_domain_block) other_email_domain_block = EmailDomainBlock.create!(domain: domain, allow_with_approval: @email_domain_block.allow_with_approval, parent: @email_domain_block)
log_action :create, other_email_domain_block log_action :create, other_email_domain_block

View file

@ -36,17 +36,7 @@ module Admin
reject_reports: row.fetch('#reject_reports', false), reject_reports: row.fetch('#reject_reports', false),
private_comment: @global_private_comment, private_comment: @global_private_comment,
public_comment: row['#public_comment'], public_comment: row['#public_comment'],
obfuscate: row.fetch('#obfuscate', false), obfuscate: row.fetch('#obfuscate', false))
reject_favourite: row.fetch('#reject_favourite', false),
reject_send_sensitive: row.fetch('#reject_send_sensitive', false),
reject_hashtag: row.fetch('#reject_hashtag', false),
reject_straight_follow: row.fetch('#reject_straight_follow', false),
reject_new_follow: row.fetch('#reject_new_follow', false),
hidden: row.fetch('#hidden', false),
detect_invalid_subscription: row.fetch('#detect_invalid_subscription', false),
reject_reply_exclude_followers: row.fetch('#reject_reply_exclude_followers', false),
reject_friend: row.fetch('#reject_friend', false),
block_trends: row.fetch('#block_trends', false))
if domain_block.invalid? if domain_block.invalid?
flash.now[:alert] = I18n.t('admin.export_domain_blocks.invalid_domain_block', error: domain_block.errors.full_messages.join(', ')) flash.now[:alert] = I18n.t('admin.export_domain_blocks.invalid_domain_block', error: domain_block.errors.full_messages.join(', '))
@ -59,7 +49,7 @@ module Admin
next next
end end
@warning_domains = instances_from_imported_blocks.pluck(:domain) @warning_domains = Instance.where(domain: @domain_blocks.map(&:domain)).where('EXISTS (SELECT 1 FROM follows JOIN accounts ON follows.account_id = accounts.id OR follows.target_account_id = accounts.id WHERE accounts.domain = instances.domain)').pluck(:domain)
rescue ActionController::ParameterMissing rescue ActionController::ParameterMissing
flash.now[:alert] = I18n.t('admin.export_domain_blocks.no_file') flash.now[:alert] = I18n.t('admin.export_domain_blocks.no_file')
set_dummy_import! set_dummy_import!
@ -68,56 +58,18 @@ module Admin
private private
def instances_from_imported_blocks
Instance.with_domain_follows(@domain_blocks.map(&:domain))
end
def export_filename def export_filename
'domain_blocks.csv' 'domain_blocks.csv'
end end
def export_headers def export_headers
%w( %w(#domain #severity #reject_media #reject_reports #public_comment #obfuscate)
#domain
#severity
#reject_media
#reject_reports
#public_comment
#obfuscate
#reject_favourite
#reject_send_sensitive
#reject_hashtag
#reject_straight_follow
#reject_new_follow
#hidden
#detect_invalid_subscription
#reject_reply_exclude_followers
#reject_friend
#block_trends
)
end end
def export_data def export_data
CSV.generate(headers: export_headers, write_headers: true) do |content| CSV.generate(headers: export_headers, write_headers: true) do |content|
DomainBlock.with_limitations.order(id: :asc).each do |instance| DomainBlock.with_limitations.order(id: :asc).each do |instance|
content << [ content << [instance.domain, instance.severity, instance.reject_media, instance.reject_reports, instance.public_comment, instance.obfuscate]
instance.domain,
instance.severity,
instance.reject_media,
instance.reject_reports,
instance.public_comment,
instance.obfuscate,
instance.reject_favourite,
instance.reject_send_sensitive,
instance.reject_hashtag,
instance.reject_straight_follow,
instance.reject_new_follow,
instance.hidden,
instance.detect_invalid_subscription,
instance.reject_reply_exclude_followers,
instance.reject_friend,
instance.block_trends,
]
end end
end end
end end

View file

@ -1,93 +0,0 @@
# frozen_string_literal: true
module Admin
class FriendServersController < BaseController
before_action :set_friend, except: [:index, :new, :create]
before_action :warn_signatures_not_enabled!, only: [:new, :edit, :create, :follow, :unfollow, :accept, :reject]
def index
authorize :friend_server, :update?
@friends = FriendDomain.all
end
def new
authorize :friend_server, :update?
@friend = FriendDomain.new
end
def edit
authorize :friend_server, :update?
end
def create
authorize :friend_server, :update?
@friend = FriendDomain.new(resource_params)
if @friend.save
@friend.follow!
redirect_to admin_friend_servers_path
else
render action: :new
end
end
def update
authorize :friend_server, :update?
if @friend.update(update_resource_params)
redirect_to admin_friend_servers_path
else
render action: :edit
end
end
def destroy
authorize :friend_server, :update?
@friend.destroy
redirect_to admin_friend_servers_path
end
def follow
authorize :friend_server, :update?
@friend.follow!
render action: :edit
end
def unfollow
authorize :friend_server, :update?
@friend.unfollow!
render action: :edit
end
def accept
authorize :friend_server, :update?
@friend.accept!
render action: :edit
end
def reject
authorize :friend_server, :update?
@friend.reject!
render action: :edit
end
private
def set_friend
@friend = FriendDomain.find(params[:id])
end
def resource_params
params.require(:friend_domain).permit(:domain, :inbox_url, :available, :pseudo_relay, :delivery_local, :unlocked, :allow_all_posts)
end
def update_resource_params
params.require(:friend_domain).permit(:inbox_url, :available, :pseudo_relay, :delivery_local, :unlocked, :allow_all_posts)
end
def warn_signatures_not_enabled!
flash.now[:error] = I18n.t('admin.relays.signatures_not_enabled') if authorized_fetch_mode?
end
end
end

View file

@ -1,24 +0,0 @@
# frozen_string_literal: true
module Admin
class NgRuleHistoriesController < BaseController
before_action :set_ng_rule
before_action :set_histories
PER_PAGE = 20
def show
authorize :ng_words, :show?
end
private
def set_ng_rule
@ng_rule = ::NgRule.find(params[:id])
end
def set_histories
@histories = NgRuleHistory.where(ng_rule_id: params[:id]).order(id: :desc).page(params[:page]).per(PER_PAGE)
end
end
end

View file

@ -1,115 +0,0 @@
# frozen_string_literal: true
module Admin
class NgRulesController < BaseController
before_action :set_ng_rule, only: [:edit, :update, :destroy, :duplicate]
def index
authorize :ng_words, :show?
@ng_rules = ::NgRule.order(id: :asc)
end
def new
authorize :ng_words, :show?
@ng_rule = ::NgRule.build
end
def edit
authorize :ng_words, :show?
end
def create
authorize :ng_words, :create?
begin
test_words!
rescue
flash[:alert] = I18n.t('admin.ng_rules.test_error')
redirect_to new_admin_ng_rule_path
return
end
@ng_rule = ::NgRule.build(resource_params)
if @ng_rule.save
redirect_to admin_ng_rules_path
else
render :new
end
end
def update
authorize :ng_words, :create?
begin
test_words!
rescue
flash[:alert] = I18n.t('admin.ng_rules.test_error')
redirect_to edit_admin_ng_rule_path(id: @ng_rule.id)
return
end
if @ng_rule.update(resource_params)
redirect_to admin_ng_rules_path
else
render :edit
end
end
def duplicate
authorize :ng_words, :create?
@ng_rule = @ng_rule.copy!
flash[:alert] = I18n.t('admin.ng_rules.copy_error') unless @ng_rule.save
redirect_to admin_ng_rules_path
end
def destroy
authorize :ng_words, :create?
@ng_rule.destroy
redirect_to admin_ng_rules_path
end
private
def set_ng_rule
@ng_rule = ::NgRule.find(params[:id])
end
def resource_params
params.require(:ng_rule).permit(:title, :expires_in, :available, :account_domain, :account_username, :account_display_name,
:account_note, :account_field_name, :account_field_value, :account_avatar_state,
:account_header_state, :account_include_local, :status_spoiler_text, :status_text, :status_tag,
:status_sensitive_state, :status_cw_state, :status_media_state, :status_poll_state,
:status_mention_state, :status_reference_state,
:status_quote_state, :status_reply_state, :status_media_threshold, :status_poll_threshold,
:status_mention_threshold, :status_allow_follower_mention,
:reaction_allow_follower, :emoji_reaction_name, :emoji_reaction_origin_domain,
:status_reference_threshold, :account_allow_followed_by_local, :record_history_also_local,
status_visibility: [], status_searchability: [], reaction_type: [])
end
def test_words!
arr = [
resource_params[:account_domain],
resource_params[:account_username],
resource_params[:account_display_name],
resource_params[:account_note],
resource_params[:account_field_name],
resource_params[:account_field_value],
resource_params[:status_spoiler_text],
resource_params[:status_text],
resource_params[:status_tag],
resource_params[:emoji_reaction_name],
resource_params[:emoji_reaction_origin_domain],
].compact_blank.join("\n")
Admin::NgRule.extract_test!(arr) if arr.present?
end
end
end

View file

@ -1,30 +0,0 @@
# frozen_string_literal: true
module Admin
class NgWords::KeywordsController < NgWordsController
def show
super
@ng_words = ::NgWord.caches.presence || [::NgWord.new]
end
protected
def validate
begin
::NgWord.save_from_raws(settings_params_test)
return true
rescue
flash[:alert] = I18n.t('admin.ng_words.test_error')
redirect_to after_update_redirect_path
end
false
end
private
def after_update_redirect_path
admin_ng_words_keywords_path
end
end
end

View file

@ -1,11 +0,0 @@
# frozen_string_literal: true
module Admin
class NgWords::SettingsController < NgWordsController
protected
def after_update_redirect_path
admin_ng_words_settings_path
end
end
end

View file

@ -1,34 +0,0 @@
# frozen_string_literal: true
module Admin
class NgWords::WhiteListController < NgWordsController
def show
super
@white_list_domains = SpecifiedDomain.white_list_domain_caches.presence || [SpecifiedDomain.new]
end
protected
def validate
begin
SpecifiedDomain.save_from_raws_as_white_list(settings_params_list)
return true
rescue
flash[:alert] = I18n.t('admin.ng_words.save_error')
redirect_to after_update_redirect_path
end
false
end
def after_update_redirect_path
admin_ng_words_white_list_path
end
private
def settings_params_list
params.require(:form_admin_settings)[:specified_domains]
end
end
end

View file

@ -1,46 +0,0 @@
# frozen_string_literal: true
module Admin
class NgWordsController < BaseController
def show
authorize :ng_words, :show?
@admin_settings = Form::AdminSettings.new
end
def create
authorize :ng_words, :create?
return unless validate
@admin_settings = Form::AdminSettings.new(settings_params)
if @admin_settings.save
flash[:notice] = I18n.t('generic.changes_saved_msg')
redirect_to after_update_redirect_path
else
render :show
end
end
protected
def validate
true
end
def after_update_redirect_path
admin_ng_words_path
end
private
def settings_params
params.require(:form_admin_settings).permit(*Form::AdminSettings::KEYS)
end
def settings_params_test
params.require(:form_admin_settings)[:ng_words_test]
end
end
end

View file

@ -1,19 +0,0 @@
# frozen_string_literal: true
module Admin
class NgwordHistoriesController < BaseController
before_action :set_histories
PER_PAGE = 20
def index
authorize :ng_words, :show?
end
private
def set_histories
@histories = NgwordHistory.order(id: :desc).page(params[:page]).per(PER_PAGE)
end
end
end

View file

@ -12,7 +12,7 @@ class Admin::Reports::ActionsController < Admin::BaseController
authorize @report, :show? authorize @report, :show?
case action_from_button case action_from_button
when 'delete', 'mark_as_sensitive', 'force_cw' when 'delete', 'mark_as_sensitive'
status_batch_action = Admin::StatusBatchAction.new( status_batch_action = Admin::StatusBatchAction.new(
type: action_from_button, type: action_from_button,
status_ids: @report.status_ids, status_ids: @report.status_ids,
@ -52,8 +52,6 @@ class Admin::Reports::ActionsController < Admin::BaseController
'delete' 'delete'
elsif params[:mark_as_sensitive] elsif params[:mark_as_sensitive]
'mark_as_sensitive' 'mark_as_sensitive'
elsif params[:force_cw]
'force_cw'
elsif params[:silence] elsif params[:silence]
'silence' 'silence'
elsif params[:suspend] elsif params[:suspend]

View file

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

View file

@ -1,47 +0,0 @@
# frozen_string_literal: true
module Admin
class SensitiveWordsController < BaseController
def show
authorize :sensitive_words, :show?
@admin_settings = Form::AdminSettings.new
@sensitive_words = ::SensitiveWord.caches.presence || [::SensitiveWord.new]
end
def create
authorize :sensitive_words, :create?
begin
::SensitiveWord.save_from_raws(settings_params_test)
rescue
flash[:alert] = I18n.t('admin.ng_words.test_error')
redirect_to after_update_redirect_path
return
end
@admin_settings = Form::AdminSettings.new(settings_params)
if @admin_settings.save
flash[:notice] = I18n.t('generic.changes_saved_msg')
redirect_to after_update_redirect_path
else
render :index
end
end
private
def after_update_redirect_path
admin_sensitive_words_path
end
def settings_params
params.require(:form_admin_settings).permit(*Form::AdminSettings::KEYS)
end
def settings_params_test
params.require(:form_admin_settings)[:sensitive_words_test]
end
end
end

View file

@ -1,18 +1,9 @@
# frozen_string_literal: true # frozen_string_literal: true
class Admin::Settings::RegistrationsController < Admin::SettingsController class Admin::Settings::RegistrationsController < Admin::SettingsController
include RegistrationLimitationHelper
before_action :set_limitation_counts, only: :show # rubocop:disable Rails/LexicallyScopedActionFilter
private private
def after_update_redirect_path def after_update_redirect_path
admin_settings_registrations_path admin_settings_registrations_path
end end
def set_limitation_counts
@current_users_count = user_count_for_registration
@current_users_count_today = today_increase_user_count
end
end end

View file

@ -9,7 +9,7 @@ module Admin
@site_upload.destroy! @site_upload.destroy!
redirect_back fallback_location: admin_settings_path, notice: I18n.t('admin.site_uploads.destroyed_msg') redirect_to admin_settings_path, notice: I18n.t('admin.site_uploads.destroyed_msg')
end end
private private

View file

@ -1,34 +0,0 @@
# frozen_string_literal: true
module Admin
class SpecialDomainsController < BaseController
def show
authorize :instance, :show?
@admin_settings = Form::AdminSettings.new
end
def create
authorize :instance, :destroy?
@admin_settings = Form::AdminSettings.new(settings_params)
if @admin_settings.save
flash[:notice] = I18n.t('generic.changes_saved_msg')
redirect_to after_update_redirect_path
else
render :show
end
end
private
def after_update_redirect_path
admin_special_domains_path
end
def settings_params
params.require(:form_admin_settings).permit(*Form::AdminSettings::KEYS)
end
end
end

View file

@ -1,34 +0,0 @@
# frozen_string_literal: true
module Admin
class SpecialInstancesController < BaseController
def show
authorize :instance, :show?
@admin_settings = Form::AdminSettings.new
end
def create
authorize :instance, :destroy?
@admin_settings = Form::AdminSettings.new(settings_params)
if @admin_settings.save
flash[:notice] = I18n.t('generic.changes_saved_msg')
redirect_to after_update_redirect_path
else
render :show
end
end
private
def after_update_redirect_path
admin_special_instances_path
end
def settings_params
params.require(:form_admin_settings).permit(*Form::AdminSettings::KEYS)
end
end
end

View file

@ -4,7 +4,7 @@ module Admin
class StatusesController < BaseController class StatusesController < BaseController
before_action :set_account before_action :set_account
before_action :set_statuses, except: :show before_action :set_statuses, except: :show
before_action :set_status, only: [:show, :remove_history, :remove_media, :force_sensitive, :force_cw, :remove_status] before_action :set_status, only: :show
PER_PAGE = 20 PER_PAGE = 20
@ -29,65 +29,6 @@ module Admin
redirect_to after_create_redirect_path redirect_to after_create_redirect_path
end end
def remove_history
authorize [:admin, @status], :show?
UpdateStatusService.new.call(
@status,
edit_status_account_id,
no_history: true,
bypass_validation: true
)
log_action(:remove_history, @status)
redirect_to admin_account_status_path
end
def remove_media
authorize [:admin, @status], :show?
UpdateStatusService.new.call(
@status,
edit_status_account_id,
media_ids: [],
media_attributes: [],
bypass_validation: true
)
log_action(:remove_media, @status)
redirect_to admin_account_status_path
end
def force_sensitive
authorize [:admin, @status], :show?
UpdateStatusService.new.call(
@status,
edit_status_account_id,
sensitive: true,
bypass_validation: true
)
log_action(:force_sensitive, @status)
redirect_to admin_account_status_path
end
def force_cw
authorize [:admin, @status], :show?
UpdateStatusService.new.call(
@status,
edit_status_account_id,
spoiler_text: 'CW',
bypass_validation: true
)
log_action(:force_cw, @status)
redirect_to admin_account_status_path
end
def remove_status
authorize [:admin, @status], :show?
@status.discard_with_reblogs
StatusPin.find_by(status: @status)&.destroy
@status.account.statuses_count = @status.account.statuses_count - 1
RemovalWorker.perform_async(@status.id, { 'redraft' => false })
log_action(:remove_status, @status)
redirect_to admin_account_path
end
private private
def batched_ordered_status_edits def batched_ordered_status_edits
@ -121,13 +62,6 @@ module Admin
@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) @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 end
def edit_status_account_id
return @edit_account_id || @account.id if @edit_account_checked
@edit_account_checked = true
@edit_account_id = Account.representative.id
end
def filter_params def filter_params
params.slice(*Admin::StatusFilter::KEYS).permit(*Admin::StatusFilter::KEYS) params.slice(*Admin::StatusFilter::KEYS).permit(*Admin::StatusFilter::KEYS)
end end

View file

@ -1,51 +0,0 @@
# frozen_string_literal: true
class AntennasController < ApplicationController
layout 'admin'
before_action :authenticate_user!
before_action :set_antenna, only: [:edit, :update, :destroy]
before_action :set_body_classes
before_action :set_cache_headers
def index
@antennas = current_account.antennas.includes(:antenna_domains).includes(:antenna_tags).includes(:antenna_accounts)
end
def edit; end
def update
if @antenna.update(resource_params)
redirect_to antennas_path
else
render action: :edit
end
end
def destroy
@antenna.destroy
redirect_to antennas_path
end
private
def set_antenna
@antenna = current_account.antennas.find(params[:id])
end
def resource_params
params.require(:antenna).permit(:title, :available, :expires_in)
end
def thin_resource_params
params.require(:antenna).permit(:title)
end
def set_body_classes
@body_classes = 'admin'
end
def set_cache_headers
response.cache_control.replace(private: true, no_store: true)
end
end

View file

@ -1,15 +1,13 @@
# frozen_string_literal: true # frozen_string_literal: true
class Api::BaseController < ApplicationController class Api::BaseController < ApplicationController
DEFAULT_STATUSES_LIMIT = 20 DEFAULT_STATUSES_LIMIT = 20
DEFAULT_ACCOUNTS_LIMIT = 40 DEFAULT_ACCOUNTS_LIMIT = 40
include Api::RateLimitHeaders include Api::RateLimitHeaders
include Api::AccessTokenTrackingConcern include Api::AccessTokenTrackingConcern
include Api::CachingConcern include Api::CachingConcern
include Api::ContentSecurityPolicy include Api::ContentSecurityPolicy
include Api::ErrorHandling
include Api::Pagination
skip_before_action :require_functional!, unless: :limited_federation_mode? skip_before_action :require_functional!, unless: :limited_federation_mode?
@ -20,6 +18,51 @@ class Api::BaseController < ApplicationController
protect_from_forgery with: :null_session protect_from_forgery with: :null_session
rescue_from ActiveRecord::RecordInvalid, Mastodon::ValidationError do |e|
render json: { error: e.to_s }, status: 422
end
rescue_from ActiveRecord::RecordNotUnique do
render json: { error: 'Duplicate record' }, status: 422
end
rescue_from Date::Error do
render json: { error: 'Invalid date supplied' }, status: 422
end
rescue_from ActiveRecord::RecordNotFound do
render json: { error: 'Record not found' }, status: 404
end
rescue_from HTTP::Error, Mastodon::UnexpectedResponseError do
render json: { error: 'Remote data could not be fetched' }, status: 503
end
rescue_from OpenSSL::SSL::SSLError do
render json: { error: 'Remote SSL certificate could not be verified' }, status: 503
end
rescue_from Mastodon::NotPermittedError do
render json: { error: 'This action is not allowed' }, status: 403
end
rescue_from Seahorse::Client::NetworkingError do |e|
Rails.logger.warn "Storage server error: #{e}"
render json: { error: 'There was a temporary problem serving your request, please try again' }, status: 503
end
rescue_from Mastodon::RaceConditionError, Stoplight::Error::RedLight do
render json: { error: 'There was a temporary problem serving your request, please try again' }, status: 503
end
rescue_from Mastodon::RateLimitExceededError do
render json: { error: I18n.t('errors.429') }, status: 429
end
rescue_from ActionController::ParameterMissing, Mastodon::InvalidParameterError do |e|
render json: { error: e.to_s }, status: 400
end
def doorkeeper_unauthorized_render_options(error: nil) def doorkeeper_unauthorized_render_options(error: nil)
{ json: { error: error.try(:description) || 'Not authorized' } } { json: { error: error.try(:description) || 'Not authorized' } }
end end
@ -30,6 +73,13 @@ class Api::BaseController < ApplicationController
protected protected
def set_pagination_headers(next_path = nil, prev_path = nil)
links = []
links << [next_path, [%w(rel next)]] if next_path
links << [prev_path, [%w(rel prev)]] if prev_path
response.headers['Link'] = LinkHeader.new(links) unless links.empty?
end
def limit_param(default_limit) def limit_param(default_limit)
return default_limit unless params[:limit] return default_limit unless params[:limit]
@ -58,6 +108,10 @@ class Api::BaseController < ApplicationController
render json: { error: 'Your login is currently disabled' }, status: 403 if current_user&.account&.unavailable? render json: { error: 'Your login is currently disabled' }, status: 403 if current_user&.account&.unavailable?
end end
def require_valid_pagination_options!
render json: { error: 'Pagination values for `offset` and `limit` must be positive' }, status: 400 if pagination_options_invalid?
end
def require_user! def require_user!
if !current_user if !current_user
render json: { error: 'This method requires an authenticated user' }, status: 422 render json: { error: 'This method requires an authenticated user' }, status: 422
@ -86,6 +140,10 @@ class Api::BaseController < ApplicationController
private private
def pagination_options_invalid?
params.slice(:limit, :offset).values.map(&:to_i).any?(&:negative?)
end
def respond_with_error(code) def respond_with_error(code)
render json: { error: Rack::Utils::HTTP_STATUS_CODES[code] }, status: code render json: { error: Rack::Utils::HTTP_STATUS_CODES[code] }, status: code
end end

View file

@ -1,18 +0,0 @@
# frozen_string_literal: true
class Api::V1::Accounts::AntennasController < Api::BaseController
before_action -> { doorkeeper_authorize! :read, :'read:lists' }
before_action :require_user!
before_action :set_account
def index
@antennas = @account.suspended? ? [] : @account.joined_antennas.where(account: current_account)
render json: @antennas, each_serializer: REST::AntennaSerializer
end
private
def set_account
@account = Account.find(params[:account_id])
end
end

View file

@ -1,18 +0,0 @@
# frozen_string_literal: true
class Api::V1::Accounts::CirclesController < Api::BaseController
before_action -> { doorkeeper_authorize! :read, :'read:lists' }
before_action :require_user!
before_action :set_account
def index
@circles = @account.suspended? ? [] : @account.joined_circles.where(account: current_account)
render json: @circles, each_serializer: REST::CircleSerializer
end
private
def set_account
@account = Account.find(params[:account_id])
end
end

View file

@ -1,7 +1,7 @@
# frozen_string_literal: true # frozen_string_literal: true
class Api::V1::Accounts::CredentialsController < Api::BaseController class Api::V1::Accounts::CredentialsController < Api::BaseController
before_action -> { doorkeeper_authorize! :read, :'read:accounts', :'read:me' }, except: [:update] before_action -> { doorkeeper_authorize! :read, :'read:accounts' }, except: [:update]
before_action -> { doorkeeper_authorize! :write, :'write:accounts' }, only: [:update] before_action -> { doorkeeper_authorize! :write, :'write:accounts' }, only: [:update]
before_action :require_user! before_action :require_user!
@ -31,8 +31,6 @@ class Api::V1::Accounts::CredentialsController < Api::BaseController
:locked, :locked,
:bot, :bot,
:discoverable, :discoverable,
:searchability,
:dissubscribable,
:hide_collections, :hide_collections,
:indexable, :indexable,
fields_attributes: [:name, :value] fields_attributes: [:name, :value]
@ -47,7 +45,6 @@ class Api::V1::Accounts::CredentialsController < Api::BaseController
{ {
settings_attributes: { settings_attributes: {
default_privacy: source_params.fetch(:privacy, @account.user.setting_default_privacy), default_privacy: source_params.fetch(:privacy, @account.user.setting_default_privacy),
default_searchability: source_params.fetch(:searchability, @account.user.setting_default_searchability),
default_sensitive: source_params.fetch(:sensitive, @account.user.setting_default_sensitive), default_sensitive: source_params.fetch(:sensitive, @account.user.setting_default_sensitive),
default_language: source_params.fetch(:language, @account.user.setting_default_language), default_language: source_params.fetch(:language, @account.user.setting_default_language),
}, },

View file

@ -1,18 +0,0 @@
# frozen_string_literal: true
class Api::V1::Accounts::ExcludeAntennasController < Api::BaseController
before_action -> { doorkeeper_authorize! :read, :'read:lists' }
before_action :require_user!
before_action :set_account
def index
@antennas = @account.suspended? ? [] : current_account.antennas.where('exclude_accounts @> \'[?]\'', @account.id)
render json: @antennas, each_serializer: REST::AntennaSerializer
end
private
def set_account
@account = Account.find(params[:account_id])
end
end

View file

@ -21,7 +21,7 @@ class Api::V1::Accounts::FollowerAccountsController < Api::BaseController
return [] if hide_results? return [] if hide_results?
scope = default_accounts scope = default_accounts
scope = scope.not_excluded_by_account(current_account) unless current_account.nil? || current_account.id == @account.id scope = scope.where.not(id: current_account.excluded_from_timeline_account_ids) unless current_account.nil? || current_account.id == @account.id
scope.merge(paginated_follows).to_a scope.merge(paginated_follows).to_a
end end
@ -30,7 +30,7 @@ class Api::V1::Accounts::FollowerAccountsController < Api::BaseController
end end
def default_accounts def default_accounts
Account.includes(:active_relationships, :account_stat, :user).references(:active_relationships) Account.includes(:active_relationships, :account_stat).references(:active_relationships)
end end
def paginated_follows def paginated_follows
@ -41,6 +41,10 @@ class Api::V1::Accounts::FollowerAccountsController < Api::BaseController
) )
end end
def insert_pagination_headers
set_pagination_headers(next_path, prev_path)
end
def next_path def next_path
api_v1_account_followers_url pagination_params(max_id: pagination_max_id) if records_continue? api_v1_account_followers_url pagination_params(max_id: pagination_max_id) if records_continue?
end end

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