Compare commits
1 commit
main
...
feature-co
Author | SHA1 | Date | |
---|---|---|---|
|
7e31cce96d |
4852 changed files with 70085 additions and 268468 deletions
|
@ -1,7 +0,0 @@
|
||||||
[production]
|
|
||||||
defaults
|
|
||||||
not IE 11
|
|
||||||
not dead
|
|
||||||
|
|
||||||
[development]
|
|
||||||
supports es6-module
|
|
|
@ -1,3 +1,4 @@
|
||||||
https://github.com/heroku/heroku-buildpack-apt
|
https://github.com/heroku/heroku-buildpack-apt
|
||||||
https://github.com/Scalingo/ffmpeg-buildpack
|
https://github.com/Scalingo/ffmpeg-buildpack
|
||||||
|
https://github.com/Scalingo/nodejs-buildpack
|
||||||
https://github.com/Scalingo/ruby-buildpack
|
https://github.com/Scalingo/ruby-buildpack
|
||||||
|
|
|
@ -1,225 +1,216 @@
|
||||||
version: 2.1
|
version: 2
|
||||||
|
|
||||||
orbs:
|
aliases:
|
||||||
ruby: circleci/ruby@2.0.0
|
- &defaults
|
||||||
node: circleci/node@5.0.3
|
|
||||||
|
|
||||||
executors:
|
|
||||||
default:
|
|
||||||
parameters:
|
|
||||||
ruby-version:
|
|
||||||
type: string
|
|
||||||
docker:
|
docker:
|
||||||
- image: cimg/ruby:<< parameters.ruby-version >>
|
- image: circleci/ruby:2.6.0-stretch-node
|
||||||
environment:
|
environment: &ruby_environment
|
||||||
BUNDLE_JOBS: 3
|
BUNDLE_APP_CONFIG: ./.bundle/
|
||||||
BUNDLE_RETRY: 3
|
|
||||||
CONTINUOUS_INTEGRATION: true
|
|
||||||
DB_HOST: localhost
|
DB_HOST: localhost
|
||||||
DB_USER: root
|
DB_USER: root
|
||||||
DISABLE_SIMPLECOV: true
|
|
||||||
RAILS_ENV: test
|
RAILS_ENV: test
|
||||||
- image: cimg/postgres:14.5
|
PARALLEL_TEST_PROCESSORS: 4
|
||||||
environment:
|
ALLOW_NOPAM: true
|
||||||
POSTGRES_USER: root
|
CONTINUOUS_INTEGRATION: true
|
||||||
POSTGRES_HOST_AUTH_METHOD: trust
|
DISABLE_SIMPLECOV: true
|
||||||
- image: cimg/redis:7.0
|
PAM_ENABLED: true
|
||||||
|
PAM_DEFAULT_SERVICE: pam_test
|
||||||
|
PAM_CONTROLLED_SERVICE: pam_test_controlled
|
||||||
|
working_directory: ~/projects/mastodon/
|
||||||
|
|
||||||
commands:
|
- &attach_workspace
|
||||||
install-system-dependencies:
|
attach_workspace:
|
||||||
|
at: ~/projects/
|
||||||
|
|
||||||
|
- &persist_to_workspace
|
||||||
|
persist_to_workspace:
|
||||||
|
root: ~/projects/
|
||||||
|
paths:
|
||||||
|
- ./mastodon/
|
||||||
|
|
||||||
|
- &restore_ruby_dependencies
|
||||||
|
restore_cache:
|
||||||
|
keys:
|
||||||
|
- v2-ruby-dependencies-{{ checksum "/tmp/.ruby-version" }}-{{ checksum "Gemfile.lock" }}
|
||||||
|
- v2-ruby-dependencies-{{ checksum "/tmp/.ruby-version" }}-
|
||||||
|
- v2-ruby-dependencies-
|
||||||
|
|
||||||
|
- &install_steps
|
||||||
steps:
|
steps:
|
||||||
- run:
|
- checkout
|
||||||
name: Install system dependencies
|
- *attach_workspace
|
||||||
command: |
|
|
||||||
sudo apt-get update
|
- restore_cache:
|
||||||
sudo apt-get install -y libicu-dev libidn11-dev
|
keys:
|
||||||
install-ruby-dependencies:
|
- v1-node-dependencies-{{ checksum "yarn.lock" }}
|
||||||
parameters:
|
- v1-node-dependencies-
|
||||||
ruby-version:
|
- run: yarn install --frozen-lockfile
|
||||||
type: string
|
- save_cache:
|
||||||
steps:
|
key: v1-node-dependencies-{{ checksum "yarn.lock" }}
|
||||||
- run:
|
paths:
|
||||||
command: |
|
- ./node_modules/
|
||||||
bundle config clean 'true'
|
|
||||||
bundle config frozen 'true'
|
- *persist_to_workspace
|
||||||
bundle config without 'development production'
|
|
||||||
name: Set bundler settings
|
- &install_system_dependencies
|
||||||
- ruby/install-deps:
|
run:
|
||||||
bundler-version: '2.3.26'
|
name: Install system dependencies
|
||||||
key: ruby<< parameters.ruby-version >>-gems-v1
|
command: |
|
||||||
wait-db:
|
sudo apt-get update
|
||||||
steps:
|
sudo apt-get install -y libicu-dev libidn11-dev libprotobuf-dev protobuf-compiler
|
||||||
- run:
|
|
||||||
command: dockerize -wait tcp://localhost:5432 -wait tcp://localhost:6379 -timeout 1m
|
- &install_ruby_dependencies
|
||||||
name: Wait for PostgreSQL and Redis
|
steps:
|
||||||
|
- *attach_workspace
|
||||||
|
|
||||||
|
- *install_system_dependencies
|
||||||
|
|
||||||
|
- run: ruby -e 'puts RUBY_VERSION' | tee /tmp/.ruby-version
|
||||||
|
- *restore_ruby_dependencies
|
||||||
|
- run: bundle install --clean --jobs 16 --path ./vendor/bundle/ --retry 3 --with pam_authentication --without development production && bundle clean
|
||||||
|
- save_cache:
|
||||||
|
key: v2-ruby-dependencies-{{ checksum "/tmp/.ruby-version" }}-{{ checksum "Gemfile.lock" }}
|
||||||
|
paths:
|
||||||
|
- ./.bundle/
|
||||||
|
- ./vendor/bundle/
|
||||||
|
- persist_to_workspace:
|
||||||
|
root: ~/projects/
|
||||||
|
paths:
|
||||||
|
- ./mastodon/.bundle/
|
||||||
|
- ./mastodon/vendor/bundle/
|
||||||
|
|
||||||
|
- &test_steps
|
||||||
|
steps:
|
||||||
|
- *attach_workspace
|
||||||
|
|
||||||
|
- *install_system_dependencies
|
||||||
|
- run: sudo apt-get install -y ffmpeg
|
||||||
|
|
||||||
|
- run:
|
||||||
|
name: Prepare Tests
|
||||||
|
command: ./bin/rails parallel:create parallel:load_schema parallel:prepare
|
||||||
|
- run:
|
||||||
|
name: Run Tests
|
||||||
|
command: ./bin/retry bundle exec parallel_test ./spec/ --group-by filesize --type rspec
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
install:
|
||||||
|
<<: *defaults
|
||||||
|
<<: *install_steps
|
||||||
|
|
||||||
|
install-ruby2.6:
|
||||||
|
<<: *defaults
|
||||||
|
<<: *install_ruby_dependencies
|
||||||
|
|
||||||
|
install-ruby2.5:
|
||||||
|
<<: *defaults
|
||||||
docker:
|
docker:
|
||||||
- image: cimg/ruby:3.0-node
|
- image: circleci/ruby:2.5.3-stretch-node
|
||||||
environment:
|
environment: *ruby_environment
|
||||||
RAILS_ENV: test
|
<<: *install_ruby_dependencies
|
||||||
|
|
||||||
|
install-ruby2.4:
|
||||||
|
<<: *defaults
|
||||||
|
docker:
|
||||||
|
- image: circleci/ruby:2.4.5-stretch-node
|
||||||
|
environment: *ruby_environment
|
||||||
|
<<: *install_ruby_dependencies
|
||||||
|
|
||||||
|
build:
|
||||||
|
<<: *defaults
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
- *attach_workspace
|
||||||
- install-system-dependencies
|
- *install_system_dependencies
|
||||||
- install-ruby-dependencies:
|
- run: ./bin/rails assets:precompile
|
||||||
ruby-version: '3.0'
|
|
||||||
- node/install-packages:
|
|
||||||
cache-version: v1
|
|
||||||
pkg-manager: yarn
|
|
||||||
- run:
|
|
||||||
command: |
|
|
||||||
export NODE_OPTIONS=--openssl-legacy-provider
|
|
||||||
./bin/rails assets:precompile
|
|
||||||
name: Precompile assets
|
|
||||||
- persist_to_workspace:
|
- persist_to_workspace:
|
||||||
|
root: ~/projects/
|
||||||
paths:
|
paths:
|
||||||
- public/assets
|
- ./mastodon/public/assets
|
||||||
- public/packs-test
|
- ./mastodon/public/packs-test/
|
||||||
root: .
|
|
||||||
|
|
||||||
test:
|
test-ruby2.6:
|
||||||
parameters:
|
<<: *defaults
|
||||||
ruby-version:
|
docker:
|
||||||
type: string
|
- image: circleci/ruby:2.6.0-stretch-node
|
||||||
executor:
|
environment: *ruby_environment
|
||||||
name: default
|
- image: circleci/postgres:10.6-alpine
|
||||||
ruby-version: << parameters.ruby-version >>
|
environment:
|
||||||
environment:
|
POSTGRES_USER: root
|
||||||
ALLOW_NOPAM: true
|
- image: circleci/redis:5.0.3-alpine3.8
|
||||||
PAM_ENABLED: true
|
<<: *test_steps
|
||||||
PAM_DEFAULT_SERVICE: pam_test
|
|
||||||
PAM_CONTROLLED_SERVICE: pam_test_controlled
|
|
||||||
parallelism: 4
|
|
||||||
steps:
|
|
||||||
- checkout
|
|
||||||
- install-system-dependencies
|
|
||||||
- run:
|
|
||||||
command: sudo apt-get install -y ffmpeg imagemagick libpam-dev
|
|
||||||
name: Install additional system dependencies
|
|
||||||
- run:
|
|
||||||
command: bundle config with 'pam_authentication'
|
|
||||||
name: Enable PAM authentication
|
|
||||||
- install-ruby-dependencies:
|
|
||||||
ruby-version: << parameters.ruby-version >>
|
|
||||||
- attach_workspace:
|
|
||||||
at: .
|
|
||||||
- wait-db
|
|
||||||
- run:
|
|
||||||
command: ./bin/rails db:create db:schema:load db:seed
|
|
||||||
name: Load database schema
|
|
||||||
- ruby/rspec-test
|
|
||||||
|
|
||||||
test-migrations:
|
test-ruby2.5:
|
||||||
executor:
|
<<: *defaults
|
||||||
name: default
|
docker:
|
||||||
ruby-version: '3.0'
|
- image: circleci/ruby:2.5.3-stretch-node
|
||||||
steps:
|
environment: *ruby_environment
|
||||||
- checkout
|
- image: circleci/postgres:10.6-alpine
|
||||||
- install-system-dependencies
|
environment:
|
||||||
- install-ruby-dependencies:
|
POSTGRES_USER: root
|
||||||
ruby-version: '3.0'
|
- image: circleci/redis:4.0.12-alpine
|
||||||
- wait-db
|
<<: *test_steps
|
||||||
- run:
|
|
||||||
command: ./bin/rails db:create
|
|
||||||
name: Create database
|
|
||||||
- run:
|
|
||||||
command: ./bin/rails db:migrate VERSION=20171010025614
|
|
||||||
name: Run migrations up to v2.0.0
|
|
||||||
- run:
|
|
||||||
command: ./bin/rails tests:migrations:populate_v2
|
|
||||||
name: Populate database with test data
|
|
||||||
- run:
|
|
||||||
command: ./bin/rails db:migrate VERSION=20180514140000
|
|
||||||
name: Run migrations up to v2.4.0
|
|
||||||
- run:
|
|
||||||
command: ./bin/rails tests:migrations:populate_v2_4
|
|
||||||
name: Populate database with test data
|
|
||||||
- run:
|
|
||||||
command: ./bin/rails db:migrate VERSION=20180707154237
|
|
||||||
name: Run migrations up to v2.4.3
|
|
||||||
- run:
|
|
||||||
command: ./bin/rails tests:migrations:populate_v2_4_3
|
|
||||||
name: Populate database with test data
|
|
||||||
- run:
|
|
||||||
command: ./bin/rails db:migrate
|
|
||||||
name: Run all remaining migrations
|
|
||||||
- run:
|
|
||||||
command: ./bin/rails tests:migrations:check_database
|
|
||||||
name: Check migration result
|
|
||||||
|
|
||||||
test-two-step-migrations:
|
test-ruby2.4:
|
||||||
executor:
|
<<: *defaults
|
||||||
name: default
|
docker:
|
||||||
ruby-version: '3.0'
|
- image: circleci/ruby:2.4.5-stretch-node
|
||||||
|
environment: *ruby_environment
|
||||||
|
- image: circleci/postgres:10.6-alpine
|
||||||
|
environment:
|
||||||
|
POSTGRES_USER: root
|
||||||
|
- image: circleci/redis:4.0.12-alpine
|
||||||
|
<<: *test_steps
|
||||||
|
|
||||||
|
test-webui:
|
||||||
|
<<: *defaults
|
||||||
|
docker:
|
||||||
|
- image: circleci/node:8.15.0-stretch
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
- *attach_workspace
|
||||||
- install-system-dependencies
|
- run: ./bin/retry yarn test:jest
|
||||||
- install-ruby-dependencies:
|
|
||||||
ruby-version: '3.0'
|
check-i18n:
|
||||||
- wait-db
|
<<: *defaults
|
||||||
- run:
|
steps:
|
||||||
command: ./bin/rails db:create
|
- *attach_workspace
|
||||||
name: Create database
|
- run: bundle exec i18n-tasks check-normalized
|
||||||
- run:
|
- run: bundle exec i18n-tasks unused -l en
|
||||||
command: ./bin/rails db:migrate VERSION=20171010025614
|
- run: bundle exec i18n-tasks check-consistent-interpolations
|
||||||
name: Run migrations up to v2.0.0
|
|
||||||
- run:
|
|
||||||
command: ./bin/rails tests:migrations:populate_v2
|
|
||||||
name: Populate database with test data
|
|
||||||
- run:
|
|
||||||
command: ./bin/rails db:migrate VERSION=20180514140000
|
|
||||||
name: Run pre-deployment migrations up to v2.4.0
|
|
||||||
environment:
|
|
||||||
SKIP_POST_DEPLOYMENT_MIGRATIONS: true
|
|
||||||
- run:
|
|
||||||
command: ./bin/rails tests:migrations:populate_v2_4
|
|
||||||
name: Populate database with test data
|
|
||||||
- run:
|
|
||||||
command: ./bin/rails db:migrate VERSION=20180707154237
|
|
||||||
name: Run migrations up to v2.4.3
|
|
||||||
environment:
|
|
||||||
SKIP_POST_DEPLOYMENT_MIGRATIONS: true
|
|
||||||
- run:
|
|
||||||
command: ./bin/rails tests:migrations:populate_v2_4_3
|
|
||||||
name: Populate database with test data
|
|
||||||
- run:
|
|
||||||
command: ./bin/rails db:migrate
|
|
||||||
name: Run all remaining pre-deployment migrations
|
|
||||||
environment:
|
|
||||||
SKIP_POST_DEPLOYMENT_MIGRATIONS: true
|
|
||||||
- run:
|
|
||||||
command: ./bin/rails db:migrate
|
|
||||||
name: Run all post-deployment migrations
|
|
||||||
- run:
|
|
||||||
command: ./bin/rails tests:migrations:check_database
|
|
||||||
name: Check migration result
|
|
||||||
|
|
||||||
workflows:
|
workflows:
|
||||||
version: 2
|
version: 2
|
||||||
build-and-test:
|
build-and-test:
|
||||||
jobs:
|
jobs:
|
||||||
- build
|
- install
|
||||||
- test:
|
- install-ruby2.6:
|
||||||
matrix:
|
|
||||||
parameters:
|
|
||||||
ruby-version:
|
|
||||||
- '2.7'
|
|
||||||
- '3.0'
|
|
||||||
name: test-ruby<< matrix.ruby-version >>
|
|
||||||
requires:
|
requires:
|
||||||
- build
|
- install
|
||||||
- test-migrations:
|
- install-ruby2.5:
|
||||||
requires:
|
requires:
|
||||||
- build
|
- install
|
||||||
- test-two-step-migrations:
|
- install-ruby2.6
|
||||||
|
- install-ruby2.4:
|
||||||
requires:
|
requires:
|
||||||
- build
|
- install
|
||||||
- node/run:
|
- install-ruby2.6
|
||||||
cache-version: v1
|
- build:
|
||||||
name: test-webui
|
|
||||||
pkg-manager: yarn
|
|
||||||
requires:
|
requires:
|
||||||
|
- install-ruby2.6
|
||||||
|
- test-ruby2.6:
|
||||||
|
requires:
|
||||||
|
- install-ruby2.6
|
||||||
- build
|
- build
|
||||||
version: '16.18'
|
- test-ruby2.5:
|
||||||
yarn-run: test:jest
|
requires:
|
||||||
|
- install-ruby2.5
|
||||||
|
- build
|
||||||
|
- test-ruby2.4:
|
||||||
|
requires:
|
||||||
|
- install-ruby2.4
|
||||||
|
- build
|
||||||
|
- test-webui:
|
||||||
|
requires:
|
||||||
|
- install
|
||||||
|
- check-i18n:
|
||||||
|
requires:
|
||||||
|
- install-ruby2.6
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
version: '2'
|
version: "2"
|
||||||
checks:
|
checks:
|
||||||
argument-count:
|
argument-count:
|
||||||
enabled: false
|
enabled: false
|
||||||
|
@ -26,14 +26,13 @@ plugins:
|
||||||
bundler-audit:
|
bundler-audit:
|
||||||
enabled: true
|
enabled: true
|
||||||
eslint:
|
eslint:
|
||||||
enabled: false
|
enabled: true
|
||||||
|
channel: eslint-5
|
||||||
rubocop:
|
rubocop:
|
||||||
enabled: false
|
enabled: true
|
||||||
|
channel: rubocop-0-71
|
||||||
sass-lint:
|
sass-lint:
|
||||||
enabled: false
|
enabled: true
|
||||||
exclude_patterns:
|
exclude_patterns:
|
||||||
- spec/
|
- spec/
|
||||||
- vendor/asset/
|
- vendor/asset
|
||||||
|
|
||||||
- app/javascript/mastodon/locales/**/*.json
|
|
||||||
- config/locales/**/*.yml
|
|
||||||
|
|
|
@ -1,23 +0,0 @@
|
||||||
version = 1
|
|
||||||
|
|
||||||
test_patterns = ["app/javascript/mastodon/**/__tests__/**"]
|
|
||||||
|
|
||||||
exclude_patterns = [
|
|
||||||
"db/migrate/**",
|
|
||||||
"db/post_migrate/**"
|
|
||||||
]
|
|
||||||
|
|
||||||
[[analyzers]]
|
|
||||||
name = "ruby"
|
|
||||||
enabled = true
|
|
||||||
|
|
||||||
[[analyzers]]
|
|
||||||
name = "javascript"
|
|
||||||
enabled = true
|
|
||||||
|
|
||||||
[analyzers.meta]
|
|
||||||
environment = [
|
|
||||||
"browser",
|
|
||||||
"jest",
|
|
||||||
"nodejs"
|
|
||||||
]
|
|
10
.dependabot/config.yml
Normal file
10
.dependabot/config.yml
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
version: 1
|
||||||
|
|
||||||
|
update_configs:
|
||||||
|
- package_manager: "ruby:bundler"
|
||||||
|
directory: "/"
|
||||||
|
update_schedule: "weekly"
|
||||||
|
|
||||||
|
- package_manager: "javascript"
|
||||||
|
directory: "/"
|
||||||
|
update_schedule: "weekly"
|
|
@ -1,24 +0,0 @@
|
||||||
# [Choice] Ruby version (use -bullseye variants on local arm64/Apple Silicon): 3, 3.1, 3.0, 2, 2.7, 2.6, 3-bullseye, 3.1-bullseye, 3.0-bullseye, 2-bullseye, 2.7-bullseye, 2.6-bullseye, 3-buster, 3.1-buster, 3.0-buster, 2-buster, 2.7-buster, 2.6-buster
|
|
||||||
ARG VARIANT=3.1-bullseye
|
|
||||||
FROM mcr.microsoft.com/vscode/devcontainers/ruby:${VARIANT}
|
|
||||||
|
|
||||||
# Install Rails
|
|
||||||
# RUN gem install rails webdrivers
|
|
||||||
|
|
||||||
# Default value to allow debug server to serve content over GitHub Codespace's port forwarding service
|
|
||||||
# The value is a comma-separated list of allowed domains
|
|
||||||
ENV RAILS_DEVELOPMENT_HOSTS=".githubpreview.dev"
|
|
||||||
|
|
||||||
# [Choice] Node.js version: lts/*, 18, 16, 14
|
|
||||||
ARG NODE_VERSION="lts/*"
|
|
||||||
RUN su vscode -c "source /usr/local/share/nvm/nvm.sh && nvm install ${NODE_VERSION} 2>&1"
|
|
||||||
|
|
||||||
# [Optional] Uncomment this section to install additional OS packages.
|
|
||||||
RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
|
|
||||||
&& apt-get -y install --no-install-recommends libicu-dev libidn11-dev ffmpeg imagemagick libpam-dev
|
|
||||||
|
|
||||||
# [Optional] Uncomment this line to install additional gems.
|
|
||||||
RUN gem install foreman
|
|
||||||
|
|
||||||
# [Optional] Uncomment this line to install global node packages.
|
|
||||||
RUN su vscode -c "source /usr/local/share/nvm/nvm.sh && npm install -g yarn" 2>&1
|
|
|
@ -1,27 +0,0 @@
|
||||||
{
|
|
||||||
"name": "Mastodon",
|
|
||||||
"dockerComposeFile": "docker-compose.yml",
|
|
||||||
"service": "app",
|
|
||||||
"workspaceFolder": "/mastodon",
|
|
||||||
|
|
||||||
// Set *default* container specific settings.json values on container create.
|
|
||||||
"settings": {},
|
|
||||||
|
|
||||||
// Add the IDs of extensions you want installed when the container is created.
|
|
||||||
"extensions": [
|
|
||||||
"EditorConfig.EditorConfig",
|
|
||||||
"dbaeumer.vscode-eslint",
|
|
||||||
"rebornix.Ruby",
|
|
||||||
"webben.browserslist"
|
|
||||||
],
|
|
||||||
|
|
||||||
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
|
||||||
// This can be used to network with other containers or the host.
|
|
||||||
"forwardPorts": [3000, 4000],
|
|
||||||
|
|
||||||
// Use 'postCreateCommand' to run commands after the container is created.
|
|
||||||
"postCreateCommand": ".devcontainer/post-create.sh",
|
|
||||||
|
|
||||||
// Comment out to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root.
|
|
||||||
"remoteUser": "vscode"
|
|
||||||
}
|
|
|
@ -1,90 +0,0 @@
|
||||||
version: '3'
|
|
||||||
|
|
||||||
services:
|
|
||||||
app:
|
|
||||||
build:
|
|
||||||
context: .
|
|
||||||
dockerfile: Dockerfile
|
|
||||||
args:
|
|
||||||
# Update 'VARIANT' to pick a version of Ruby: 3, 3.1, 3.0, 2, 2.7, 2.6
|
|
||||||
# Append -bullseye or -buster to pin to an OS version.
|
|
||||||
# Use -bullseye variants on local arm64/Apple Silicon.
|
|
||||||
VARIANT: '3.0-bullseye'
|
|
||||||
# Optional Node.js version to install
|
|
||||||
NODE_VERSION: '16'
|
|
||||||
volumes:
|
|
||||||
- ..:/mastodon:cached
|
|
||||||
environment:
|
|
||||||
RAILS_ENV: development
|
|
||||||
NODE_ENV: development
|
|
||||||
|
|
||||||
REDIS_HOST: redis
|
|
||||||
REDIS_PORT: '6379'
|
|
||||||
DB_HOST: db
|
|
||||||
DB_USER: postgres
|
|
||||||
DB_PASS: postgres
|
|
||||||
DB_PORT: '5432'
|
|
||||||
ES_ENABLED: 'true'
|
|
||||||
ES_HOST: es
|
|
||||||
ES_PORT: '9200'
|
|
||||||
LIBRE_TRANSLATE_ENDPOINT: http://libretranslate:5000
|
|
||||||
# Overrides default command so things don't shut down after the process ends.
|
|
||||||
command: sleep infinity
|
|
||||||
networks:
|
|
||||||
- external_network
|
|
||||||
- internal_network
|
|
||||||
user: vscode
|
|
||||||
|
|
||||||
db:
|
|
||||||
image: postgres:14-alpine
|
|
||||||
restart: unless-stopped
|
|
||||||
volumes:
|
|
||||||
- postgres-data:/var/lib/postgresql/data
|
|
||||||
environment:
|
|
||||||
POSTGRES_USER: postgres
|
|
||||||
POSTGRES_DB: postgres
|
|
||||||
POSTGRES_PASSWORD: postgres
|
|
||||||
POSTGRES_HOST_AUTH_METHOD: trust
|
|
||||||
networks:
|
|
||||||
- internal_network
|
|
||||||
|
|
||||||
redis:
|
|
||||||
image: redis:6-alpine
|
|
||||||
restart: unless-stopped
|
|
||||||
volumes:
|
|
||||||
- redis-data:/data
|
|
||||||
networks:
|
|
||||||
- internal_network
|
|
||||||
|
|
||||||
es:
|
|
||||||
image: docker.elastic.co/elasticsearch/elasticsearch-oss:7.10.2
|
|
||||||
restart: unless-stopped
|
|
||||||
environment:
|
|
||||||
ES_JAVA_OPTS: -Xms512m -Xmx512m
|
|
||||||
cluster.name: es-mastodon
|
|
||||||
discovery.type: single-node
|
|
||||||
bootstrap.memory_lock: 'true'
|
|
||||||
volumes:
|
|
||||||
- es-data:/usr/share/elasticsearch/data
|
|
||||||
networks:
|
|
||||||
- internal_network
|
|
||||||
ulimits:
|
|
||||||
memlock:
|
|
||||||
soft: -1
|
|
||||||
hard: -1
|
|
||||||
|
|
||||||
libretranslate:
|
|
||||||
image: libretranslate/libretranslate:v1.2.9
|
|
||||||
restart: unless-stopped
|
|
||||||
networks:
|
|
||||||
- internal_network
|
|
||||||
|
|
||||||
volumes:
|
|
||||||
postgres-data:
|
|
||||||
redis-data:
|
|
||||||
es-data:
|
|
||||||
|
|
||||||
networks:
|
|
||||||
external_network:
|
|
||||||
internal_network:
|
|
||||||
internal: true
|
|
|
@ -1,21 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
set -e # Fail the whole script on first error
|
|
||||||
|
|
||||||
# Fetch Ruby gem dependencies
|
|
||||||
bundle install --path vendor/bundle --with='development test'
|
|
||||||
|
|
||||||
# Fetch Javascript dependencies
|
|
||||||
yarn install
|
|
||||||
|
|
||||||
# Make Gemfile.lock pristine again
|
|
||||||
git checkout -- Gemfile.lock
|
|
||||||
|
|
||||||
# [re]create, migrate, and seed the test database
|
|
||||||
RAILS_ENV=test ./bin/rails db:setup
|
|
||||||
|
|
||||||
# Precompile assets for development
|
|
||||||
RAILS_ENV=development ./bin/rails assets:precompile
|
|
||||||
|
|
||||||
# Precompile assets for test
|
|
||||||
RAILS_ENV=test NODE_ENV=tests ./bin/rails assets:precompile
|
|
|
@ -1,10 +1,6 @@
|
||||||
.bundle
|
.bundle
|
||||||
.env
|
.env
|
||||||
.env.*
|
.env.*
|
||||||
.git
|
|
||||||
.gitattributes
|
|
||||||
.gitignore
|
|
||||||
.github
|
|
||||||
public/system
|
public/system
|
||||||
public/assets
|
public/assets
|
||||||
public/packs
|
public/packs
|
||||||
|
@ -15,7 +11,5 @@ vendor/bundle
|
||||||
*.swp
|
*.swp
|
||||||
*~
|
*~
|
||||||
postgres
|
postgres
|
||||||
postgres14
|
|
||||||
redis
|
redis
|
||||||
elasticsearch
|
elasticsearch
|
||||||
chart
|
|
||||||
|
|
226
.env.nanobox
Normal file
226
.env.nanobox
Normal file
|
@ -0,0 +1,226 @@
|
||||||
|
# Service dependencies
|
||||||
|
# You may set REDIS_URL instead for more advanced options
|
||||||
|
REDIS_HOST=$DATA_REDIS_HOST
|
||||||
|
REDIS_PORT=6379
|
||||||
|
# REDIS_DB=0
|
||||||
|
|
||||||
|
# You may set DATABASE_URL instead for more advanced options
|
||||||
|
DB_HOST=$DATA_DB_HOST
|
||||||
|
DB_USER=$DATA_DB_USER
|
||||||
|
DB_NAME=gonano
|
||||||
|
DB_PASS=$DATA_DB_PASS
|
||||||
|
DB_PORT=5432
|
||||||
|
|
||||||
|
DATABASE_URL=postgresql://$DATA_DB_USER:$DATA_DB_PASS@$DATA_DB_HOST/gonano
|
||||||
|
|
||||||
|
# Optional ElasticSearch configuration
|
||||||
|
ES_ENABLED=true
|
||||||
|
ES_HOST=$DATA_ELASTIC_HOST
|
||||||
|
ES_PORT=9200
|
||||||
|
|
||||||
|
# Optimizations
|
||||||
|
LD_PRELOAD=/data/lib/libjemalloc.so
|
||||||
|
|
||||||
|
# ImageMagick optimizations
|
||||||
|
MAGICK_TEMPORARY_PATH=/app/tmp
|
||||||
|
MAGICK_MEMORY_LIMIT=128MiB
|
||||||
|
MAGICK_MAP_LIMIT=64MiB
|
||||||
|
MAGICK_TIME_LIMIT=15
|
||||||
|
MAGICK_AREA_LIMIT=16MP
|
||||||
|
MAGICK_WIDTH_LIMIT=8KP
|
||||||
|
MAGICK_HEIGHT_LIMIT=8KP
|
||||||
|
|
||||||
|
# Federation
|
||||||
|
# Note: Changing LOCAL_DOMAIN at a later time will cause unwanted side effects, including breaking all existing federation.
|
||||||
|
# LOCAL_DOMAIN should *NOT* contain the protocol part of the domain e.g https://example.com.
|
||||||
|
LOCAL_DOMAIN=${APP_NAME}.nanoapp.io
|
||||||
|
|
||||||
|
# Changing LOCAL_HTTPS in production is no longer supported. (Mastodon will always serve https:// links)
|
||||||
|
|
||||||
|
# Use this only if you need to run mastodon on a different domain than the one used for federation.
|
||||||
|
# You can read more about this option on https://github.com/tootsuite/documentation/blob/master/Running-Mastodon/Serving_a_different_domain.md
|
||||||
|
# DO *NOT* USE THIS UNLESS YOU KNOW *EXACTLY* WHAT YOU ARE DOING.
|
||||||
|
# WEB_DOMAIN=mastodon.example.com
|
||||||
|
|
||||||
|
# Use this if you want to have several aliases handler@example1.com
|
||||||
|
# handler@example2.com etc. for the same user. LOCAL_DOMAIN should not
|
||||||
|
# be added. Comma separated values
|
||||||
|
# ALTERNATE_DOMAINS=example1.com,example2.com
|
||||||
|
|
||||||
|
# Application secrets
|
||||||
|
# Generate each with the `rake secret` task (`nanobox run bundle exec rake secret`)
|
||||||
|
SECRET_KEY_BASE=$SECRET_KEY_BASE
|
||||||
|
OTP_SECRET=$OTP_SECRET
|
||||||
|
|
||||||
|
# VAPID keys (used for push notifications)
|
||||||
|
# You can generate the keys using the following command (first is the private key, second is the public one)
|
||||||
|
# You should only generate this once per instance. If you later decide to change it, all push subscription will
|
||||||
|
# be invalidated, requiring the users to access the website again to resubscribe.
|
||||||
|
#
|
||||||
|
# Generate with `rake mastodon:webpush:generate_vapid_key` task (`nanobox run bundle exec rake mastodon:webpush:generate_vapid_key`)
|
||||||
|
#
|
||||||
|
# For more information visit https://rossta.net/blog/using-the-web-push-api-with-vapid.html
|
||||||
|
VAPID_PRIVATE_KEY=$VAPID_PRIVATE_KEY
|
||||||
|
VAPID_PUBLIC_KEY=$VAPID_PUBLIC_KEY
|
||||||
|
|
||||||
|
# Registrations
|
||||||
|
# Single user mode will disable registrations and redirect frontpage to the first profile
|
||||||
|
# SINGLE_USER_MODE=true
|
||||||
|
# Prevent registrations with following e-mail domains
|
||||||
|
# EMAIL_DOMAIN_BLACKLIST=example1.com|example2.de|etc
|
||||||
|
# Only allow registrations with the following e-mail domains
|
||||||
|
# EMAIL_DOMAIN_WHITELIST=example1.com|example2.de|etc
|
||||||
|
|
||||||
|
# Optionally change default language
|
||||||
|
# DEFAULT_LOCALE=de
|
||||||
|
|
||||||
|
# E-mail configuration
|
||||||
|
# Note: Mailgun and SparkPost (https://sparkpo.st/smtp) each have good free tiers
|
||||||
|
# If you want to use an SMTP server without authentication (e.g local Postfix relay)
|
||||||
|
# then set SMTP_AUTH_METHOD and SMTP_OPENSSL_VERIFY_MODE to 'none' and
|
||||||
|
# *comment* SMTP_LOGIN and SMTP_PASSWORD (leaving them blank is not enough).
|
||||||
|
SMTP_SERVER=$SMTP_SERVER
|
||||||
|
SMTP_PORT=587
|
||||||
|
SMTP_LOGIN=$SMTP_LOGIN
|
||||||
|
SMTP_PASSWORD=$SMTP_PASSWORD
|
||||||
|
SMTP_FROM_ADDRESS=notifications@${APP_NAME}.nanoapp.io
|
||||||
|
#SMTP_DOMAIN= # defaults to LOCAL_DOMAIN
|
||||||
|
#SMTP_DELIVERY_METHOD=smtp # delivery method can also be sendmail
|
||||||
|
#SMTP_AUTH_METHOD=plain
|
||||||
|
#SMTP_CA_FILE=/etc/ssl/certs/ca-certificates.crt
|
||||||
|
#SMTP_OPENSSL_VERIFY_MODE=peer
|
||||||
|
#SMTP_ENABLE_STARTTLS_AUTO=true
|
||||||
|
#SMTP_TLS=true
|
||||||
|
|
||||||
|
# Optional user upload path and URL (images, avatars). Default is :rails_root/public/system. If you set this variable, you are responsible for making your HTTP server (eg. nginx) serve these files.
|
||||||
|
# PAPERCLIP_ROOT_PATH=/var/lib/mastodon/public-system
|
||||||
|
# PAPERCLIP_ROOT_URL=/system
|
||||||
|
|
||||||
|
# Optional asset host for multi-server setups
|
||||||
|
# CDN_HOST=https://assets.example.com
|
||||||
|
|
||||||
|
# S3 (optional)
|
||||||
|
# S3_ENABLED=true
|
||||||
|
# S3_BUCKET=
|
||||||
|
# AWS_ACCESS_KEY_ID=
|
||||||
|
# AWS_SECRET_ACCESS_KEY=
|
||||||
|
# S3_REGION=
|
||||||
|
# S3_PROTOCOL=http
|
||||||
|
# S3_HOSTNAME=192.168.1.123:9000
|
||||||
|
|
||||||
|
# S3 (Minio Config (optional) Please check Minio instance for details)
|
||||||
|
# S3_ENABLED=true
|
||||||
|
# S3_BUCKET=
|
||||||
|
# AWS_ACCESS_KEY_ID=
|
||||||
|
# AWS_SECRET_ACCESS_KEY=
|
||||||
|
# S3_REGION=
|
||||||
|
# S3_PROTOCOL=https
|
||||||
|
# S3_HOSTNAME=
|
||||||
|
# S3_ENDPOINT=
|
||||||
|
# S3_SIGNATURE_VERSION=
|
||||||
|
|
||||||
|
# Swift (optional)
|
||||||
|
# SWIFT_ENABLED=true
|
||||||
|
# SWIFT_USERNAME=
|
||||||
|
# For Keystone V3, the value for SWIFT_TENANT should be the project name
|
||||||
|
# SWIFT_TENANT=
|
||||||
|
# SWIFT_PASSWORD=
|
||||||
|
# Keystone V2 and V3 URLs are supported. Use a V3 URL if possible to avoid
|
||||||
|
# issues with token rate-limiting during high load.
|
||||||
|
# SWIFT_AUTH_URL=
|
||||||
|
# SWIFT_CONTAINER=
|
||||||
|
# SWIFT_OBJECT_URL=
|
||||||
|
# SWIFT_REGION=
|
||||||
|
# Defaults to 'default'
|
||||||
|
# SWIFT_DOMAIN_NAME=
|
||||||
|
# Defaults to 60 seconds. Set to 0 to disable
|
||||||
|
# SWIFT_CACHE_TTL=
|
||||||
|
|
||||||
|
# Optional alias for S3 (e.g. to serve files on a custom domain, possibly using Cloudfront or Cloudflare)
|
||||||
|
# S3_ALIAS_HOST=
|
||||||
|
|
||||||
|
# Streaming API integration
|
||||||
|
# STREAMING_API_BASE_URL=
|
||||||
|
|
||||||
|
# Advanced settings
|
||||||
|
# If you need to use pgBouncer, you need to disable prepared statements:
|
||||||
|
# PREPARED_STATEMENTS=false
|
||||||
|
|
||||||
|
# Cluster number setting for streaming API server.
|
||||||
|
# If you comment out following line, cluster number will be `numOfCpuCores - 1`.
|
||||||
|
# STREAMING_CLUSTER_NUM=1
|
||||||
|
|
||||||
|
# Docker mastodon user
|
||||||
|
# If you use Docker, you may want to assign UID/GID manually.
|
||||||
|
# UID=1000
|
||||||
|
# GID=1000
|
||||||
|
|
||||||
|
# LDAP authentication (optional)
|
||||||
|
# LDAP_ENABLED=true
|
||||||
|
# LDAP_HOST=localhost
|
||||||
|
# LDAP_PORT=389
|
||||||
|
# LDAP_METHOD=simple_tls
|
||||||
|
# LDAP_BASE=
|
||||||
|
# LDAP_BIND_DN=
|
||||||
|
# LDAP_PASSWORD=
|
||||||
|
# LDAP_UID=cn
|
||||||
|
|
||||||
|
# PAM authentication (optional)
|
||||||
|
# PAM authentication uses for the email generation the "email" pam variable
|
||||||
|
# and optional as fallback PAM_DEFAULT_SUFFIX
|
||||||
|
# The pam environment variable "email" is provided by:
|
||||||
|
# https://github.com/devkral/pam_email_extractor
|
||||||
|
# PAM_ENABLED=true
|
||||||
|
# Fallback Suffix for email address generation (nil by default)
|
||||||
|
# PAM_DEFAULT_SUFFIX=pam
|
||||||
|
# Name of the pam service (pam "auth" section is evaluated)
|
||||||
|
# PAM_DEFAULT_SERVICE=rpam
|
||||||
|
# Name of the pam service used for checking if an user can register (pam "account" section is evaluated) (nil (disabled) by default)
|
||||||
|
# PAM_CONTROLLED_SERVICE=rpam
|
||||||
|
|
||||||
|
# Global OAuth settings (optional) :
|
||||||
|
# If you have only one strategy, you may want to enable this
|
||||||
|
# OAUTH_REDIRECT_AT_SIGN_IN=true
|
||||||
|
|
||||||
|
# Optional CAS authentication (cf. omniauth-cas) :
|
||||||
|
# CAS_ENABLED=true
|
||||||
|
# CAS_URL=https://sso.myserver.com/
|
||||||
|
# CAS_HOST=sso.myserver.com/
|
||||||
|
# CAS_PORT=443
|
||||||
|
# CAS_SSL=true
|
||||||
|
# CAS_VALIDATE_URL=
|
||||||
|
# CAS_CALLBACK_URL=
|
||||||
|
# CAS_LOGOUT_URL=
|
||||||
|
# CAS_LOGIN_URL=
|
||||||
|
# CAS_UID_FIELD='user'
|
||||||
|
# CAS_CA_PATH=
|
||||||
|
# CAS_DISABLE_SSL_VERIFICATION=false
|
||||||
|
# CAS_UID_KEY='user'
|
||||||
|
# CAS_NAME_KEY='name'
|
||||||
|
# CAS_EMAIL_KEY='email'
|
||||||
|
# CAS_NICKNAME_KEY='nickname'
|
||||||
|
# CAS_FIRST_NAME_KEY='firstname'
|
||||||
|
# CAS_LAST_NAME_KEY='lastname'
|
||||||
|
# CAS_LOCATION_KEY='location'
|
||||||
|
# CAS_IMAGE_KEY='image'
|
||||||
|
# CAS_PHONE_KEY='phone'
|
||||||
|
|
||||||
|
# Optional SAML authentication (cf. omniauth-saml)
|
||||||
|
# SAML_ENABLED=true
|
||||||
|
# SAML_ACS_URL=
|
||||||
|
# SAML_ISSUER=http://localhost:3000/auth/auth/saml/callback
|
||||||
|
# SAML_IDP_SSO_TARGET_URL=https://idp.testshib.org/idp/profile/SAML2/Redirect/SSO
|
||||||
|
# SAML_IDP_CERT=
|
||||||
|
# SAML_IDP_CERT_FINGERPRINT=
|
||||||
|
# SAML_NAME_IDENTIFIER_FORMAT=
|
||||||
|
# SAML_CERT=
|
||||||
|
# SAML_PRIVATE_KEY=
|
||||||
|
# SAML_SECURITY_WANT_ASSERTION_SIGNED=true
|
||||||
|
# SAML_SECURITY_WANT_ASSERTION_ENCRYPTED=true
|
||||||
|
# SAML_SECURITY_ASSUME_EMAIL_IS_VERIFIED=true
|
||||||
|
# SAML_ATTRIBUTES_STATEMENTS_UID="urn:oid:0.9.2342.19200300.100.1.1"
|
||||||
|
# SAML_ATTRIBUTES_STATEMENTS_EMAIL="urn:oid:1.3.6.1.4.1.5923.1.1.1.6"
|
||||||
|
# SAML_ATTRIBUTES_STATEMENTS_FULL_NAME="urn:oid:2.5.4.42"
|
||||||
|
# SAML_UID_ATTRIBUTE="urn:oid:0.9.2342.19200300.100.1.1"
|
||||||
|
# SAML_ATTRIBUTES_STATEMENTS_VERIFIED=
|
||||||
|
# SAML_ATTRIBUTES_STATEMENTS_VERIFIED_EMAIL=
|
|
@ -1,77 +1,233 @@
|
||||||
# This is a sample configuration file. You can generate your configuration
|
# Service dependencies
|
||||||
# with the `rake mastodon:setup` interactive setup wizard, but to customize
|
# You may set REDIS_URL instead for more advanced options
|
||||||
# your setup even further, you'll need to edit it manually. This sample does
|
# You may also set REDIS_NAMESPACE to share Redis between multiple Mastodon servers
|
||||||
# not demonstrate all available configuration options. Please look at
|
REDIS_HOST=redis
|
||||||
# https://docs.joinmastodon.org/admin/config/ for the full documentation.
|
|
||||||
|
|
||||||
# Note that this file accepts slightly different syntax depending on whether
|
|
||||||
# you are using `docker-compose` or not. In particular, if you use
|
|
||||||
# `docker-compose`, the value of each declared variable will be taken verbatim,
|
|
||||||
# including surrounding quotes.
|
|
||||||
# See: https://github.com/mastodon/mastodon/issues/16895
|
|
||||||
|
|
||||||
# Federation
|
|
||||||
# ----------
|
|
||||||
# This identifies your server and cannot be changed safely later
|
|
||||||
# ----------
|
|
||||||
LOCAL_DOMAIN=example.com
|
|
||||||
|
|
||||||
# Redis
|
|
||||||
# -----
|
|
||||||
REDIS_HOST=localhost
|
|
||||||
REDIS_PORT=6379
|
REDIS_PORT=6379
|
||||||
|
# You may set DATABASE_URL instead for more advanced options
|
||||||
# PostgreSQL
|
DB_HOST=db
|
||||||
# ----------
|
DB_USER=postgres
|
||||||
DB_HOST=/var/run/postgresql
|
DB_NAME=postgres
|
||||||
DB_USER=mastodon
|
|
||||||
DB_NAME=mastodon_production
|
|
||||||
DB_PASS=
|
DB_PASS=
|
||||||
DB_PORT=5432
|
DB_PORT=5432
|
||||||
|
# Optional ElasticSearch configuration
|
||||||
|
# You may also set ES_PREFIX to share the same cluster between multiple Mastodon servers (falls back to REDIS_NAMESPACE if not set)
|
||||||
|
# ES_ENABLED=true
|
||||||
|
# ES_HOST=es
|
||||||
|
# ES_PORT=9200
|
||||||
|
|
||||||
# Elasticsearch (optional)
|
# Federation
|
||||||
# ------------------------
|
# Note: Changing LOCAL_DOMAIN at a later time will cause unwanted side effects, including breaking all existing federation.
|
||||||
ES_ENABLED=true
|
# LOCAL_DOMAIN should *NOT* contain the protocol part of the domain e.g https://example.com.
|
||||||
ES_HOST=localhost
|
LOCAL_DOMAIN=example.com
|
||||||
ES_PORT=9200
|
|
||||||
# Authentication for ES (optional)
|
|
||||||
ES_USER=elastic
|
|
||||||
ES_PASS=password
|
|
||||||
|
|
||||||
# Secrets
|
# Changing LOCAL_HTTPS in production is no longer supported. (Mastodon will always serve https:// links)
|
||||||
# -------
|
|
||||||
# Make sure to use `rake secret` to generate secrets
|
# Use this only if you need to run mastodon on a different domain than the one used for federation.
|
||||||
# -------
|
# You can read more about this option on https://github.com/tootsuite/documentation/blob/master/Running-Mastodon/Serving_a_different_domain.md
|
||||||
|
# DO *NOT* USE THIS UNLESS YOU KNOW *EXACTLY* WHAT YOU ARE DOING.
|
||||||
|
# WEB_DOMAIN=mastodon.example.com
|
||||||
|
|
||||||
|
# Use this if you want to have several aliases handler@example1.com
|
||||||
|
# handler@example2.com etc. for the same user. LOCAL_DOMAIN should not
|
||||||
|
# be added. Comma separated values
|
||||||
|
# ALTERNATE_DOMAINS=example1.com,example2.com
|
||||||
|
|
||||||
|
# Application secrets
|
||||||
|
# Generate each with the `RAILS_ENV=production bundle exec rake secret` task (`docker-compose run --rm web rake secret` if you use docker compose)
|
||||||
SECRET_KEY_BASE=
|
SECRET_KEY_BASE=
|
||||||
OTP_SECRET=
|
OTP_SECRET=
|
||||||
|
|
||||||
# Web Push
|
# VAPID keys (used for push notifications
|
||||||
# --------
|
# You can generate the keys using the following command (first is the private key, second is the public one)
|
||||||
# Generate with `rake mastodon:webpush:generate_vapid_key`
|
# You should only generate this once per instance. If you later decide to change it, all push subscription will
|
||||||
# --------
|
# be invalidated, requiring the users to access the website again to resubscribe.
|
||||||
|
#
|
||||||
|
# Generate with `RAILS_ENV=production bundle exec rake mastodon:webpush:generate_vapid_key` task (`docker-compose run --rm web rake mastodon:webpush:generate_vapid_key` if you use docker compose)
|
||||||
|
#
|
||||||
|
# For more information visit https://rossta.net/blog/using-the-web-push-api-with-vapid.html
|
||||||
VAPID_PRIVATE_KEY=
|
VAPID_PRIVATE_KEY=
|
||||||
VAPID_PUBLIC_KEY=
|
VAPID_PUBLIC_KEY=
|
||||||
|
|
||||||
# Sending mail
|
# Registrations
|
||||||
# ------------
|
# Single user mode will disable registrations and redirect frontpage to the first profile
|
||||||
SMTP_SERVER=
|
# SINGLE_USER_MODE=true
|
||||||
|
# Prevent registrations with following e-mail domains
|
||||||
|
# EMAIL_DOMAIN_BLACKLIST=example1.com|example2.de|etc
|
||||||
|
# Only allow registrations with the following e-mail domains
|
||||||
|
# EMAIL_DOMAIN_WHITELIST=example1.com|example2.de|etc
|
||||||
|
|
||||||
|
# Optionally change default language
|
||||||
|
# DEFAULT_LOCALE=de
|
||||||
|
|
||||||
|
# E-mail configuration
|
||||||
|
# Note: Mailgun and SparkPost (https://sparkpo.st/smtp) each have good free tiers
|
||||||
|
# If you want to use an SMTP server without authentication (e.g local Postfix relay)
|
||||||
|
# then set SMTP_AUTH_METHOD and SMTP_OPENSSL_VERIFY_MODE to 'none' and
|
||||||
|
# *comment* SMTP_LOGIN and SMTP_PASSWORD (leaving them blank is not enough).
|
||||||
|
SMTP_SERVER=smtp.mailgun.org
|
||||||
SMTP_PORT=587
|
SMTP_PORT=587
|
||||||
SMTP_LOGIN=
|
SMTP_LOGIN=
|
||||||
SMTP_PASSWORD=
|
SMTP_PASSWORD=
|
||||||
SMTP_FROM_ADDRESS=notifications@example.com
|
SMTP_FROM_ADDRESS=notifications@example.com
|
||||||
|
#SMTP_DOMAIN= # defaults to LOCAL_DOMAIN
|
||||||
|
#SMTP_DELIVERY_METHOD=smtp # delivery method can also be sendmail
|
||||||
|
#SMTP_AUTH_METHOD=plain
|
||||||
|
#SMTP_CA_FILE=/etc/ssl/certs/ca-certificates.crt
|
||||||
|
#SMTP_OPENSSL_VERIFY_MODE=peer
|
||||||
|
#SMTP_ENABLE_STARTTLS_AUTO=true
|
||||||
|
#SMTP_TLS=true
|
||||||
|
|
||||||
# File storage (optional)
|
# Optional user upload path and URL (images, avatars). Default is :rails_root/public/system. If you set this variable, you are responsible for making your HTTP server (eg. nginx) serve these files.
|
||||||
# -----------------------
|
# PAPERCLIP_ROOT_PATH=/var/lib/mastodon/public-system
|
||||||
S3_ENABLED=true
|
# PAPERCLIP_ROOT_URL=/system
|
||||||
S3_BUCKET=files.example.com
|
|
||||||
AWS_ACCESS_KEY_ID=
|
|
||||||
AWS_SECRET_ACCESS_KEY=
|
|
||||||
S3_ALIAS_HOST=files.example.com
|
|
||||||
|
|
||||||
# IP and session retention
|
# Optional asset host for multi-server setups
|
||||||
# -----------------------
|
# The asset host must allow cross origin request from WEB_DOMAIN or LOCAL_DOMAIN
|
||||||
# Make sure to modify the scheduling of ip_cleanup_scheduler in config/sidekiq.yml
|
# if WEB_DOMAIN is not set. For example, the server may have the
|
||||||
# to be less than daily if you lower IP_RETENTION_PERIOD below two days (172800).
|
# following header field:
|
||||||
# -----------------------
|
# Access-Control-Allow-Origin: https://example.com/
|
||||||
IP_RETENTION_PERIOD=31556952
|
# CDN_HOST=https://assets.example.com
|
||||||
SESSION_RETENTION_PERIOD=31556952
|
|
||||||
|
# S3 (optional)
|
||||||
|
# The attachment host must allow cross origin request from WEB_DOMAIN or
|
||||||
|
# LOCAL_DOMAIN if WEB_DOMAIN is not set. For example, the server may have the
|
||||||
|
# following header field:
|
||||||
|
# Access-Control-Allow-Origin: https://192.168.1.123:9000/
|
||||||
|
# S3_ENABLED=true
|
||||||
|
# S3_BUCKET=
|
||||||
|
# AWS_ACCESS_KEY_ID=
|
||||||
|
# AWS_SECRET_ACCESS_KEY=
|
||||||
|
# S3_REGION=
|
||||||
|
# S3_PROTOCOL=http
|
||||||
|
# S3_HOSTNAME=192.168.1.123:9000
|
||||||
|
|
||||||
|
# S3 (Minio Config (optional) Please check Minio instance for details)
|
||||||
|
# The attachment host must allow cross origin request - see the description
|
||||||
|
# above.
|
||||||
|
# S3_ENABLED=true
|
||||||
|
# S3_BUCKET=
|
||||||
|
# AWS_ACCESS_KEY_ID=
|
||||||
|
# AWS_SECRET_ACCESS_KEY=
|
||||||
|
# S3_REGION=
|
||||||
|
# S3_PROTOCOL=https
|
||||||
|
# S3_HOSTNAME=
|
||||||
|
# S3_ENDPOINT=
|
||||||
|
# S3_SIGNATURE_VERSION=
|
||||||
|
|
||||||
|
# Swift (optional)
|
||||||
|
# The attachment host must allow cross origin request - see the description
|
||||||
|
# above.
|
||||||
|
# SWIFT_ENABLED=true
|
||||||
|
# SWIFT_USERNAME=
|
||||||
|
# For Keystone V3, the value for SWIFT_TENANT should be the project name
|
||||||
|
# SWIFT_TENANT=
|
||||||
|
# SWIFT_PASSWORD=
|
||||||
|
# Some OpenStack V3 providers require PROJECT_ID (optional)
|
||||||
|
# SWIFT_PROJECT_ID=
|
||||||
|
# Keystone V2 and V3 URLs are supported. Use a V3 URL if possible to avoid
|
||||||
|
# issues with token rate-limiting during high load.
|
||||||
|
# SWIFT_AUTH_URL=
|
||||||
|
# SWIFT_CONTAINER=
|
||||||
|
# SWIFT_OBJECT_URL=
|
||||||
|
# SWIFT_REGION=
|
||||||
|
# Defaults to 'default'
|
||||||
|
# SWIFT_DOMAIN_NAME=
|
||||||
|
# Defaults to 60 seconds. Set to 0 to disable
|
||||||
|
# SWIFT_CACHE_TTL=
|
||||||
|
|
||||||
|
# Optional alias for S3 (e.g. to serve files on a custom domain, possibly using Cloudfront or Cloudflare)
|
||||||
|
# S3_ALIAS_HOST=
|
||||||
|
|
||||||
|
# Streaming API integration
|
||||||
|
# STREAMING_API_BASE_URL=
|
||||||
|
|
||||||
|
# Advanced settings
|
||||||
|
# If you need to use pgBouncer, you need to disable prepared statements:
|
||||||
|
# PREPARED_STATEMENTS=false
|
||||||
|
|
||||||
|
# Cluster number setting for streaming API server.
|
||||||
|
# If you comment out following line, cluster number will be `numOfCpuCores - 1`.
|
||||||
|
STREAMING_CLUSTER_NUM=1
|
||||||
|
|
||||||
|
# Docker mastodon user
|
||||||
|
# If you use Docker, you may want to assign UID/GID manually.
|
||||||
|
# UID=1000
|
||||||
|
# GID=1000
|
||||||
|
|
||||||
|
# LDAP authentication (optional)
|
||||||
|
# LDAP_ENABLED=true
|
||||||
|
# LDAP_HOST=localhost
|
||||||
|
# LDAP_PORT=389
|
||||||
|
# LDAP_METHOD=simple_tls
|
||||||
|
# LDAP_BASE=
|
||||||
|
# LDAP_BIND_DN=
|
||||||
|
# LDAP_PASSWORD=
|
||||||
|
# LDAP_UID=cn
|
||||||
|
# LDAP_SEARCH_FILTER="%{uid}=%{email}"
|
||||||
|
|
||||||
|
# PAM authentication (optional)
|
||||||
|
# PAM authentication uses for the email generation the "email" pam variable
|
||||||
|
# and optional as fallback PAM_DEFAULT_SUFFIX
|
||||||
|
# The pam environment variable "email" is provided by:
|
||||||
|
# https://github.com/devkral/pam_email_extractor
|
||||||
|
# PAM_ENABLED=true
|
||||||
|
# Fallback email domain for email address generation (LOCAL_DOMAIN by default)
|
||||||
|
# PAM_EMAIL_DOMAIN=example.com
|
||||||
|
# Name of the pam service (pam "auth" section is evaluated)
|
||||||
|
# PAM_DEFAULT_SERVICE=rpam
|
||||||
|
# Name of the pam service used for checking if an user can register (pam "account" section is evaluated) (nil (disabled) by default)
|
||||||
|
# PAM_CONTROLLED_SERVICE=rpam
|
||||||
|
|
||||||
|
# Global OAuth settings (optional) :
|
||||||
|
# If you have only one strategy, you may want to enable this
|
||||||
|
# OAUTH_REDIRECT_AT_SIGN_IN=true
|
||||||
|
|
||||||
|
# Optional CAS authentication (cf. omniauth-cas) :
|
||||||
|
# CAS_ENABLED=true
|
||||||
|
# CAS_URL=https://sso.myserver.com/
|
||||||
|
# CAS_HOST=sso.myserver.com/
|
||||||
|
# CAS_PORT=443
|
||||||
|
# CAS_SSL=true
|
||||||
|
# CAS_VALIDATE_URL=
|
||||||
|
# CAS_CALLBACK_URL=
|
||||||
|
# CAS_LOGOUT_URL=
|
||||||
|
# CAS_LOGIN_URL=
|
||||||
|
# CAS_UID_FIELD='user'
|
||||||
|
# CAS_CA_PATH=
|
||||||
|
# CAS_DISABLE_SSL_VERIFICATION=false
|
||||||
|
# CAS_UID_KEY='user'
|
||||||
|
# CAS_NAME_KEY='name'
|
||||||
|
# CAS_EMAIL_KEY='email'
|
||||||
|
# CAS_NICKNAME_KEY='nickname'
|
||||||
|
# CAS_FIRST_NAME_KEY='firstname'
|
||||||
|
# CAS_LAST_NAME_KEY='lastname'
|
||||||
|
# CAS_LOCATION_KEY='location'
|
||||||
|
# CAS_IMAGE_KEY='image'
|
||||||
|
# CAS_PHONE_KEY='phone'
|
||||||
|
|
||||||
|
# Optional SAML authentication (cf. omniauth-saml)
|
||||||
|
# SAML_ENABLED=true
|
||||||
|
# SAML_ACS_URL=
|
||||||
|
# SAML_ISSUER=http://localhost:3000/auth/auth/saml/callback
|
||||||
|
# SAML_IDP_SSO_TARGET_URL=https://idp.testshib.org/idp/profile/SAML2/Redirect/SSO
|
||||||
|
# SAML_IDP_CERT=
|
||||||
|
# SAML_IDP_CERT_FINGERPRINT=
|
||||||
|
# SAML_NAME_IDENTIFIER_FORMAT=
|
||||||
|
# SAML_CERT=
|
||||||
|
# SAML_PRIVATE_KEY=
|
||||||
|
# SAML_SECURITY_WANT_ASSERTION_SIGNED=true
|
||||||
|
# SAML_SECURITY_WANT_ASSERTION_ENCRYPTED=true
|
||||||
|
# SAML_SECURITY_ASSUME_EMAIL_IS_VERIFIED=true
|
||||||
|
# SAML_ATTRIBUTES_STATEMENTS_UID="urn:oid:0.9.2342.19200300.100.1.1"
|
||||||
|
# SAML_ATTRIBUTES_STATEMENTS_EMAIL="urn:oid:1.3.6.1.4.1.5923.1.1.1.6"
|
||||||
|
# SAML_ATTRIBUTES_STATEMENTS_FULL_NAME="urn:oid:2.16.840.1.113730.3.1.241"
|
||||||
|
# SAML_ATTRIBUTES_STATEMENTS_FIRST_NAME="urn:oid:2.5.4.42"
|
||||||
|
# SAML_ATTRIBUTES_STATEMENTS_LAST_NAME="urn:oid:2.5.4.4"
|
||||||
|
# SAML_UID_ATTRIBUTE="urn:oid:0.9.2342.19200300.100.1.1"
|
||||||
|
# SAML_ATTRIBUTES_STATEMENTS_VERIFIED=
|
||||||
|
# SAML_ATTRIBUTES_STATEMENTS_VERIFIED_EMAIL=
|
||||||
|
|
||||||
|
# Use HTTP proxy for outgoing request (optional)
|
||||||
|
# http_proxy=http://gateway.local:8118
|
||||||
|
# Access control for hidden service.
|
||||||
|
# ALLOW_ACCESS_TO_HIDDEN_SERVICE=true
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Node.js
|
# Node.js
|
||||||
NODE_ENV=tests
|
NODE_ENV=test
|
||||||
# Federation
|
# Federation
|
||||||
LOCAL_DOMAIN=cb6e6126.ngrok.io
|
LOCAL_DOMAIN=cb6e6126.ngrok.io
|
||||||
LOCAL_HTTPS=true
|
LOCAL_HTTPS=true
|
||||||
|
|
|
@ -1,4 +1,2 @@
|
||||||
VAGRANT=true
|
VAGRANT=true
|
||||||
LOCAL_DOMAIN=mastodon.local
|
LOCAL_DOMAIN=mastodon.local
|
||||||
BIND=0.0.0.0
|
|
||||||
DB_HOST=/var/run/postgresql/
|
|
||||||
|
|
32
.eslintrc.js
32
.eslintrc.js
|
@ -1,10 +1,6 @@
|
||||||
module.exports = {
|
module.exports = {
|
||||||
root: true,
|
root: true,
|
||||||
|
|
||||||
extends: [
|
|
||||||
'eslint:recommended',
|
|
||||||
],
|
|
||||||
|
|
||||||
env: {
|
env: {
|
||||||
browser: true,
|
browser: true,
|
||||||
node: true,
|
node: true,
|
||||||
|
@ -16,7 +12,7 @@ module.exports = {
|
||||||
ATTACHMENT_HOST: false,
|
ATTACHMENT_HOST: false,
|
||||||
},
|
},
|
||||||
|
|
||||||
parser: '@babel/eslint-parser',
|
parser: 'babel-eslint',
|
||||||
|
|
||||||
plugins: [
|
plugins: [
|
||||||
'react',
|
'react',
|
||||||
|
@ -31,7 +27,7 @@ module.exports = {
|
||||||
experimentalObjectRestSpread: true,
|
experimentalObjectRestSpread: true,
|
||||||
jsx: true,
|
jsx: true,
|
||||||
},
|
},
|
||||||
ecmaVersion: 2021,
|
ecmaVersion: 2018,
|
||||||
},
|
},
|
||||||
|
|
||||||
settings: {
|
settings: {
|
||||||
|
@ -68,8 +64,8 @@ module.exports = {
|
||||||
eqeqeq: 'error',
|
eqeqeq: 'error',
|
||||||
indent: ['warn', 2],
|
indent: ['warn', 2],
|
||||||
'jsx-quotes': ['error', 'prefer-single'],
|
'jsx-quotes': ['error', 'prefer-single'],
|
||||||
'no-case-declarations': 'off',
|
|
||||||
'no-catch-shadow': 'error',
|
'no-catch-shadow': 'error',
|
||||||
|
'no-cond-assign': 'error',
|
||||||
'no-console': [
|
'no-console': [
|
||||||
'warn',
|
'warn',
|
||||||
{
|
{
|
||||||
|
@ -79,16 +75,13 @@ module.exports = {
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
'no-empty': 'off',
|
'no-fallthrough': 'error',
|
||||||
|
'no-irregular-whitespace': 'error',
|
||||||
|
'no-mixed-spaces-and-tabs': 'warn',
|
||||||
'no-nested-ternary': 'warn',
|
'no-nested-ternary': 'warn',
|
||||||
'no-prototype-builtins': 'off',
|
|
||||||
'no-restricted-properties': [
|
|
||||||
'error',
|
|
||||||
{ property: 'substring', message: 'Use .slice instead of .substring.' },
|
|
||||||
{ property: 'substr', message: 'Use .slice instead of .substr.' },
|
|
||||||
],
|
|
||||||
'no-self-assign': 'off',
|
|
||||||
'no-trailing-spaces': 'warn',
|
'no-trailing-spaces': 'warn',
|
||||||
|
'no-undef': 'error',
|
||||||
|
'no-unreachable': 'error',
|
||||||
'no-unused-expressions': 'error',
|
'no-unused-expressions': 'error',
|
||||||
'no-unused-vars': [
|
'no-unused-vars': [
|
||||||
'error',
|
'error',
|
||||||
|
@ -98,7 +91,6 @@ module.exports = {
|
||||||
ignoreRestSiblings: true,
|
ignoreRestSiblings: true,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
'no-useless-escape': 'off',
|
|
||||||
'object-curly-spacing': ['error', 'always'],
|
'object-curly-spacing': ['error', 'always'],
|
||||||
'padded-blocks': [
|
'padded-blocks': [
|
||||||
'error',
|
'error',
|
||||||
|
@ -108,6 +100,7 @@ module.exports = {
|
||||||
],
|
],
|
||||||
quotes: ['error', 'single'],
|
quotes: ['error', 'single'],
|
||||||
semi: 'error',
|
semi: 'error',
|
||||||
|
strict: 'off',
|
||||||
'valid-typeof': 'error',
|
'valid-typeof': 'error',
|
||||||
|
|
||||||
'react/jsx-boolean-value': 'error',
|
'react/jsx-boolean-value': 'error',
|
||||||
|
@ -206,11 +199,6 @@ module.exports = {
|
||||||
'import/no-unresolved': 'error',
|
'import/no-unresolved': 'error',
|
||||||
'import/no-webpack-loader-syntax': 'error',
|
'import/no-webpack-loader-syntax': 'error',
|
||||||
|
|
||||||
'promise/catch-or-return': [
|
'promise/catch-or-return': 'error',
|
||||||
'error',
|
|
||||||
{
|
|
||||||
allowFinally: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
32
.github/CODEOWNERS
vendored
Normal file
32
.github/CODEOWNERS
vendored
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
# CODEOWNERS for tootsuite/mastodon
|
||||||
|
|
||||||
|
# Translators
|
||||||
|
# To add translator, copy these lines, replace `fr` with appropriate language code and replace `@żelipapą` with user's GitHub nickname preceded by `@` sign or e-mail address.
|
||||||
|
# /app/javascript/mastodon/locales/fr.json @żelipapą
|
||||||
|
# /app/views/user_mailer/*.fr.html.erb @żelipapą
|
||||||
|
# /app/views/user_mailer/*.fr.text.erb @żelipapą
|
||||||
|
# /config/locales/*.fr.yml @żelipapą
|
||||||
|
# /config/locales/fr.yml @żelipapą
|
||||||
|
|
||||||
|
# Polish
|
||||||
|
/app/javascript/mastodon/locales/pl.json @m4sk1n
|
||||||
|
/app/views/user_mailer/*.pl.html.erb @m4sk1n
|
||||||
|
/app/views/user_mailer/*.pl.text.erb @m4sk1n
|
||||||
|
/config/locales/*.pl.yml @m4sk1n
|
||||||
|
/config/locales/pl.yml @m4sk1n
|
||||||
|
|
||||||
|
# French
|
||||||
|
/app/javascript/mastodon/locales/fr.json @aldarone
|
||||||
|
/app/javascript/mastodon/locales/whitelist_fr.json @aldarone
|
||||||
|
/app/views/user_mailer/*.fr.html.erb @aldarone
|
||||||
|
/app/views/user_mailer/*.fr.text.erb @aldarone
|
||||||
|
/config/locales/*.fr.yml @aldarone
|
||||||
|
/config/locales/fr.yml @aldarone
|
||||||
|
|
||||||
|
# Dutch
|
||||||
|
/app/javascript/mastodon/locales/nl.json @jeroenpraat
|
||||||
|
/app/javascript/mastodon/locales/whitelist_nl.json @jeroenpraat
|
||||||
|
/app/views/user_mailer/*.nl.html.erb @jeroenpraat
|
||||||
|
/app/views/user_mailer/*.nl.text.erb @jeroenpraat
|
||||||
|
/config/locales/*.nl.yml @jeroenpraat
|
||||||
|
/config/locales/nl.yml @jeroenpraat
|
1
.github/FUNDING.yml
vendored
1
.github/FUNDING.yml
vendored
|
@ -1,3 +1,2 @@
|
||||||
patreon: mastodon
|
patreon: mastodon
|
||||||
open_collective: mastodon
|
open_collective: mastodon
|
||||||
custom: https://sponsor.joinmastodon.org
|
|
||||||
|
|
56
.github/ISSUE_TEMPLATE/1.bug_report.yml
vendored
56
.github/ISSUE_TEMPLATE/1.bug_report.yml
vendored
|
@ -1,56 +0,0 @@
|
||||||
name: Bug Report
|
|
||||||
description: If something isn't working as expected
|
|
||||||
labels: [bug]
|
|
||||||
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: textarea
|
|
||||||
attributes:
|
|
||||||
label: Specifications
|
|
||||||
description: |
|
|
||||||
What version or commit hash of Mastodon did you find this bug in?
|
|
||||||
|
|
||||||
If a front-end issue, what browser and operating systems were you using?
|
|
||||||
placeholder: |
|
|
||||||
Mastodon 3.5.3 (or Edge)
|
|
||||||
Ruby 2.7.6 (or v3.1.2)
|
|
||||||
Node.js 16.18.0
|
|
||||||
|
|
||||||
Google Chrome 106.0.5249.119
|
|
||||||
Firefox 105.0.3
|
|
||||||
|
|
||||||
etc...
|
|
||||||
validations:
|
|
||||||
required: true
|
|
22
.github/ISSUE_TEMPLATE/2.feature_request.yml
vendored
22
.github/ISSUE_TEMPLATE/2.feature_request.yml
vendored
|
@ -1,22 +0,0 @@
|
||||||
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
|
|
27
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
27
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
---
|
||||||
|
name: Bug Report
|
||||||
|
about: If something isn't working as expected
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<!-- 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 -->
|
||||||
|
|
||||||
|
### Expected behaviour
|
||||||
|
|
||||||
|
<!-- What should have happened? -->
|
||||||
|
|
||||||
|
### Actual behaviour
|
||||||
|
|
||||||
|
<!-- What happened? -->
|
||||||
|
|
||||||
|
### Steps to reproduce the problem
|
||||||
|
|
||||||
|
<!-- What were you trying to do? -->
|
||||||
|
|
||||||
|
### Specifications
|
||||||
|
|
||||||
|
<!-- What version or commit hash of Mastodon did you find this bug in? -->
|
||||||
|
|
||||||
|
<!-- If a front-end issue, what browser and operating systems were you using? -->
|
5
.github/ISSUE_TEMPLATE/config.yml
vendored
5
.github/ISSUE_TEMPLATE/config.yml
vendored
|
@ -1,5 +0,0 @@
|
||||||
blank_issues_enabled: false
|
|
||||||
contact_links:
|
|
||||||
- name: GitHub Discussions
|
|
||||||
url: https://github.com/mastodon/mastodon/discussions
|
|
||||||
about: Please ask and answer questions here.
|
|
17
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
17
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
---
|
||||||
|
name: Feature Request
|
||||||
|
about: I have a suggestion
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<!-- 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? -->
|
||||||
|
|
||||||
|
### Pitch
|
||||||
|
|
||||||
|
<!-- Describe your idea for a feature. Make sure it has not already been suggested/implemented/turned down before -->
|
||||||
|
|
||||||
|
### Motivation
|
||||||
|
|
||||||
|
<!-- Why do you think this feature is needed? Who would benefit from it? -->
|
10
.github/ISSUE_TEMPLATE/support.md
vendored
Normal file
10
.github/ISSUE_TEMPLATE/support.md
vendored
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
---
|
||||||
|
name: Support
|
||||||
|
about: Ask for help with your deployment
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
We primarily use GitHub as a bug and feature tracker. For usage questions, troubleshooting of deployments and other individual technical assistance, please use one of the resources below:
|
||||||
|
|
||||||
|
- https://discourse.joinmastodon.org
|
||||||
|
- #mastodon on irc.freenode.net
|
30
.github/dependabot.yml
vendored
30
.github/dependabot.yml
vendored
|
@ -1,30 +0,0 @@
|
||||||
# To get started with Dependabot version updates, you'll need to specify which
|
|
||||||
# package ecosystems to update and where the package manifests are located.
|
|
||||||
# Please see the documentation for all configuration options:
|
|
||||||
# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
|
|
||||||
|
|
||||||
version: 2
|
|
||||||
updates:
|
|
||||||
- package-ecosystem: npm
|
|
||||||
directory: '/'
|
|
||||||
schedule:
|
|
||||||
interval: weekly
|
|
||||||
open-pull-requests-limit: 99
|
|
||||||
allow:
|
|
||||||
- dependency-type: direct
|
|
||||||
|
|
||||||
- package-ecosystem: bundler
|
|
||||||
directory: '/'
|
|
||||||
schedule:
|
|
||||||
interval: weekly
|
|
||||||
open-pull-requests-limit: 99
|
|
||||||
allow:
|
|
||||||
- dependency-type: direct
|
|
||||||
|
|
||||||
- package-ecosystem: github-actions
|
|
||||||
directory: '/'
|
|
||||||
schedule:
|
|
||||||
interval: weekly
|
|
||||||
open-pull-requests-limit: 99
|
|
||||||
allow:
|
|
||||||
- dependency-type: direct
|
|
10
.github/stale.yml
vendored
10
.github/stale.yml
vendored
|
@ -1,10 +0,0 @@
|
||||||
daysUntilStale: 120
|
|
||||||
daysUntilClose: 7
|
|
||||||
exemptLabels:
|
|
||||||
- security
|
|
||||||
staleLabel: wontfix
|
|
||||||
markComment: >
|
|
||||||
This issue has been automatically marked as stale because it has not had
|
|
||||||
recent activity. It will be closed if no further activity occurs. Thank you
|
|
||||||
for your contributions.
|
|
||||||
only: pulls
|
|
21
.github/stylelint-matcher.json
vendored
21
.github/stylelint-matcher.json
vendored
|
@ -1,21 +0,0 @@
|
||||||
{
|
|
||||||
"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
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
48
.github/workflows/build-image.yml
vendored
48
.github/workflows/build-image.yml
vendored
|
@ -1,48 +0,0 @@
|
||||||
name: Build container image
|
|
||||||
on:
|
|
||||||
workflow_dispatch:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- 'main'
|
|
||||||
tags:
|
|
||||||
- '*'
|
|
||||||
pull_request:
|
|
||||||
paths:
|
|
||||||
- .github/workflows/build-image.yml
|
|
||||||
- Dockerfile
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build-image:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
- uses: hadolint/hadolint-action@v3.0.0
|
|
||||||
- uses: docker/setup-qemu-action@v2
|
|
||||||
- uses: docker/setup-buildx-action@v2
|
|
||||||
- uses: docker/login-action@v2
|
|
||||||
with:
|
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
|
||||||
if: github.event_name != 'pull_request'
|
|
||||||
- uses: docker/metadata-action@v4
|
|
||||||
id: meta
|
|
||||||
with:
|
|
||||||
images: tootsuite/mastodon
|
|
||||||
flavor: |
|
|
||||||
latest=auto
|
|
||||||
tags: |
|
|
||||||
type=edge,branch=main
|
|
||||||
type=pep440,pattern={{raw}}
|
|
||||||
type=pep440,pattern=v{{major}}.{{minor}}
|
|
||||||
type=ref,event=pr
|
|
||||||
- uses: docker/build-push-action@v3
|
|
||||||
with:
|
|
||||||
context: .
|
|
||||||
platforms: linux/amd64,linux/arm64
|
|
||||||
builder: ${{ steps.buildx.outputs.name }}
|
|
||||||
push: ${{ github.event_name != 'pull_request' }}
|
|
||||||
tags: ${{ steps.meta.outputs.tags }}
|
|
||||||
cache-from: type=gha
|
|
||||||
cache-to: type=gha,mode=max
|
|
37
.github/workflows/check-i18n.yml
vendored
37
.github/workflows/check-i18n.yml
vendored
|
@ -1,37 +0,0 @@
|
||||||
name: Check i18n
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [main]
|
|
||||||
pull_request:
|
|
||||||
branches: [main]
|
|
||||||
|
|
||||||
env:
|
|
||||||
RAILS_ENV: test
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
check-i18n:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
- name: Install system dependencies
|
|
||||||
run: |
|
|
||||||
sudo apt-get update
|
|
||||||
sudo apt-get install -y libicu-dev libidn11-dev
|
|
||||||
- name: Set up Ruby
|
|
||||||
uses: ruby/setup-ruby@v1
|
|
||||||
with:
|
|
||||||
ruby-version: .ruby-version
|
|
||||||
bundler-cache: true
|
|
||||||
- name: Check locale file normalization
|
|
||||||
run: bundle exec i18n-tasks check-normalized
|
|
||||||
- name: Check for unused strings
|
|
||||||
run: bundle exec i18n-tasks unused -l en
|
|
||||||
- name: Check for wrong string interpolations
|
|
||||||
run: bundle exec i18n-tasks check-consistent-interpolations
|
|
||||||
- name: Check that all required locale files exist
|
|
||||||
run: bundle exec rake repo:check_locales_files
|
|
62
.github/workflows/codeql.yml
vendored
62
.github/workflows/codeql.yml
vendored
|
@ -1,62 +0,0 @@
|
||||||
name: 'CodeQL'
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: ['main']
|
|
||||||
pull_request:
|
|
||||||
# The branches below must be a subset of the branches above
|
|
||||||
branches: ['main']
|
|
||||||
schedule:
|
|
||||||
- cron: '22 6 * * 1'
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
analyze:
|
|
||||||
name: Analyze
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
permissions:
|
|
||||||
actions: read
|
|
||||||
contents: read
|
|
||||||
security-events: write
|
|
||||||
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
language: ['javascript', 'ruby']
|
|
||||||
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
|
|
||||||
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
|
|
||||||
# Initializes the CodeQL tools for scanning.
|
|
||||||
- name: Initialize CodeQL
|
|
||||||
uses: github/codeql-action/init@v2
|
|
||||||
with:
|
|
||||||
languages: ${{ matrix.language }}
|
|
||||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
|
||||||
# By default, queries listed here will override any specified in a config file.
|
|
||||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
|
||||||
|
|
||||||
# Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
|
|
||||||
# queries: security-extended,security-and-quality
|
|
||||||
|
|
||||||
# Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java).
|
|
||||||
# If this step fails, then you should remove it and run the build manually (see below)
|
|
||||||
- name: Autobuild
|
|
||||||
uses: github/codeql-action/autobuild@v2
|
|
||||||
|
|
||||||
# ℹ️ Command-line programs to run using the OS shell.
|
|
||||||
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
|
||||||
|
|
||||||
# If the Autobuild fails above, remove it and uncomment the following three lines.
|
|
||||||
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
|
|
||||||
|
|
||||||
# - run: |
|
|
||||||
# echo "Run, Build Application using script"
|
|
||||||
# ./location_of_script_within_repo/buildscript.sh
|
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
|
||||||
uses: github/codeql-action/analyze@v2
|
|
||||||
with:
|
|
||||||
category: '/language:${{matrix.language}}'
|
|
85
.github/workflows/linter.yml
vendored
85
.github/workflows/linter.yml
vendored
|
@ -1,85 +0,0 @@
|
||||||
---
|
|
||||||
#################################
|
|
||||||
#################################
|
|
||||||
## Super Linter GitHub Actions ##
|
|
||||||
#################################
|
|
||||||
#################################
|
|
||||||
name: Lint Code Base
|
|
||||||
|
|
||||||
#
|
|
||||||
# Documentation:
|
|
||||||
# https://docs.github.com/en/actions/learn-github-actions/workflow-syntax-for-github-actions
|
|
||||||
#
|
|
||||||
|
|
||||||
#############################
|
|
||||||
# Start the job on all push #
|
|
||||||
#############################
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches-ignore: [main]
|
|
||||||
# Remove the line above to run when pushing to master
|
|
||||||
pull_request:
|
|
||||||
branches: [main]
|
|
||||||
|
|
||||||
###############
|
|
||||||
# Set the Job #
|
|
||||||
###############
|
|
||||||
permissions:
|
|
||||||
checks: write
|
|
||||||
contents: read
|
|
||||||
pull-requests: write
|
|
||||||
statuses: write
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
# Name the Job
|
|
||||||
name: Lint Code Base
|
|
||||||
# Set the agent to run on
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
##################
|
|
||||||
# Load all steps #
|
|
||||||
##################
|
|
||||||
steps:
|
|
||||||
##########################
|
|
||||||
# Checkout the code base #
|
|
||||||
##########################
|
|
||||||
- name: Checkout Code
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
with:
|
|
||||||
# Full git history is needed to get a proper list of changed files within `super-linter`
|
|
||||||
fetch-depth: 0
|
|
||||||
|
|
||||||
- name: Set-up Node.js
|
|
||||||
uses: actions/setup-node@v3
|
|
||||||
with:
|
|
||||||
node-version-file: .nvmrc
|
|
||||||
cache: yarn
|
|
||||||
- name: Install dependencies
|
|
||||||
run: yarn install --frozen-lockfile
|
|
||||||
- name: Check prettier formatting
|
|
||||||
run: yarn format-check
|
|
||||||
- name: Set-up RuboCop Problem Mathcher
|
|
||||||
uses: r7kamura/rubocop-problem-matchers-action@v1
|
|
||||||
- name: Set-up Stylelint Problem Matcher
|
|
||||||
uses: xt0rted/stylelint-problem-matcher@v1
|
|
||||||
# https://github.com/xt0rted/stylelint-problem-matcher/issues/360
|
|
||||||
- run: echo "::add-matcher::.github/stylelint-matcher.json"
|
|
||||||
|
|
||||||
################################
|
|
||||||
# Run Linter against code base #
|
|
||||||
################################
|
|
||||||
- name: Lint Code Base
|
|
||||||
uses: github/super-linter@v4
|
|
||||||
env:
|
|
||||||
CSS_FILE_NAME: stylelint.config.js
|
|
||||||
DEFAULT_BRANCH: main
|
|
||||||
NO_COLOR: 1 # https://github.com/xt0rted/stylelint-problem-matcher/issues/360
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
JAVASCRIPT_ES_CONFIG_FILE: .eslintrc.js
|
|
||||||
LINTER_RULES_PATH: .
|
|
||||||
RUBY_CONFIG_FILE: .rubocop.yml
|
|
||||||
VALIDATE_ALL_CODEBASE: false
|
|
||||||
VALIDATE_CSS: true
|
|
||||||
VALIDATE_JAVASCRIPT_ES: true
|
|
||||||
VALIDATE_RUBY: true
|
|
17
.github/workflows/rebase-needed.yml
vendored
17
.github/workflows/rebase-needed.yml
vendored
|
@ -1,17 +0,0 @@
|
||||||
name: PR Needs Rebase
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
pull_request_target:
|
|
||||||
types: [synchronize]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
label-rebase-needed:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Check for merge conflicts
|
|
||||||
uses: eps1lon/actions-label-merge-conflict@releases/2.x
|
|
||||||
with:
|
|
||||||
dirtyLabel: 'rebase needed :construction:'
|
|
||||||
repoToken: '${{ secrets.GITHUB_TOKEN }}'
|
|
||||||
commentOnDirty: This pull request has merge conflicts that must be resolved before it can be merged.
|
|
29
.gitignore
vendored
29
.gitignore
vendored
|
@ -13,36 +13,33 @@
|
||||||
/db/*.sqlite3-journal
|
/db/*.sqlite3-journal
|
||||||
|
|
||||||
# Ignore all logfiles and tempfiles.
|
# Ignore all logfiles and tempfiles.
|
||||||
.eslintcache
|
|
||||||
/log/*
|
/log/*
|
||||||
!/log/.keep
|
!/log/.keep
|
||||||
/tmp
|
/tmp
|
||||||
/coverage
|
coverage
|
||||||
/public/system
|
public/system
|
||||||
/public/assets
|
public/assets
|
||||||
/public/packs
|
public/packs
|
||||||
/public/packs-test
|
public/packs-test
|
||||||
.env
|
.env
|
||||||
.env.production
|
.env.production
|
||||||
.env.development
|
node_modules/
|
||||||
/node_modules/
|
build/
|
||||||
/build/
|
|
||||||
|
|
||||||
# Ignore Vagrant files
|
# Ignore Vagrant files
|
||||||
.vagrant/
|
.vagrant/
|
||||||
|
|
||||||
# Ignore Capistrano customizations
|
# Ignore Capistrano customizations
|
||||||
/config/deploy/*
|
config/deploy/*
|
||||||
|
|
||||||
# Ignore IDE files
|
# Ignore IDE files
|
||||||
.vscode/
|
.vscode/
|
||||||
.idea/
|
.idea/
|
||||||
|
|
||||||
# Ignore postgres + redis + elasticsearch volume optionally created by docker-compose
|
# Ignore postgres + redis + elasticsearch volume optionally created by docker-compose
|
||||||
/postgres
|
postgres
|
||||||
/postgres14
|
redis
|
||||||
/redis
|
elasticsearch
|
||||||
/elasticsearch
|
|
||||||
|
|
||||||
# Ignore Apple files
|
# Ignore Apple files
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
@ -58,8 +55,6 @@ npm-debug.log
|
||||||
yarn-error.log
|
yarn-error.log
|
||||||
yarn-debug.log
|
yarn-debug.log
|
||||||
|
|
||||||
# Ignore vagrant log files
|
|
||||||
*-cloudimg-console.log
|
|
||||||
|
|
||||||
# Ignore Docker option files
|
# Ignore Docker option files
|
||||||
docker-compose.override.yml
|
docker-compose.override.yml
|
||||||
|
|
||||||
|
|
2
.nvmrc
2
.nvmrc
|
@ -1 +1 @@
|
||||||
16
|
8
|
||||||
|
|
|
@ -1,72 +0,0 @@
|
||||||
# See https://help.github.com/articles/ignoring-files for more about ignoring files.
|
|
||||||
#
|
|
||||||
# If you find yourself ignoring temporary files generated by your text editor
|
|
||||||
# or operating system, you probably want to add a global ignore instead:
|
|
||||||
# git config --global core.excludesfile '~/.gitignore_global'
|
|
||||||
|
|
||||||
# Ignore bundler config and downloaded libraries.
|
|
||||||
/.bundle
|
|
||||||
/vendor/bundle
|
|
||||||
|
|
||||||
# Ignore the default SQLite database.
|
|
||||||
/db/*.sqlite3
|
|
||||||
/db/*.sqlite3-journal
|
|
||||||
|
|
||||||
# Ignore all logfiles and tempfiles.
|
|
||||||
.eslintcache
|
|
||||||
/log/*
|
|
||||||
!/log/.keep
|
|
||||||
/tmp
|
|
||||||
/coverage
|
|
||||||
/public/system
|
|
||||||
/public/assets
|
|
||||||
/public/packs
|
|
||||||
/public/packs-test
|
|
||||||
.env
|
|
||||||
.env.production
|
|
||||||
.env.development
|
|
||||||
/node_modules/
|
|
||||||
/build/
|
|
||||||
|
|
||||||
# Ignore Vagrant files
|
|
||||||
.vagrant/
|
|
||||||
|
|
||||||
# Ignore Capistrano customizations
|
|
||||||
/config/deploy/*
|
|
||||||
|
|
||||||
# Ignore IDE files
|
|
||||||
.vscode/
|
|
||||||
.idea/
|
|
||||||
|
|
||||||
# Ignore postgres + redis + elasticsearch volume optionally created by docker-compose
|
|
||||||
/postgres
|
|
||||||
/postgres14
|
|
||||||
/redis
|
|
||||||
/elasticsearch
|
|
||||||
|
|
||||||
# Ignore Apple files
|
|
||||||
.DS_Store
|
|
||||||
|
|
||||||
# Ignore vim files
|
|
||||||
*~
|
|
||||||
*.swp
|
|
||||||
|
|
||||||
# Ignore npm debug log
|
|
||||||
npm-debug.log
|
|
||||||
|
|
||||||
# Ignore yarn log files
|
|
||||||
yarn-error.log
|
|
||||||
yarn-debug.log
|
|
||||||
|
|
||||||
# Ignore vagrant log files
|
|
||||||
*-cloudimg-console.log
|
|
||||||
|
|
||||||
# Ignore Docker option files
|
|
||||||
docker-compose.override.yml
|
|
||||||
|
|
||||||
# Ignore emoji map file
|
|
||||||
/app/javascript/mastodon/features/emoji/emoji_map.json
|
|
||||||
|
|
||||||
# Ignore locale files
|
|
||||||
/app/javascript/mastodon/locales
|
|
||||||
/config/locales
|
|
|
@ -1,3 +0,0 @@
|
||||||
module.exports = {
|
|
||||||
singleQuote: true
|
|
||||||
}
|
|
399
.rubocop.yml
399
.rubocop.yml
|
@ -1,27 +1,20 @@
|
||||||
require:
|
require:
|
||||||
- rubocop-rails
|
- rubocop-rails
|
||||||
- rubocop-rspec
|
|
||||||
- rubocop-performance
|
|
||||||
|
|
||||||
AllCops:
|
AllCops:
|
||||||
TargetRubyVersion: 2.7
|
TargetRubyVersion: 2.3
|
||||||
DisplayCopNames: true
|
|
||||||
DisplayStyleGuide: true
|
|
||||||
ExtraDetails: true
|
|
||||||
UseCache: true
|
|
||||||
CacheRootDirectory: tmp
|
|
||||||
NewCops: enable
|
|
||||||
Exclude:
|
Exclude:
|
||||||
- db/schema.rb
|
- 'spec/**/*'
|
||||||
- 'app/views/**/*'
|
- 'db/**/*'
|
||||||
- 'config/**/*'
|
- 'app/views/**/*'
|
||||||
- 'bin/*'
|
- 'config/**/*'
|
||||||
- 'Rakefile'
|
- 'bin/*'
|
||||||
- 'node_modules/**/*'
|
- 'Rakefile'
|
||||||
- 'Vagrantfile'
|
- 'node_modules/**/*'
|
||||||
- 'vendor/**/*'
|
- 'Vagrantfile'
|
||||||
- 'lib/json_ld/*'
|
- 'vendor/**/*'
|
||||||
- 'lib/templates/**/*'
|
- 'lib/json_ld/*'
|
||||||
|
- 'lib/templates/**/*'
|
||||||
|
|
||||||
Bundler/OrderedGems:
|
Bundler/OrderedGems:
|
||||||
Enabled: false
|
Enabled: false
|
||||||
|
@ -32,267 +25,59 @@ Layout/AccessModifierIndentation:
|
||||||
Layout/EmptyLineAfterMagicComment:
|
Layout/EmptyLineAfterMagicComment:
|
||||||
Enabled: false
|
Enabled: false
|
||||||
|
|
||||||
Layout/EmptyLineAfterGuardClause:
|
|
||||||
Enabled: false
|
|
||||||
|
|
||||||
Layout/EmptyLineBetweenDefs:
|
|
||||||
AllowAdjacentOneLineDefs: true
|
|
||||||
|
|
||||||
Layout/EmptyLinesAroundAttributeAccessor:
|
|
||||||
Enabled: true
|
|
||||||
|
|
||||||
Layout/FirstHashElementIndentation:
|
|
||||||
EnforcedStyle: consistent
|
|
||||||
|
|
||||||
Layout/HashAlignment:
|
|
||||||
Enabled: false
|
|
||||||
|
|
||||||
Layout/SpaceAroundMethodCallOperator:
|
|
||||||
Enabled: true
|
|
||||||
|
|
||||||
Layout/SpaceInsideHashLiteralBraces:
|
Layout/SpaceInsideHashLiteralBraces:
|
||||||
EnforcedStyle: space
|
EnforcedStyle: space
|
||||||
|
|
||||||
Lint/DeprecatedOpenSSLConstant:
|
|
||||||
Enabled: true
|
|
||||||
|
|
||||||
Lint/DuplicateElsifCondition:
|
|
||||||
Enabled: true
|
|
||||||
|
|
||||||
Lint/MixedRegexpCaptureTypes:
|
|
||||||
Enabled: true
|
|
||||||
|
|
||||||
Lint/RaiseException:
|
|
||||||
Enabled: true
|
|
||||||
|
|
||||||
Lint/StructNewOverride:
|
|
||||||
Enabled: true
|
|
||||||
|
|
||||||
Lint/UselessAccessModifier:
|
|
||||||
ContextCreatingMethods:
|
|
||||||
- class_methods
|
|
||||||
|
|
||||||
Metrics/AbcSize:
|
Metrics/AbcSize:
|
||||||
Max: 34 # RuboCop default 17
|
Max: 100
|
||||||
Exclude:
|
|
||||||
- 'lib/**/*cli*.rb'
|
|
||||||
- db/*migrate/**/*
|
|
||||||
- lib/paperclip/color_extractor.rb
|
|
||||||
- app/workers/scheduler/follow_recommendations_scheduler.rb
|
|
||||||
- app/services/activitypub/fetch*_service.rb
|
|
||||||
- lib/paperclip/**/*
|
|
||||||
CountRepeatedAttributes: false
|
|
||||||
AllowedMethods:
|
|
||||||
- update_media_attachments!
|
|
||||||
- account_link_to
|
|
||||||
- attempt_oembed
|
|
||||||
- build_crutches
|
|
||||||
- calculate_scores
|
|
||||||
- cc
|
|
||||||
- dump_actor!
|
|
||||||
- filter_from_home?
|
|
||||||
- hydrate
|
|
||||||
- import_bookmarks!
|
|
||||||
- import_relationships!
|
|
||||||
- initialize
|
|
||||||
- link_to_mention
|
|
||||||
- log_target
|
|
||||||
- matches_time_window?
|
|
||||||
- parse_metadata
|
|
||||||
- perform_statuses_search!
|
|
||||||
- privatize_media_attachments!
|
|
||||||
- process_update
|
|
||||||
- publish_media_attachments!
|
|
||||||
- remotable_attachment
|
|
||||||
- render_initial_state
|
|
||||||
- render_with_cache
|
|
||||||
- searchable_by
|
|
||||||
- self.cached_filters_for
|
|
||||||
- set_fetchable_attributes!
|
|
||||||
- signed_request_actor
|
|
||||||
- statuses_to_delete
|
|
||||||
- update_poll!
|
|
||||||
|
|
||||||
Metrics/BlockLength:
|
Metrics/BlockLength:
|
||||||
Max: 55
|
Max: 35
|
||||||
Exclude:
|
Exclude:
|
||||||
- 'lib/mastodon/*_cli.rb'
|
- 'lib/tasks/**/*'
|
||||||
CountComments: false
|
|
||||||
CountAsOne: [array, heredoc]
|
|
||||||
AllowedMethods:
|
|
||||||
- task
|
|
||||||
- namespace
|
|
||||||
- class_methods
|
|
||||||
- included
|
|
||||||
|
|
||||||
Metrics/BlockNesting:
|
Metrics/BlockNesting:
|
||||||
Max: 3
|
Max: 3
|
||||||
Exclude:
|
|
||||||
- 'lib/mastodon/*_cli.rb'
|
|
||||||
|
|
||||||
Metrics/ClassLength:
|
Metrics/ClassLength:
|
||||||
CountComments: false
|
CountComments: false
|
||||||
Max: 500
|
Max: 300
|
||||||
CountAsOne: [array, heredoc]
|
|
||||||
Exclude:
|
|
||||||
- 'lib/mastodon/*_cli.rb'
|
|
||||||
|
|
||||||
Metrics/CyclomaticComplexity:
|
Metrics/CyclomaticComplexity:
|
||||||
Max: 12
|
Max: 25
|
||||||
Exclude:
|
|
||||||
- lib/mastodon/*cli*.rb
|
|
||||||
- db/*migrate/**/*
|
|
||||||
AllowedMethods:
|
|
||||||
- attempt_oembed
|
|
||||||
- blocked?
|
|
||||||
- build_crutches
|
|
||||||
- calculate_scores
|
|
||||||
- cc
|
|
||||||
- discover_endpoint!
|
|
||||||
- filter_from_home?
|
|
||||||
- hydrate
|
|
||||||
- klass
|
|
||||||
- link_to_mention
|
|
||||||
- log_target
|
|
||||||
- matches_time_window?
|
|
||||||
- patch_for_forwarding!
|
|
||||||
- preprocess_attributes!
|
|
||||||
- process_update
|
|
||||||
- remotable_attachment
|
|
||||||
- scan_text!
|
|
||||||
- self.cached_filters_for
|
|
||||||
- set_fetchable_attributes!
|
|
||||||
- setup_redis_env_url
|
|
||||||
- update_media_attachments!
|
|
||||||
|
|
||||||
Layout/LineLength:
|
Metrics/LineLength:
|
||||||
Max: 140 # RuboCop default 120
|
|
||||||
AllowHeredoc: true
|
|
||||||
AllowURI: true
|
AllowURI: true
|
||||||
IgnoreCopDirectives: true
|
Enabled: false
|
||||||
AllowedPatterns:
|
|
||||||
# Allow comments to be long lines
|
|
||||||
- !ruby/regexp / \# .*$/
|
|
||||||
- !ruby/regexp /^\# .*$/
|
|
||||||
Exclude:
|
|
||||||
- lib/**/*cli*.rb
|
|
||||||
- db/*migrate/**/*
|
|
||||||
- db/seeds/**/*
|
|
||||||
|
|
||||||
Metrics/MethodLength:
|
Metrics/MethodLength:
|
||||||
CountComments: false
|
CountComments: false
|
||||||
CountAsOne: [array, heredoc]
|
Max: 55
|
||||||
Max: 25 # RuboCop default 10
|
|
||||||
Exclude:
|
|
||||||
- 'lib/mastodon/*_cli.rb'
|
|
||||||
AllowedMethods:
|
|
||||||
- account_link_to
|
|
||||||
- attempt_oembed
|
|
||||||
- body_with_limit
|
|
||||||
- build_crutches
|
|
||||||
- cached_filters_for
|
|
||||||
- calculate_scores
|
|
||||||
- check_webfinger!
|
|
||||||
- clean_feeds!
|
|
||||||
- collection_items
|
|
||||||
- collection_presenter
|
|
||||||
- copy_account_notes!
|
|
||||||
- deduplicate_accounts!
|
|
||||||
- deduplicate_conversations!
|
|
||||||
- deduplicate_local_accounts!
|
|
||||||
- deduplicate_statuses!
|
|
||||||
- deduplicate_tags!
|
|
||||||
- deduplicate_users!
|
|
||||||
- discover_endpoint!
|
|
||||||
- extract_extra_uris_with_indices
|
|
||||||
- extract_hashtags_with_indices
|
|
||||||
- extract_mentions_or_lists_with_indices
|
|
||||||
- filter_from_home?
|
|
||||||
- from_elasticsearch
|
|
||||||
- handle_explicit_update!
|
|
||||||
- handle_mark_as_sensitive!
|
|
||||||
- hsl_to_rgb
|
|
||||||
- import_bookmarks!
|
|
||||||
- import_domain_blocks!
|
|
||||||
- import_relationships!
|
|
||||||
- ldap_options
|
|
||||||
- matches_time_window?
|
|
||||||
- outbox_presenter
|
|
||||||
- pam_get_user
|
|
||||||
- parallelize_with_progress
|
|
||||||
- parse_and_transform
|
|
||||||
- patch_for_forwarding!
|
|
||||||
- populate_home
|
|
||||||
- post_process_style
|
|
||||||
- preload_cache_collection_target_statuses
|
|
||||||
- privatize_media_attachments!
|
|
||||||
- provides_callback_for
|
|
||||||
- publish_media_attachments!
|
|
||||||
- relevant_account_timestamp
|
|
||||||
- remotable_attachment
|
|
||||||
- rgb_to_hsl
|
|
||||||
- rss_status_content_format
|
|
||||||
- set_fetchable_attributes!
|
|
||||||
- setup_redis_env_url
|
|
||||||
- signed_request_actor
|
|
||||||
- to_preview_card_attributes
|
|
||||||
- upgrade_storage_filesystem
|
|
||||||
- upgrade_storage_s3
|
|
||||||
- user_settings_params
|
|
||||||
- hydrate
|
|
||||||
- cc
|
|
||||||
- self_destruct
|
|
||||||
|
|
||||||
Metrics/ModuleLength:
|
Metrics/ModuleLength:
|
||||||
CountComments: false
|
CountComments: false
|
||||||
Max: 200
|
Max: 200
|
||||||
CountAsOne: [array, heredoc]
|
|
||||||
|
|
||||||
Metrics/ParameterLists:
|
Metrics/ParameterLists:
|
||||||
Max: 5 # RuboCop default 5
|
Max: 5
|
||||||
CountKeywordArgs: true # RuboCop default true
|
CountKeywordArgs: true
|
||||||
MaxOptionalParameters: 3 # RuboCop default 3
|
|
||||||
Exclude:
|
|
||||||
- app/models/concerns/account_interactions.rb
|
|
||||||
- app/services/activitypub/fetch_remote_account_service.rb
|
|
||||||
- app/services/activitypub/fetch_remote_actor_service.rb
|
|
||||||
|
|
||||||
Metrics/PerceivedComplexity:
|
Metrics/PerceivedComplexity:
|
||||||
Max: 16 # RuboCop default 8
|
Max: 20
|
||||||
AllowedMethods:
|
|
||||||
- attempt_oembed
|
|
||||||
- build_crutches
|
|
||||||
- calculate_scores
|
|
||||||
- deduplicate_users!
|
|
||||||
- discover_endpoint!
|
|
||||||
- filter_from_home?
|
|
||||||
- hydrate
|
|
||||||
- patch_for_forwarding!
|
|
||||||
- process_update
|
|
||||||
- remove_orphans
|
|
||||||
- update_media_attachments!
|
|
||||||
|
|
||||||
Naming/MemoizedInstanceVariableName:
|
Naming/MemoizedInstanceVariableName:
|
||||||
Enabled: false
|
Enabled: false
|
||||||
|
|
||||||
Naming/MethodParameterName:
|
|
||||||
Enabled: true
|
|
||||||
|
|
||||||
Rails:
|
Rails:
|
||||||
Enabled: true
|
Enabled: true
|
||||||
|
|
||||||
Rails/ApplicationController:
|
Rails/HasAndBelongsToMany:
|
||||||
Enabled: false
|
|
||||||
Exclude:
|
|
||||||
- 'app/controllers/well_known/**/*.rb'
|
|
||||||
|
|
||||||
Rails/BelongsTo:
|
|
||||||
Enabled: false
|
Enabled: false
|
||||||
|
|
||||||
Rails/ContentTag:
|
Rails/SkipsModelValidations:
|
||||||
Enabled: false
|
Enabled: false
|
||||||
|
|
||||||
Rails/EnumHash:
|
Rails/HttpStatus:
|
||||||
Enabled: false
|
Enabled: false
|
||||||
|
|
||||||
Rails/Exit:
|
Rails/Exit:
|
||||||
|
@ -300,60 +85,9 @@ Rails/Exit:
|
||||||
- 'lib/mastodon/*'
|
- 'lib/mastodon/*'
|
||||||
- 'lib/cli.rb'
|
- 'lib/cli.rb'
|
||||||
|
|
||||||
Rails/FilePath:
|
|
||||||
Enabled: false
|
|
||||||
|
|
||||||
Rails/HasAndBelongsToMany:
|
|
||||||
Enabled: false
|
|
||||||
|
|
||||||
Rails/HasManyOrHasOneDependent:
|
|
||||||
Enabled: false
|
|
||||||
|
|
||||||
Rails/HelperInstanceVariable:
|
Rails/HelperInstanceVariable:
|
||||||
Enabled: false
|
Enabled: false
|
||||||
|
|
||||||
Rails/HttpStatus:
|
|
||||||
Enabled: false
|
|
||||||
|
|
||||||
Rails/IndexBy:
|
|
||||||
Enabled: false
|
|
||||||
|
|
||||||
Rails/InverseOf:
|
|
||||||
Enabled: false
|
|
||||||
|
|
||||||
Rails/LexicallyScopedActionFilter:
|
|
||||||
Enabled: false
|
|
||||||
|
|
||||||
Rails/OutputSafety:
|
|
||||||
Enabled: true
|
|
||||||
|
|
||||||
Rails/RakeEnvironment:
|
|
||||||
Enabled: false
|
|
||||||
|
|
||||||
Rails/RedundantForeignKey:
|
|
||||||
Enabled: false
|
|
||||||
|
|
||||||
Rails/SkipsModelValidations:
|
|
||||||
Enabled: false
|
|
||||||
|
|
||||||
Rails/UniqueValidationWithoutIndex:
|
|
||||||
Enabled: false
|
|
||||||
|
|
||||||
Style/AccessorGrouping:
|
|
||||||
Enabled: true
|
|
||||||
|
|
||||||
Style/AccessModifierDeclarations:
|
|
||||||
Enabled: false
|
|
||||||
|
|
||||||
Style/ArrayCoercion:
|
|
||||||
Enabled: true
|
|
||||||
|
|
||||||
Style/BisectedAttrAccessor:
|
|
||||||
Enabled: true
|
|
||||||
|
|
||||||
Style/CaseLikeIf:
|
|
||||||
Enabled: false
|
|
||||||
|
|
||||||
Style/ClassAndModuleChildren:
|
Style/ClassAndModuleChildren:
|
||||||
Enabled: false
|
Enabled: false
|
||||||
|
|
||||||
|
@ -368,55 +102,15 @@ Style/Documentation:
|
||||||
Style/DoubleNegation:
|
Style/DoubleNegation:
|
||||||
Enabled: true
|
Enabled: true
|
||||||
|
|
||||||
Style/ExpandPathArguments:
|
|
||||||
Enabled: false
|
|
||||||
|
|
||||||
Style/ExponentialNotation:
|
|
||||||
Enabled: true
|
|
||||||
|
|
||||||
Style/FormatString:
|
|
||||||
Enabled: false
|
|
||||||
|
|
||||||
Style/FormatStringToken:
|
|
||||||
Enabled: false
|
|
||||||
|
|
||||||
Style/FrozenStringLiteralComment:
|
Style/FrozenStringLiteralComment:
|
||||||
Enabled: true
|
Enabled: true
|
||||||
|
|
||||||
Style/GuardClause:
|
Style/GuardClause:
|
||||||
Enabled: false
|
Enabled: false
|
||||||
|
|
||||||
Style/HashAsLastArrayItem:
|
|
||||||
Enabled: false
|
|
||||||
|
|
||||||
Style/HashEachMethods:
|
|
||||||
Enabled: true
|
|
||||||
|
|
||||||
Style/HashLikeCase:
|
|
||||||
Enabled: true
|
|
||||||
|
|
||||||
Style/HashTransformKeys:
|
|
||||||
Enabled: true
|
|
||||||
|
|
||||||
Style/HashTransformValues:
|
|
||||||
Enabled: false
|
|
||||||
|
|
||||||
Style/HashSyntax:
|
|
||||||
Enabled: true
|
|
||||||
EnforcedStyle: ruby19_no_mixed_keys
|
|
||||||
|
|
||||||
Style/IfUnlessModifier:
|
|
||||||
Enabled: false
|
|
||||||
|
|
||||||
Style/InverseMethods:
|
|
||||||
Enabled: false
|
|
||||||
|
|
||||||
Style/Lambda:
|
Style/Lambda:
|
||||||
Enabled: false
|
Enabled: false
|
||||||
|
|
||||||
Style/MutableConstant:
|
|
||||||
Enabled: false
|
|
||||||
|
|
||||||
Style/PercentLiteralDelimiters:
|
Style/PercentLiteralDelimiters:
|
||||||
PreferredDelimiters:
|
PreferredDelimiters:
|
||||||
'%i': '()'
|
'%i': '()'
|
||||||
|
@ -425,36 +119,9 @@ Style/PercentLiteralDelimiters:
|
||||||
Style/PerlBackrefs:
|
Style/PerlBackrefs:
|
||||||
AutoCorrect: false
|
AutoCorrect: false
|
||||||
|
|
||||||
Style/RedundantFetchBlock:
|
|
||||||
Enabled: true
|
|
||||||
|
|
||||||
Style/RedundantFileExtensionInRequire:
|
|
||||||
Enabled: true
|
|
||||||
|
|
||||||
Style/RedundantRegexpCharacterClass:
|
|
||||||
Enabled: false
|
|
||||||
|
|
||||||
Style/RedundantRegexpEscape:
|
|
||||||
Enabled: false
|
|
||||||
|
|
||||||
Style/RedundantReturn:
|
|
||||||
Enabled: true
|
|
||||||
|
|
||||||
Style/RedundantBegin:
|
|
||||||
Enabled: false
|
|
||||||
|
|
||||||
Style/RegexpLiteral:
|
Style/RegexpLiteral:
|
||||||
Enabled: false
|
Enabled: false
|
||||||
|
|
||||||
Style/RescueStandardError:
|
|
||||||
Enabled: true
|
|
||||||
|
|
||||||
Style/SignalException:
|
|
||||||
Enabled: false
|
|
||||||
|
|
||||||
Style/SlicingWithRange:
|
|
||||||
Enabled: true
|
|
||||||
|
|
||||||
Style/SymbolArray:
|
Style/SymbolArray:
|
||||||
Enabled: false
|
Enabled: false
|
||||||
|
|
||||||
|
@ -463,17 +130,3 @@ Style/TrailingCommaInArrayLiteral:
|
||||||
|
|
||||||
Style/TrailingCommaInHashLiteral:
|
Style/TrailingCommaInHashLiteral:
|
||||||
EnforcedStyleForMultiline: 'comma'
|
EnforcedStyleForMultiline: 'comma'
|
||||||
|
|
||||||
Style/UnpackFirst:
|
|
||||||
Enabled: false
|
|
||||||
|
|
||||||
RSpec/ScatteredSetup:
|
|
||||||
Enabled: false
|
|
||||||
RSpec/ImplicitExpect:
|
|
||||||
Enabled: false
|
|
||||||
RSpec/NamedSubject:
|
|
||||||
Enabled: false
|
|
||||||
RSpec/DescribeClass:
|
|
||||||
Enabled: false
|
|
||||||
RSpec/LetSetup:
|
|
||||||
Enabled: false
|
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
mastodon
|
|
|
@ -1 +1 @@
|
||||||
3.0.4
|
2.6.1
|
||||||
|
|
37
.sass-lint.yml
Normal file
37
.sass-lint.yml
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
# Linter Documentation:
|
||||||
|
# https://github.com/sasstools/sass-lint/tree/v1.13.1/docs/options
|
||||||
|
|
||||||
|
files:
|
||||||
|
include: app/javascript/styles/**/*.scss
|
||||||
|
ignore:
|
||||||
|
- app/javascript/styles/mastodon/reset.scss
|
||||||
|
|
||||||
|
rules:
|
||||||
|
# Disallows
|
||||||
|
no-color-literals: 0
|
||||||
|
no-css-comments: 0
|
||||||
|
no-duplicate-properties: 0
|
||||||
|
no-ids: 0
|
||||||
|
no-important: 0
|
||||||
|
no-mergeable-selectors: 0
|
||||||
|
no-misspelled-properties: 0
|
||||||
|
no-qualifying-elements: 0
|
||||||
|
no-transition-all: 0
|
||||||
|
no-vendor-prefixes: 0
|
||||||
|
|
||||||
|
# Nesting
|
||||||
|
force-element-nesting: 0
|
||||||
|
force-attribute-nesting: 0
|
||||||
|
force-pseudo-nesting: 0
|
||||||
|
|
||||||
|
# Name Formats
|
||||||
|
class-name-format: 0
|
||||||
|
leading-zero: 0
|
||||||
|
|
||||||
|
# Style Guide
|
||||||
|
attribute-quotes: 0
|
||||||
|
hex-length: 0
|
||||||
|
indentation: 0
|
||||||
|
nesting-depth: 0
|
||||||
|
property-sort-order: 0
|
||||||
|
quotes: 0
|
1891
AUTHORS.md
1891
AUTHORS.md
File diff suppressed because it is too large
Load diff
25
Aptfile
25
Aptfile
|
@ -1,4 +1,29 @@
|
||||||
ffmpeg
|
ffmpeg
|
||||||
|
libicu[0-9][0-9]
|
||||||
|
libicu-dev
|
||||||
|
libidn11
|
||||||
|
libidn11-dev
|
||||||
libpq-dev
|
libpq-dev
|
||||||
|
libprotobuf-dev
|
||||||
|
libssl-dev
|
||||||
libxdamage1
|
libxdamage1
|
||||||
libxfixes3
|
libxfixes3
|
||||||
|
protobuf-compiler
|
||||||
|
zlib1g-dev
|
||||||
|
libcairo2
|
||||||
|
libcroco3
|
||||||
|
libdatrie1
|
||||||
|
libgdk-pixbuf2.0-0
|
||||||
|
libgraphite2-3
|
||||||
|
libharfbuzz0b
|
||||||
|
libpango-1.0-0
|
||||||
|
libpangocairo-1.0-0
|
||||||
|
libpangoft2-1.0-0
|
||||||
|
libpixman-1-0
|
||||||
|
librsvg2-2
|
||||||
|
libthai-data
|
||||||
|
libthai0
|
||||||
|
libvpx5
|
||||||
|
libxcb-render0
|
||||||
|
libxcb-shm0
|
||||||
|
libxrender1
|
||||||
|
|
2776
CHANGELOG.md
2776
CHANGELOG.md
File diff suppressed because it is too large
Load diff
|
@ -40,7 +40,7 @@ Project maintainers who do not follow or enforce the Code of Conduct in good fai
|
||||||
|
|
||||||
## Attribution
|
## Attribution
|
||||||
|
|
||||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [https://contributor-covenant.org/version/1/4][version]
|
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
|
||||||
|
|
||||||
[homepage]: https://contributor-covenant.org
|
[homepage]: http://contributor-covenant.org
|
||||||
[version]: https://contributor-covenant.org/version/1/4/
|
[version]: http://contributor-covenant.org/version/1/4/
|
||||||
|
|
|
@ -14,27 +14,19 @@ If your contributions are accepted into Mastodon, you can request to be paid thr
|
||||||
|
|
||||||
## Bug reports
|
## Bug reports
|
||||||
|
|
||||||
Bug reports and feature suggestions must use descriptive and concise titles and be submitted to [GitHub Issues](https://github.com/mastodon/mastodon/issues). Please use the search function to make sure that you are not submitting duplicates, and that a similar report or request has not already been resolved or rejected.
|
Bug reports and feature suggestions can be submitted to [GitHub Issues](https://github.com/tootsuite/mastodon/issues). Please make sure that you are not submitting duplicates, and that a similar report or request has not already been resolved or rejected in the past using the search function. Please also use descriptive, concise titles.
|
||||||
|
|
||||||
## Translations
|
## Translations
|
||||||
|
|
||||||
You can submit translations via [Crowdin](https://crowdin.com/project/mastodon). They are periodically merged into the codebase.
|
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)
|
[![Crowdin](https://d322cqt584bo4o.cloudfront.net/mastodon/localized.svg)][crowdin]
|
||||||
|
|
||||||
## Pull requests
|
## 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).
|
Please use clean, concise titles for your pull requests. We use commit squashing, so the final commit in the master branch will carry the title of the pull request.
|
||||||
|
|
||||||
Example:
|
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.
|
||||||
|
|
||||||
|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:
|
**Pull requests that do not pass automated checks may not be reviewed**. In particular, you need to keep in mind:
|
||||||
|
|
||||||
|
@ -42,8 +34,6 @@ It is not always possible to phrase every change in such a manner, but it is des
|
||||||
- Code style rules (rubocop, eslint)
|
- Code style rules (rubocop, eslint)
|
||||||
- Normalization of locale files (i18n-tasks)
|
- Normalization of locale files (i18n-tasks)
|
||||||
|
|
||||||
**Note**: You may need to log in and authorise the GitHub account your fork of this repository belongs to with CircleCI to enable some of the automated checks to run.
|
|
||||||
|
|
||||||
## Documentation
|
## 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).
|
The [Mastodon documentation](https://docs.joinmastodon.org) is a statically generated site. You can [submit merge requests to mastodon/docs](https://source.joinmastodon.org/mastodon/docs).
|
||||||
|
|
180
Dockerfile
180
Dockerfile
|
@ -1,99 +1,127 @@
|
||||||
# syntax=docker/dockerfile:1.4
|
FROM ubuntu:18.04 as build-dep
|
||||||
# This needs to be bullseye-slim because the Ruby image is built on bullseye-slim
|
|
||||||
ARG NODE_VERSION="16.18.1-bullseye-slim"
|
|
||||||
|
|
||||||
FROM ghcr.io/moritzheiber/ruby-jemalloc:3.0.4-slim as ruby
|
# Use bash for the shell
|
||||||
FROM node:${NODE_VERSION} as build
|
SHELL ["bash", "-c"]
|
||||||
|
|
||||||
COPY --link --from=ruby /opt/ruby /opt/ruby
|
# Install Node
|
||||||
|
ENV NODE_VER="8.15.0"
|
||||||
|
RUN echo "Etc/UTC" > /etc/localtime && \
|
||||||
|
apt update && \
|
||||||
|
apt -y install wget make gcc g++ python && \
|
||||||
|
cd ~ && \
|
||||||
|
wget https://nodejs.org/download/release/v$NODE_VER/node-v$NODE_VER.tar.gz && \
|
||||||
|
tar xf node-v$NODE_VER.tar.gz && \
|
||||||
|
cd node-v$NODE_VER && \
|
||||||
|
./configure --prefix=/opt/node && \
|
||||||
|
make -j$(nproc) > /dev/null && \
|
||||||
|
make install
|
||||||
|
|
||||||
ENV DEBIAN_FRONTEND="noninteractive" \
|
# Install jemalloc
|
||||||
PATH="${PATH}:/opt/ruby/bin"
|
ENV JE_VER="5.1.0"
|
||||||
|
RUN apt update && \
|
||||||
|
apt -y install autoconf && \
|
||||||
|
cd ~ && \
|
||||||
|
wget https://github.com/jemalloc/jemalloc/archive/$JE_VER.tar.gz && \
|
||||||
|
tar xf $JE_VER.tar.gz && \
|
||||||
|
cd jemalloc-$JE_VER && \
|
||||||
|
./autogen.sh && \
|
||||||
|
./configure --prefix=/opt/jemalloc && \
|
||||||
|
make -j$(nproc) > /dev/null && \
|
||||||
|
make install_bin install_include install_lib
|
||||||
|
|
||||||
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
|
# Install ruby
|
||||||
|
ENV RUBY_VER="2.6.1"
|
||||||
|
ENV CPPFLAGS="-I/opt/jemalloc/include"
|
||||||
|
ENV LDFLAGS="-L/opt/jemalloc/lib/"
|
||||||
|
RUN apt update && \
|
||||||
|
apt -y install build-essential \
|
||||||
|
bison libyaml-dev libgdbm-dev libreadline-dev \
|
||||||
|
libncurses5-dev libffi-dev zlib1g-dev libssl-dev && \
|
||||||
|
cd ~ && \
|
||||||
|
wget https://cache.ruby-lang.org/pub/ruby/${RUBY_VER%.*}/ruby-$RUBY_VER.tar.gz && \
|
||||||
|
tar xf ruby-$RUBY_VER.tar.gz && \
|
||||||
|
cd ruby-$RUBY_VER && \
|
||||||
|
./configure --prefix=/opt/ruby \
|
||||||
|
--with-jemalloc \
|
||||||
|
--with-shared \
|
||||||
|
--disable-install-doc && \
|
||||||
|
ln -s /opt/jemalloc/lib/* /usr/lib/ && \
|
||||||
|
make -j$(nproc) > /dev/null && \
|
||||||
|
make install
|
||||||
|
|
||||||
|
ENV PATH="${PATH}:/opt/ruby/bin:/opt/node/bin"
|
||||||
|
|
||||||
|
RUN npm install -g yarn && \
|
||||||
|
gem install bundler && \
|
||||||
|
apt update && \
|
||||||
|
apt -y install git libicu-dev libidn11-dev \
|
||||||
|
libpq-dev libprotobuf-dev protobuf-compiler
|
||||||
|
|
||||||
WORKDIR /opt/mastodon
|
|
||||||
COPY Gemfile* package.json yarn.lock /opt/mastodon/
|
COPY Gemfile* package.json yarn.lock /opt/mastodon/
|
||||||
|
|
||||||
# hadolint ignore=DL3008
|
RUN cd /opt/mastodon && \
|
||||||
RUN apt-get update && \
|
bundle install -j$(nproc) --deployment --without development test && \
|
||||||
apt-get install -y --no-install-recommends build-essential \
|
yarn install --pure-lockfile
|
||||||
ca-certificates \
|
|
||||||
git \
|
|
||||||
libicu-dev \
|
|
||||||
libidn11-dev \
|
|
||||||
libpq-dev \
|
|
||||||
libjemalloc-dev \
|
|
||||||
zlib1g-dev \
|
|
||||||
libgdbm-dev \
|
|
||||||
libgmp-dev \
|
|
||||||
libssl-dev \
|
|
||||||
libyaml-0-2 \
|
|
||||||
ca-certificates \
|
|
||||||
libreadline8 \
|
|
||||||
python3 \
|
|
||||||
shared-mime-info && \
|
|
||||||
bundle config set --local deployment 'true' && \
|
|
||||||
bundle config set --local without 'development test' && \
|
|
||||||
bundle config set silence_root_warning true && \
|
|
||||||
bundle install -j"$(nproc)" && \
|
|
||||||
yarn install --pure-lockfile --network-timeout 600000
|
|
||||||
|
|
||||||
FROM node:${NODE_VERSION}
|
FROM ubuntu:18.04
|
||||||
|
|
||||||
ARG UID="991"
|
# Copy over all the langs needed for runtime
|
||||||
ARG GID="991"
|
COPY --from=build-dep /opt/node /opt/node
|
||||||
|
COPY --from=build-dep /opt/ruby /opt/ruby
|
||||||
|
COPY --from=build-dep /opt/jemalloc /opt/jemalloc
|
||||||
|
|
||||||
COPY --link --from=ruby /opt/ruby /opt/ruby
|
# Add more PATHs to the PATH
|
||||||
|
ENV PATH="${PATH}:/opt/ruby/bin:/opt/node/bin:/opt/mastodon/bin"
|
||||||
|
|
||||||
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
|
# Create the mastodon user
|
||||||
|
ARG UID=991
|
||||||
|
ARG GID=991
|
||||||
|
RUN apt update && \
|
||||||
|
echo "Etc/UTC" > /etc/localtime && \
|
||||||
|
ln -s /opt/jemalloc/lib/* /usr/lib/ && \
|
||||||
|
apt install -y whois wget && \
|
||||||
|
addgroup --gid $GID mastodon && \
|
||||||
|
useradd -m -u $UID -g $GID -d /opt/mastodon mastodon && \
|
||||||
|
echo "mastodon:`head /dev/urandom | tr -dc A-Za-z0-9 | head -c 24 | mkpasswd -s -m sha-256`" | chpasswd
|
||||||
|
|
||||||
ENV DEBIAN_FRONTEND="noninteractive" \
|
# Install mastodon runtime deps
|
||||||
PATH="${PATH}:/opt/ruby/bin:/opt/mastodon/bin"
|
RUN apt -y --no-install-recommends install \
|
||||||
|
libssl1.1 libpq5 imagemagick ffmpeg \
|
||||||
|
libicu60 libprotobuf10 libidn11 libyaml-0-2 \
|
||||||
|
file ca-certificates tzdata libreadline7 && \
|
||||||
|
apt -y install gcc && \
|
||||||
|
ln -s /opt/mastodon /mastodon && \
|
||||||
|
gem install bundler && \
|
||||||
|
rm -rf /var/cache && \
|
||||||
|
rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
# Ignoreing these here since we don't want to pin any versions and the Debian image removes apt-get content after use
|
# Add tini
|
||||||
# hadolint ignore=DL3008,DL3009
|
ENV TINI_VERSION="0.18.0"
|
||||||
RUN apt-get update && \
|
ENV TINI_SUM="12d20136605531b09a2c2dac02ccee85e1b874eb322ef6baf7561cd93f93c855"
|
||||||
echo "Etc/UTC" > /etc/localtime && \
|
ADD https://github.com/krallin/tini/releases/download/v${TINI_VERSION}/tini /tini
|
||||||
groupadd -g "${GID}" mastodon && \
|
RUN echo "$TINI_SUM tini" | sha256sum -c -
|
||||||
useradd -l -u "$UID" -g "${GID}" -m -d /opt/mastodon mastodon && \
|
RUN chmod +x /tini
|
||||||
apt-get -y --no-install-recommends install whois \
|
|
||||||
wget \
|
|
||||||
procps \
|
|
||||||
libssl1.1 \
|
|
||||||
libpq5 \
|
|
||||||
imagemagick \
|
|
||||||
ffmpeg \
|
|
||||||
libjemalloc2 \
|
|
||||||
libicu67 \
|
|
||||||
libidn11 \
|
|
||||||
libyaml-0-2 \
|
|
||||||
file \
|
|
||||||
ca-certificates \
|
|
||||||
tzdata \
|
|
||||||
libreadline8 \
|
|
||||||
tini && \
|
|
||||||
ln -s /opt/mastodon /mastodon
|
|
||||||
|
|
||||||
# Note: no, cleaning here since Debian does this automatically
|
|
||||||
# See the file /etc/apt/apt.conf.d/docker-clean within the Docker image's filesystem
|
|
||||||
|
|
||||||
|
# Copy over mastodon source, and dependencies from building, and set permissions
|
||||||
COPY --chown=mastodon:mastodon . /opt/mastodon
|
COPY --chown=mastodon:mastodon . /opt/mastodon
|
||||||
COPY --chown=mastodon:mastodon --from=build /opt/mastodon /opt/mastodon
|
COPY --from=build-dep --chown=mastodon:mastodon /opt/mastodon /opt/mastodon
|
||||||
|
|
||||||
ENV RAILS_ENV="production" \
|
# Run mastodon services in prod mode
|
||||||
NODE_ENV="production" \
|
ENV RAILS_ENV="production"
|
||||||
RAILS_SERVE_STATIC_FILES="true" \
|
ENV NODE_ENV="production"
|
||||||
BIND="0.0.0.0"
|
|
||||||
|
# Tell rails to serve static files
|
||||||
|
ENV RAILS_SERVE_STATIC_FILES="true"
|
||||||
|
ENV BIND="0.0.0.0"
|
||||||
|
|
||||||
# Set the run user
|
# Set the run user
|
||||||
USER mastodon
|
USER mastodon
|
||||||
WORKDIR /opt/mastodon
|
|
||||||
|
|
||||||
# Precompile assets
|
# Precompile assets
|
||||||
RUN OTP_SECRET=precompile_placeholder SECRET_KEY_BASE=precompile_placeholder rails assets:precompile && \
|
RUN cd ~ && \
|
||||||
yarn cache clean
|
OTP_SECRET=precompile_placeholder SECRET_KEY_BASE=precompile_placeholder rails assets:precompile && \
|
||||||
|
yarn cache clean
|
||||||
|
|
||||||
# Set the work dir and the container entry point
|
# Set the work dir and the container entry point
|
||||||
ENTRYPOINT ["/usr/bin/tini", "--"]
|
WORKDIR /opt/mastodon
|
||||||
EXPOSE 3000 4000
|
ENTRYPOINT ["/tini", "--"]
|
||||||
|
|
|
@ -1,30 +0,0 @@
|
||||||
## ActivityPub federation 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.
|
|
||||||
|
|
||||||
Supported vocabulary: https://docs.joinmastodon.org/spec/activitypub/
|
|
||||||
|
|
||||||
### Required extensions
|
|
||||||
|
|
||||||
#### Webfinger
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
As a result, Mastodon requires that each ActivityPub actor uniquely maps back to an `acct:` URI that can be resolved via WebFinger.
|
|
||||||
|
|
||||||
More information and examples are available at: https://docs.joinmastodon.org/spec/webfinger/
|
|
||||||
|
|
||||||
#### HTTP Signatures
|
|
||||||
|
|
||||||
In order to authenticate activities, Mastodon relies on HTTP Signatures, signing every `POST` and `GET` request to other ActivityPub implementations on behalf of the user authoring an activity (for `POST` requests) or an actor representing the Mastodon server itself (for most `GET` requests).
|
|
||||||
|
|
||||||
Mastodon requires all `POST` requests to be signed, and MAY require `GET` requests to be signed, depending on the configuration of the Mastodon server.
|
|
||||||
|
|
||||||
More information on HTTP Signatures, as well as examples, can be found here: https://docs.joinmastodon.org/spec/security/#http
|
|
||||||
|
|
||||||
### Optional extensions
|
|
||||||
|
|
||||||
- Linked-Data Signatures: https://docs.joinmastodon.org/spec/security/#ld
|
|
||||||
- Bearcaps: https://docs.joinmastodon.org/spec/bearcaps/
|
|
||||||
- Followers collection synchronization: https://git.activitypub.dev/ActivityPubDev/Fediverse-Enhancement-Proposals/src/branch/main/feps/fep-8fcf.md
|
|
192
Gemfile
192
Gemfile
|
@ -1,116 +1,108 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
source 'https://rubygems.org'
|
source 'https://rubygems.org'
|
||||||
ruby '>= 2.7.0', '< 3.1.0'
|
ruby '>= 2.4.0', '< 2.7.0'
|
||||||
|
|
||||||
gem 'pkg-config', '~> 1.5'
|
gem 'pkg-config', '~> 1.3'
|
||||||
gem 'rexml', '~> 3.2'
|
|
||||||
|
|
||||||
gem 'puma', '~> 5.6'
|
gem 'puma', '~> 4.0'
|
||||||
gem 'rails', '~> 6.1.7'
|
gem 'rails', '~> 5.2.3'
|
||||||
gem 'sprockets', '~> 3.7.2'
|
gem 'thor', '~> 0.20'
|
||||||
gem 'thor', '~> 1.2'
|
|
||||||
gem 'rack', '~> 2.2.4'
|
|
||||||
|
|
||||||
gem 'hamlit-rails', '~> 0.2'
|
gem 'hamlit-rails', '~> 0.2'
|
||||||
gem 'pg', '~> 1.4'
|
gem 'pg', '~> 1.1'
|
||||||
gem 'makara', '~> 0.5'
|
gem 'makara', '~> 0.4'
|
||||||
gem 'pghero', '~> 2.8'
|
gem 'pghero', '~> 2.2'
|
||||||
gem 'dotenv-rails', '~> 2.8'
|
gem 'dotenv-rails', '~> 2.7'
|
||||||
|
|
||||||
gem 'aws-sdk-s3', '~> 1.117', require: false
|
gem 'aws-sdk-s3', '~> 1.46', require: false
|
||||||
gem 'fog-core', '<= 2.4.0'
|
gem 'fog-core', '<= 2.1.0'
|
||||||
gem 'fog-openstack', '~> 0.3', require: false
|
gem 'fog-openstack', '~> 0.3', require: false
|
||||||
gem 'kt-paperclip', '~> 7.1'
|
gem 'paperclip', '~> 6.0'
|
||||||
|
gem 'paperclip-av-transcoder', '~> 0.6'
|
||||||
|
gem 'streamio-ffmpeg', '~> 3.0'
|
||||||
gem 'blurhash', '~> 0.1'
|
gem 'blurhash', '~> 0.1'
|
||||||
|
|
||||||
gem 'active_model_serializers', '~> 0.10'
|
gem 'active_model_serializers', '~> 0.10'
|
||||||
gem 'addressable', '~> 2.8'
|
gem 'addressable', '~> 2.6'
|
||||||
gem 'bootsnap', '~> 1.15.0', require: false
|
gem 'bootsnap', '~> 1.4', require: false
|
||||||
gem 'browser'
|
gem 'browser'
|
||||||
gem 'charlock_holmes', '~> 0.7.7'
|
gem 'charlock_holmes', '~> 0.7.6'
|
||||||
gem 'chewy', '~> 7.2'
|
gem 'iso-639'
|
||||||
gem 'devise', '~> 4.8'
|
gem 'chewy', '~> 5.0'
|
||||||
gem 'devise-two-factor', '~> 4.0'
|
gem 'cld3', '~> 3.2.4'
|
||||||
|
gem 'devise', '~> 4.6'
|
||||||
|
gem 'devise-two-factor', '~> 3.0'
|
||||||
|
|
||||||
group :pam_authentication, optional: true do
|
group :pam_authentication, optional: true do
|
||||||
gem 'devise_pam_authenticatable2', '~> 9.2'
|
gem 'devise_pam_authenticatable2', '~> 9.2'
|
||||||
end
|
end
|
||||||
|
|
||||||
gem 'net-ldap', '~> 0.17'
|
gem 'net-ldap', '~> 0.10'
|
||||||
gem 'omniauth-cas', '~> 2.0'
|
gem 'omniauth-cas', '~> 1.1'
|
||||||
gem 'omniauth-saml', '~> 1.10'
|
gem 'omniauth-saml', '~> 1.10'
|
||||||
gem 'gitlab-omniauth-openid-connect', '~>0.10.0', require: 'omniauth_openid_connect'
|
|
||||||
gem 'omniauth', '~> 1.9'
|
gem 'omniauth', '~> 1.9'
|
||||||
gem 'omniauth-rails_csrf_protection', '~> 0.1'
|
|
||||||
|
|
||||||
gem 'color_diff', '~> 0.1'
|
gem 'doorkeeper', '~> 5.1'
|
||||||
gem 'discard', '~> 1.2'
|
|
||||||
gem 'doorkeeper', '~> 5.6'
|
|
||||||
gem 'ed25519', '~> 1.3'
|
|
||||||
gem 'fast_blank', '~> 1.0'
|
gem 'fast_blank', '~> 1.0'
|
||||||
gem 'fastimage'
|
gem 'fastimage'
|
||||||
|
gem 'goldfinger', '~> 2.1'
|
||||||
gem 'hiredis', '~> 0.6'
|
gem 'hiredis', '~> 0.6'
|
||||||
gem 'redis-namespace', '~> 1.9'
|
gem 'redis-namespace', '~> 1.5'
|
||||||
gem 'htmlentities', '~> 4.3'
|
gem 'htmlentities', '~> 4.3'
|
||||||
gem 'http', '~> 5.1'
|
gem 'http', '~> 3.3'
|
||||||
gem 'http_accept_language', '~> 2.1'
|
gem 'http_accept_language', '~> 2.1'
|
||||||
gem 'httplog', '~> 1.6.2'
|
gem 'http_parser.rb', '~> 0.6', git: 'https://github.com/tmm1/http_parser.rb', ref: '54b17ba8c7d8d20a16dfc65d1775241833219cf2'
|
||||||
|
gem 'httplog', '~> 1.3'
|
||||||
gem 'idn-ruby', require: 'idn'
|
gem 'idn-ruby', require: 'idn'
|
||||||
gem 'kaminari', '~> 1.2'
|
gem 'kaminari', '~> 1.1'
|
||||||
gem 'link_header', '~> 0.0'
|
gem 'link_header', '~> 0.0'
|
||||||
gem 'mime-types', '~> 3.4.1', require: 'mime/types/columnar'
|
gem 'mime-types', '~> 3.2', require: 'mime/types/columnar'
|
||||||
gem 'nokogiri', '~> 1.13'
|
gem 'nilsimsa', git: 'https://github.com/witgo/nilsimsa', ref: 'fd184883048b922b176939f851338d0a4971a532'
|
||||||
|
gem 'nokogiri', '~> 1.10'
|
||||||
gem 'nsa', '~> 0.2'
|
gem 'nsa', '~> 0.2'
|
||||||
gem 'oj', '~> 3.13'
|
gem 'oj', '~> 3.8'
|
||||||
gem 'ox', '~> 2.14'
|
gem 'ostatus2', '~> 2.0'
|
||||||
|
gem 'ox', '~> 2.11'
|
||||||
gem 'parslet'
|
gem 'parslet'
|
||||||
gem 'posix-spawn'
|
gem 'posix-spawn', git: 'https://github.com/rtomayko/posix-spawn', ref: '58465d2e213991f8afb13b984854a49fcdcc980c'
|
||||||
gem 'public_suffix', '~> 5.0'
|
gem 'pundit', '~> 2.0'
|
||||||
gem 'pundit', '~> 2.3'
|
|
||||||
gem 'premailer-rails'
|
gem 'premailer-rails'
|
||||||
gem 'rack-attack', '~> 6.6'
|
gem 'rack-attack', '~> 6.1'
|
||||||
gem 'rack-cors', '~> 1.1', require: 'rack/cors'
|
gem 'rack-cors', '~> 1.0', require: 'rack/cors'
|
||||||
gem 'rails-i18n', '~> 6.0'
|
gem 'rails-i18n', '~> 5.1'
|
||||||
gem 'rails-settings-cached', '~> 0.6'
|
gem 'rails-settings-cached', '~> 0.6'
|
||||||
gem 'redcarpet', '~> 3.5'
|
gem 'redis', '~> 4.1', require: ['redis', 'redis/connection/hiredis']
|
||||||
gem 'redis', '~> 4.5', require: ['redis', 'redis/connection/hiredis']
|
|
||||||
gem 'mario-redis-lock', '~> 1.2', require: 'redis_lock'
|
gem 'mario-redis-lock', '~> 1.2', require: 'redis_lock'
|
||||||
gem 'rqrcode', '~> 2.1'
|
gem 'rqrcode', '~> 0.10'
|
||||||
gem 'ruby-progressbar', '~> 1.11'
|
gem 'sanitize', '~> 5.0'
|
||||||
gem 'sanitize', '~> 6.0'
|
gem 'sidekiq', '~> 5.2'
|
||||||
gem 'scenic', '~> 1.7'
|
gem 'sidekiq-scheduler', '~> 3.0'
|
||||||
gem 'sidekiq', '~> 6.5'
|
gem 'sidekiq-unique-jobs', '~> 6.0'
|
||||||
gem 'sidekiq-scheduler', '~> 4.0'
|
gem 'sidekiq-bulk', '~>0.2.0'
|
||||||
gem 'sidekiq-unique-jobs', '~> 7.1'
|
gem 'simple-navigation', '~> 4.0'
|
||||||
gem 'sidekiq-bulk', '~> 0.2.0'
|
gem 'simple_form', '~> 4.1'
|
||||||
gem 'simple-navigation', '~> 4.4'
|
gem 'sprockets-rails', '~> 3.2', require: 'sprockets/railtie'
|
||||||
gem 'simple_form', '~> 5.1'
|
gem 'stoplight', '~> 2.1.3'
|
||||||
gem 'sprockets-rails', '~> 3.4', require: 'sprockets/railtie'
|
gem 'strong_migrations', '~> 0.4'
|
||||||
gem 'stoplight', '~> 3.0.1'
|
gem 'tty-command', '~> 0.8', require: false
|
||||||
gem 'strong_migrations', '~> 0.7'
|
gem 'tty-prompt', '~> 0.19', require: false
|
||||||
gem 'tty-prompt', '~> 0.23', require: false
|
gem 'twitter-text', '~> 1.14'
|
||||||
gem 'twitter-text', '~> 3.1.0'
|
gem 'tzinfo-data', '~> 1.2019'
|
||||||
gem 'tzinfo-data', '~> 1.2022'
|
gem 'webpacker', '~> 4.0'
|
||||||
gem 'webpacker', '~> 5.4'
|
gem 'webpush'
|
||||||
gem 'webpush', github: 'ClearlyClaire/webpush', ref: 'f14a4d52e201128b1b00245d11b6de80d6cfdcd9'
|
|
||||||
gem 'webauthn', '~> 2.5'
|
|
||||||
|
|
||||||
gem 'json-ld'
|
gem 'json-ld', git: 'https://github.com/ruby-rdf/json-ld.git', ref: '345b7a5733308af827e8491d284dbafa9128d7a2'
|
||||||
gem 'json-ld-preloaded', '~> 3.2'
|
gem 'json-ld-preloaded', '~> 3.0'
|
||||||
gem 'rdf-normalize', '~> 0.5'
|
gem 'rdf-normalize', '~> 0.3'
|
||||||
|
|
||||||
group :development, :test do
|
group :development, :test do
|
||||||
gem 'fabrication', '~> 2.30'
|
gem 'fabrication', '~> 2.20'
|
||||||
gem 'fuubar', '~> 2.5'
|
gem 'fuubar', '~> 2.4'
|
||||||
gem 'i18n-tasks', '~> 1.0', require: false
|
gem 'i18n-tasks', '~> 0.9', require: false
|
||||||
gem 'pry-byebug', '~> 3.10'
|
gem 'pry-byebug', '~> 3.7'
|
||||||
gem 'pry-rails', '~> 0.3'
|
gem 'pry-rails', '~> 0.3'
|
||||||
gem 'rspec-rails', '~> 5.1'
|
gem 'rspec-rails', '~> 3.8'
|
||||||
gem 'rubocop-performance', require: false
|
|
||||||
gem 'rubocop-rails', require: false
|
|
||||||
gem 'rubocop-rspec', require: false
|
|
||||||
gem 'rubocop', require: false
|
|
||||||
end
|
end
|
||||||
|
|
||||||
group :production, :test do
|
group :production, :test do
|
||||||
|
@ -118,44 +110,44 @@ group :production, :test do
|
||||||
end
|
end
|
||||||
|
|
||||||
group :test do
|
group :test do
|
||||||
gem 'capybara', '~> 3.38'
|
gem 'capybara', '~> 3.27'
|
||||||
gem 'climate_control', '~> 0.2'
|
gem 'climate_control', '~> 0.2'
|
||||||
gem 'faker', '~> 3.0'
|
gem 'faker', '~> 1.9'
|
||||||
gem 'json-schema', '~> 3.0'
|
gem 'microformats', '~> 4.1'
|
||||||
gem 'microformats', '~> 4.4'
|
|
||||||
gem 'rack-test', '~> 2.0'
|
|
||||||
gem 'rails-controller-testing', '~> 1.0'
|
gem 'rails-controller-testing', '~> 1.0'
|
||||||
gem 'rspec_junit_formatter', '~> 0.6'
|
gem 'rspec-sidekiq', '~> 3.0'
|
||||||
gem 'rspec-sidekiq', '~> 3.1'
|
gem 'simplecov', '~> 0.17', require: false
|
||||||
gem 'simplecov', '~> 0.21', require: false
|
gem 'webmock', '~> 3.6'
|
||||||
gem 'webmock', '~> 3.18'
|
gem 'parallel_tests', '~> 2.29'
|
||||||
end
|
end
|
||||||
|
|
||||||
group :development do
|
group :development do
|
||||||
gem 'active_record_query_trace', '~> 1.8'
|
gem 'active_record_query_trace', '~> 1.6'
|
||||||
gem 'annotate', '~> 3.2'
|
gem 'annotate', '~> 2.7'
|
||||||
gem 'better_errors', '~> 2.9'
|
gem 'better_errors', '~> 2.5'
|
||||||
gem 'binding_of_caller', '~> 1.0'
|
gem 'binding_of_caller', '~> 0.7'
|
||||||
gem 'bullet', '~> 7.0'
|
gem 'bullet', '~> 6.0'
|
||||||
gem 'letter_opener', '~> 1.8'
|
gem 'letter_opener', '~> 1.7'
|
||||||
gem 'letter_opener_web', '~> 2.0'
|
gem 'letter_opener_web', '~> 1.3'
|
||||||
gem 'memory_profiler'
|
gem 'memory_profiler'
|
||||||
gem 'brakeman', '~> 5.4', require: false
|
gem 'rubocop', '~> 0.73', require: false
|
||||||
gem 'bundler-audit', '~> 0.9', require: false
|
gem 'rubocop-rails', '~> 2.2', require: false
|
||||||
|
gem 'brakeman', '~> 4.6', require: false
|
||||||
|
gem 'bundler-audit', '~> 0.6', require: false
|
||||||
|
|
||||||
gem 'capistrano', '~> 3.17'
|
gem 'capistrano', '~> 3.11'
|
||||||
gem 'capistrano-rails', '~> 1.6'
|
gem 'capistrano-rails', '~> 1.4'
|
||||||
gem 'capistrano-rbenv', '~> 2.2'
|
gem 'capistrano-rbenv', '~> 2.1'
|
||||||
gem 'capistrano-yarn', '~> 2.0'
|
gem 'capistrano-yarn', '~> 2.0'
|
||||||
|
|
||||||
|
gem 'derailed_benchmarks'
|
||||||
gem 'stackprof'
|
gem 'stackprof'
|
||||||
end
|
end
|
||||||
|
|
||||||
group :production do
|
group :production do
|
||||||
gem 'lograge', '~> 0.12'
|
gem 'lograge', '~> 0.11'
|
||||||
|
gem 'redis-rails', '~> 5.0'
|
||||||
end
|
end
|
||||||
|
|
||||||
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 'cocoon', '~> 1.2'
|
|
||||||
|
|
1216
Gemfile.lock
1216
Gemfile.lock
File diff suppressed because it is too large
Load diff
2
Procfile
2
Procfile
|
@ -1,4 +1,4 @@
|
||||||
web: bin/heroku-web
|
web: if [ "$RUN_STREAMING" != "true" ]; then BIND=0.0.0.0 bundle exec puma -C config/puma.rb; else BIND=0.0.0.0 node ./streaming; fi
|
||||||
worker: bundle exec sidekiq
|
worker: bundle exec sidekiq
|
||||||
|
|
||||||
# For the streaming API, you need a separate app that shares Postgres and Redis:
|
# For the streaming API, you need a separate app that shares Postgres and Redis:
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
web: env PORT=3000 RAILS_ENV=development bundle exec puma -C config/puma.rb
|
web: env PORT=3000 bundle exec puma -C config/puma.rb
|
||||||
sidekiq: env PORT=3000 RAILS_ENV=development bundle exec sidekiq
|
sidekiq: env PORT=3000 bundle exec sidekiq
|
||||||
stream: env PORT=4000 yarn run start
|
stream: env PORT=4000 yarn run start
|
||||||
webpack: ./bin/webpack-dev-server --listen-host 0.0.0.0
|
webpack: ./bin/webpack-dev-server --listen-host 0.0.0.0
|
||||||
|
|
73
README.md
73
README.md
|
@ -1,22 +1,19 @@
|
||||||
<h1><picture>
|
![Mastodon](https://i.imgur.com/NhZc40l.png)
|
||||||
<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>
|
|
||||||
|
|
||||||
[![GitHub release](https://img.shields.io/github/release/mastodon/mastodon.svg)][releases]
|
[![GitHub release](https://img.shields.io/github/release/tootsuite/mastodon.svg)][releases]
|
||||||
[![Build Status](https://img.shields.io/circleci/project/github/mastodon/mastodon.svg)][circleci]
|
[![Build Status](https://img.shields.io/circleci/project/github/tootsuite/mastodon.svg)][circleci]
|
||||||
[![Code Climate](https://img.shields.io/codeclimate/maintainability/mastodon/mastodon.svg)][code_climate]
|
[![Code Climate](https://img.shields.io/codeclimate/maintainability/tootsuite/mastodon.svg)][code_climate]
|
||||||
[![Crowdin](https://d322cqt584bo4o.cloudfront.net/mastodon/localized.svg)][crowdin]
|
[![Crowdin](https://d322cqt584bo4o.cloudfront.net/mastodon/localized.svg)][crowdin]
|
||||||
[![Docker Pulls](https://img.shields.io/docker/pulls/tootsuite/mastodon.svg)][docker]
|
[![Docker Pulls](https://img.shields.io/docker/pulls/tootsuite/mastodon.svg)][docker]
|
||||||
|
|
||||||
[releases]: https://github.com/mastodon/mastodon/releases
|
[releases]: https://github.com/tootsuite/mastodon/releases
|
||||||
[circleci]: https://circleci.com/gh/mastodon/mastodon
|
[circleci]: https://circleci.com/gh/tootsuite/mastodon
|
||||||
[code_climate]: https://codeclimate.com/github/mastodon/mastodon
|
[code_climate]: https://codeclimate.com/github/tootsuite/mastodon
|
||||||
[crowdin]: https://crowdin.com/project/mastodon
|
[crowdin]: https://crowdin.com/project/mastodon
|
||||||
[docker]: https://hub.docker.com/r/tootsuite/mastodon/
|
[docker]: https://hub.docker.com/r/tootsuite/mastodon/
|
||||||
|
|
||||||
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, video. All Mastodon servers are interoperable as a federated network (users on one server can seamlessly communicate with users from another one, including non-Mastodon software that implements ActivityPub)!
|
Mastodon is a **free, open-source social network server** based on ActivityPub. Follow friends and discover new ones. Publish anything you want: links, pictures, text, video. All servers of Mastodon are interoperable as a federated network, i.e. users on one server can seamlessly communicate with users from another one. This includes non-Mastodon software that also implements ActivityPub!
|
||||||
|
|
||||||
Click below to **learn more** in a video:
|
Click below to **learn more** in a video:
|
||||||
|
|
||||||
|
@ -31,71 +28,65 @@ Click below to **learn more** in a video:
|
||||||
- [View sponsors](https://joinmastodon.org/sponsors)
|
- [View sponsors](https://joinmastodon.org/sponsors)
|
||||||
- [Blog](https://blog.joinmastodon.org)
|
- [Blog](https://blog.joinmastodon.org)
|
||||||
- [Documentation](https://docs.joinmastodon.org)
|
- [Documentation](https://docs.joinmastodon.org)
|
||||||
- [Browse Mastodon servers](https://joinmastodon.org/communities)
|
- [Browse Mastodon servers](https://joinmastodon.org/#getting-started)
|
||||||
- [Browse Mastodon apps](https://joinmastodon.org/apps)
|
- [Browse Mastodon apps](https://joinmastodon.org/apps)
|
||||||
|
|
||||||
[patreon]: https://www.patreon.com/mastodon
|
[patreon]: https://www.patreon.com/mastodon
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
<img src="/app/javascript/images/elephant_ui_working.svg?raw=true" align="right" width="30%" />
|
<img src="https://docs.joinmastodon.org/elephant.svg" align="right" width="30%" />
|
||||||
|
|
||||||
### No vendor lock-in: Fully interoperable with any conforming platform
|
**No vendor lock-in: Fully interoperable with any conforming platform**
|
||||||
|
|
||||||
It doesn't have to be Mastodon; whatever implements ActivityPub is part of the social network! [Learn more](https://blog.joinmastodon.org/2018/06/why-activitypub-is-the-future/)
|
It doesn't have to be Mastodon, whatever implements ActivityPub is part of the social network! [Learn more](https://blog.joinmastodon.org/2018/06/why-activitypub-is-the-future/)
|
||||||
|
|
||||||
### Real-time, chronological timeline updates
|
**Real-time, chronological timeline updates**
|
||||||
|
|
||||||
Updates of people you're following appear in real-time in the UI via WebSockets. There's a firehose view as well!
|
See the updates of people you're following appear in real-time in the UI via WebSockets. There's a firehose view as well!
|
||||||
|
|
||||||
### Media attachments like images and short videos
|
**Media attachments like images and short videos**
|
||||||
|
|
||||||
Upload and view images and WebM/MP4 videos attached to the updates. Videos with no audio track are treated like GIFs; normal videos loop continuously!
|
Upload and view images and WebM/MP4 videos attached to the updates. Videos with no audio track are treated like GIFs; normal videos are looped - like vines!
|
||||||
|
|
||||||
### Safety and moderation tools
|
**Safety and moderation tools**
|
||||||
|
|
||||||
Mastodon includes private posts, locked accounts, phrase filtering, muting, blocking and all sorts of other features, along with a reporting and moderation system. [Learn more](https://blog.joinmastodon.org/2018/07/cage-the-mastodon/)
|
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
|
**OAuth2 and a straightforward REST API**
|
||||||
|
|
||||||
Mastodon acts as an OAuth2 provider, so 3rd party apps can use the REST and Streaming APIs. This results in a rich app ecosystem with a lot of choices!
|
Mastodon acts as an OAuth2 provider so 3rd party apps can use the REST and Streaming APIs, resulting in a rich app ecosystem with a lot of choice!
|
||||||
|
|
||||||
## Deployment
|
## Deployment
|
||||||
|
|
||||||
### Tech stack:
|
**Tech stack:**
|
||||||
|
|
||||||
- **Ruby on Rails** powers the REST API and other web pages
|
- **Ruby on Rails** powers the REST API and other web pages
|
||||||
- **React.js** and Redux are used for the dynamic parts of the interface
|
- **React.js** and Redux are used for the dynamic parts of the interface
|
||||||
- **Node.js** powers the streaming API
|
- **Node.js** powers the streaming API
|
||||||
|
|
||||||
### Requirements:
|
**Requirements:**
|
||||||
|
|
||||||
- **PostgreSQL** 9.5+
|
- **PostgreSQL** 9.5+
|
||||||
- **Redis** 4+
|
- **Redis**
|
||||||
- **Ruby** 2.7+
|
- **Ruby** 2.4+
|
||||||
- **Node.js** 16+
|
- **Node.js** 8+
|
||||||
|
|
||||||
The repository includes deployment configurations for **Docker and docker-compose** as well as specific platforms like **Heroku**, **Scalingo**, and **Nanobox**. The [**standalone** installation guide](https://docs.joinmastodon.org/admin/install/) is available in the documentation.
|
The repository includes deployment configurations for **Docker and docker-compose**, but also a few specific platforms like **Heroku**, **Scalingo**, and **Nanobox**. The [**stand-alone** installation guide](https://docs.joinmastodon.org/administration/installation/) is available in the documentation.
|
||||||
|
|
||||||
A **Vagrant** configuration is included for development purposes. To use it, complete following steps:
|
A **Vagrant** configuration is included for development purposes.
|
||||||
|
|
||||||
- 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
|
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
Mastodon is **free, open-source software** licensed under **AGPLv3**.
|
Mastodon is **free, open source software** licensed under **AGPLv3**.
|
||||||
|
|
||||||
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).
|
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 Weblate. 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).
|
||||||
|
|
||||||
**IRC channel**: #mastodon on irc.libera.chat
|
**IRC channel**: #mastodon on irc.freenode.net
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
Copyright (C) 2016-2022 Eugen Rochko & other Mastodon contributors (see [AUTHORS.md](AUTHORS.md))
|
Copyright (C) 2016-2019 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 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.
|
||||||
|
|
||||||
|
|
17
SECURITY.md
17
SECURITY.md
|
@ -1,17 +0,0 @@
|
||||||
# Security Policy
|
|
||||||
|
|
||||||
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 reach us at <security@joinmastodon.org>.
|
|
||||||
|
|
||||||
You should *not* report such issues on GitHub or in other public spaces to give us time to publish a fix for the issue without exposing Mastodon's users to increased risk.
|
|
||||||
|
|
||||||
## 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
|
|
||||||
|
|
||||||
| Version | Supported |
|
|
||||||
| ------- | ----------|
|
|
||||||
| 4.0.x | Yes |
|
|
||||||
| 3.5.x | Yes |
|
|
||||||
| < 3.5 | No |
|
|
81
Vagrantfile
vendored
81
Vagrantfile
vendored
|
@ -3,14 +3,16 @@
|
||||||
|
|
||||||
ENV["PORT"] ||= "3000"
|
ENV["PORT"] ||= "3000"
|
||||||
|
|
||||||
$provisionA = <<SCRIPT
|
$provision = <<SCRIPT
|
||||||
|
|
||||||
|
cd /vagrant # This is where the host folder/repo is mounted
|
||||||
|
|
||||||
# Add the yarn repo + yarn repo keys
|
# Add the yarn repo + yarn repo keys
|
||||||
curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
|
curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
|
||||||
sudo apt-add-repository 'deb https://dl.yarnpkg.com/debian/ stable main'
|
sudo apt-add-repository 'deb https://dl.yarnpkg.com/debian/ stable main'
|
||||||
|
|
||||||
# Add repo for NodeJS
|
# Add repo for NodeJS
|
||||||
curl -sL https://deb.nodesource.com/setup_16.x | sudo bash -
|
curl -sL https://deb.nodesource.com/setup_8.x | sudo bash -
|
||||||
|
|
||||||
# Add firewall rule to redirect 80 to PORT and save
|
# Add firewall rule to redirect 80 to PORT and save
|
||||||
sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port #{ENV["PORT"]}
|
sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port #{ENV["PORT"]}
|
||||||
|
@ -31,74 +33,65 @@ sudo apt-get install \
|
||||||
redis-tools \
|
redis-tools \
|
||||||
postgresql \
|
postgresql \
|
||||||
postgresql-contrib \
|
postgresql-contrib \
|
||||||
|
protobuf-compiler \
|
||||||
|
yarn \
|
||||||
libicu-dev \
|
libicu-dev \
|
||||||
libidn11-dev \
|
libidn11-dev \
|
||||||
libreadline6-dev \
|
|
||||||
autoconf \
|
|
||||||
bison \
|
|
||||||
build-essential \
|
|
||||||
ffmpeg \
|
|
||||||
file \
|
|
||||||
gcc \
|
|
||||||
libffi-dev \
|
|
||||||
libgdbm-dev \
|
|
||||||
libjemalloc-dev \
|
|
||||||
libncurses5-dev \
|
|
||||||
libprotobuf-dev \
|
libprotobuf-dev \
|
||||||
libssl-dev \
|
libreadline-dev \
|
||||||
libyaml-dev \
|
libpam0g-dev \
|
||||||
pkg-config \
|
|
||||||
protobuf-compiler \
|
|
||||||
zlib1g-dev \
|
|
||||||
-y
|
-y
|
||||||
|
|
||||||
# Install rvm
|
# Install rvm
|
||||||
sudo apt-add-repository -y ppa:rael-gc/rvm
|
read RUBY_VERSION < .ruby-version
|
||||||
sudo apt-get install rvm -y
|
|
||||||
|
|
||||||
sudo usermod -a -G rvm $USER
|
gpg_command="gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 7D2BAF1CF37B13E2069D6956105BD0E739499BDB"
|
||||||
|
$($gpg_command)
|
||||||
|
if [ $? -ne 0 ];then
|
||||||
|
echo "GPG command failed, This prevented RVM from installing."
|
||||||
|
echo "Retrying once..." && $($gpg_command)
|
||||||
|
if [ $? -ne 0 ];then
|
||||||
|
echo "GPG failed for the second time, please ensure network connectivity."
|
||||||
|
echo "Exiting..." && exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
SCRIPT
|
curl -sSL https://raw.githubusercontent.com/rvm/rvm/stable/binscripts/rvm-installer | bash -s stable --ruby=$RUBY_VERSION
|
||||||
|
source /home/vagrant/.rvm/scripts/rvm
|
||||||
$provisionB = <<SCRIPT
|
|
||||||
|
|
||||||
source "/etc/profile.d/rvm.sh"
|
|
||||||
|
|
||||||
# Install Ruby
|
# Install Ruby
|
||||||
read RUBY_VERSION < /vagrant/.ruby-version
|
rvm reinstall ruby-$RUBY_VERSION --disable-binary
|
||||||
rvm install ruby-$RUBY_VERSION --disable-binary
|
|
||||||
|
|
||||||
# Configure database
|
# Configure database
|
||||||
sudo -u postgres createuser -U postgres vagrant -s
|
sudo -u postgres createuser -U postgres vagrant -s
|
||||||
sudo -u postgres createdb -U postgres mastodon_development
|
sudo -u postgres createdb -U postgres mastodon_development
|
||||||
|
|
||||||
cd /vagrant # This is where the host folder/repo is mounted
|
# Install gems and node modules
|
||||||
|
|
||||||
# Install gems
|
|
||||||
gem install bundler foreman
|
gem install bundler foreman
|
||||||
bundle install
|
bundle install
|
||||||
|
|
||||||
# Install node modules
|
|
||||||
sudo corepack enable
|
|
||||||
yarn set version classic
|
|
||||||
yarn install
|
yarn install
|
||||||
|
|
||||||
# Build Mastodon
|
# Build Mastodon
|
||||||
export RAILS_ENV=development
|
|
||||||
export $(cat ".env.vagrant" | xargs)
|
export $(cat ".env.vagrant" | xargs)
|
||||||
bundle exec rails db:setup
|
bundle exec rails db:setup
|
||||||
|
|
||||||
# Configure automatic loading of environment variable
|
# Configure automatic loading of environment variable
|
||||||
echo 'export RAILS_ENV=development' >> ~/.bash_profile
|
|
||||||
echo 'export $(cat "/vagrant/.env.vagrant" | xargs)' >> ~/.bash_profile
|
echo 'export $(cat "/vagrant/.env.vagrant" | xargs)' >> ~/.bash_profile
|
||||||
|
|
||||||
SCRIPT
|
SCRIPT
|
||||||
|
|
||||||
|
$start = <<SCRIPT
|
||||||
|
|
||||||
|
echo 'To start server'
|
||||||
|
echo ' $ vagrant ssh -c "cd /vagrant && foreman start"'
|
||||||
|
|
||||||
|
SCRIPT
|
||||||
|
|
||||||
VAGRANTFILE_API_VERSION = "2"
|
VAGRANTFILE_API_VERSION = "2"
|
||||||
|
|
||||||
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
|
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
|
||||||
|
|
||||||
config.vm.box = "ubuntu/focal64"
|
config.vm.box = "ubuntu/xenial64"
|
||||||
|
|
||||||
config.vm.provider :virtualbox do |vb|
|
config.vm.provider :virtualbox do |vb|
|
||||||
vb.name = "mastodon"
|
vb.name = "mastodon"
|
||||||
|
@ -115,6 +108,7 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
|
||||||
# Use "virtio" network interfaces for better performance.
|
# Use "virtio" network interfaces for better performance.
|
||||||
vb.customize ["modifyvm", :id, "--nictype1", "virtio"]
|
vb.customize ["modifyvm", :id, "--nictype1", "virtio"]
|
||||||
vb.customize ["modifyvm", :id, "--nictype2", "virtio"]
|
vb.customize ["modifyvm", :id, "--nictype2", "virtio"]
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# This uses the vagrant-hostsupdater plugin, and lets you
|
# This uses the vagrant-hostsupdater plugin, and lets you
|
||||||
|
@ -132,7 +126,7 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
|
||||||
end
|
end
|
||||||
|
|
||||||
if config.vm.networks.any? { |type, options| type == :private_network }
|
if config.vm.networks.any? { |type, options| type == :private_network }
|
||||||
config.vm.synced_folder ".", "/vagrant", type: "nfs", mount_options: ['rw', 'actimeo=1']
|
config.vm.synced_folder ".", "/vagrant", type: "nfs", mount_options: ['rw', 'vers=3', 'tcp', 'actimeo=1']
|
||||||
else
|
else
|
||||||
config.vm.synced_folder ".", "/vagrant"
|
config.vm.synced_folder ".", "/vagrant"
|
||||||
end
|
end
|
||||||
|
@ -143,12 +137,9 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
|
||||||
config.vm.network :forwarded_port, guest: 8080, host: 8080
|
config.vm.network :forwarded_port, guest: 8080, host: 8080
|
||||||
|
|
||||||
# Full provisioning script, only runs on first 'vagrant up' or with 'vagrant provision'
|
# Full provisioning script, only runs on first 'vagrant up' or with 'vagrant provision'
|
||||||
config.vm.provision :shell, inline: $provisionA, privileged: false, reset: true
|
config.vm.provision :shell, inline: $provision, privileged: false
|
||||||
config.vm.provision :shell, inline: $provisionB, privileged: false
|
|
||||||
|
|
||||||
config.vm.post_up_message = <<MESSAGE
|
# Start up script, runs on every 'vagrant up'
|
||||||
To start server
|
config.vm.provision :shell, inline: $start, run: 'always', privileged: false
|
||||||
$ vagrant ssh -c "cd /vagrant && foreman start"
|
|
||||||
MESSAGE
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
28
app.json
28
app.json
|
@ -1,8 +1,8 @@
|
||||||
{
|
{
|
||||||
"name": "Mastodon",
|
"name": "Mastodon",
|
||||||
"description": "A GNU Social-compatible microblogging server",
|
"description": "A GNU Social-compatible microblogging server",
|
||||||
"repository": "https://github.com/mastodon/mastodon",
|
"repository": "https://github.com/tootsuite/mastodon",
|
||||||
"logo": "https://github.com/mastodon.png",
|
"logo": "https://github.com/tootsuite.png",
|
||||||
"env": {
|
"env": {
|
||||||
"HEROKU": {
|
"HEROKU": {
|
||||||
"description": "Leave this as true",
|
"description": "Leave this as true",
|
||||||
|
@ -13,6 +13,15 @@
|
||||||
"description": "The domain that your Mastodon instance will run on (this can be appname.herokuapp.com or a custom domain)",
|
"description": "The domain that your Mastodon instance will run on (this can be appname.herokuapp.com or a custom domain)",
|
||||||
"required": true
|
"required": true
|
||||||
},
|
},
|
||||||
|
"LOCAL_HTTPS": {
|
||||||
|
"description": "Will your domain support HTTPS? (Automatic for herokuapp, requires manual configuration for custom domains)",
|
||||||
|
"value": "false",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
"PAPERCLIP_SECRET": {
|
||||||
|
"description": "The secret key for storing media files",
|
||||||
|
"generator": "secret"
|
||||||
|
},
|
||||||
"SECRET_KEY_BASE": {
|
"SECRET_KEY_BASE": {
|
||||||
"description": "The secret key base",
|
"description": "The secret key base",
|
||||||
"generator": "secret"
|
"generator": "secret"
|
||||||
|
@ -79,13 +88,8 @@
|
||||||
"description": "SMTP server certificate verification mode. Defaults is 'peer'.",
|
"description": "SMTP server certificate verification mode. Defaults is 'peer'.",
|
||||||
"required": false
|
"required": false
|
||||||
},
|
},
|
||||||
"SMTP_ENABLE_STARTTLS": {
|
|
||||||
"description": "Enable STARTTLS? Default is 'auto'.",
|
|
||||||
"value": "auto",
|
|
||||||
"required": false
|
|
||||||
},
|
|
||||||
"SMTP_ENABLE_STARTTLS_AUTO": {
|
"SMTP_ENABLE_STARTTLS_AUTO": {
|
||||||
"description": "Enable STARTTLS if SMTP server supports it? Deprecated by SMTP_ENABLE_STARTTLS.",
|
"description": "Enable STARTTLS if SMTP server supports it? Default is true.",
|
||||||
"required": false
|
"required": false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -93,6 +97,9 @@
|
||||||
{
|
{
|
||||||
"url": "https://github.com/heroku/heroku-buildpack-apt"
|
"url": "https://github.com/heroku/heroku-buildpack-apt"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"url": "heroku/nodejs"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"url": "heroku/ruby"
|
"url": "heroku/ruby"
|
||||||
}
|
}
|
||||||
|
@ -100,5 +107,8 @@
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"postdeploy": "bundle exec rails db:migrate && bundle exec rails db:seed"
|
"postdeploy": "bundle exec rails db:migrate && bundle exec rails db:seed"
|
||||||
},
|
},
|
||||||
"addons": ["heroku-postgresql", "heroku-redis"]
|
"addons": [
|
||||||
|
"heroku-postgresql",
|
||||||
|
"heroku-redis"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,43 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
class AccountsIndex < Chewy::Index
|
|
||||||
settings index: { refresh_interval: '30s' }, analysis: {
|
|
||||||
analyzer: {
|
|
||||||
content: {
|
|
||||||
tokenizer: 'whitespace',
|
|
||||||
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)
|
|
||||||
|
|
||||||
root date_detection: false do
|
|
||||||
field :id, type: 'long'
|
|
||||||
|
|
||||||
field :display_name, type: 'text', analyzer: 'content' do
|
|
||||||
field :edge_ngram, type: 'text', analyzer: 'edge_ngram', search_analyzer: 'content'
|
|
||||||
end
|
|
||||||
|
|
||||||
field :acct, type: 'text', analyzer: 'content', value: ->(account) { [account.username, account.domain].compact.join('@') } do
|
|
||||||
field :edge_ngram, type: 'text', analyzer: 'edge_ngram', search_analyzer: 'content'
|
|
||||||
end
|
|
||||||
|
|
||||||
field :following_count, type: 'long', value: ->(account) { account.following_count }
|
|
||||||
field :followers_count, type: 'long', value: ->(account) { account.followers_count }
|
|
||||||
field :last_status_at, type: 'date', value: ->(account) { account.last_status_at || account.created_at }
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,9 +1,7 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class StatusesIndex < Chewy::Index
|
class StatusesIndex < Chewy::Index
|
||||||
include FormattingHelper
|
settings index: { refresh_interval: '15m' }, analysis: {
|
||||||
|
|
||||||
settings index: { refresh_interval: '30s' }, analysis: {
|
|
||||||
filter: {
|
filter: {
|
||||||
english_stop: {
|
english_stop: {
|
||||||
type: 'stop',
|
type: 'stop',
|
||||||
|
@ -33,43 +31,31 @@ class StatusesIndex < Chewy::Index
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
# We do not use delete_if option here because it would call a method that we
|
define_type ::Status.unscoped.without_reblogs.includes(:media_attachments) do
|
||||||
# expect to be called with crutches without crutches, causing n+1 queries
|
crutch :mentions do |collection|
|
||||||
index_scope ::Status.unscoped.kept.without_reblogs.includes(:media_attachments, :preloadable_poll)
|
data = ::Mention.where(status_id: collection.map(&:id)).pluck(:status_id, :account_id)
|
||||||
|
data.each.with_object({}) { |(id, name), result| (result[id] ||= []).push(name) }
|
||||||
crutch :mentions do |collection|
|
|
||||||
data = ::Mention.where(status_id: collection.map(&:id)).where(account: Account.local, silent: false).pluck(:status_id, :account_id)
|
|
||||||
data.each.with_object({}) { |(id, name), result| (result[id] ||= []).push(name) }
|
|
||||||
end
|
|
||||||
|
|
||||||
crutch :favourites do |collection|
|
|
||||||
data = ::Favourite.where(status_id: collection.map(&:id)).where(account: Account.local).pluck(:status_id, :account_id)
|
|
||||||
data.each.with_object({}) { |(id, name), result| (result[id] ||= []).push(name) }
|
|
||||||
end
|
|
||||||
|
|
||||||
crutch :reblogs do |collection|
|
|
||||||
data = ::Status.where(reblog_of_id: collection.map(&:id)).where(account: Account.local).pluck(:reblog_of_id, :account_id)
|
|
||||||
data.each.with_object({}) { |(id, name), result| (result[id] ||= []).push(name) }
|
|
||||||
end
|
|
||||||
|
|
||||||
crutch :bookmarks do |collection|
|
|
||||||
data = ::Bookmark.where(status_id: collection.map(&:id)).where(account: Account.local).pluck(:status_id, :account_id)
|
|
||||||
data.each.with_object({}) { |(id, name), result| (result[id] ||= []).push(name) }
|
|
||||||
end
|
|
||||||
|
|
||||||
crutch :votes do |collection|
|
|
||||||
data = ::PollVote.joins(:poll).where(poll: { status_id: collection.map(&:id) }).where(account: Account.local).pluck(:status_id, :account_id)
|
|
||||||
data.each.with_object({}) { |(id, name), result| (result[id] ||= []).push(name) }
|
|
||||||
end
|
|
||||||
|
|
||||||
root date_detection: false do
|
|
||||||
field :id, type: 'long'
|
|
||||||
field :account_id, type: 'long'
|
|
||||||
|
|
||||||
field :text, type: 'text', value: ->(status) { status.searchable_text } do
|
|
||||||
field :stemmed, type: 'text', analyzer: 'content'
|
|
||||||
end
|
end
|
||||||
|
|
||||||
field :searchable_by, type: 'long', value: ->(status, crutches) { status.searchable_by(crutches) }
|
crutch :favourites do |collection|
|
||||||
|
data = ::Favourite.where(status_id: collection.map(&:id)).pluck(:status_id, :account_id)
|
||||||
|
data.each.with_object({}) { |(id, name), result| (result[id] ||= []).push(name) }
|
||||||
|
end
|
||||||
|
|
||||||
|
crutch :reblogs do |collection|
|
||||||
|
data = ::Status.where(reblog_of_id: collection.map(&:id)).pluck(:reblog_of_id, :account_id)
|
||||||
|
data.each.with_object({}) { |(id, name), result| (result[id] ||= []).push(name) }
|
||||||
|
end
|
||||||
|
|
||||||
|
root date_detection: false do
|
||||||
|
field :id, type: 'long'
|
||||||
|
field :account_id, type: 'long'
|
||||||
|
|
||||||
|
field :text, type: 'text', value: ->(status) { [status.spoiler_text, Formatter.instance.plaintext(status)].concat(status.media_attachments.map(&:description)).concat(status.preloadable_poll ? status.preloadable_poll.options : []).join("\n\n") } do
|
||||||
|
field :stemmed, type: 'text', analyzer: 'content'
|
||||||
|
end
|
||||||
|
|
||||||
|
field :searchable_by, type: 'long', value: ->(status, crutches) { status.searchable_by(crutches) }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,41 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
class TagsIndex < Chewy::Index
|
|
||||||
settings index: { refresh_interval: '30s' }, analysis: {
|
|
||||||
analyzer: {
|
|
||||||
content: {
|
|
||||||
tokenizer: 'keyword',
|
|
||||||
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: 2,
|
|
||||||
max_gram: 15,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
index_scope ::Tag.listable
|
|
||||||
|
|
||||||
crutch :time_period do
|
|
||||||
7.days.ago.to_date..0.days.ago.to_date
|
|
||||||
end
|
|
||||||
|
|
||||||
root date_detection: false do
|
|
||||||
field :name, type: 'text', analyzer: 'content' do
|
|
||||||
field :edge_ngram, type: 'text', analyzer: 'edge_ngram', search_analyzer: 'content'
|
|
||||||
end
|
|
||||||
|
|
||||||
field :reviewed, type: 'boolean', value: ->(tag) { tag.reviewed? }
|
|
||||||
field :usage, type: 'long', value: ->(tag, crutches) { tag.history.aggregate(crutches.time_period).accounts }
|
|
||||||
field :last_status_at, type: 'date', value: ->(tag) { tag.last_status_at || tag.created_at }
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,19 +1,42 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class AboutController < ApplicationController
|
class AboutController < ApplicationController
|
||||||
include WebAppControllerConcern
|
layout 'public'
|
||||||
|
|
||||||
skip_before_action :require_functional!
|
|
||||||
|
|
||||||
|
before_action :set_body_classes, only: :show
|
||||||
before_action :set_instance_presenter
|
before_action :set_instance_presenter
|
||||||
|
before_action :set_expires_in
|
||||||
|
|
||||||
def show
|
skip_before_action :require_functional!, only: [:more, :terms]
|
||||||
expires_in 0, public: true unless user_signed_in?
|
|
||||||
|
def show; end
|
||||||
|
|
||||||
|
def more
|
||||||
|
flash.now[:notice] = I18n.t('about.instance_actor_flash') if params[:instance_actor]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def terms; end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def new_user
|
||||||
|
User.new.tap do |user|
|
||||||
|
user.build_account
|
||||||
|
user.build_invite_request
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
helper_method :new_user
|
||||||
|
|
||||||
def set_instance_presenter
|
def set_instance_presenter
|
||||||
@instance_presenter = InstancePresenter.new
|
@instance_presenter = InstancePresenter.new
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def set_body_classes
|
||||||
|
@hide_navbar = true
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_expires_in
|
||||||
|
expires_in 0, public: true
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
12
app/controllers/account_follow_controller.rb
Normal file
12
app/controllers/account_follow_controller.rb
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class AccountFollowController < ApplicationController
|
||||||
|
include AccountControllerConcern
|
||||||
|
|
||||||
|
before_action :authenticate_user!
|
||||||
|
|
||||||
|
def create
|
||||||
|
FollowService.new.call(current_user.account, @account.acct)
|
||||||
|
redirect_to account_path(@account)
|
||||||
|
end
|
||||||
|
end
|
12
app/controllers/account_unfollow_controller.rb
Normal file
12
app/controllers/account_unfollow_controller.rb
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class AccountUnfollowController < ApplicationController
|
||||||
|
include AccountControllerConcern
|
||||||
|
|
||||||
|
before_action :authenticate_user!
|
||||||
|
|
||||||
|
def create
|
||||||
|
UnfollowService.new.call(current_user.account, @account)
|
||||||
|
redirect_to account_path(@account)
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,43 +1,61 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class AccountsController < ApplicationController
|
class AccountsController < ApplicationController
|
||||||
PAGE_SIZE = 20
|
PAGE_SIZE = 20
|
||||||
PAGE_SIZE_MAX = 200
|
|
||||||
|
|
||||||
include AccountControllerConcern
|
include AccountControllerConcern
|
||||||
include SignatureAuthentication
|
include SignatureAuthentication
|
||||||
|
|
||||||
before_action :require_account_signature!, if: -> { request.format == :json && authorized_fetch_mode? }
|
|
||||||
before_action :set_cache_headers
|
before_action :set_cache_headers
|
||||||
|
before_action :set_body_classes
|
||||||
skip_around_action :set_locale, if: -> { [:json, :rss].include?(request.format&.to_sym) }
|
|
||||||
skip_before_action :require_functional!, unless: :whitelist_mode?
|
|
||||||
|
|
||||||
def show
|
def show
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html do
|
format.html do
|
||||||
expires_in 0, public: true unless user_signed_in?
|
expires_in 0, public: true unless user_signed_in?
|
||||||
|
|
||||||
@rss_url = rss_url
|
@pinned_statuses = []
|
||||||
|
@endorsed_accounts = @account.endorsed_accounts.to_a.sample(4)
|
||||||
|
|
||||||
|
if current_account && @account.blocking?(current_account)
|
||||||
|
@statuses = []
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
@pinned_statuses = cache_collection(@account.pinned_statuses, Status) if show_pinned_statuses?
|
||||||
|
@statuses = filtered_status_page(params)
|
||||||
|
@statuses = cache_collection(@statuses, Status)
|
||||||
|
|
||||||
|
unless @statuses.empty?
|
||||||
|
@older_url = older_url if @statuses.last.id > filtered_statuses.last.id
|
||||||
|
@newer_url = newer_url if @statuses.first.id < filtered_statuses.first.id
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
format.rss do
|
format.rss do
|
||||||
expires_in 1.minute, public: true
|
expires_in 0, public: true
|
||||||
|
|
||||||
limit = params[:limit].present? ? [params[:limit].to_i, PAGE_SIZE_MAX].min : PAGE_SIZE
|
@statuses = cache_collection(default_statuses.without_reblogs.without_replies.limit(PAGE_SIZE), Status)
|
||||||
@statuses = filtered_statuses.without_reblogs.limit(limit)
|
render xml: RSS::AccountSerializer.render(@account, @statuses)
|
||||||
@statuses = cache_collection(@statuses, Status)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
format.json do
|
format.json do
|
||||||
expires_in 3.minutes, public: !(authorized_fetch_mode? && signed_request_account.present?)
|
expires_in 3.minutes, public: !(authorized_fetch_mode? && signed_request_account.present?)
|
||||||
render_with_cache json: @account, content_type: 'application/activity+json', serializer: ActivityPub::ActorSerializer, adapter: ActivityPub::Adapter
|
render_with_cache json: @account, content_type: 'application/activity+json', serializer: ActivityPub::ActorSerializer, adapter: ActivityPub::Adapter, fields: restrict_fields_to
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def set_body_classes
|
||||||
|
@body_classes = 'with-modals'
|
||||||
|
end
|
||||||
|
|
||||||
|
def show_pinned_statuses?
|
||||||
|
[replies_requested?, media_requested?, tag_requested?, params[:max_id].present?, params[:min_id].present?].none?
|
||||||
|
end
|
||||||
|
|
||||||
def filtered_statuses
|
def filtered_statuses
|
||||||
default_statuses.tap do |statuses|
|
default_statuses.tap do |statuses|
|
||||||
statuses.merge!(hashtag_scope) if tag_requested?
|
statuses.merge!(hashtag_scope) if tag_requested?
|
||||||
|
@ -51,7 +69,11 @@ class AccountsController < ApplicationController
|
||||||
end
|
end
|
||||||
|
|
||||||
def only_media_scope
|
def only_media_scope
|
||||||
Status.joins(:media_attachments).merge(@account.media_attachments.reorder(nil)).group(:id)
|
Status.where(id: account_media_status_ids)
|
||||||
|
end
|
||||||
|
|
||||||
|
def account_media_status_ids
|
||||||
|
@account.media_attachments.attached.reorder(nil).select(:status_id).distinct
|
||||||
end
|
end
|
||||||
|
|
||||||
def no_replies_scope
|
def no_replies_scope
|
||||||
|
@ -72,40 +94,51 @@ class AccountsController < ApplicationController
|
||||||
params[:username]
|
params[:username]
|
||||||
end
|
end
|
||||||
|
|
||||||
def skip_temporary_suspension_response?
|
def older_url
|
||||||
request.format == :json
|
pagination_url(max_id: @statuses.last.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
def rss_url
|
def newer_url
|
||||||
|
pagination_url(min_id: @statuses.first.id)
|
||||||
|
end
|
||||||
|
|
||||||
|
def pagination_url(max_id: nil, min_id: nil)
|
||||||
if tag_requested?
|
if tag_requested?
|
||||||
short_account_tag_url(@account, params[:tag], format: 'rss')
|
short_account_tag_url(@account, params[:tag], max_id: max_id, min_id: min_id)
|
||||||
|
elsif media_requested?
|
||||||
|
short_account_media_url(@account, max_id: max_id, min_id: min_id)
|
||||||
|
elsif replies_requested?
|
||||||
|
short_account_with_replies_url(@account, max_id: max_id, min_id: min_id)
|
||||||
else
|
else
|
||||||
short_account_url(@account, format: 'rss')
|
short_account_url(@account, max_id: max_id, min_id: min_id)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def media_requested?
|
def media_requested?
|
||||||
request.path.split('.').first.end_with?('/media') && !tag_requested?
|
request.path.ends_with?('/media')
|
||||||
end
|
end
|
||||||
|
|
||||||
def replies_requested?
|
def replies_requested?
|
||||||
request.path.split('.').first.end_with?('/with_replies') && !tag_requested?
|
request.path.ends_with?('/with_replies')
|
||||||
end
|
end
|
||||||
|
|
||||||
def tag_requested?
|
def tag_requested?
|
||||||
request.path.split('.').first.end_with?(Addressable::URI.parse("/tagged/#{params[:tag]}").normalize)
|
request.path.ends_with?(Addressable::URI.parse("/tagged/#{params[:tag]}").normalize)
|
||||||
end
|
end
|
||||||
|
|
||||||
def cached_filtered_status_page
|
def filtered_status_page(params)
|
||||||
cache_collection_paginated_by_id(
|
if params[:min_id].present?
|
||||||
filtered_statuses,
|
filtered_statuses.paginate_by_min_id(PAGE_SIZE, params[:min_id]).reverse
|
||||||
Status,
|
else
|
||||||
PAGE_SIZE,
|
filtered_statuses.paginate_by_max_id(PAGE_SIZE, params[:max_id], params[:since_id]).to_a
|
||||||
params_slice(:max_id, :min_id, :since_id)
|
end
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def params_slice(*keys)
|
def restrict_fields_to
|
||||||
params.slice(*keys).permit(*keys)
|
if signed_request_account.present? || public_fetch_mode?
|
||||||
|
# Return all fields
|
||||||
|
else
|
||||||
|
%i(id type preferred_username inbox public_key endpoints)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,17 +1,9 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class ActivityPub::BaseController < Api::BaseController
|
class ActivityPub::BaseController < Api::BaseController
|
||||||
skip_before_action :require_authenticated_user!
|
|
||||||
skip_before_action :require_not_suspended!
|
|
||||||
skip_around_action :set_locale
|
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def set_cache_headers
|
def set_cache_headers
|
||||||
response.headers['Vary'] = 'Signature' if authorized_fetch_mode?
|
response.headers['Vary'] = 'Signature' if authorized_fetch_mode?
|
||||||
end
|
end
|
||||||
|
|
||||||
def skip_temporary_suspension_response?
|
|
||||||
false
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,21 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
class ActivityPub::ClaimsController < ActivityPub::BaseController
|
|
||||||
include SignatureVerification
|
|
||||||
include AccountOwnedConcern
|
|
||||||
|
|
||||||
skip_before_action :authenticate_user!
|
|
||||||
|
|
||||||
before_action :require_account_signature!
|
|
||||||
before_action :set_claim_result
|
|
||||||
|
|
||||||
def create
|
|
||||||
render json: @claim_result, serializer: ActivityPub::OneTimeKeySerializer
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def set_claim_result
|
|
||||||
@claim_result = ::Keys::ClaimService.new.call(@account.id, params[:id])
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -4,71 +4,49 @@ class ActivityPub::CollectionsController < ActivityPub::BaseController
|
||||||
include SignatureVerification
|
include SignatureVerification
|
||||||
include AccountOwnedConcern
|
include AccountOwnedConcern
|
||||||
|
|
||||||
before_action :require_account_signature!, if: :authorized_fetch_mode?
|
before_action :require_signature!, if: :authorized_fetch_mode?
|
||||||
before_action :set_items
|
|
||||||
before_action :set_size
|
before_action :set_size
|
||||||
before_action :set_type
|
before_action :set_statuses
|
||||||
before_action :set_cache_headers
|
before_action :set_cache_headers
|
||||||
|
|
||||||
def show
|
def show
|
||||||
expires_in 3.minutes, public: public_fetch_mode?
|
expires_in 3.minutes, public: public_fetch_mode?
|
||||||
render_with_cache json: collection_presenter, content_type: 'application/activity+json', serializer: ActivityPub::CollectionSerializer, adapter: ActivityPub::Adapter
|
render_with_cache json: collection_presenter, content_type: 'application/activity+json', serializer: ActivityPub::CollectionSerializer, adapter: ActivityPub::Adapter, skip_activities: true
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def set_items
|
def set_statuses
|
||||||
case params[:id]
|
@statuses = scope_for_collection
|
||||||
when 'featured'
|
@statuses = cache_collection(@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) }
|
|
||||||
when 'tags'
|
|
||||||
@items = for_signed_account { @account.featured_tags }
|
|
||||||
when 'devices'
|
|
||||||
@items = @account.devices
|
|
||||||
else
|
|
||||||
not_found
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_size
|
def set_size
|
||||||
case params[:id]
|
case params[:id]
|
||||||
when 'featured', 'devices', 'tags'
|
when 'featured'
|
||||||
@size = @items.size
|
@account.pinned_statuses.count
|
||||||
else
|
else
|
||||||
not_found
|
raise ActiveRecord::RecordNotFound
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_type
|
def scope_for_collection
|
||||||
case params[:id]
|
case params[:id]
|
||||||
when 'featured'
|
when 'featured'
|
||||||
@type = :ordered
|
@account.statuses.permitted_for(@account, signed_request_account).tap do |scope|
|
||||||
when 'devices', 'tags'
|
scope.merge!(@account.pinned_statuses)
|
||||||
@type = :unordered
|
end
|
||||||
else
|
else
|
||||||
not_found
|
raise ActiveRecord::RecordNotFound
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def collection_presenter
|
def collection_presenter
|
||||||
ActivityPub::CollectionPresenter.new(
|
ActivityPub::CollectionPresenter.new(
|
||||||
id: account_collection_url(@account, params[:id]),
|
id: account_collection_url(@account, params[:id]),
|
||||||
type: @type,
|
type: :ordered,
|
||||||
size: @size,
|
size: @size,
|
||||||
items: @items
|
items: @statuses
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def for_signed_account
|
|
||||||
# Because in public fetch mode we cache the response, there would be no
|
|
||||||
# benefit from performing the check below, since a blocked account or domain
|
|
||||||
# would likely be served the cache from the reverse proxy anyway
|
|
||||||
|
|
||||||
if authorized_fetch_mode? && !signed_request_account.nil? && (@account.blocking?(signed_request_account) || (!signed_request_account.domain.nil? && @account.domain_blocking?(signed_request_account.domain)))
|
|
||||||
[]
|
|
||||||
else
|
|
||||||
yield
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,36 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
class ActivityPub::FollowersSynchronizationsController < ActivityPub::BaseController
|
|
||||||
include SignatureVerification
|
|
||||||
include AccountOwnedConcern
|
|
||||||
|
|
||||||
before_action :require_account_signature!
|
|
||||||
before_action :set_items
|
|
||||||
before_action :set_cache_headers
|
|
||||||
|
|
||||||
def show
|
|
||||||
expires_in 0, public: false
|
|
||||||
render json: collection_presenter,
|
|
||||||
serializer: ActivityPub::CollectionSerializer,
|
|
||||||
adapter: ActivityPub::Adapter,
|
|
||||||
content_type: 'application/activity+json'
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def uri_prefix
|
|
||||||
signed_request_account.uri[Account::URL_PREFIX_RE]
|
|
||||||
end
|
|
||||||
|
|
||||||
def set_items
|
|
||||||
@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
|
|
||||||
|
|
||||||
def collection_presenter
|
|
||||||
ActivityPub::CollectionPresenter.new(
|
|
||||||
id: account_followers_synchronization_url(@account),
|
|
||||||
type: :ordered,
|
|
||||||
items: @items
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,30 +1,28 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class ActivityPub::InboxesController < ActivityPub::BaseController
|
class ActivityPub::InboxesController < Api::BaseController
|
||||||
include SignatureVerification
|
include SignatureVerification
|
||||||
include JsonLdHelper
|
include JsonLdHelper
|
||||||
include AccountOwnedConcern
|
include AccountOwnedConcern
|
||||||
|
|
||||||
before_action :skip_unknown_actor_activity
|
before_action :skip_unknown_actor_delete
|
||||||
before_action :require_actor_signature!
|
before_action :require_signature!
|
||||||
skip_before_action :authenticate_user!
|
|
||||||
|
|
||||||
def create
|
def create
|
||||||
upgrade_account
|
upgrade_account
|
||||||
process_collection_synchronization
|
|
||||||
process_payload
|
process_payload
|
||||||
head 202
|
head 202
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def skip_unknown_actor_activity
|
def skip_unknown_actor_delete
|
||||||
head 202 if unknown_affected_account?
|
head 202 if unknown_deleted_account?
|
||||||
end
|
end
|
||||||
|
|
||||||
def unknown_affected_account?
|
def unknown_deleted_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.where(uri: json['actor']).exists?
|
json.is_a?(Hash) && json['type'] == 'Delete' && 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
|
||||||
|
@ -33,10 +31,6 @@ class ActivityPub::InboxesController < ActivityPub::BaseController
|
||||||
params[:account_username].present?
|
params[:account_username].present?
|
||||||
end
|
end
|
||||||
|
|
||||||
def skip_temporary_suspension_response?
|
|
||||||
true
|
|
||||||
end
|
|
||||||
|
|
||||||
def body
|
def body
|
||||||
return @body if defined?(@body)
|
return @body if defined?(@body)
|
||||||
|
|
||||||
|
@ -49,28 +43,15 @@ class ActivityPub::InboxesController < ActivityPub::BaseController
|
||||||
end
|
end
|
||||||
|
|
||||||
def upgrade_account
|
def upgrade_account
|
||||||
if signed_request_account&.ostatus?
|
if signed_request_account.ostatus?
|
||||||
signed_request_account.update(last_webfingered_at: nil)
|
signed_request_account.update(last_webfingered_at: nil)
|
||||||
ResolveAccountWorker.perform_async(signed_request_account.acct)
|
ResolveAccountWorker.perform_async(signed_request_account.acct)
|
||||||
end
|
end
|
||||||
|
|
||||||
DeliveryFailureTracker.reset!(signed_request_actor.inbox_url)
|
DeliveryFailureTracker.track_inverse_success!(signed_request_account)
|
||||||
end
|
|
||||||
|
|
||||||
def process_collection_synchronization
|
|
||||||
raw_params = request.headers['Collection-Synchronization']
|
|
||||||
return if raw_params.blank? || ENV['DISABLE_FOLLOWERS_SYNCHRONIZATION'] == 'true' || signed_request_account.nil?
|
|
||||||
|
|
||||||
# Re-using the syntax for signature parameters
|
|
||||||
tree = SignatureParamsParser.new.parse(raw_params)
|
|
||||||
params = SignatureParamsTransformer.new.apply(tree)
|
|
||||||
|
|
||||||
ActivityPub::PrepareFollowersSynchronizationService.new.call(signed_request_account, params)
|
|
||||||
rescue Parslet::ParseFailed
|
|
||||||
Rails.logger.warn 'Error parsing Collection-Synchronization header'
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def process_payload
|
def process_payload
|
||||||
ActivityPub::ProcessingWorker.perform_async(signed_request_actor.id, body, @account&.id, signed_request_actor.class.name)
|
ActivityPub::ProcessingWorker.perform_async(signed_request_account.id, body, @account&.id)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,16 +6,12 @@ class ActivityPub::OutboxesController < ActivityPub::BaseController
|
||||||
include SignatureVerification
|
include SignatureVerification
|
||||||
include AccountOwnedConcern
|
include AccountOwnedConcern
|
||||||
|
|
||||||
before_action :require_account_signature!, if: :authorized_fetch_mode?
|
before_action :require_signature!, if: :authorized_fetch_mode?
|
||||||
before_action :set_statuses
|
before_action :set_statuses
|
||||||
before_action :set_cache_headers
|
before_action :set_cache_headers
|
||||||
|
|
||||||
def show
|
def show
|
||||||
if page_requested?
|
expires_in(page_requested? ? 0 : 3.minutes, public: public_fetch_mode?)
|
||||||
expires_in(1.minute, public: public_fetch_mode? && signed_request_account.nil?)
|
|
||||||
else
|
|
||||||
expires_in(3.minutes, public: public_fetch_mode?)
|
|
||||||
end
|
|
||||||
render json: outbox_presenter, serializer: ActivityPub::OutboxSerializer, adapter: ActivityPub::Adapter, content_type: 'application/activity+json'
|
render json: outbox_presenter, serializer: ActivityPub::OutboxSerializer, adapter: ActivityPub::Adapter, content_type: 'application/activity+json'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -24,64 +20,45 @@ class ActivityPub::OutboxesController < ActivityPub::BaseController
|
||||||
def outbox_presenter
|
def outbox_presenter
|
||||||
if page_requested?
|
if page_requested?
|
||||||
ActivityPub::CollectionPresenter.new(
|
ActivityPub::CollectionPresenter.new(
|
||||||
id: outbox_url(**page_params),
|
id: account_outbox_url(@account, page_params),
|
||||||
type: :ordered,
|
type: :ordered,
|
||||||
part_of: outbox_url,
|
part_of: account_outbox_url(@account),
|
||||||
prev: prev_page,
|
prev: prev_page,
|
||||||
next: next_page,
|
next: next_page,
|
||||||
items: @statuses
|
items: @statuses
|
||||||
)
|
)
|
||||||
else
|
else
|
||||||
ActivityPub::CollectionPresenter.new(
|
ActivityPub::CollectionPresenter.new(
|
||||||
id: outbox_url,
|
id: account_outbox_url(@account),
|
||||||
type: :ordered,
|
type: :ordered,
|
||||||
size: @account.statuses_count,
|
size: @account.statuses_count,
|
||||||
first: outbox_url(page: true),
|
first: account_outbox_url(@account, page: true),
|
||||||
last: outbox_url(page: true, min_id: 0)
|
last: account_outbox_url(@account, page: true, min_id: 0)
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def outbox_url(**kwargs)
|
|
||||||
if params[:account_username].present?
|
|
||||||
account_outbox_url(@account, **kwargs)
|
|
||||||
else
|
|
||||||
instance_actor_outbox_url(**kwargs)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def next_page
|
def next_page
|
||||||
outbox_url(page: true, max_id: @statuses.last.id) if @statuses.size == LIMIT
|
account_outbox_url(@account, page: true, max_id: @statuses.last.id) if @statuses.size == LIMIT
|
||||||
end
|
end
|
||||||
|
|
||||||
def prev_page
|
def prev_page
|
||||||
outbox_url(page: true, min_id: @statuses.first.id) unless @statuses.empty?
|
account_outbox_url(@account, page: true, min_id: @statuses.first.id) unless @statuses.empty?
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_statuses
|
def set_statuses
|
||||||
return unless page_requested?
|
return unless page_requested?
|
||||||
|
|
||||||
@statuses = cache_collection_paginated_by_id(
|
@statuses = @account.statuses.permitted_for(@account, signed_request_account)
|
||||||
AccountStatusesFilter.new(@account, signed_request_account).results,
|
@statuses = params[:min_id].present? ? @statuses.paginate_by_min_id(LIMIT, params[:min_id]).reverse : @statuses.paginate_by_max_id(LIMIT, params[:max_id])
|
||||||
Status,
|
@statuses = cache_collection(@statuses, Status)
|
||||||
LIMIT,
|
|
||||||
params_slice(:max_id, :min_id, :since_id)
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def page_requested?
|
def page_requested?
|
||||||
truthy_param?(:page)
|
params[:page] == 'true'
|
||||||
end
|
end
|
||||||
|
|
||||||
def page_params
|
def page_params
|
||||||
{ page: true, max_id: params[:max_id], min_id: params[:min_id] }.compact
|
{ page: true, max_id: params[:max_id], min_id: params[:min_id] }.compact
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_account
|
|
||||||
@account = params[:account_username].present? ? Account.find_local!(username_param) : Account.representative
|
|
||||||
end
|
|
||||||
|
|
||||||
def set_cache_headers
|
|
||||||
response.headers['Vary'] = 'Signature' if authorized_fetch_mode? || page_requested?
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class ActivityPub::RepliesController < ActivityPub::BaseController
|
class ActivityPub::RepliesController < ActivityPub::BaseController
|
||||||
include SignatureVerification
|
include SignatureAuthentication
|
||||||
include Authorization
|
include Authorization
|
||||||
include AccountOwnedConcern
|
include AccountOwnedConcern
|
||||||
|
|
||||||
DESCENDANTS_LIMIT = 60
|
DESCENDANTS_LIMIT = 60
|
||||||
|
|
||||||
before_action :require_account_signature!, if: :authorized_fetch_mode?
|
before_action :require_signature!, if: :authorized_fetch_mode?
|
||||||
before_action :set_status
|
before_action :set_status
|
||||||
before_action :set_cache_headers
|
before_action :set_cache_headers
|
||||||
before_action :set_replies
|
before_action :set_replies
|
||||||
|
@ -19,19 +19,15 @@ class ActivityPub::RepliesController < ActivityPub::BaseController
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def pundit_user
|
|
||||||
signed_request_account
|
|
||||||
end
|
|
||||||
|
|
||||||
def set_status
|
def set_status
|
||||||
@status = @account.statuses.find(params[:status_id])
|
@status = @account.statuses.find(params[:status_id])
|
||||||
authorize @status, :show?
|
authorize @status, :show?
|
||||||
rescue Mastodon::NotPermittedError
|
rescue Mastodon::NotPermittedError
|
||||||
not_found
|
raise ActiveRecord::RecordNotFound
|
||||||
end
|
end
|
||||||
|
|
||||||
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 = page_params[:other_accounts] ? Status.where.not(account_id: @account.id) : @account.statuses
|
||||||
@replies = @replies.where(in_reply_to_id: @status.id, visibility: [:public, :unlisted])
|
@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
|
||||||
|
@ -42,7 +38,7 @@ class ActivityPub::RepliesController < ActivityPub::BaseController
|
||||||
type: :unordered,
|
type: :unordered,
|
||||||
part_of: account_status_replies_url(@account, @status),
|
part_of: account_status_replies_url(@account, @status),
|
||||||
next: next_page,
|
next: next_page,
|
||||||
items: @replies.map { |status| status.local? ? status : status.uri }
|
items: @replies.map { |status| status.local ? status : status.id }
|
||||||
)
|
)
|
||||||
|
|
||||||
return page if page_requested?
|
return page if page_requested?
|
||||||
|
@ -55,40 +51,20 @@ class ActivityPub::RepliesController < ActivityPub::BaseController
|
||||||
end
|
end
|
||||||
|
|
||||||
def page_requested?
|
def page_requested?
|
||||||
truthy_param?(:page)
|
params[:page] == 'true'
|
||||||
end
|
|
||||||
|
|
||||||
def only_other_accounts?
|
|
||||||
truthy_param?(:only_other_accounts)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def next_page
|
def next_page
|
||||||
if only_other_accounts?
|
account_status_replies_url(
|
||||||
# Only consider remote accounts
|
@account,
|
||||||
return nil if @replies.size < DESCENDANTS_LIMIT
|
@status,
|
||||||
|
page: true,
|
||||||
account_status_replies_url(
|
min_id: @replies&.last&.id,
|
||||||
@account,
|
other_accounts: !(@replies&.last&.account_id == @account.id && @replies.size == DESCENDANTS_LIMIT)
|
||||||
@status,
|
)
|
||||||
page: true,
|
|
||||||
min_id: @replies&.last&.id,
|
|
||||||
only_other_accounts: true
|
|
||||||
)
|
|
||||||
else
|
|
||||||
# For now, we're serving only self-replies, but next page might be other accounts
|
|
||||||
next_only_other_accounts = @replies&.last&.account_id != @account.id || @replies.size < DESCENDANTS_LIMIT
|
|
||||||
|
|
||||||
account_status_replies_url(
|
|
||||||
@account,
|
|
||||||
@status,
|
|
||||||
page: true,
|
|
||||||
min_id: next_only_other_accounts ? nil : @replies&.last&.id,
|
|
||||||
only_other_accounts: next_only_other_accounts
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def page_params
|
def page_params
|
||||||
params_slice(:only_other_accounts, :min_id).merge(page: true)
|
params_slice(:other_accounts, :min_id).merge(page: true)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,15 +5,11 @@ module Admin
|
||||||
before_action :set_account
|
before_action :set_account
|
||||||
|
|
||||||
def new
|
def new
|
||||||
authorize @account, :show?
|
@account_action = Admin::AccountAction.new(type: params[:type], report_id: params[:report_id], send_email_notification: true)
|
||||||
|
|
||||||
@account_action = Admin::AccountAction.new(type: params[:type], report_id: params[:report_id], send_email_notification: true, include_statuses: true)
|
|
||||||
@warning_presets = AccountWarningPreset.all
|
@warning_presets = AccountWarningPreset.all
|
||||||
end
|
end
|
||||||
|
|
||||||
def create
|
def create
|
||||||
authorize @account, :show?
|
|
||||||
|
|
||||||
account_action = Admin::AccountAction.new(resource_params)
|
account_action = Admin::AccountAction.new(resource_params)
|
||||||
account_action.target_account = @account
|
account_action.target_account = @account
|
||||||
account_action.current_account = current_account
|
account_action.current_account = current_account
|
||||||
|
@ -34,7 +30,7 @@ module Admin
|
||||||
end
|
end
|
||||||
|
|
||||||
def resource_params
|
def resource_params
|
||||||
params.require(:admin_account_action).permit(:type, :report_id, :warning_preset_id, :text, :send_email_notification, :include_statuses)
|
params.require(:admin_account_action).permit(:type, :report_id, :warning_preset_id, :text, :send_email_notification)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -14,7 +14,7 @@ module Admin
|
||||||
else
|
else
|
||||||
@account = @account_moderation_note.target_account
|
@account = @account_moderation_note.target_account
|
||||||
@moderation_notes = @account.targeted_moderation_notes.latest
|
@moderation_notes = @account.targeted_moderation_notes.latest
|
||||||
@warnings = @account.strikes.custom.latest
|
@warnings = @account.targeted_account_warnings.latest.custom
|
||||||
|
|
||||||
render template: 'admin/accounts/show'
|
render template: 'admin/accounts/show'
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,96 +2,61 @@
|
||||||
|
|
||||||
module Admin
|
module Admin
|
||||||
class AccountsController < BaseController
|
class AccountsController < BaseController
|
||||||
before_action :set_account, except: [:index, :batch]
|
before_action :set_account, only: [:show, :redownload, :remove_avatar, :remove_header, :enable, :unsilence, :unsuspend, :memorialize, :approve, :reject]
|
||||||
before_action :require_remote_account!, only: [:redownload]
|
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])
|
@accounts = filtered_accounts.page(params[:page])
|
||||||
@form = Form::AccountBatch.new
|
|
||||||
end
|
|
||||||
|
|
||||||
def batch
|
|
||||||
authorize :account, :index?
|
|
||||||
|
|
||||||
@form = Form::AccountBatch.new(form_account_batch_params)
|
|
||||||
@form.current_account = current_account
|
|
||||||
@form.action = action_from_button
|
|
||||||
@form.select_all_matching = params[:select_all_matching]
|
|
||||||
@form.query = filtered_accounts
|
|
||||||
@form.save
|
|
||||||
rescue ActionController::ParameterMissing
|
|
||||||
flash[:alert] = I18n.t('admin.accounts.no_account_selected')
|
|
||||||
ensure
|
|
||||||
redirect_to admin_accounts_path(filter_params)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def show
|
def show
|
||||||
authorize @account, :show?
|
authorize @account, :show?
|
||||||
|
|
||||||
@deletion_request = @account.deletion_request
|
|
||||||
@account_moderation_note = current_account.account_moderation_notes.new(target_account: @account)
|
@account_moderation_note = current_account.account_moderation_notes.new(target_account: @account)
|
||||||
@moderation_notes = @account.targeted_moderation_notes.latest
|
@moderation_notes = @account.targeted_moderation_notes.latest
|
||||||
@warnings = @account.strikes.includes(:target_account, :account, :appeal).latest
|
@warnings = @account.targeted_account_warnings.latest.custom
|
||||||
@domain_block = DomainBlock.rule_for(@account.domain)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def memorialize
|
def memorialize
|
||||||
authorize @account, :memorialize?
|
authorize @account, :memorialize?
|
||||||
@account.memorialize!
|
@account.memorialize!
|
||||||
log_action :memorialize, @account
|
log_action :memorialize, @account
|
||||||
redirect_to admin_account_path(@account.id), notice: I18n.t('admin.accounts.memorialized_msg', username: @account.acct)
|
redirect_to admin_account_path(@account.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
def enable
|
def enable
|
||||||
authorize @account.user, :enable?
|
authorize @account.user, :enable?
|
||||||
@account.user.enable!
|
@account.user.enable!
|
||||||
log_action :enable, @account.user
|
log_action :enable, @account.user
|
||||||
redirect_to admin_account_path(@account.id), notice: I18n.t('admin.accounts.enabled_msg', username: @account.acct)
|
redirect_to admin_account_path(@account.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
def approve
|
def approve
|
||||||
authorize @account.user, :approve?
|
authorize @account.user, :approve?
|
||||||
@account.user.approve!
|
@account.user.approve!
|
||||||
log_action :approve, @account.user
|
redirect_to admin_pending_accounts_path
|
||||||
redirect_to admin_accounts_path(status: 'pending'), notice: I18n.t('admin.accounts.approved_msg', username: @account.acct)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def reject
|
def reject
|
||||||
authorize @account.user, :reject?
|
authorize @account.user, :reject?
|
||||||
DeleteAccountService.new.call(@account, reserve_email: false, reserve_username: false)
|
SuspendAccountService.new.call(@account, including_user: true, destroy: true, skip_distribution: true)
|
||||||
log_action :reject, @account.user
|
redirect_to admin_pending_accounts_path
|
||||||
redirect_to admin_accounts_path(status: 'pending'), notice: I18n.t('admin.accounts.rejected_msg', username: @account.acct)
|
|
||||||
end
|
|
||||||
|
|
||||||
def destroy
|
|
||||||
authorize @account, :destroy?
|
|
||||||
Admin::AccountDeletionWorker.perform_async(@account.id)
|
|
||||||
redirect_to admin_account_path(@account.id), notice: I18n.t('admin.accounts.destroyed_msg', username: @account.acct)
|
|
||||||
end
|
|
||||||
|
|
||||||
def unsensitive
|
|
||||||
authorize @account, :unsensitive?
|
|
||||||
@account.unsensitize!
|
|
||||||
log_action :unsensitive, @account
|
|
||||||
redirect_to admin_account_path(@account.id)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def unsilence
|
def unsilence
|
||||||
authorize @account, :unsilence?
|
authorize @account, :unsilence?
|
||||||
@account.unsilence!
|
@account.unsilence!
|
||||||
log_action :unsilence, @account
|
log_action :unsilence, @account
|
||||||
redirect_to admin_account_path(@account.id), notice: I18n.t('admin.accounts.unsilenced_msg', username: @account.acct)
|
redirect_to admin_account_path(@account.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
def unsuspend
|
def unsuspend
|
||||||
authorize @account, :unsuspend?
|
authorize @account, :unsuspend?
|
||||||
@account.unsuspend!
|
@account.unsuspend!
|
||||||
Admin::UnsuspensionWorker.perform_async(@account.id)
|
|
||||||
log_action :unsuspend, @account
|
log_action :unsuspend, @account
|
||||||
redirect_to admin_account_path(@account.id), notice: I18n.t('admin.accounts.unsuspended_msg', username: @account.acct)
|
redirect_to admin_account_path(@account.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
def redownload
|
def redownload
|
||||||
|
@ -100,7 +65,7 @@ module Admin
|
||||||
@account.update!(last_webfingered_at: nil)
|
@account.update!(last_webfingered_at: nil)
|
||||||
ResolveAccountService.new.call(@account)
|
ResolveAccountService.new.call(@account)
|
||||||
|
|
||||||
redirect_to admin_account_path(@account.id), notice: I18n.t('admin.accounts.redownloaded_msg', username: @account.acct)
|
redirect_to admin_account_path(@account.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
def remove_avatar
|
def remove_avatar
|
||||||
|
@ -111,7 +76,7 @@ module Admin
|
||||||
|
|
||||||
log_action :remove_avatar, @account.user
|
log_action :remove_avatar, @account.user
|
||||||
|
|
||||||
redirect_to admin_account_path(@account.id), notice: I18n.t('admin.accounts.removed_avatar_msg', username: @account.acct)
|
redirect_to admin_account_path(@account.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
def remove_header
|
def remove_header
|
||||||
|
@ -122,17 +87,7 @@ module Admin
|
||||||
|
|
||||||
log_action :remove_header, @account.user
|
log_action :remove_header, @account.user
|
||||||
|
|
||||||
redirect_to admin_account_path(@account.id), notice: I18n.t('admin.accounts.removed_header_msg', username: @account.acct)
|
redirect_to admin_account_path(@account.id)
|
||||||
end
|
|
||||||
|
|
||||||
def unblock_email
|
|
||||||
authorize @account, :unblock_email?
|
|
||||||
|
|
||||||
CanonicalEmailBlock.where(reference_account: @account).delete_all
|
|
||||||
|
|
||||||
log_action :unblock_email, @account
|
|
||||||
|
|
||||||
redirect_to admin_account_path(@account.id), notice: I18n.t('admin.accounts.unblocked_email_msg', username: @account.acct)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
@ -150,25 +105,25 @@ module Admin
|
||||||
end
|
end
|
||||||
|
|
||||||
def filtered_accounts
|
def filtered_accounts
|
||||||
AccountFilter.new(filter_params.with_defaults(order: 'recent')).results
|
AccountFilter.new(filter_params).results
|
||||||
end
|
end
|
||||||
|
|
||||||
def filter_params
|
def filter_params
|
||||||
params.slice(:page, *AccountFilter::KEYS).permit(:page, *AccountFilter::KEYS)
|
params.permit(
|
||||||
end
|
:local,
|
||||||
|
:remote,
|
||||||
def form_account_batch_params
|
:by_domain,
|
||||||
params.require(:form_account_batch).permit(:action, account_ids: [])
|
:active,
|
||||||
end
|
:pending,
|
||||||
|
:disabled,
|
||||||
def action_from_button
|
:silenced,
|
||||||
if params[:suspend]
|
:suspended,
|
||||||
'suspend'
|
:username,
|
||||||
elsif params[:approve]
|
:display_name,
|
||||||
'approve'
|
:email,
|
||||||
elsif params[:reject]
|
:ip,
|
||||||
'reject'
|
:staff
|
||||||
end
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,21 +2,8 @@
|
||||||
|
|
||||||
module Admin
|
module Admin
|
||||||
class ActionLogsController < BaseController
|
class ActionLogsController < BaseController
|
||||||
before_action :set_action_logs
|
|
||||||
|
|
||||||
def index
|
def index
|
||||||
authorize :audit_log, :index?
|
@action_logs = Admin::ActionLog.page(params[:page])
|
||||||
@auditable_accounts = Account.where(id: Admin::ActionLog.reorder(nil).select('distinct account_id')).select(:id, :username)
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def set_action_logs
|
|
||||||
@action_logs = Admin::ActionLogFilter.new(filter_params).results.page(params[:page])
|
|
||||||
end
|
|
||||||
|
|
||||||
def filter_params
|
|
||||||
params.slice(:page, *Admin::ActionLogFilter::KEYS).permit(:page, *Admin::ActionLogFilter::KEYS)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,88 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
class Admin::AnnouncementsController < Admin::BaseController
|
|
||||||
before_action :set_announcements, only: :index
|
|
||||||
before_action :set_announcement, except: [:index, :new, :create]
|
|
||||||
|
|
||||||
def index
|
|
||||||
authorize :announcement, :index?
|
|
||||||
end
|
|
||||||
|
|
||||||
def new
|
|
||||||
authorize :announcement, :create?
|
|
||||||
|
|
||||||
@announcement = Announcement.new
|
|
||||||
end
|
|
||||||
|
|
||||||
def create
|
|
||||||
authorize :announcement, :create?
|
|
||||||
|
|
||||||
@announcement = Announcement.new(resource_params)
|
|
||||||
|
|
||||||
if @announcement.save
|
|
||||||
PublishScheduledAnnouncementWorker.perform_async(@announcement.id) if @announcement.published?
|
|
||||||
log_action :create, @announcement
|
|
||||||
redirect_to admin_announcements_path, notice: @announcement.published? ? I18n.t('admin.announcements.published_msg') : I18n.t('admin.announcements.scheduled_msg')
|
|
||||||
else
|
|
||||||
render :new
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def edit
|
|
||||||
authorize :announcement, :update?
|
|
||||||
end
|
|
||||||
|
|
||||||
def update
|
|
||||||
authorize :announcement, :update?
|
|
||||||
|
|
||||||
if @announcement.update(resource_params)
|
|
||||||
PublishScheduledAnnouncementWorker.perform_async(@announcement.id) if @announcement.published?
|
|
||||||
log_action :update, @announcement
|
|
||||||
redirect_to admin_announcements_path, notice: I18n.t('admin.announcements.updated_msg')
|
|
||||||
else
|
|
||||||
render :edit
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def publish
|
|
||||||
authorize :announcement, :update?
|
|
||||||
@announcement.publish!
|
|
||||||
PublishScheduledAnnouncementWorker.perform_async(@announcement.id)
|
|
||||||
log_action :update, @announcement
|
|
||||||
redirect_to admin_announcements_path, notice: I18n.t('admin.announcements.published_msg')
|
|
||||||
end
|
|
||||||
|
|
||||||
def unpublish
|
|
||||||
authorize :announcement, :update?
|
|
||||||
@announcement.unpublish!
|
|
||||||
UnpublishAnnouncementWorker.perform_async(@announcement.id)
|
|
||||||
log_action :update, @announcement
|
|
||||||
redirect_to admin_announcements_path, notice: I18n.t('admin.announcements.unpublished_msg')
|
|
||||||
end
|
|
||||||
|
|
||||||
def destroy
|
|
||||||
authorize :announcement, :destroy?
|
|
||||||
@announcement.destroy!
|
|
||||||
UnpublishAnnouncementWorker.perform_async(@announcement.id) if @announcement.published?
|
|
||||||
log_action :destroy, @announcement
|
|
||||||
redirect_to admin_announcements_path, notice: I18n.t('admin.announcements.destroyed_msg')
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def set_announcements
|
|
||||||
@announcements = AnnouncementFilter.new(filter_params).results.reverse_chronological.page(params[:page])
|
|
||||||
end
|
|
||||||
|
|
||||||
def set_announcement
|
|
||||||
@announcement = Announcement.find(params[:id])
|
|
||||||
end
|
|
||||||
|
|
||||||
def filter_params
|
|
||||||
params.slice(*AnnouncementFilter::KEYS).permit(*AnnouncementFilter::KEYS)
|
|
||||||
end
|
|
||||||
|
|
||||||
def resource_params
|
|
||||||
params.require(:announcement).permit(:text, :scheduled_at, :starts_at, :ends_at, :all_day)
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -7,8 +7,8 @@ module Admin
|
||||||
|
|
||||||
layout 'admin'
|
layout 'admin'
|
||||||
|
|
||||||
|
before_action :require_staff!
|
||||||
before_action :set_body_classes
|
before_action :set_body_classes
|
||||||
after_action :verify_authorized
|
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,7 @@ module Admin
|
||||||
|
|
||||||
@user.resend_confirmation_instructions
|
@user.resend_confirmation_instructions
|
||||||
|
|
||||||
log_action :resend, @user
|
log_action :confirm, @user
|
||||||
|
|
||||||
flash[:notice] = I18n.t('admin.accounts.resend_confirmation.success')
|
flash[:notice] = I18n.t('admin.accounts.resend_confirmation.success')
|
||||||
redirect_to admin_accounts_path
|
redirect_to admin_accounts_path
|
||||||
|
|
|
@ -2,16 +2,19 @@
|
||||||
|
|
||||||
module Admin
|
module Admin
|
||||||
class CustomEmojisController < BaseController
|
class CustomEmojisController < BaseController
|
||||||
|
before_action :set_custom_emoji, except: [:index, :new, :create]
|
||||||
|
before_action :set_filter_params
|
||||||
|
|
||||||
|
include ObfuscateFilename
|
||||||
|
obfuscate_filename [:custom_emoji, :image]
|
||||||
|
|
||||||
def index
|
def index
|
||||||
authorize :custom_emoji, :index?
|
authorize :custom_emoji, :index?
|
||||||
|
|
||||||
@custom_emojis = filtered_custom_emojis.eager_load(:local_counterpart).page(params[:page])
|
@custom_emojis = filtered_custom_emojis.eager_load(:local_counterpart).page(params[:page])
|
||||||
@form = Form::CustomEmojiBatch.new
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def new
|
def new
|
||||||
authorize :custom_emoji, :create?
|
authorize :custom_emoji, :create?
|
||||||
|
|
||||||
@custom_emoji = CustomEmoji.new
|
@custom_emoji = CustomEmoji.new
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -28,21 +31,69 @@ module Admin
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def batch
|
def update
|
||||||
authorize :custom_emoji, :index?
|
authorize @custom_emoji, :update?
|
||||||
|
|
||||||
@form = Form::CustomEmojiBatch.new(form_custom_emoji_batch_params.merge(current_account: current_account, action: action_from_button))
|
if @custom_emoji.update(resource_params)
|
||||||
@form.save
|
log_action :update, @custom_emoji
|
||||||
rescue ActionController::ParameterMissing
|
flash[:notice] = I18n.t('admin.custom_emojis.updated_msg')
|
||||||
flash[:alert] = I18n.t('admin.custom_emojis.no_emoji_selected')
|
else
|
||||||
rescue Mastodon::NotPermittedError
|
flash[:alert] = I18n.t('admin.custom_emojis.update_failed_msg')
|
||||||
flash[:alert] = I18n.t('admin.custom_emojis.not_permitted')
|
end
|
||||||
ensure
|
redirect_to admin_custom_emojis_path(page: params[:page], **@filter_params)
|
||||||
redirect_to admin_custom_emojis_path(filter_params)
|
end
|
||||||
|
|
||||||
|
def destroy
|
||||||
|
authorize @custom_emoji, :destroy?
|
||||||
|
@custom_emoji.destroy!
|
||||||
|
log_action :destroy, @custom_emoji
|
||||||
|
flash[:notice] = I18n.t('admin.custom_emojis.destroyed_msg')
|
||||||
|
redirect_to admin_custom_emojis_path(page: params[:page], **@filter_params)
|
||||||
|
end
|
||||||
|
|
||||||
|
def copy
|
||||||
|
authorize @custom_emoji, :copy?
|
||||||
|
|
||||||
|
emoji = CustomEmoji.find_or_initialize_by(domain: nil,
|
||||||
|
shortcode: @custom_emoji.shortcode)
|
||||||
|
emoji.image = @custom_emoji.image
|
||||||
|
|
||||||
|
if emoji.save
|
||||||
|
log_action :create, emoji
|
||||||
|
flash[:notice] = I18n.t('admin.custom_emojis.copied_msg')
|
||||||
|
else
|
||||||
|
flash[:alert] = I18n.t('admin.custom_emojis.copy_failed_msg')
|
||||||
|
end
|
||||||
|
|
||||||
|
redirect_to admin_custom_emojis_path(page: params[:page], **@filter_params)
|
||||||
|
end
|
||||||
|
|
||||||
|
def enable
|
||||||
|
authorize @custom_emoji, :enable?
|
||||||
|
@custom_emoji.update!(disabled: false)
|
||||||
|
log_action :enable, @custom_emoji
|
||||||
|
flash[:notice] = I18n.t('admin.custom_emojis.enabled_msg')
|
||||||
|
redirect_to admin_custom_emojis_path(page: params[:page], **@filter_params)
|
||||||
|
end
|
||||||
|
|
||||||
|
def disable
|
||||||
|
authorize @custom_emoji, :disable?
|
||||||
|
@custom_emoji.update!(disabled: true)
|
||||||
|
log_action :disable, @custom_emoji
|
||||||
|
flash[:notice] = I18n.t('admin.custom_emojis.disabled_msg')
|
||||||
|
redirect_to admin_custom_emojis_path(page: params[:page], **@filter_params)
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def set_custom_emoji
|
||||||
|
@custom_emoji = CustomEmoji.find(params[:id])
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_filter_params
|
||||||
|
@filter_params = filter_params.to_hash.symbolize_keys
|
||||||
|
end
|
||||||
|
|
||||||
def resource_params
|
def resource_params
|
||||||
params.require(:custom_emoji).permit(:shortcode, :image, :visible_in_picker)
|
params.require(:custom_emoji).permit(:shortcode, :image, :visible_in_picker)
|
||||||
end
|
end
|
||||||
|
@ -52,29 +103,12 @@ module Admin
|
||||||
end
|
end
|
||||||
|
|
||||||
def filter_params
|
def filter_params
|
||||||
params.slice(:page, *CustomEmojiFilter::KEYS).permit(:page, *CustomEmojiFilter::KEYS)
|
params.permit(
|
||||||
end
|
:local,
|
||||||
|
:remote,
|
||||||
def action_from_button
|
:by_domain,
|
||||||
if params[:update]
|
:shortcode
|
||||||
'update'
|
)
|
||||||
elsif params[:list]
|
|
||||||
'list'
|
|
||||||
elsif params[:unlist]
|
|
||||||
'unlist'
|
|
||||||
elsif params[:enable]
|
|
||||||
'enable'
|
|
||||||
elsif params[:disable]
|
|
||||||
'disable'
|
|
||||||
elsif params[:copy]
|
|
||||||
'copy'
|
|
||||||
elsif params[:delete]
|
|
||||||
'delete'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def form_custom_emoji_batch_params
|
|
||||||
params.require(:form_custom_emoji_batch).permit(:action, :category_id, :category_name, custom_emoji_ids: [])
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,30 +1,46 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
require 'sidekiq/api'
|
||||||
|
|
||||||
module Admin
|
module Admin
|
||||||
class DashboardController < BaseController
|
class DashboardController < BaseController
|
||||||
include Redisable
|
|
||||||
|
|
||||||
def index
|
def index
|
||||||
authorize :dashboard, :index?
|
@users_count = User.count
|
||||||
|
@registrations_week = Redis.current.get("activity:accounts:local:#{current_week}") || 0
|
||||||
@system_checks = Admin::SystemCheck.perform(current_user)
|
@logins_week = Redis.current.pfcount("activity:logins:#{current_week}")
|
||||||
@time_period = (29.days.ago.to_date...Time.now.utc.to_date)
|
@interactions_week = Redis.current.get("activity:interactions:#{current_week}") || 0
|
||||||
@pending_users_count = User.pending.count
|
@relay_enabled = Relay.enabled.exists?
|
||||||
@pending_reports_count = Report.unresolved.count
|
@single_user_mode = Rails.configuration.x.single_user_mode
|
||||||
@pending_tags_count = Tag.pending_review.count
|
@registrations_enabled = Setting.registrations_mode != 'none'
|
||||||
@pending_appeals_count = Appeal.pending.count
|
@deletions_enabled = Setting.open_deletion
|
||||||
|
@invites_enabled = Setting.min_invite_role == 'user'
|
||||||
|
@search_enabled = Chewy.enabled?
|
||||||
|
@version = Mastodon::Version.to_s
|
||||||
|
@database_version = ActiveRecord::Base.connection.execute('SELECT VERSION()').first['version'].match(/\A(?:PostgreSQL |)([^\s]+).*\z/)[1]
|
||||||
|
@redis_version = redis_info['redis_version']
|
||||||
|
@reports_count = Report.unresolved.count
|
||||||
|
@queue_backlog = Sidekiq::Stats.new.enqueued
|
||||||
|
@recent_users = User.confirmed.recent.includes(:account).limit(4)
|
||||||
|
@database_size = ActiveRecord::Base.connection.execute('SELECT pg_database_size(current_database())').first['pg_database_size']
|
||||||
|
@redis_size = redis_info['used_memory']
|
||||||
|
@ldap_enabled = ENV['LDAP_ENABLED'] == 'true'
|
||||||
|
@cas_enabled = ENV['CAS_ENABLED'] == 'true'
|
||||||
|
@saml_enabled = ENV['SAML_ENABLED'] == 'true'
|
||||||
|
@pam_enabled = ENV['PAM_ENABLED'] == 'true'
|
||||||
|
@hidden_service = ENV['ALLOW_ACCESS_TO_HIDDEN_SERVICE'] == 'true'
|
||||||
|
@trending_hashtags = TrendingTags.get(7)
|
||||||
|
@profile_directory = Setting.profile_directory
|
||||||
|
@timeline_preview = Setting.timeline_preview
|
||||||
|
@spam_check_enabled = Setting.spam_check_enabled
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def current_week
|
||||||
|
@current_week ||= Time.now.utc.to_date.cweek
|
||||||
|
end
|
||||||
|
|
||||||
def redis_info
|
def redis_info
|
||||||
@redis_info ||= begin
|
@redis_info ||= Redis.current.info
|
||||||
if redis.is_a?(Redis::Namespace)
|
|
||||||
redis.redis.info
|
|
||||||
else
|
|
||||||
redis.info
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,40 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
class Admin::Disputes::AppealsController < Admin::BaseController
|
|
||||||
before_action :set_appeal, except: :index
|
|
||||||
|
|
||||||
def index
|
|
||||||
authorize :appeal, :index?
|
|
||||||
|
|
||||||
@appeals = filtered_appeals.page(params[:page])
|
|
||||||
end
|
|
||||||
|
|
||||||
def approve
|
|
||||||
authorize @appeal, :approve?
|
|
||||||
log_action :approve, @appeal
|
|
||||||
ApproveAppealService.new.call(@appeal, current_account)
|
|
||||||
redirect_to disputes_strike_path(@appeal.strike)
|
|
||||||
end
|
|
||||||
|
|
||||||
def reject
|
|
||||||
authorize @appeal, :approve?
|
|
||||||
log_action :reject, @appeal
|
|
||||||
@appeal.reject!(current_account)
|
|
||||||
UserMailer.appeal_rejected(@appeal.account.user, @appeal)
|
|
||||||
redirect_to disputes_strike_path(@appeal.strike)
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def filtered_appeals
|
|
||||||
Admin::AppealFilter.new(filter_params.with_defaults(status: 'pending')).results.includes(strike: :account)
|
|
||||||
end
|
|
||||||
|
|
||||||
def filter_params
|
|
||||||
params.slice(:page, *Admin::AppealFilter::KEYS).permit(:page, *Admin::AppealFilter::KEYS)
|
|
||||||
end
|
|
||||||
|
|
||||||
def set_appeal
|
|
||||||
@appeal = Appeal.find(params[:id])
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,40 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
class Admin::DomainAllowsController < Admin::BaseController
|
|
||||||
before_action :set_domain_allow, only: [:destroy]
|
|
||||||
|
|
||||||
def new
|
|
||||||
authorize :domain_allow, :create?
|
|
||||||
|
|
||||||
@domain_allow = DomainAllow.new(domain: params[:_domain])
|
|
||||||
end
|
|
||||||
|
|
||||||
def create
|
|
||||||
authorize :domain_allow, :create?
|
|
||||||
|
|
||||||
@domain_allow = DomainAllow.new(resource_params)
|
|
||||||
|
|
||||||
if @domain_allow.save
|
|
||||||
log_action :create, @domain_allow
|
|
||||||
redirect_to admin_instances_path, notice: I18n.t('admin.domain_allows.created_msg')
|
|
||||||
else
|
|
||||||
render :new
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def destroy
|
|
||||||
authorize @domain_allow, :destroy?
|
|
||||||
UnallowDomainService.new.call(@domain_allow)
|
|
||||||
redirect_to admin_instances_path, notice: I18n.t('admin.domain_allows.destroyed_msg')
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def set_domain_allow
|
|
||||||
@domain_allow = DomainAllow.find(params[:id])
|
|
||||||
end
|
|
||||||
|
|
||||||
def resource_params
|
|
||||||
params.require(:domain_allow).permit(:domain)
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -2,29 +2,13 @@
|
||||||
|
|
||||||
module Admin
|
module Admin
|
||||||
class DomainBlocksController < BaseController
|
class DomainBlocksController < BaseController
|
||||||
before_action :set_domain_block, only: [:show, :destroy, :edit, :update]
|
before_action :set_domain_block, only: [:show, :destroy]
|
||||||
|
|
||||||
def batch
|
|
||||||
authorize :domain_block, :create?
|
|
||||||
@form = Form::DomainBlockBatch.new(form_domain_block_batch_params.merge(current_account: current_account, action: action_from_button))
|
|
||||||
@form.save
|
|
||||||
rescue ActionController::ParameterMissing
|
|
||||||
flash[:alert] = I18n.t('admin.domain_blocks.no_domain_block_selected')
|
|
||||||
rescue Mastodon::NotPermittedError
|
|
||||||
flash[:alert] = I18n.t('admin.domain_blocks.not_permitted')
|
|
||||||
else
|
|
||||||
redirect_to admin_instances_path(limited: '1'), notice: I18n.t('admin.domain_blocks.created_msg')
|
|
||||||
end
|
|
||||||
|
|
||||||
def new
|
def new
|
||||||
authorize :domain_block, :create?
|
authorize :domain_block, :create?
|
||||||
@domain_block = DomainBlock.new(domain: params[:_domain])
|
@domain_block = DomainBlock.new(domain: params[:_domain])
|
||||||
end
|
end
|
||||||
|
|
||||||
def edit
|
|
||||||
authorize :domain_block, :create?
|
|
||||||
end
|
|
||||||
|
|
||||||
def create
|
def create
|
||||||
authorize :domain_block, :create?
|
authorize :domain_block, :create?
|
||||||
|
|
||||||
|
@ -34,14 +18,13 @@ module Admin
|
||||||
if existing_domain_block.present? && !@domain_block.stricter_than?(existing_domain_block)
|
if existing_domain_block.present? && !@domain_block.stricter_than?(existing_domain_block)
|
||||||
@domain_block.save
|
@domain_block.save
|
||||||
flash.now[:alert] = I18n.t('admin.domain_blocks.existing_domain_block_html', name: existing_domain_block.domain, unblock_url: admin_domain_block_path(existing_domain_block)).html_safe # rubocop:disable Rails/OutputSafety
|
flash.now[:alert] = I18n.t('admin.domain_blocks.existing_domain_block_html', name: existing_domain_block.domain, unblock_url: admin_domain_block_path(existing_domain_block)).html_safe # rubocop:disable Rails/OutputSafety
|
||||||
@domain_block.errors.delete(:domain)
|
@domain_block.errors[:domain].clear
|
||||||
render :new
|
render :new
|
||||||
else
|
else
|
||||||
if existing_domain_block.present?
|
if existing_domain_block.present?
|
||||||
@domain_block = existing_domain_block
|
@domain_block = existing_domain_block
|
||||||
@domain_block.update(resource_params)
|
@domain_block.update(resource_params)
|
||||||
end
|
end
|
||||||
|
|
||||||
if @domain_block.save
|
if @domain_block.save
|
||||||
DomainBlockWorker.perform_async(@domain_block.id)
|
DomainBlockWorker.perform_async(@domain_block.id)
|
||||||
log_action :create, @domain_block
|
log_action :create, @domain_block
|
||||||
|
@ -52,16 +35,8 @@ module Admin
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def update
|
def show
|
||||||
authorize :domain_block, :update?
|
authorize @domain_block, :show?
|
||||||
|
|
||||||
if @domain_block.update(update_params)
|
|
||||||
DomainBlockWorker.perform_async(@domain_block.id, @domain_block.severity_previously_changed?)
|
|
||||||
log_action :update, @domain_block
|
|
||||||
redirect_to admin_instances_path(limited: '1'), notice: I18n.t('admin.domain_blocks.created_msg')
|
|
||||||
else
|
|
||||||
render :edit
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def destroy
|
def destroy
|
||||||
|
@ -77,22 +52,8 @@ module Admin
|
||||||
@domain_block = DomainBlock.find(params[:id])
|
@domain_block = DomainBlock.find(params[:id])
|
||||||
end
|
end
|
||||||
|
|
||||||
def update_params
|
|
||||||
params.require(:domain_block).permit(:severity, :reject_media, :reject_reports, :private_comment, :public_comment, :obfuscate)
|
|
||||||
end
|
|
||||||
|
|
||||||
def resource_params
|
def resource_params
|
||||||
params.require(:domain_block).permit(:domain, :severity, :reject_media, :reject_reports, :private_comment, :public_comment, :obfuscate)
|
params.require(:domain_block).permit(:domain, :severity, :reject_media, :reject_reports)
|
||||||
end
|
|
||||||
|
|
||||||
def form_domain_block_batch_params
|
|
||||||
params.require(:form_domain_block_batch).permit(domain_blocks_attributes: [:enabled, :domain, :severity, :reject_media, :reject_reports, :private_comment, :public_comment, :obfuscate])
|
|
||||||
end
|
|
||||||
|
|
||||||
def action_from_button
|
|
||||||
if params[:save]
|
|
||||||
'save'
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,27 +6,12 @@ module Admin
|
||||||
|
|
||||||
def index
|
def index
|
||||||
authorize :email_domain_block, :index?
|
authorize :email_domain_block, :index?
|
||||||
|
@email_domain_blocks = EmailDomainBlock.page(params[:page])
|
||||||
@email_domain_blocks = EmailDomainBlock.where(parent_id: nil).includes(:children).order(id: :desc).page(params[:page])
|
|
||||||
@form = Form::EmailDomainBlockBatch.new
|
|
||||||
end
|
|
||||||
|
|
||||||
def batch
|
|
||||||
authorize :email_domain_block, :index?
|
|
||||||
|
|
||||||
@form = Form::EmailDomainBlockBatch.new(form_email_domain_block_batch_params.merge(current_account: current_account, action: action_from_button))
|
|
||||||
@form.save
|
|
||||||
rescue ActionController::ParameterMissing
|
|
||||||
flash[:alert] = I18n.t('admin.email_domain_blocks.no_email_domain_block_selected')
|
|
||||||
rescue Mastodon::NotPermittedError
|
|
||||||
flash[:alert] = I18n.t('admin.email_domain_blocks.not_permitted')
|
|
||||||
ensure
|
|
||||||
redirect_to admin_email_domain_blocks_path
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def new
|
def new
|
||||||
authorize :email_domain_block, :create?
|
authorize :email_domain_block, :create?
|
||||||
@email_domain_block = EmailDomainBlock.new(domain: params[:_domain])
|
@email_domain_block = EmailDomainBlock.new
|
||||||
end
|
end
|
||||||
|
|
||||||
def create
|
def create
|
||||||
|
@ -34,27 +19,19 @@ module Admin
|
||||||
|
|
||||||
@email_domain_block = EmailDomainBlock.new(resource_params)
|
@email_domain_block = EmailDomainBlock.new(resource_params)
|
||||||
|
|
||||||
if action_from_button == 'save'
|
if @email_domain_block.save
|
||||||
EmailDomainBlock.transaction do
|
log_action :create, @email_domain_block
|
||||||
@email_domain_block.save!
|
|
||||||
log_action :create, @email_domain_block
|
|
||||||
|
|
||||||
(@email_domain_block.other_domains || []).uniq.each do |domain|
|
|
||||||
next if EmailDomainBlock.where(domain: domain).exists?
|
|
||||||
|
|
||||||
other_email_domain_block = EmailDomainBlock.create!(domain: domain, parent: @email_domain_block)
|
|
||||||
log_action :create, other_email_domain_block
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
redirect_to admin_email_domain_blocks_path, notice: I18n.t('admin.email_domain_blocks.created_msg')
|
redirect_to admin_email_domain_blocks_path, notice: I18n.t('admin.email_domain_blocks.created_msg')
|
||||||
else
|
else
|
||||||
set_resolved_records
|
|
||||||
render :new
|
render :new
|
||||||
end
|
end
|
||||||
rescue ActiveRecord::RecordInvalid
|
end
|
||||||
set_resolved_records
|
|
||||||
render :new
|
def destroy
|
||||||
|
authorize @email_domain_block, :destroy?
|
||||||
|
@email_domain_block.destroy!
|
||||||
|
log_action :destroy, @email_domain_block
|
||||||
|
redirect_to admin_email_domain_blocks_path, notice: I18n.t('admin.email_domain_blocks.destroyed_msg')
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
@ -63,27 +40,8 @@ module Admin
|
||||||
@email_domain_block = EmailDomainBlock.find(params[:id])
|
@email_domain_block = EmailDomainBlock.find(params[:id])
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_resolved_records
|
|
||||||
Resolv::DNS.open do |dns|
|
|
||||||
dns.timeouts = 5
|
|
||||||
@resolved_records = dns.getresources(@email_domain_block.domain, Resolv::DNS::Resource::IN::MX).to_a
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def resource_params
|
def resource_params
|
||||||
params.require(:email_domain_block).permit(:domain, other_domains: [])
|
params.require(:email_domain_block).permit(:domain)
|
||||||
end
|
|
||||||
|
|
||||||
def form_email_domain_block_batch_params
|
|
||||||
params.require(:form_email_domain_block_batch).permit(email_domain_block_ids: [])
|
|
||||||
end
|
|
||||||
|
|
||||||
def action_from_button
|
|
||||||
if params[:delete]
|
|
||||||
'delete'
|
|
||||||
elsif params[:save]
|
|
||||||
'save'
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,60 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
require 'csv'
|
|
||||||
|
|
||||||
module Admin
|
|
||||||
class ExportDomainAllowsController < BaseController
|
|
||||||
include AdminExportControllerConcern
|
|
||||||
|
|
||||||
before_action :set_dummy_import!, only: [:new]
|
|
||||||
|
|
||||||
def new
|
|
||||||
authorize :domain_allow, :create?
|
|
||||||
end
|
|
||||||
|
|
||||||
def export
|
|
||||||
authorize :instance, :index?
|
|
||||||
send_export_file
|
|
||||||
end
|
|
||||||
|
|
||||||
def import
|
|
||||||
authorize :domain_allow, :create?
|
|
||||||
begin
|
|
||||||
@import = Admin::Import.new(import_params)
|
|
||||||
return render :new unless @import.validate
|
|
||||||
|
|
||||||
parse_import_data!(export_headers)
|
|
||||||
|
|
||||||
@data.take(Admin::Import::ROWS_PROCESSING_LIMIT).each do |row|
|
|
||||||
domain = row['#domain'].strip
|
|
||||||
next if DomainAllow.allowed?(domain)
|
|
||||||
|
|
||||||
domain_allow = DomainAllow.new(domain: domain)
|
|
||||||
log_action :create, domain_allow if domain_allow.save
|
|
||||||
end
|
|
||||||
flash[:notice] = I18n.t('admin.domain_allows.created_msg')
|
|
||||||
rescue ActionController::ParameterMissing
|
|
||||||
flash[:error] = I18n.t('admin.export_domain_allows.no_file')
|
|
||||||
end
|
|
||||||
redirect_to admin_instances_path
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def export_filename
|
|
||||||
'domain_allows.csv'
|
|
||||||
end
|
|
||||||
|
|
||||||
def export_headers
|
|
||||||
%w(#domain)
|
|
||||||
end
|
|
||||||
|
|
||||||
def export_data
|
|
||||||
CSV.generate(headers: export_headers, write_headers: true) do |content|
|
|
||||||
DomainAllow.allowed_domains.each do |instance|
|
|
||||||
content << [instance.domain]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,71 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
require 'csv'
|
|
||||||
|
|
||||||
module Admin
|
|
||||||
class ExportDomainBlocksController < BaseController
|
|
||||||
include AdminExportControllerConcern
|
|
||||||
|
|
||||||
before_action :set_dummy_import!, only: [:new]
|
|
||||||
|
|
||||||
def new
|
|
||||||
authorize :domain_block, :create?
|
|
||||||
end
|
|
||||||
|
|
||||||
def export
|
|
||||||
authorize :instance, :index?
|
|
||||||
send_export_file
|
|
||||||
end
|
|
||||||
|
|
||||||
def import
|
|
||||||
authorize :domain_block, :create?
|
|
||||||
|
|
||||||
@import = Admin::Import.new(import_params)
|
|
||||||
return render :new unless @import.validate
|
|
||||||
|
|
||||||
parse_import_data!(export_headers)
|
|
||||||
|
|
||||||
@global_private_comment = I18n.t('admin.export_domain_blocks.import.private_comment_template', source: @import.data_file_name, date: I18n.l(Time.now.utc))
|
|
||||||
|
|
||||||
@form = Form::DomainBlockBatch.new
|
|
||||||
@domain_blocks = @data.take(Admin::Import::ROWS_PROCESSING_LIMIT).filter_map do |row|
|
|
||||||
domain = row['#domain'].strip
|
|
||||||
next if DomainBlock.rule_for(domain).present?
|
|
||||||
|
|
||||||
domain_block = DomainBlock.new(domain: domain,
|
|
||||||
severity: row['#severity'].strip,
|
|
||||||
reject_media: row['#reject_media'].strip,
|
|
||||||
reject_reports: row['#reject_reports'].strip,
|
|
||||||
private_comment: @global_private_comment,
|
|
||||||
public_comment: row['#public_comment']&.strip,
|
|
||||||
obfuscate: row['#obfuscate'].strip)
|
|
||||||
|
|
||||||
domain_block if domain_block.valid?
|
|
||||||
end
|
|
||||||
|
|
||||||
@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
|
|
||||||
flash.now[:alert] = I18n.t('admin.export_domain_blocks.no_file')
|
|
||||||
set_dummy_import!
|
|
||||||
render :new
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def export_filename
|
|
||||||
'domain_blocks.csv'
|
|
||||||
end
|
|
||||||
|
|
||||||
def export_headers
|
|
||||||
%w(#domain #severity #reject_media #reject_reports #public_comment #obfuscate)
|
|
||||||
end
|
|
||||||
|
|
||||||
def export_data
|
|
||||||
CSV.generate(headers: export_headers, write_headers: true) do |content|
|
|
||||||
DomainBlock.with_limitations.each do |instance|
|
|
||||||
content << [instance.domain, instance.severity, instance.reject_media, instance.reject_reports, instance.public_comment, instance.obfuscate]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,55 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
module Admin
|
|
||||||
class FollowRecommendationsController < BaseController
|
|
||||||
before_action :set_language
|
|
||||||
|
|
||||||
def show
|
|
||||||
authorize :follow_recommendation, :show?
|
|
||||||
|
|
||||||
@form = Form::AccountBatch.new
|
|
||||||
@accounts = filtered_follow_recommendations
|
|
||||||
end
|
|
||||||
|
|
||||||
def update
|
|
||||||
authorize :follow_recommendation, :show?
|
|
||||||
|
|
||||||
@form = Form::AccountBatch.new(form_account_batch_params.merge(current_account: current_account, action: action_from_button))
|
|
||||||
@form.save
|
|
||||||
rescue ActionController::ParameterMissing
|
|
||||||
# Do nothing
|
|
||||||
ensure
|
|
||||||
redirect_to admin_follow_recommendations_path(filter_params)
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def set_language
|
|
||||||
@language = follow_recommendation_filter.language
|
|
||||||
end
|
|
||||||
|
|
||||||
def filtered_follow_recommendations
|
|
||||||
follow_recommendation_filter.results
|
|
||||||
end
|
|
||||||
|
|
||||||
def follow_recommendation_filter
|
|
||||||
@follow_recommendation_filter ||= FollowRecommendationFilter.new(filter_params)
|
|
||||||
end
|
|
||||||
|
|
||||||
def form_account_batch_params
|
|
||||||
params.require(:form_account_batch).permit(:action, account_ids: [])
|
|
||||||
end
|
|
||||||
|
|
||||||
def filter_params
|
|
||||||
params.slice(*FollowRecommendationFilter::KEYS).permit(*FollowRecommendationFilter::KEYS)
|
|
||||||
end
|
|
||||||
|
|
||||||
def action_from_button
|
|
||||||
if params[:suppress]
|
|
||||||
'suppress_follow_recommendation'
|
|
||||||
elsif params[:unsuppress]
|
|
||||||
'unsuppress_follow_recommendation'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
18
app/controllers/admin/followers_controller.rb
Normal file
18
app/controllers/admin/followers_controller.rb
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module Admin
|
||||||
|
class FollowersController < BaseController
|
||||||
|
before_action :set_account
|
||||||
|
|
||||||
|
PER_PAGE = 40
|
||||||
|
|
||||||
|
def index
|
||||||
|
authorize :account, :index?
|
||||||
|
@followers = @account.followers.local.recent.page(params[:page]).per(PER_PAGE)
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_account
|
||||||
|
@account = Account.find(params[:account_id])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -2,74 +2,43 @@
|
||||||
|
|
||||||
module Admin
|
module Admin
|
||||||
class InstancesController < BaseController
|
class InstancesController < BaseController
|
||||||
before_action :set_instances, only: :index
|
|
||||||
before_action :set_instance, except: :index
|
|
||||||
|
|
||||||
def index
|
def index
|
||||||
authorize :instance, :index?
|
authorize :instance, :index?
|
||||||
preload_delivery_failures!
|
|
||||||
|
@instances = ordered_instances
|
||||||
end
|
end
|
||||||
|
|
||||||
def show
|
def show
|
||||||
authorize :instance, :show?
|
authorize :instance, :show?
|
||||||
@time_period = (6.days.ago.to_date...Time.now.utc.to_date)
|
|
||||||
end
|
|
||||||
|
|
||||||
def destroy
|
@instance = Instance.new(Account.by_domain_accounts.find_by(domain: params[:id]) || DomainBlock.find_by!(domain: params[:id]))
|
||||||
authorize :instance, :destroy?
|
@following_count = Follow.where(account: Account.where(domain: params[:id])).count
|
||||||
Admin::DomainPurgeWorker.perform_async(@instance.domain)
|
@followers_count = Follow.where(target_account: Account.where(domain: params[:id])).count
|
||||||
log_action :destroy, @instance
|
@reports_count = Report.where(target_account: Account.where(domain: params[:id])).count
|
||||||
redirect_to admin_instances_path, notice: I18n.t('admin.instances.destroyed_msg', domain: @instance.domain)
|
@blocks_count = Block.where(target_account: Account.where(domain: params[:id])).count
|
||||||
end
|
@available = DeliveryFailureTracker.available?(Account.select(:shared_inbox_url).where(domain: params[:id]).first&.shared_inbox_url)
|
||||||
|
@media_storage = MediaAttachment.where(account: Account.where(domain: params[:id])).sum(:file_file_size)
|
||||||
def clear_delivery_errors
|
@domain_block = DomainBlock.rule_for(params[:id])
|
||||||
authorize :delivery, :clear_delivery_errors?
|
|
||||||
@instance.delivery_failure_tracker.clear_failures!
|
|
||||||
redirect_to admin_instance_path(@instance.domain)
|
|
||||||
end
|
|
||||||
|
|
||||||
def restart_delivery
|
|
||||||
authorize :delivery, :restart_delivery?
|
|
||||||
|
|
||||||
if @instance.unavailable?
|
|
||||||
@instance.delivery_failure_tracker.track_success!
|
|
||||||
log_action :destroy, @instance.unavailable_domain
|
|
||||||
end
|
|
||||||
|
|
||||||
redirect_to admin_instance_path(@instance.domain)
|
|
||||||
end
|
|
||||||
|
|
||||||
def stop_delivery
|
|
||||||
authorize :delivery, :stop_delivery?
|
|
||||||
unavailable_domain = UnavailableDomain.create!(domain: @instance.domain)
|
|
||||||
log_action :create, unavailable_domain
|
|
||||||
redirect_to admin_instance_path(@instance.domain)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def set_instance
|
|
||||||
@instance = Instance.find(params[:id])
|
|
||||||
end
|
|
||||||
|
|
||||||
def set_instances
|
|
||||||
@instances = filtered_instances.page(params[:page])
|
|
||||||
end
|
|
||||||
|
|
||||||
def preload_delivery_failures!
|
|
||||||
warning_domains_map = DeliveryFailureTracker.warning_domains_map(@instances.map(&:domain))
|
|
||||||
|
|
||||||
@instances.each do |instance|
|
|
||||||
instance.failure_days = warning_domains_map[instance.domain]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def filtered_instances
|
def filtered_instances
|
||||||
InstanceFilter.new(whitelist_mode? ? { allowed: true } : filter_params).results
|
InstanceFilter.new(filter_params).results
|
||||||
|
end
|
||||||
|
|
||||||
|
def paginated_instances
|
||||||
|
filtered_instances.page(params[:page])
|
||||||
|
end
|
||||||
|
|
||||||
|
helper_method :paginated_instances
|
||||||
|
|
||||||
|
def ordered_instances
|
||||||
|
paginated_instances.map { |resource| Instance.new(resource) }
|
||||||
end
|
end
|
||||||
|
|
||||||
def filter_params
|
def filter_params
|
||||||
params.slice(*InstanceFilter::KEYS).permit(*InstanceFilter::KEYS)
|
params.permit(:limited, :by_domain)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -47,7 +47,7 @@ module Admin
|
||||||
end
|
end
|
||||||
|
|
||||||
def filter_params
|
def filter_params
|
||||||
params.slice(*InviteFilter::KEYS).permit(*InviteFilter::KEYS)
|
params.permit(:available, :expired)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,58 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
module Admin
|
|
||||||
class IpBlocksController < BaseController
|
|
||||||
def index
|
|
||||||
authorize :ip_block, :index?
|
|
||||||
|
|
||||||
@ip_blocks = IpBlock.order(ip: :asc).page(params[:page])
|
|
||||||
@form = Form::IpBlockBatch.new
|
|
||||||
end
|
|
||||||
|
|
||||||
def new
|
|
||||||
authorize :ip_block, :create?
|
|
||||||
|
|
||||||
@ip_block = IpBlock.new(ip: '', severity: :no_access, expires_in: 1.year)
|
|
||||||
end
|
|
||||||
|
|
||||||
def create
|
|
||||||
authorize :ip_block, :create?
|
|
||||||
|
|
||||||
@ip_block = IpBlock.new(resource_params)
|
|
||||||
|
|
||||||
if @ip_block.save
|
|
||||||
log_action :create, @ip_block
|
|
||||||
redirect_to admin_ip_blocks_path, notice: I18n.t('admin.ip_blocks.created_msg')
|
|
||||||
else
|
|
||||||
render :new
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def batch
|
|
||||||
authorize :ip_block, :index?
|
|
||||||
|
|
||||||
@form = Form::IpBlockBatch.new(form_ip_block_batch_params.merge(current_account: current_account, action: action_from_button))
|
|
||||||
@form.save
|
|
||||||
rescue ActionController::ParameterMissing
|
|
||||||
flash[:alert] = I18n.t('admin.ip_blocks.no_ip_block_selected')
|
|
||||||
rescue Mastodon::NotPermittedError
|
|
||||||
flash[:alert] = I18n.t('admin.custom_emojis.not_permitted')
|
|
||||||
ensure
|
|
||||||
redirect_to admin_ip_blocks_path
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def resource_params
|
|
||||||
params.require(:ip_block).permit(:ip, :severity, :comment, :expires_in)
|
|
||||||
end
|
|
||||||
|
|
||||||
def action_from_button
|
|
||||||
'delete' if params[:delete]
|
|
||||||
end
|
|
||||||
|
|
||||||
def form_ip_block_batch_params
|
|
||||||
params.require(:form_ip_block_batch).permit(ip_block_ids: [])
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
52
app/controllers/admin/pending_accounts_controller.rb
Normal file
52
app/controllers/admin/pending_accounts_controller.rb
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module Admin
|
||||||
|
class PendingAccountsController < BaseController
|
||||||
|
before_action :set_accounts, only: :index
|
||||||
|
|
||||||
|
def index
|
||||||
|
@form = Form::AccountBatch.new
|
||||||
|
end
|
||||||
|
|
||||||
|
def batch
|
||||||
|
@form = Form::AccountBatch.new(form_account_batch_params.merge(current_account: current_account, action: action_from_button))
|
||||||
|
@form.save
|
||||||
|
rescue ActionController::ParameterMissing
|
||||||
|
flash[:alert] = I18n.t('admin.accounts.no_account_selected')
|
||||||
|
ensure
|
||||||
|
redirect_to admin_pending_accounts_path(current_params)
|
||||||
|
end
|
||||||
|
|
||||||
|
def approve_all
|
||||||
|
Form::AccountBatch.new(current_account: current_account, account_ids: User.pending.pluck(:account_id), action: 'approve').save
|
||||||
|
redirect_to admin_pending_accounts_path(current_params)
|
||||||
|
end
|
||||||
|
|
||||||
|
def reject_all
|
||||||
|
Form::AccountBatch.new(current_account: current_account, account_ids: User.pending.pluck(:account_id), action: 'reject').save
|
||||||
|
redirect_to admin_pending_accounts_path(current_params)
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def set_accounts
|
||||||
|
@accounts = Account.joins(:user).merge(User.pending.recent).includes(user: :invite_request).page(params[:page])
|
||||||
|
end
|
||||||
|
|
||||||
|
def form_account_batch_params
|
||||||
|
params.require(:form_account_batch).permit(:action, account_ids: [])
|
||||||
|
end
|
||||||
|
|
||||||
|
def action_from_button
|
||||||
|
if params[:approve]
|
||||||
|
'approve'
|
||||||
|
elsif params[:reject]
|
||||||
|
'reject'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def current_params
|
||||||
|
params.slice(:page).permit(:page)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,26 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
module Admin
|
|
||||||
class RelationshipsController < BaseController
|
|
||||||
before_action :set_account
|
|
||||||
|
|
||||||
PER_PAGE = 40
|
|
||||||
|
|
||||||
def index
|
|
||||||
authorize @account, :show?
|
|
||||||
|
|
||||||
@accounts = RelationshipFilter.new(@account, filter_params).results.includes(:account_stat, user: [:ips, :invite_request]).page(params[:page]).per(PER_PAGE)
|
|
||||||
@form = Form::AccountBatch.new
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def set_account
|
|
||||||
@account = Account.find(params[:account_id])
|
|
||||||
end
|
|
||||||
|
|
||||||
def filter_params
|
|
||||||
params.slice(*RelationshipFilter::KEYS).permit(*RelationshipFilter::KEYS)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -3,7 +3,6 @@
|
||||||
module Admin
|
module Admin
|
||||||
class RelaysController < BaseController
|
class RelaysController < BaseController
|
||||||
before_action :set_relay, except: [:index, :new, :create]
|
before_action :set_relay, except: [:index, :new, :create]
|
||||||
before_action :warn_signatures_not_enabled!, only: [:new, :create, :enable]
|
|
||||||
|
|
||||||
def index
|
def index
|
||||||
authorize :relay, :update?
|
authorize :relay, :update?
|
||||||
|
@ -12,7 +11,7 @@ module Admin
|
||||||
|
|
||||||
def new
|
def new
|
||||||
authorize :relay, :update?
|
authorize :relay, :update?
|
||||||
@relay = Relay.new
|
@relay = Relay.new(inbox_url: Relay::PRESET_RELAY)
|
||||||
end
|
end
|
||||||
|
|
||||||
def create
|
def create
|
||||||
|
@ -55,9 +54,5 @@ module Admin
|
||||||
def resource_params
|
def resource_params
|
||||||
params.require(:relay).permit(:inbox_url)
|
params.require(:relay).permit(:inbox_url)
|
||||||
end
|
end
|
||||||
|
|
||||||
def warn_signatures_not_enabled!
|
|
||||||
flash.now[:error] = I18n.t('admin.relays.signatures_not_enabled') if authorized_fetch_mode?
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,26 +5,30 @@ module Admin
|
||||||
before_action :set_report_note, only: [:destroy]
|
before_action :set_report_note, only: [:destroy]
|
||||||
|
|
||||||
def create
|
def create
|
||||||
authorize :report_note, :create?
|
authorize ReportNote, :create?
|
||||||
|
|
||||||
@report_note = current_account.report_notes.new(resource_params)
|
@report_note = current_account.report_notes.new(resource_params)
|
||||||
@report = @report_note.report
|
@report = @report_note.report
|
||||||
|
|
||||||
if @report_note.save
|
if @report_note.save
|
||||||
if params[:create_and_resolve]
|
if params[:create_and_resolve]
|
||||||
@report.resolve!(current_account)
|
@report.resolve!(current_account)
|
||||||
log_action :resolve, @report
|
log_action :resolve, @report
|
||||||
elsif params[:create_and_unresolve]
|
|
||||||
|
redirect_to admin_reports_path, notice: I18n.t('admin.reports.resolved_msg')
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if params[:create_and_unresolve]
|
||||||
@report.unresolve!
|
@report.unresolve!
|
||||||
log_action :reopen, @report
|
log_action :reopen, @report
|
||||||
end
|
end
|
||||||
|
|
||||||
redirect_to after_create_redirect_path, notice: I18n.t('admin.report_notes.created_msg')
|
redirect_to admin_report_path(@report), notice: I18n.t('admin.report_notes.created_msg')
|
||||||
else
|
else
|
||||||
@report_notes = @report.notes.includes(:account).order(id: :desc)
|
@report_notes = @report.notes.latest
|
||||||
@action_logs = @report.history.includes(:target)
|
@report_history = @report.history
|
||||||
@form = Admin::StatusBatchAction.new
|
@form = Form::StatusBatch.new
|
||||||
@statuses = @report.statuses.with_includes
|
|
||||||
|
|
||||||
render template: 'admin/reports/show'
|
render template: 'admin/reports/show'
|
||||||
end
|
end
|
||||||
|
@ -38,14 +42,6 @@ module Admin
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def after_create_redirect_path
|
|
||||||
if params[:create_and_resolve]
|
|
||||||
admin_reports_path
|
|
||||||
else
|
|
||||||
admin_report_path(@report)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def resource_params
|
def resource_params
|
||||||
params.require(:report_note).permit(
|
params.require(:report_note).permit(
|
||||||
:content,
|
:content,
|
||||||
|
|
44
app/controllers/admin/reported_statuses_controller.rb
Normal file
44
app/controllers/admin/reported_statuses_controller.rb
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module Admin
|
||||||
|
class ReportedStatusesController < BaseController
|
||||||
|
before_action :set_report
|
||||||
|
|
||||||
|
def create
|
||||||
|
authorize :status, :update?
|
||||||
|
|
||||||
|
@form = Form::StatusBatch.new(form_status_batch_params.merge(current_account: current_account, action: action_from_button))
|
||||||
|
flash[:alert] = I18n.t('admin.statuses.failed_to_execute') unless @form.save
|
||||||
|
|
||||||
|
redirect_to admin_report_path(@report)
|
||||||
|
rescue ActionController::ParameterMissing
|
||||||
|
flash[:alert] = I18n.t('admin.statuses.no_status_selected')
|
||||||
|
|
||||||
|
redirect_to admin_report_path(@report)
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def status_params
|
||||||
|
params.require(:status).permit(:sensitive)
|
||||||
|
end
|
||||||
|
|
||||||
|
def form_status_batch_params
|
||||||
|
params.require(:form_status_batch).permit(status_ids: [])
|
||||||
|
end
|
||||||
|
|
||||||
|
def action_from_button
|
||||||
|
if params[:nsfw_on]
|
||||||
|
'nsfw_on'
|
||||||
|
elsif params[:nsfw_off]
|
||||||
|
'nsfw_off'
|
||||||
|
elsif params[:delete]
|
||||||
|
'delete'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_report
|
||||||
|
@report = Report.find(params[:report_id])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,52 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
class Admin::Reports::ActionsController < Admin::BaseController
|
|
||||||
before_action :set_report
|
|
||||||
|
|
||||||
def create
|
|
||||||
authorize @report, :show?
|
|
||||||
|
|
||||||
case action_from_button
|
|
||||||
when 'delete', 'mark_as_sensitive'
|
|
||||||
status_batch_action = Admin::StatusBatchAction.new(
|
|
||||||
type: action_from_button,
|
|
||||||
status_ids: @report.status_ids,
|
|
||||||
current_account: current_account,
|
|
||||||
report_id: @report.id,
|
|
||||||
send_email_notification: !@report.spam?
|
|
||||||
)
|
|
||||||
|
|
||||||
status_batch_action.save!
|
|
||||||
when 'silence', 'suspend'
|
|
||||||
account_action = Admin::AccountAction.new(
|
|
||||||
type: action_from_button,
|
|
||||||
report_id: @report.id,
|
|
||||||
target_account: @report.target_account,
|
|
||||||
current_account: current_account,
|
|
||||||
send_email_notification: !@report.spam?
|
|
||||||
)
|
|
||||||
|
|
||||||
account_action.save!
|
|
||||||
end
|
|
||||||
|
|
||||||
redirect_to admin_reports_path
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def set_report
|
|
||||||
@report = Report.find(params[:report_id])
|
|
||||||
end
|
|
||||||
|
|
||||||
def action_from_button
|
|
||||||
if params[:delete]
|
|
||||||
'delete'
|
|
||||||
elsif params[:mark_as_sensitive]
|
|
||||||
'mark_as_sensitive'
|
|
||||||
elsif params[:silence]
|
|
||||||
'silence'
|
|
||||||
elsif params[:suspend]
|
|
||||||
'suspend'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -13,10 +13,8 @@ module Admin
|
||||||
authorize @report, :show?
|
authorize @report, :show?
|
||||||
|
|
||||||
@report_note = @report.notes.new
|
@report_note = @report.notes.new
|
||||||
@report_notes = @report.notes.includes(:account).order(id: :desc)
|
@report_notes = (@report.notes.latest + @report.history + @report.target_account.targeted_account_warnings.latest.custom).sort_by(&:created_at)
|
||||||
@action_logs = @report.history.includes(:target)
|
@form = Form::StatusBatch.new
|
||||||
@form = Admin::StatusBatchAction.new
|
|
||||||
@statuses = @report.statuses.with_includes
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def assign_to_self
|
def assign_to_self
|
||||||
|
@ -54,7 +52,11 @@ module Admin
|
||||||
end
|
end
|
||||||
|
|
||||||
def filter_params
|
def filter_params
|
||||||
params.slice(*ReportFilter::KEYS).permit(*ReportFilter::KEYS)
|
params.permit(
|
||||||
|
:account_id,
|
||||||
|
:resolved,
|
||||||
|
:target_account_id
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_report
|
def set_report
|
||||||
|
|
|
@ -6,9 +6,9 @@ module Admin
|
||||||
|
|
||||||
def create
|
def create
|
||||||
authorize @user, :reset_password?
|
authorize @user, :reset_password?
|
||||||
@user.reset_password!
|
@user.send_reset_password_instructions
|
||||||
log_action :reset_password, @user
|
log_action :reset_password, @user
|
||||||
redirect_to admin_account_path(@user.account_id)
|
redirect_to admin_accounts_path
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,66 +2,20 @@
|
||||||
|
|
||||||
module Admin
|
module Admin
|
||||||
class RolesController < BaseController
|
class RolesController < BaseController
|
||||||
before_action :set_role, except: [:index, :new, :create]
|
before_action :set_user
|
||||||
|
|
||||||
def index
|
def promote
|
||||||
authorize :user_role, :index?
|
authorize @user, :promote?
|
||||||
|
@user.promote!
|
||||||
@roles = UserRole.order(position: :desc).page(params[:page])
|
log_action :promote, @user
|
||||||
|
redirect_to admin_account_path(@user.account_id)
|
||||||
end
|
end
|
||||||
|
|
||||||
def new
|
def demote
|
||||||
authorize :user_role, :create?
|
authorize @user, :demote?
|
||||||
|
@user.demote!
|
||||||
@role = UserRole.new
|
log_action :demote, @user
|
||||||
end
|
redirect_to admin_account_path(@user.account_id)
|
||||||
|
|
||||||
def create
|
|
||||||
authorize :user_role, :create?
|
|
||||||
|
|
||||||
@role = UserRole.new(resource_params)
|
|
||||||
@role.current_account = current_account
|
|
||||||
|
|
||||||
if @role.save
|
|
||||||
log_action :create, @role
|
|
||||||
redirect_to admin_roles_path
|
|
||||||
else
|
|
||||||
render :new
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def edit
|
|
||||||
authorize @role, :update?
|
|
||||||
end
|
|
||||||
|
|
||||||
def update
|
|
||||||
authorize @role, :update?
|
|
||||||
|
|
||||||
@role.current_account = current_account
|
|
||||||
|
|
||||||
if @role.update(resource_params)
|
|
||||||
log_action :update, @role
|
|
||||||
redirect_to admin_roles_path
|
|
||||||
else
|
|
||||||
render :edit
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def destroy
|
|
||||||
authorize @role, :destroy?
|
|
||||||
@role.destroy!
|
|
||||||
log_action :destroy, @role
|
|
||||||
redirect_to admin_roles_path
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def set_role
|
|
||||||
@role = UserRole.find(params[:id])
|
|
||||||
end
|
|
||||||
|
|
||||||
def resource_params
|
|
||||||
params.require(:user_role).permit(:name, :color, :highlighted, :position, permissions_as_keys: [])
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,59 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
module Admin
|
|
||||||
class RulesController < BaseController
|
|
||||||
before_action :set_rule, except: [:index, :create]
|
|
||||||
|
|
||||||
def index
|
|
||||||
authorize :rule, :index?
|
|
||||||
|
|
||||||
@rules = Rule.ordered
|
|
||||||
@rule = Rule.new
|
|
||||||
end
|
|
||||||
|
|
||||||
def create
|
|
||||||
authorize :rule, :create?
|
|
||||||
|
|
||||||
@rule = Rule.new(resource_params)
|
|
||||||
|
|
||||||
if @rule.save
|
|
||||||
redirect_to admin_rules_path
|
|
||||||
else
|
|
||||||
@rules = Rule.ordered
|
|
||||||
render :index
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def edit
|
|
||||||
authorize @rule, :update?
|
|
||||||
end
|
|
||||||
|
|
||||||
def update
|
|
||||||
authorize @rule, :update?
|
|
||||||
|
|
||||||
if @rule.update(resource_params)
|
|
||||||
redirect_to admin_rules_path
|
|
||||||
else
|
|
||||||
render :edit
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def destroy
|
|
||||||
authorize @rule, :destroy?
|
|
||||||
|
|
||||||
@rule.discard
|
|
||||||
|
|
||||||
redirect_to admin_rules_path
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def set_rule
|
|
||||||
@rule = Rule.find(params[:id])
|
|
||||||
end
|
|
||||||
|
|
||||||
def resource_params
|
|
||||||
params.require(:rule).permit(:text, :priority)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,9 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
class Admin::Settings::AboutController < Admin::SettingsController
|
|
||||||
private
|
|
||||||
|
|
||||||
def after_update_redirect_path
|
|
||||||
admin_settings_about_path
|
|
||||||
end
|
|
||||||
end
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue