Compare commits
1 commit
main
...
tootbutton
Author | SHA1 | Date | |
---|---|---|---|
0e0ff911b4 |
65
.babelrc
Normal file
65
.babelrc
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
{
|
||||||
|
"presets": [
|
||||||
|
"react",
|
||||||
|
[
|
||||||
|
"env",
|
||||||
|
{
|
||||||
|
"loose": true,
|
||||||
|
"modules": false,
|
||||||
|
"targets": {
|
||||||
|
"browsers": ["last 2 versions", "IE >= 11", "iOS >= 9"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"plugins": [
|
||||||
|
"syntax-dynamic-import",
|
||||||
|
["transform-object-rest-spread", { "useBuiltIns": true }],
|
||||||
|
"transform-decorators-legacy",
|
||||||
|
"transform-class-properties",
|
||||||
|
[
|
||||||
|
"react-intl",
|
||||||
|
{
|
||||||
|
"messagesDir": "./build/messages"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"preval"
|
||||||
|
],
|
||||||
|
"env": {
|
||||||
|
"development": {
|
||||||
|
"plugins": [
|
||||||
|
"transform-react-jsx-source",
|
||||||
|
"transform-react-jsx-self"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"production": {
|
||||||
|
"plugins": [
|
||||||
|
"lodash",
|
||||||
|
[
|
||||||
|
"transform-react-remove-prop-types",
|
||||||
|
{
|
||||||
|
"mode": "remove",
|
||||||
|
"removeImport": true,
|
||||||
|
"additionalLibraries": [
|
||||||
|
"react-immutable-proptypes"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"transform-react-inline-elements",
|
||||||
|
[
|
||||||
|
"transform-runtime",
|
||||||
|
{
|
||||||
|
"helpers": true,
|
||||||
|
"polyfill": false,
|
||||||
|
"regenerator": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"test": {
|
||||||
|
"plugins": [
|
||||||
|
"transform-es2015-modules-commonjs"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +0,0 @@
|
||||||
[production]
|
|
||||||
defaults
|
|
||||||
not IE 11
|
|
||||||
not dead
|
|
||||||
|
|
||||||
[development]
|
|
||||||
supports es6-module
|
|
|
@ -1,3 +1,3 @@
|
||||||
https://github.com/heroku/heroku-buildpack-apt
|
https://github.com/heroku/heroku-buildpack-apt
|
||||||
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 +0,0 @@
|
||||||
version: 2.1
|
|
||||||
|
|
||||||
orbs:
|
|
||||||
ruby: circleci/ruby@1.4.1
|
|
||||||
node: circleci/node@5.0.1
|
|
||||||
|
|
||||||
executors:
|
|
||||||
default:
|
|
||||||
parameters:
|
|
||||||
ruby-version:
|
|
||||||
type: string
|
|
||||||
docker:
|
|
||||||
- image: cimg/ruby:<< parameters.ruby-version >>
|
|
||||||
environment:
|
|
||||||
BUNDLE_JOBS: 3
|
|
||||||
BUNDLE_RETRY: 3
|
|
||||||
CONTINUOUS_INTEGRATION: true
|
|
||||||
DB_HOST: localhost
|
|
||||||
DB_USER: root
|
|
||||||
DISABLE_SIMPLECOV: true
|
|
||||||
RAILS_ENV: test
|
|
||||||
- image: cimg/postgres:14.0
|
|
||||||
environment:
|
|
||||||
POSTGRES_USER: root
|
|
||||||
POSTGRES_HOST_AUTH_METHOD: trust
|
|
||||||
- image: cimg/redis:6.2
|
|
||||||
|
|
||||||
commands:
|
|
||||||
install-system-dependencies:
|
|
||||||
steps:
|
|
||||||
- run:
|
|
||||||
name: Install system dependencies
|
|
||||||
command: |
|
|
||||||
sudo apt-get update
|
|
||||||
sudo apt-get install -y libicu-dev libidn11-dev
|
|
||||||
install-ruby-dependencies:
|
|
||||||
parameters:
|
|
||||||
ruby-version:
|
|
||||||
type: string
|
|
||||||
steps:
|
|
||||||
- run:
|
|
||||||
command: |
|
|
||||||
bundle config clean 'true'
|
|
||||||
bundle config frozen 'true'
|
|
||||||
bundle config without 'development production'
|
|
||||||
name: Set bundler settings
|
|
||||||
- ruby/install-deps:
|
|
||||||
bundler-version: '2.3.8'
|
|
||||||
key: ruby<< parameters.ruby-version >>-gems-v1
|
|
||||||
wait-db:
|
|
||||||
steps:
|
|
||||||
- run:
|
|
||||||
command: dockerize -wait tcp://localhost:5432 -wait tcp://localhost:6379 -timeout 1m
|
|
||||||
name: Wait for PostgreSQL and Redis
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
docker:
|
|
||||||
- image: cimg/ruby:3.0-node
|
|
||||||
environment:
|
|
||||||
RAILS_ENV: test
|
|
||||||
steps:
|
|
||||||
- checkout
|
|
||||||
- install-system-dependencies
|
|
||||||
- install-ruby-dependencies:
|
|
||||||
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:
|
|
||||||
paths:
|
|
||||||
- public/assets
|
|
||||||
- public/packs-test
|
|
||||||
root: .
|
|
||||||
|
|
||||||
test:
|
|
||||||
parameters:
|
|
||||||
ruby-version:
|
|
||||||
type: string
|
|
||||||
executor:
|
|
||||||
name: default
|
|
||||||
ruby-version: << parameters.ruby-version >>
|
|
||||||
environment:
|
|
||||||
ALLOW_NOPAM: true
|
|
||||||
PAM_ENABLED: true
|
|
||||||
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:
|
|
||||||
executor:
|
|
||||||
name: default
|
|
||||||
ruby-version: '3.0'
|
|
||||||
steps:
|
|
||||||
- checkout
|
|
||||||
- install-system-dependencies
|
|
||||||
- install-ruby-dependencies:
|
|
||||||
ruby-version: '3.0'
|
|
||||||
- wait-db
|
|
||||||
- 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:
|
|
||||||
executor:
|
|
||||||
name: default
|
|
||||||
ruby-version: '3.0'
|
|
||||||
steps:
|
|
||||||
- checkout
|
|
||||||
- install-system-dependencies
|
|
||||||
- install-ruby-dependencies:
|
|
||||||
ruby-version: '3.0'
|
|
||||||
- wait-db
|
|
||||||
- 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 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:
|
|
||||||
version: 2
|
|
||||||
build-and-test:
|
|
||||||
jobs:
|
|
||||||
- build
|
|
||||||
- test:
|
|
||||||
matrix:
|
|
||||||
parameters:
|
|
||||||
ruby-version:
|
|
||||||
- '2.7'
|
|
||||||
- '3.0'
|
|
||||||
name: test-ruby<< matrix.ruby-version >>
|
|
||||||
requires:
|
|
||||||
- build
|
|
||||||
- test-migrations:
|
|
||||||
requires:
|
|
||||||
- build
|
|
||||||
- test-two-step-migrations:
|
|
||||||
requires:
|
|
||||||
- build
|
|
||||||
- node/run:
|
|
||||||
cache-version: v1
|
|
||||||
name: test-webui
|
|
||||||
pkg-manager: yarn
|
|
||||||
requires:
|
|
||||||
- build
|
|
||||||
version: lts
|
|
||||||
yarn-run: test:jest
|
|
|
@ -1,39 +1,21 @@
|
||||||
version: '2'
|
engines:
|
||||||
checks:
|
|
||||||
argument-count:
|
|
||||||
enabled: false
|
|
||||||
complex-logic:
|
|
||||||
enabled: false
|
|
||||||
file-lines:
|
|
||||||
enabled: false
|
|
||||||
method-complexity:
|
|
||||||
enabled: false
|
|
||||||
method-count:
|
|
||||||
enabled: false
|
|
||||||
method-lines:
|
|
||||||
enabled: false
|
|
||||||
nested-control-flow:
|
|
||||||
enabled: false
|
|
||||||
return-statements:
|
|
||||||
enabled: false
|
|
||||||
similar-code:
|
|
||||||
enabled: false
|
|
||||||
identical-code:
|
|
||||||
enabled: false
|
|
||||||
plugins:
|
|
||||||
brakeman:
|
brakeman:
|
||||||
enabled: true
|
enabled: true
|
||||||
bundler-audit:
|
bundler-audit:
|
||||||
enabled: true
|
enabled: true
|
||||||
|
duplication:
|
||||||
|
enabled: false
|
||||||
eslint:
|
eslint:
|
||||||
enabled: false
|
enabled: true
|
||||||
rubocop:
|
rubocop:
|
||||||
enabled: false
|
enabled: true
|
||||||
sass-lint:
|
scss-lint:
|
||||||
enabled: false
|
enabled: true
|
||||||
exclude_patterns:
|
ratings:
|
||||||
- spec/
|
paths:
|
||||||
- vendor/asset/
|
- "**.rb"
|
||||||
|
- "**.js"
|
||||||
- app/javascript/mastodon/locales/**/*.json
|
- "**.scss"
|
||||||
- config/locales/**/*.yml
|
exclude_paths:
|
||||||
|
- spec/
|
||||||
|
- vendor/asset
|
||||||
|
|
|
@ -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"
|
|
||||||
]
|
|
|
@ -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/*, 16, 14, 12, 10
|
|
||||||
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": "/workspaces/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": "bundle install --path vendor/bundle && yarn install && git checkout -- Gemfile.lock && ./bin/rails db:setup",
|
|
||||||
|
|
||||||
// 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: '14'
|
|
||||||
volumes:
|
|
||||||
- ..:/workspaces/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,10 +1,5 @@
|
||||||
.bundle
|
|
||||||
.env
|
.env
|
||||||
.env.*
|
.env.*
|
||||||
.git
|
|
||||||
.gitattributes
|
|
||||||
.gitignore
|
|
||||||
.github
|
|
||||||
public/system
|
public/system
|
||||||
public/assets
|
public/assets
|
||||||
public/packs
|
public/packs
|
||||||
|
@ -15,7 +10,4 @@ vendor/bundle
|
||||||
*.swp
|
*.swp
|
||||||
*~
|
*~
|
||||||
postgres
|
postgres
|
||||||
postgres14
|
|
||||||
redis
|
redis
|
||||||
elasticsearch
|
|
||||||
chart
|
|
||||||
|
|
111
.env.nanobox
Normal file
111
.env.nanobox
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
# 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
|
||||||
|
|
||||||
|
# Federation
|
||||||
|
# Note: Changing LOCAL_DOMAIN or LOCAL_HTTPS at a later time will cause unwanted side effects.
|
||||||
|
# LOCAL_DOMAIN should *NOT* contain the protocol part of the domain e.g https://example.com.
|
||||||
|
LOCAL_DOMAIN=${APP_NAME}.nanoapp.io
|
||||||
|
LOCAL_HTTPS=false
|
||||||
|
|
||||||
|
# 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`)
|
||||||
|
PAPERCLIP_SECRET=$PAPERCLIP_SECRET
|
||||||
|
SECRET_KEY_BASE=$SECRET_KEY_BASE
|
||||||
|
OTP_SECRET=$OTP_SECRET
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
|
|
||||||
|
# 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=
|
||||||
|
|
||||||
|
# Optional alias for S3 if you want to use Cloudfront or Cloudflare in front
|
||||||
|
# S3_CLOUDFRONT_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
|
|
@ -1,23 +1,23 @@
|
||||||
# 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.
|
REDIS_PORT=6379
|
||||||
|
# You may set DATABASE_URL instead for more advanced options
|
||||||
# Note that this file accepts slightly different syntax depending on whether
|
DB_HOST=db
|
||||||
# you are using `docker-compose` or not. In particular, if you use
|
DB_USER=postgres
|
||||||
# `docker-compose`, the value of each declared variable will be taken verbatim,
|
DB_NAME=postgres
|
||||||
# including surrounding quotes.
|
DB_PASS=
|
||||||
# See: https://github.com/mastodon/mastodon/issues/16895
|
DB_PORT=5432
|
||||||
|
|
||||||
# Federation
|
# Federation
|
||||||
# ----------
|
# Note: Changing LOCAL_DOMAIN or LOCAL_HTTPS at a later time will cause unwanted side effects.
|
||||||
# This identifies your server and cannot be changed safely later
|
# LOCAL_DOMAIN should *NOT* contain the protocol part of the domain e.g https://example.com.
|
||||||
# ----------
|
|
||||||
LOCAL_DOMAIN=example.com
|
LOCAL_DOMAIN=example.com
|
||||||
|
LOCAL_HTTPS=true
|
||||||
|
|
||||||
# Use this only if you need to run mastodon on a different domain than the one used for federation.
|
# 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://docs.joinmastodon.org/admin/config/#web-domain
|
# 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.
|
# DO *NOT* USE THIS UNLESS YOU KNOW *EXACTLY* WHAT YOU ARE DOING.
|
||||||
# WEB_DOMAIN=mastodon.example.com
|
# WEB_DOMAIN=mastodon.example.com
|
||||||
|
|
||||||
|
@ -26,113 +26,85 @@ LOCAL_DOMAIN=example.com
|
||||||
# be added. Comma separated values
|
# be added. Comma separated values
|
||||||
# ALTERNATE_DOMAINS=example1.com,example2.com
|
# ALTERNATE_DOMAINS=example1.com,example2.com
|
||||||
|
|
||||||
# Use HTTP proxy for outgoing request (optional)
|
# Application secrets
|
||||||
# http_proxy=http://gateway.local:8118
|
# Generate each with the `RAILS_ENV=production bundle exec rake secret` task (`docker-compose run --rm web rake secret` if you use docker compose)
|
||||||
# Access control for hidden service.
|
PAPERCLIP_SECRET=
|
||||||
# ALLOW_ACCESS_TO_HIDDEN_SERVICE=true
|
|
||||||
|
|
||||||
# Authorized fetch mode (optional)
|
|
||||||
# Require remote servers to authentify when fetching toots, see
|
|
||||||
# https://docs.joinmastodon.org/admin/config/#authorized_fetch
|
|
||||||
# AUTHORIZED_FETCH=true
|
|
||||||
|
|
||||||
# Limited federation mode (optional)
|
|
||||||
# Only allow federation with specific domains, see
|
|
||||||
# https://docs.joinmastodon.org/admin/config/#whitelist_mode
|
|
||||||
# LIMITED_FEDERATION_MODE=true
|
|
||||||
|
|
||||||
# Redis
|
|
||||||
# -----
|
|
||||||
REDIS_HOST=localhost
|
|
||||||
REDIS_PORT=6379
|
|
||||||
|
|
||||||
|
|
||||||
# PostgreSQL
|
|
||||||
# ----------
|
|
||||||
DB_HOST=/var/run/postgresql
|
|
||||||
DB_USER=mastodon
|
|
||||||
DB_NAME=mastodon_production
|
|
||||||
DB_PASS=
|
|
||||||
DB_PORT=5432
|
|
||||||
|
|
||||||
|
|
||||||
# Elasticsearch (optional)
|
|
||||||
# ------------------------
|
|
||||||
#ES_ENABLED=true
|
|
||||||
#ES_HOST=localhost
|
|
||||||
#ES_PORT=9200
|
|
||||||
# Authentication for ES (optional)
|
|
||||||
#ES_USER=elastic
|
|
||||||
#ES_PASS=password
|
|
||||||
|
|
||||||
|
|
||||||
# Secrets
|
|
||||||
# -------
|
|
||||||
# Generate each with the `RAILS_ENV=production bundle exec rake secret` task (`docker-compose run --rm web bundle exec rake secret` if you use docker compose)
|
|
||||||
# -------
|
|
||||||
SECRET_KEY_BASE=
|
SECRET_KEY_BASE=
|
||||||
OTP_SECRET=
|
OTP_SECRET=
|
||||||
|
|
||||||
|
# VAPID keys (used for push notifications
|
||||||
# Web Push
|
# 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` (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
|
# 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.
|
# 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=
|
||||||
|
|
||||||
|
|
||||||
# Registrations
|
# Registrations
|
||||||
# -------------
|
|
||||||
|
|
||||||
# Single user mode will disable registrations and redirect frontpage to the first profile
|
# Single user mode will disable registrations and redirect frontpage to the first profile
|
||||||
# SINGLE_USER_MODE=true
|
# SINGLE_USER_MODE=true
|
||||||
|
|
||||||
# Prevent registrations with following e-mail domains
|
# Prevent registrations with following e-mail domains
|
||||||
# EMAIL_DOMAIN_DENYLIST=example1.com|example2.de|etc
|
# EMAIL_DOMAIN_BLACKLIST=example1.com|example2.de|etc
|
||||||
|
|
||||||
# Only allow registrations with the following e-mail domains
|
# Only allow registrations with the following e-mail domains
|
||||||
# EMAIL_DOMAIN_ALLOWLIST=example1.com|example2.de|etc
|
# EMAIL_DOMAIN_WHITELIST=example1.com|example2.de|etc
|
||||||
|
|
||||||
#TODO move this
|
|
||||||
# Optionally change default language
|
# Optionally change default language
|
||||||
# DEFAULT_LOCALE=de
|
# DEFAULT_LOCALE=de
|
||||||
|
|
||||||
|
# E-mail configuration
|
||||||
# Sending mail
|
# 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_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
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
# File storage (optional)
|
# Optional asset host for multi-server setups
|
||||||
# -----------------------
|
# CDN_HOST=https://assets.example.com
|
||||||
# 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
|
# S3 (optional)
|
||||||
# following header field:
|
# S3_ENABLED=true
|
||||||
# Access-Control-Allow-Origin: https://192.168.1.123:9000/
|
# S3_BUCKET=
|
||||||
# -----------------------
|
# AWS_ACCESS_KEY_ID=
|
||||||
#S3_ENABLED=true
|
# AWS_SECRET_ACCESS_KEY=
|
||||||
#S3_BUCKET=files.example.com
|
# S3_REGION=
|
||||||
#AWS_ACCESS_KEY_ID=
|
# S3_PROTOCOL=http
|
||||||
#AWS_SECRET_ACCESS_KEY=
|
# S3_HOSTNAME=192.168.1.123:9000
|
||||||
#S3_ALIAS_HOST=files.example.com
|
|
||||||
|
# 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 (optional)
|
||||||
# The attachment host must allow cross origin request - see the description
|
|
||||||
# above.
|
|
||||||
# SWIFT_ENABLED=true
|
# SWIFT_ENABLED=true
|
||||||
# SWIFT_USERNAME=
|
# SWIFT_USERNAME=
|
||||||
# For Keystone V3, the value for SWIFT_TENANT should be the project name
|
# For Keystone V3, the value for SWIFT_TENANT should be the project name
|
||||||
# SWIFT_TENANT=
|
# SWIFT_TENANT=
|
||||||
# SWIFT_PASSWORD=
|
# 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
|
# Keystone V2 and V3 URLs are supported. Use a V3 URL if possible to avoid
|
||||||
# issues with token rate-limiting during high load.
|
# issues with token rate-limiting during high load.
|
||||||
# SWIFT_AUTH_URL=
|
# SWIFT_AUTH_URL=
|
||||||
|
@ -144,160 +116,21 @@ SMTP_FROM_ADDRESS=notifications@example.com
|
||||||
# Defaults to 60 seconds. Set to 0 to disable
|
# Defaults to 60 seconds. Set to 0 to disable
|
||||||
# SWIFT_CACHE_TTL=
|
# SWIFT_CACHE_TTL=
|
||||||
|
|
||||||
# Optional asset host for multi-server setups
|
# Optional alias for S3 if you want to use Cloudfront or Cloudflare in front
|
||||||
# The asset host must allow cross origin request from WEB_DOMAIN or LOCAL_DOMAIN
|
# S3_CLOUDFRONT_HOST=
|
||||||
# if WEB_DOMAIN is not set. For example, the server may have the
|
|
||||||
# following header field:
|
|
||||||
# Access-Control-Allow-Origin: https://example.com/
|
|
||||||
# CDN_HOST=https://assets.example.com
|
|
||||||
|
|
||||||
# Optional list of hosts that are allowed to serve media for your instance
|
|
||||||
# This is useful if you include external media in your custom CSS or about page,
|
|
||||||
# or if your data storage provider makes use of redirects to other domains.
|
|
||||||
# EXTRA_DATA_HOSTS=https://data.example1.com|https://data.example2.com
|
|
||||||
|
|
||||||
# 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 integration
|
||||||
# STREAMING_API_BASE_URL=
|
# STREAMING_API_BASE_URL=
|
||||||
|
|
||||||
|
# Advanced settings
|
||||||
|
# If you need to use pgBouncer, you need to disable prepared statements:
|
||||||
|
# PREPARED_STATEMENTS=false
|
||||||
|
|
||||||
# External authentication (optional)
|
# Cluster number setting for streaming API server.
|
||||||
# ----------------------------------
|
# If you comment out following line, cluster number will be `numOfCpuCores - 1`.
|
||||||
# LDAP authentication (optional)
|
STREAMING_CLUSTER_NUM=1
|
||||||
# LDAP_ENABLED=true
|
|
||||||
# LDAP_HOST=localhost
|
|
||||||
# LDAP_PORT=389
|
|
||||||
# LDAP_METHOD=simple_tls
|
|
||||||
# LDAP_BASE=
|
|
||||||
# LDAP_BIND_DN=
|
|
||||||
# LDAP_PASSWORD=
|
|
||||||
# LDAP_UID=cn
|
|
||||||
# LDAP_MAIL=mail
|
|
||||||
# LDAP_SEARCH_FILTER=(|(%{uid}=%{email})(%{mail}=%{email}))
|
|
||||||
# LDAP_UID_CONVERSION_ENABLED=true
|
|
||||||
# LDAP_UID_CONVERSION_SEARCH=., -
|
|
||||||
# LDAP_UID_CONVERSION_REPLACE=_
|
|
||||||
|
|
||||||
# PAM authentication (optional)
|
# Docker mastodon user
|
||||||
# PAM authentication uses for the email generation the "email" pam variable
|
# If you use Docker, you may want to assign UID/GID manually.
|
||||||
# and optional as fallback PAM_DEFAULT_SUFFIX
|
# UID=1000
|
||||||
# The pam environment variable "email" is provided by:
|
# GID=1000
|
||||||
# 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=http://localhost:3000/auth/auth/saml/callback
|
|
||||||
# SAML_ISSUER=https://example.com
|
|
||||||
# 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=
|
|
||||||
|
|
||||||
|
|
||||||
# Custom settings
|
|
||||||
# ---------------
|
|
||||||
# Various ways to customize Mastodon's behavior
|
|
||||||
# ---------------
|
|
||||||
|
|
||||||
# Maximum allowed character count
|
|
||||||
MAX_TOOT_CHARS=500
|
|
||||||
|
|
||||||
# Maximum number of pinned posts
|
|
||||||
MAX_PINNED_TOOTS=5
|
|
||||||
|
|
||||||
# Maximum allowed bio characters
|
|
||||||
MAX_BIO_CHARS=500
|
|
||||||
|
|
||||||
# Maximim number of profile fields allowed
|
|
||||||
MAX_PROFILE_FIELDS=4
|
|
||||||
|
|
||||||
# Maximum allowed display name characters
|
|
||||||
MAX_DISPLAY_NAME_CHARS=30
|
|
||||||
|
|
||||||
# Maximum allowed poll options
|
|
||||||
MAX_POLL_OPTIONS=5
|
|
||||||
|
|
||||||
# Maximum allowed poll option characters
|
|
||||||
MAX_POLL_OPTION_CHARS=100
|
|
||||||
|
|
||||||
# Maximum image and video/audio upload sizes
|
|
||||||
# Units are in bytes
|
|
||||||
# 1048576 bytes equals 1 megabyte
|
|
||||||
# MAX_IMAGE_SIZE=8388608
|
|
||||||
# MAX_VIDEO_SIZE=41943040
|
|
||||||
|
|
||||||
# Maximum search results to display
|
|
||||||
# Only relevant when elasticsearch is installed
|
|
||||||
# MAX_SEARCH_RESULTS=20
|
|
||||||
|
|
||||||
# Maximum hashtags to display
|
|
||||||
# Customize the number of hashtags shown in 'Explore'
|
|
||||||
# MAX_TRENDING_TAGS=10
|
|
||||||
|
|
||||||
# Maximum custom emoji file sizes
|
|
||||||
# If undefined or smaller than MAX_EMOJI_SIZE, the value
|
|
||||||
# of MAX_EMOJI_SIZE will be used for MAX_REMOTE_EMOJI_SIZE
|
|
||||||
# Units are in bytes
|
|
||||||
# MAX_EMOJI_SIZE=262144
|
|
||||||
# MAX_REMOTE_EMOJI_SIZE=262144
|
|
||||||
|
|
||||||
# Optional hCaptcha support
|
|
||||||
# HCAPTCHA_SECRET_KEY=
|
|
||||||
# HCAPTCHA_SITE_KEY=
|
|
||||||
|
|
||||||
# IP and session retention
|
|
||||||
# -----------------------
|
|
||||||
# Make sure to modify the scheduling of ip_cleanup_scheduler in config/sidekiq.yml
|
|
||||||
# to be less than daily if you lower IP_RETENTION_PERIOD below two days (172800).
|
|
||||||
# -----------------------
|
|
||||||
IP_RETENTION_PERIOD=31556952
|
|
||||||
SESSION_RETENTION_PERIOD=31556952
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
# Node.js
|
|
||||||
NODE_ENV=tests
|
|
||||||
# Federation
|
# Federation
|
||||||
LOCAL_DOMAIN=cb6e6126.ngrok.io
|
LOCAL_DOMAIN=cb6e6126.ngrok.io
|
||||||
LOCAL_HTTPS=true
|
LOCAL_HTTPS=true
|
||||||
|
OTP_SECRET=100c7faeef00caa29242f6b04156742bf76065771fd4117990c4282b8748ff3d99f8fdae97c982ab5bd2e6756a159121377cce4421f4a8ecd2d67bd7749a3fb4
|
||||||
|
|
|
@ -1,4 +1,2 @@
|
||||||
VAGRANT=true
|
VAGRANT=true
|
||||||
LOCAL_DOMAIN=mastodon.local
|
LOCAL_DOMAIN=mastodon.dev
|
||||||
BIND=0.0.0.0
|
|
||||||
DB_HOST=/var/run/postgresql/
|
|
|
@ -1,13 +1,30 @@
|
||||||
/build/**
|
# See https://help.github.com/articles/ignoring-files for more about ignoring files.
|
||||||
/coverage/**
|
#
|
||||||
/db/**
|
# If you find yourself ignoring temporary files generated by your text editor
|
||||||
/lib/**
|
# or operating system, you probably want to add a global ignore instead:
|
||||||
/log/**
|
# git config --global core.excludesfile '~/.gitignore_global'
|
||||||
/node_modules/**
|
|
||||||
/nonobox/**
|
# Ignore bundler config.
|
||||||
/public/**
|
/.bundle
|
||||||
!/public/embed.js
|
|
||||||
/spec/**
|
# Ignore the default SQLite database.
|
||||||
/tmp/**
|
/db/*.sqlite3
|
||||||
/vendor/**
|
/db/*.sqlite3-journal
|
||||||
!.eslintrc.js
|
|
||||||
|
# Ignore all logfiles and tempfiles.
|
||||||
|
/log/*
|
||||||
|
!/log/.keep
|
||||||
|
/tmp
|
||||||
|
coverage
|
||||||
|
public/system
|
||||||
|
public/assets
|
||||||
|
.env
|
||||||
|
.env.production
|
||||||
|
node_modules/
|
||||||
|
neo4j/
|
||||||
|
|
||||||
|
# Ignore Vagrant files
|
||||||
|
.vagrant/
|
||||||
|
|
||||||
|
# Ignore Capistrano customizations
|
||||||
|
config/deploy/*
|
||||||
|
|
214
.eslintrc.js
214
.eslintrc.js
|
@ -1,214 +0,0 @@
|
||||||
module.exports = {
|
|
||||||
root: true,
|
|
||||||
|
|
||||||
env: {
|
|
||||||
browser: true,
|
|
||||||
node: true,
|
|
||||||
es6: true,
|
|
||||||
jest: true,
|
|
||||||
},
|
|
||||||
|
|
||||||
globals: {
|
|
||||||
ATTACHMENT_HOST: false,
|
|
||||||
},
|
|
||||||
|
|
||||||
parser: '@babel/eslint-parser',
|
|
||||||
|
|
||||||
plugins: [
|
|
||||||
'react',
|
|
||||||
'jsx-a11y',
|
|
||||||
'import',
|
|
||||||
'promise',
|
|
||||||
],
|
|
||||||
|
|
||||||
parserOptions: {
|
|
||||||
sourceType: 'module',
|
|
||||||
ecmaFeatures: {
|
|
||||||
experimentalObjectRestSpread: true,
|
|
||||||
jsx: true,
|
|
||||||
},
|
|
||||||
ecmaVersion: 2021,
|
|
||||||
},
|
|
||||||
|
|
||||||
settings: {
|
|
||||||
react: {
|
|
||||||
version: 'detect',
|
|
||||||
},
|
|
||||||
'import/extensions': [
|
|
||||||
'.js',
|
|
||||||
],
|
|
||||||
'import/ignore': [
|
|
||||||
'node_modules',
|
|
||||||
'\\.(css|scss|json)$',
|
|
||||||
],
|
|
||||||
'import/resolver': {
|
|
||||||
node: {
|
|
||||||
paths: ['app/javascript'],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
rules: {
|
|
||||||
'brace-style': 'warn',
|
|
||||||
'comma-dangle': ['error', 'always-multiline'],
|
|
||||||
'comma-spacing': [
|
|
||||||
'warn',
|
|
||||||
{
|
|
||||||
before: false,
|
|
||||||
after: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
'comma-style': ['warn', 'last'],
|
|
||||||
'consistent-return': 'error',
|
|
||||||
'dot-notation': 'error',
|
|
||||||
eqeqeq: 'error',
|
|
||||||
indent: ['warn', 2],
|
|
||||||
'jsx-quotes': ['error', 'prefer-single'],
|
|
||||||
'no-catch-shadow': 'error',
|
|
||||||
'no-cond-assign': 'error',
|
|
||||||
'no-console': [
|
|
||||||
'warn',
|
|
||||||
{
|
|
||||||
allow: [
|
|
||||||
'error',
|
|
||||||
'warn',
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
'no-fallthrough': 'error',
|
|
||||||
'no-irregular-whitespace': 'error',
|
|
||||||
'no-mixed-spaces-and-tabs': 'warn',
|
|
||||||
'no-nested-ternary': 'warn',
|
|
||||||
'no-restricted-properties': [
|
|
||||||
'error',
|
|
||||||
{ property: 'substring', message: 'Use .slice instead of .substring.' },
|
|
||||||
{ property: 'substr', message: 'Use .slice instead of .substr.' },
|
|
||||||
],
|
|
||||||
'no-trailing-spaces': 'warn',
|
|
||||||
'no-undef': 'error',
|
|
||||||
'no-unreachable': 'error',
|
|
||||||
'no-unused-expressions': 'error',
|
|
||||||
'no-unused-vars': [
|
|
||||||
'error',
|
|
||||||
{
|
|
||||||
vars: 'all',
|
|
||||||
args: 'after-used',
|
|
||||||
ignoreRestSiblings: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
'object-curly-spacing': ['error', 'always'],
|
|
||||||
'padded-blocks': [
|
|
||||||
'error',
|
|
||||||
{
|
|
||||||
classes: 'always',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
quotes: ['error', 'single'],
|
|
||||||
semi: 'error',
|
|
||||||
strict: 'off',
|
|
||||||
'valid-typeof': 'error',
|
|
||||||
|
|
||||||
'react/jsx-boolean-value': 'error',
|
|
||||||
'react/jsx-closing-bracket-location': ['error', 'line-aligned'],
|
|
||||||
'react/jsx-curly-spacing': 'error',
|
|
||||||
'react/jsx-equals-spacing': 'error',
|
|
||||||
'react/jsx-first-prop-new-line': ['error', 'multiline-multiprop'],
|
|
||||||
'react/jsx-indent': ['error', 2],
|
|
||||||
'react/jsx-no-bind': 'error',
|
|
||||||
'react/jsx-no-duplicate-props': 'error',
|
|
||||||
'react/jsx-no-undef': 'error',
|
|
||||||
'react/jsx-tag-spacing': 'error',
|
|
||||||
'react/jsx-uses-react': 'error',
|
|
||||||
'react/jsx-uses-vars': 'error',
|
|
||||||
'react/jsx-wrap-multilines': 'error',
|
|
||||||
'react/no-multi-comp': 'off',
|
|
||||||
'react/no-string-refs': 'error',
|
|
||||||
'react/prop-types': 'error',
|
|
||||||
'react/self-closing-comp': 'error',
|
|
||||||
|
|
||||||
'jsx-a11y/accessible-emoji': 'warn',
|
|
||||||
'jsx-a11y/alt-text': 'warn',
|
|
||||||
'jsx-a11y/anchor-has-content': 'warn',
|
|
||||||
'jsx-a11y/anchor-is-valid': [
|
|
||||||
'warn',
|
|
||||||
{
|
|
||||||
components: [
|
|
||||||
'Link',
|
|
||||||
'NavLink',
|
|
||||||
],
|
|
||||||
specialLink: [
|
|
||||||
'to',
|
|
||||||
],
|
|
||||||
aspect: [
|
|
||||||
'noHref',
|
|
||||||
'invalidHref',
|
|
||||||
'preferButton',
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
'jsx-a11y/aria-activedescendant-has-tabindex': 'warn',
|
|
||||||
'jsx-a11y/aria-props': 'warn',
|
|
||||||
'jsx-a11y/aria-proptypes': 'warn',
|
|
||||||
'jsx-a11y/aria-role': 'warn',
|
|
||||||
'jsx-a11y/aria-unsupported-elements': 'warn',
|
|
||||||
'jsx-a11y/heading-has-content': 'warn',
|
|
||||||
'jsx-a11y/html-has-lang': 'warn',
|
|
||||||
'jsx-a11y/iframe-has-title': 'warn',
|
|
||||||
'jsx-a11y/img-redundant-alt': 'warn',
|
|
||||||
'jsx-a11y/interactive-supports-focus': 'warn',
|
|
||||||
'jsx-a11y/label-has-for': 'off',
|
|
||||||
'jsx-a11y/mouse-events-have-key-events': 'warn',
|
|
||||||
'jsx-a11y/no-access-key': 'warn',
|
|
||||||
'jsx-a11y/no-distracting-elements': 'warn',
|
|
||||||
'jsx-a11y/no-noninteractive-element-interactions': [
|
|
||||||
'warn',
|
|
||||||
{
|
|
||||||
handlers: [
|
|
||||||
'onClick',
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
'jsx-a11y/no-onchange': 'warn',
|
|
||||||
'jsx-a11y/no-redundant-roles': 'warn',
|
|
||||||
'jsx-a11y/no-static-element-interactions': [
|
|
||||||
'warn',
|
|
||||||
{
|
|
||||||
handlers: [
|
|
||||||
'onClick',
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
'jsx-a11y/role-has-required-aria-props': 'warn',
|
|
||||||
'jsx-a11y/role-supports-aria-props': 'off',
|
|
||||||
'jsx-a11y/scope': 'warn',
|
|
||||||
'jsx-a11y/tabindex-no-positive': 'warn',
|
|
||||||
|
|
||||||
'import/extensions': [
|
|
||||||
'error',
|
|
||||||
'always',
|
|
||||||
{
|
|
||||||
js: 'never',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
'import/newline-after-import': 'error',
|
|
||||||
'import/no-extraneous-dependencies': [
|
|
||||||
'error',
|
|
||||||
{
|
|
||||||
devDependencies: [
|
|
||||||
'config/webpack/**',
|
|
||||||
'app/javascript/mastodon/test_setup.js',
|
|
||||||
'app/javascript/**/__tests__/**',
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
'import/no-unresolved': 'error',
|
|
||||||
'import/no-webpack-loader-syntax': 'error',
|
|
||||||
|
|
||||||
'promise/catch-or-return': [
|
|
||||||
'error',
|
|
||||||
{
|
|
||||||
allowFinally: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
};
|
|
154
.eslintrc.yml
Normal file
154
.eslintrc.yml
Normal file
|
@ -0,0 +1,154 @@
|
||||||
|
---
|
||||||
|
root: true
|
||||||
|
|
||||||
|
env:
|
||||||
|
browser: true
|
||||||
|
node: true
|
||||||
|
es6: true
|
||||||
|
jest: true
|
||||||
|
|
||||||
|
parser: babel-eslint
|
||||||
|
|
||||||
|
plugins:
|
||||||
|
- react
|
||||||
|
- jsx-a11y
|
||||||
|
- import
|
||||||
|
|
||||||
|
parserOptions:
|
||||||
|
sourceType: module
|
||||||
|
ecmaFeatures:
|
||||||
|
arrowFunctions: true
|
||||||
|
jsx: true
|
||||||
|
destructuring: true
|
||||||
|
modules: true
|
||||||
|
spread: true
|
||||||
|
|
||||||
|
settings:
|
||||||
|
import/extensions:
|
||||||
|
- .js
|
||||||
|
import/ignore:
|
||||||
|
- node_modules
|
||||||
|
- \\.(css|scss|json)$
|
||||||
|
import/resolver:
|
||||||
|
node:
|
||||||
|
moduleDirectory:
|
||||||
|
- node_modules
|
||||||
|
- app/javascript
|
||||||
|
|
||||||
|
rules:
|
||||||
|
brace-style: warn
|
||||||
|
comma-dangle:
|
||||||
|
- error
|
||||||
|
- always-multiline
|
||||||
|
comma-spacing:
|
||||||
|
- warn
|
||||||
|
- before: false
|
||||||
|
after: true
|
||||||
|
comma-style:
|
||||||
|
- warn
|
||||||
|
- last
|
||||||
|
consistent-return: error
|
||||||
|
dot-notation: error
|
||||||
|
eqeqeq: error
|
||||||
|
indent:
|
||||||
|
- warn
|
||||||
|
- 2
|
||||||
|
jsx-quotes:
|
||||||
|
- error
|
||||||
|
- prefer-single
|
||||||
|
no-catch-shadow: error
|
||||||
|
no-cond-assign: error
|
||||||
|
no-console:
|
||||||
|
- warn
|
||||||
|
- allow:
|
||||||
|
- error
|
||||||
|
- warn
|
||||||
|
no-fallthrough: error
|
||||||
|
no-irregular-whitespace: error
|
||||||
|
no-mixed-spaces-and-tabs: warn
|
||||||
|
no-nested-ternary: warn
|
||||||
|
no-trailing-spaces: warn
|
||||||
|
no-undef: error
|
||||||
|
no-unreachable: error
|
||||||
|
no-unused-expressions: error
|
||||||
|
no-unused-vars:
|
||||||
|
- error
|
||||||
|
- vars: all
|
||||||
|
args: after-used
|
||||||
|
ignoreRestSiblings: true
|
||||||
|
object-curly-spacing:
|
||||||
|
- error
|
||||||
|
- always
|
||||||
|
padded-blocks:
|
||||||
|
- error
|
||||||
|
- classes: always
|
||||||
|
quotes:
|
||||||
|
- error
|
||||||
|
- single
|
||||||
|
semi: error
|
||||||
|
strict: off
|
||||||
|
valid-typeof: error
|
||||||
|
|
||||||
|
react/jsx-boolean-value: error
|
||||||
|
react/jsx-closing-bracket-location:
|
||||||
|
- error
|
||||||
|
- line-aligned
|
||||||
|
react/jsx-curly-spacing: error
|
||||||
|
react/jsx-equals-spacing: error
|
||||||
|
react/jsx-first-prop-new-line:
|
||||||
|
- error
|
||||||
|
- multiline-multiprop
|
||||||
|
react/jsx-indent:
|
||||||
|
- error
|
||||||
|
- 2
|
||||||
|
react/jsx-no-bind: error
|
||||||
|
react/jsx-no-duplicate-props: error
|
||||||
|
react/jsx-no-undef: error
|
||||||
|
react/jsx-tag-spacing: error
|
||||||
|
react/jsx-uses-react: error
|
||||||
|
react/jsx-uses-vars: error
|
||||||
|
react/jsx-wrap-multilines: error
|
||||||
|
react/no-multi-comp: off
|
||||||
|
react/no-string-refs: error
|
||||||
|
react/prop-types: error
|
||||||
|
react/self-closing-comp: error
|
||||||
|
|
||||||
|
jsx-a11y/accessible-emoji: warn
|
||||||
|
jsx-a11y/anchor-has-content: warn
|
||||||
|
jsx-a11y/aria-activedescendant-has-tabindex: warn
|
||||||
|
jsx-a11y/aria-props: warn
|
||||||
|
jsx-a11y/aria-proptypes: warn
|
||||||
|
jsx-a11y/aria-role: warn
|
||||||
|
jsx-a11y/aria-unsupported-elements: warn
|
||||||
|
jsx-a11y/heading-has-content: warn
|
||||||
|
jsx-a11y/href-no-hash: warn
|
||||||
|
jsx-a11y/html-has-lang: warn
|
||||||
|
jsx-a11y/iframe-has-title: warn
|
||||||
|
jsx-a11y/img-has-alt: warn
|
||||||
|
jsx-a11y/img-redundant-alt: warn
|
||||||
|
jsx-a11y/label-has-for: off
|
||||||
|
jsx-a11y/mouse-events-have-key-events: warn
|
||||||
|
jsx-a11y/no-access-key: warn
|
||||||
|
jsx-a11y/no-distracting-elements: warn
|
||||||
|
jsx-a11y/no-onchange: warn
|
||||||
|
jsx-a11y/no-redundant-roles: warn
|
||||||
|
jsx-a11y/onclick-has-focus: warn
|
||||||
|
jsx-a11y/onclick-has-role: warn
|
||||||
|
jsx-a11y/role-has-required-aria-props: warn
|
||||||
|
jsx-a11y/role-supports-aria-props: off
|
||||||
|
jsx-a11y/scope: warn
|
||||||
|
jsx-a11y/tabindex-no-positive: warn
|
||||||
|
|
||||||
|
import/extensions:
|
||||||
|
- error
|
||||||
|
- always
|
||||||
|
- js: never
|
||||||
|
import/newline-after-import: error
|
||||||
|
import/no-extraneous-dependencies:
|
||||||
|
- error
|
||||||
|
- devDependencies:
|
||||||
|
- "config/webpack/**"
|
||||||
|
- "app/javascript/mastodon/test_setup.js"
|
||||||
|
- "app/javascript/**/__tests__/**"
|
||||||
|
import/no-unresolved: error
|
||||||
|
import/no-webpack-loader-syntax: error
|
3
.github/FUNDING.yml
vendored
3
.github/FUNDING.yml
vendored
|
@ -1,3 +0,0 @@
|
||||||
patreon: 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
|
|
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.
|
|
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
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
44
.github/workflows/build-image.yml
vendored
44
.github/workflows/build-image.yml
vendored
|
@ -1,44 +0,0 @@
|
||||||
name: Build container image
|
|
||||||
on:
|
|
||||||
workflow_dispatch:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- 'main'
|
|
||||||
pull_request:
|
|
||||||
paths:
|
|
||||||
- .github/workflows/build-image.yml
|
|
||||||
- Dockerfile
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
packages: write
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build-image:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
- uses: docker/setup-qemu-action@v2
|
|
||||||
- uses: docker/setup-buildx-action@v2
|
|
||||||
- uses: docker/login-action@v2
|
|
||||||
with:
|
|
||||||
registry: ghcr.io
|
|
||||||
username: ${{ github.repository_owner }}
|
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
if: github.event_name != 'pull_request'
|
|
||||||
- uses: docker/metadata-action@v4
|
|
||||||
id: meta
|
|
||||||
with:
|
|
||||||
images: ghcr.io/${{ github.repository_owner }}/mastodon
|
|
||||||
tags: |
|
|
||||||
type=raw,value=latest,enable={{is_default_branch}}
|
|
||||||
type=edge,branch=main
|
|
||||||
type=sha,prefix=,format=long
|
|
||||||
- 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: '3.0'
|
|
||||||
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
|
|
83
.github/workflows/linter.yml
vendored
83
.github/workflows/linter.yml
vendored
|
@ -1,83 +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: 16.x
|
|
||||||
cache: yarn
|
|
||||||
- name: Install dependencies
|
|
||||||
run: yarn install --frozen-lockfile
|
|
||||||
- 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
|
|
138
.github/workflows/test-chart.yml
vendored
138
.github/workflows/test-chart.yml
vendored
|
@ -1,138 +0,0 @@
|
||||||
# This is a GitHub workflow defining a set of jobs with a set of steps.
|
|
||||||
# ref: https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions
|
|
||||||
#
|
|
||||||
name: Test chart
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
paths:
|
|
||||||
- "chart/**"
|
|
||||||
- "!**.md"
|
|
||||||
- ".github/workflows/test-chart.yml"
|
|
||||||
push:
|
|
||||||
paths:
|
|
||||||
- "chart/**"
|
|
||||||
- "!**.md"
|
|
||||||
- ".github/workflows/test-chart.yml"
|
|
||||||
branches-ignore:
|
|
||||||
- "dependabot/**"
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
|
|
||||||
defaults:
|
|
||||||
run:
|
|
||||||
working-directory: chart
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
lint-templates:
|
|
||||||
runs-on: ubuntu-22.04
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
- uses: actions/setup-python@v4
|
|
||||||
with:
|
|
||||||
python-version: "3.x"
|
|
||||||
|
|
||||||
- name: Install dependencies (yamllint)
|
|
||||||
run: pip install yamllint
|
|
||||||
|
|
||||||
- run: helm dependency update
|
|
||||||
|
|
||||||
- name: helm lint
|
|
||||||
run: |
|
|
||||||
helm lint . \
|
|
||||||
--values dev-values.yaml
|
|
||||||
|
|
||||||
- name: helm template
|
|
||||||
run: |
|
|
||||||
helm template . \
|
|
||||||
--values dev-values.yaml \
|
|
||||||
--output-dir rendered-templates
|
|
||||||
|
|
||||||
- name: yamllint (only on templates we manage)
|
|
||||||
run: |
|
|
||||||
rm -rf rendered-templates/mastodon/charts
|
|
||||||
|
|
||||||
yamllint rendered-templates \
|
|
||||||
--config-data "{rules: {indentation: {spaces: 2}, line-length: disable}}"
|
|
||||||
|
|
||||||
# This job helps us validate that rendered templates are valid k8s resources
|
|
||||||
# against a k8s api-server, via "helm template --validate", but also that a
|
|
||||||
# basic configuration can be used to successfully startup mastodon.
|
|
||||||
#
|
|
||||||
test-install:
|
|
||||||
runs-on: ubuntu-22.04
|
|
||||||
timeout-minutes: 15
|
|
||||||
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
# k3s-channel reference: https://update.k3s.io/v1-release/channels
|
|
||||||
- k3s-channel: latest
|
|
||||||
- k3s-channel: stable
|
|
||||||
|
|
||||||
# This represents the oldest configuration we test against.
|
|
||||||
#
|
|
||||||
# The k8s version chosen is based on the oldest still supported k8s
|
|
||||||
# version among two managed k8s services, GKE, EKS.
|
|
||||||
# - GKE: https://endoflife.date/google-kubernetes-engine
|
|
||||||
# - EKS: https://endoflife.date/amazon-eks
|
|
||||||
#
|
|
||||||
# The helm client's version can influence what helper functions is
|
|
||||||
# available for use in the templates, currently we need v3.6.0 or
|
|
||||||
# higher.
|
|
||||||
#
|
|
||||||
- k3s-channel: v1.21
|
|
||||||
helm-version: v3.6.0
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
|
|
||||||
# This action starts a k8s cluster with NetworkPolicy enforcement and
|
|
||||||
# installs both kubectl and helm.
|
|
||||||
#
|
|
||||||
# ref: https://github.com/jupyterhub/action-k3s-helm#readme
|
|
||||||
#
|
|
||||||
- uses: jupyterhub/action-k3s-helm@v3
|
|
||||||
with:
|
|
||||||
k3s-channel: ${{ matrix.k3s-channel }}
|
|
||||||
helm-version: ${{ matrix.helm-version }}
|
|
||||||
metrics-enabled: false
|
|
||||||
traefik-enabled: false
|
|
||||||
docker-enabled: false
|
|
||||||
|
|
||||||
- run: helm dependency update
|
|
||||||
|
|
||||||
# Validate rendered helm templates against the k8s api-server
|
|
||||||
- name: helm template --validate
|
|
||||||
run: |
|
|
||||||
helm template --validate mastodon . \
|
|
||||||
--values dev-values.yaml
|
|
||||||
|
|
||||||
- name: helm install
|
|
||||||
run: |
|
|
||||||
helm install mastodon . \
|
|
||||||
--values dev-values.yaml \
|
|
||||||
--timeout 10m
|
|
||||||
|
|
||||||
# This actions provides a report about the state of the k8s cluster,
|
|
||||||
# providing logs etc on anything that has failed and workloads marked as
|
|
||||||
# important.
|
|
||||||
#
|
|
||||||
# ref: https://github.com/jupyterhub/action-k8s-namespace-report#readme
|
|
||||||
#
|
|
||||||
- name: Kubernetes namespace report
|
|
||||||
uses: jupyterhub/action-k8s-namespace-report@v1
|
|
||||||
if: always()
|
|
||||||
with:
|
|
||||||
important-workloads: >-
|
|
||||||
deploy/mastodon-sidekiq
|
|
||||||
deploy/mastodon-streaming
|
|
||||||
deploy/mastodon-web
|
|
||||||
job/mastodon-assets-precompile
|
|
||||||
job/mastodon-chewy-upgrade
|
|
||||||
job/mastodon-create-admin
|
|
||||||
job/mastodon-db-migrate
|
|
36
.gitignore
vendored
36
.gitignore
vendored
|
@ -13,42 +13,32 @@
|
||||||
/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 volume optionally created by docker-compose
|
||||||
/postgres
|
postgres
|
||||||
/postgres14
|
redis
|
||||||
/redis
|
|
||||||
/elasticsearch
|
|
||||||
|
|
||||||
# ignore Helm charts
|
|
||||||
/chart/*.tgz
|
|
||||||
|
|
||||||
# ignore Helm dependency charts
|
|
||||||
/chart/charts/*.tgz
|
|
||||||
|
|
||||||
# Ignore Apple files
|
# Ignore Apple files
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
@ -64,8 +54,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
|
||||||
|
|
||||||
|
|
9
.postcssrc.yml
Normal file
9
.postcssrc.yml
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
plugins:
|
||||||
|
postcss-smart-import: {}
|
||||||
|
precss: {}
|
||||||
|
autoprefixer:
|
||||||
|
browsers:
|
||||||
|
- last 2 versions
|
||||||
|
- IE >= 11
|
||||||
|
- iOS >= 9
|
||||||
|
postcss-object-fit-images: {}
|
|
@ -1,78 +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 Helm dependency charts
|
|
||||||
/chart/charts/*.tgz
|
|
||||||
|
|
||||||
# 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 Helm files
|
|
||||||
/chart
|
|
||||||
|
|
||||||
# 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
|
|
||||||
}
|
|
238
.rubocop.yml
238
.rubocop.yml
|
@ -1,21 +1,16 @@
|
||||||
require:
|
|
||||||
- rubocop-rails
|
|
||||||
|
|
||||||
AllCops:
|
AllCops:
|
||||||
TargetRubyVersion: 2.7
|
TargetRubyVersion: 2.3
|
||||||
NewCops: disable
|
|
||||||
Exclude:
|
Exclude:
|
||||||
- 'spec/**/*'
|
- 'spec/**/*'
|
||||||
- 'db/**/*'
|
- 'db/**/*'
|
||||||
- 'app/views/**/*'
|
- 'app/views/**/*'
|
||||||
- 'config/**/*'
|
- 'config/**/*'
|
||||||
- 'bin/*'
|
- 'bin/*'
|
||||||
- 'Rakefile'
|
- 'Rakefile'
|
||||||
- 'node_modules/**/*'
|
- 'node_modules/**/*'
|
||||||
- 'Vagrantfile'
|
- 'Vagrantfile'
|
||||||
- 'vendor/**/*'
|
- 'vendor/**/*'
|
||||||
- 'lib/json_ld/*'
|
- 'lib/json_ld/*'
|
||||||
- 'lib/templates/**/*'
|
|
||||||
|
|
||||||
Bundler/OrderedGems:
|
Bundler/OrderedGems:
|
||||||
Enabled: false
|
Enabled: false
|
||||||
|
@ -26,82 +21,34 @@ 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: 115
|
Max: 100
|
||||||
Exclude:
|
|
||||||
- 'lib/mastodon/*_cli.rb'
|
|
||||||
|
|
||||||
Metrics/BlockLength:
|
Metrics/BlockLength:
|
||||||
Max: 55
|
Max: 35
|
||||||
Exclude:
|
Exclude:
|
||||||
- 'lib/tasks/**/*'
|
- 'lib/tasks/**/*'
|
||||||
- 'lib/mastodon/*_cli.rb'
|
|
||||||
|
|
||||||
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
|
||||||
Exclude:
|
|
||||||
- 'lib/mastodon/*_cli.rb'
|
|
||||||
|
|
||||||
Metrics/CyclomaticComplexity:
|
Metrics/CyclomaticComplexity:
|
||||||
Max: 25
|
Max: 25
|
||||||
Exclude:
|
|
||||||
- 'lib/mastodon/*_cli.rb'
|
|
||||||
|
|
||||||
Layout/LineLength:
|
Metrics/LineLength:
|
||||||
AllowURI: true
|
AllowURI: true
|
||||||
Enabled: false
|
Enabled: false
|
||||||
|
|
||||||
Metrics/MethodLength:
|
Metrics/MethodLength:
|
||||||
CountComments: false
|
CountComments: false
|
||||||
Max: 65
|
Max: 55
|
||||||
Exclude:
|
|
||||||
- 'lib/mastodon/*_cli.rb'
|
|
||||||
|
|
||||||
Metrics/ModuleLength:
|
Metrics/ModuleLength:
|
||||||
CountComments: false
|
CountComments: false
|
||||||
|
@ -112,90 +59,17 @@ Metrics/ParameterLists:
|
||||||
CountKeywordArgs: true
|
CountKeywordArgs: true
|
||||||
|
|
||||||
Metrics/PerceivedComplexity:
|
Metrics/PerceivedComplexity:
|
||||||
Max: 25
|
Max: 20
|
||||||
|
|
||||||
Naming/MemoizedInstanceVariableName:
|
|
||||||
Enabled: false
|
|
||||||
|
|
||||||
Naming/MethodParameterName:
|
|
||||||
Enabled: true
|
|
||||||
|
|
||||||
Rails:
|
Rails:
|
||||||
Enabled: true
|
Enabled: true
|
||||||
|
|
||||||
Rails/ApplicationController:
|
|
||||||
Enabled: false
|
|
||||||
Exclude:
|
|
||||||
- 'app/controllers/well_known/**/*.rb'
|
|
||||||
|
|
||||||
Rails/BelongsTo:
|
|
||||||
Enabled: false
|
|
||||||
|
|
||||||
Rails/ContentTag:
|
|
||||||
Enabled: false
|
|
||||||
|
|
||||||
Rails/EnumHash:
|
|
||||||
Enabled: false
|
|
||||||
|
|
||||||
Rails/Exit:
|
|
||||||
Exclude:
|
|
||||||
- 'lib/mastodon/*'
|
|
||||||
- 'lib/cli.rb'
|
|
||||||
|
|
||||||
Rails/FilePath:
|
|
||||||
Enabled: false
|
|
||||||
|
|
||||||
Rails/HasAndBelongsToMany:
|
Rails/HasAndBelongsToMany:
|
||||||
Enabled: false
|
Enabled: false
|
||||||
|
|
||||||
Rails/HasManyOrHasOneDependent:
|
|
||||||
Enabled: false
|
|
||||||
|
|
||||||
Rails/HelperInstanceVariable:
|
|
||||||
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:
|
Rails/SkipsModelValidations:
|
||||||
Enabled: false
|
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
|
||||||
|
|
||||||
|
@ -210,55 +84,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': '()'
|
||||||
|
@ -267,47 +101,11 @@ Style/PercentLiteralDelimiters:
|
||||||
Style/PerlBackrefs:
|
Style/PerlBackrefs:
|
||||||
AutoCorrect: false
|
AutoCorrect: false
|
||||||
|
|
||||||
Style/RedundantAssignment:
|
|
||||||
Enabled: 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: false
|
|
||||||
|
|
||||||
Style/SignalException:
|
|
||||||
Enabled: false
|
|
||||||
|
|
||||||
Style/SlicingWithRange:
|
|
||||||
Enabled: true
|
|
||||||
|
|
||||||
Style/SymbolArray:
|
Style/SymbolArray:
|
||||||
Enabled: false
|
Enabled: false
|
||||||
|
|
||||||
Style/TrailingCommaInArrayLiteral:
|
Style/TrailingCommaInLiteral:
|
||||||
EnforcedStyleForMultiline: 'comma'
|
EnforcedStyleForMultiline: 'comma'
|
||||||
|
|
||||||
Style/TrailingCommaInHashLiteral:
|
|
||||||
EnforcedStyleForMultiline: 'comma'
|
|
||||||
|
|
||||||
Style/UnpackFirst:
|
|
||||||
Enabled: false
|
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
mastodon
|
|
|
@ -1 +1 @@
|
||||||
3.0.4
|
2.4.2
|
||||||
|
|
264
.scss-lint.yml
Normal file
264
.scss-lint.yml
Normal file
|
@ -0,0 +1,264 @@
|
||||||
|
# Linter Documentation:
|
||||||
|
# https://github.com/brigade/scss-lint/blob/v0.42.2/lib/scss_lint/linter/README.md
|
||||||
|
|
||||||
|
scss_files: 'app/javascript/styles/**/*.scss'
|
||||||
|
|
||||||
|
exclude:
|
||||||
|
- app/javascript/styles/reset.scss
|
||||||
|
|
||||||
|
linters:
|
||||||
|
# Reports when you use improper spacing around ! (the "bang") in !default,
|
||||||
|
# !global, !important, and !optional flags.
|
||||||
|
BangFormat:
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
# Whether or not to prefer `border: 0` over `border: none`.
|
||||||
|
BorderZero:
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
# Reports when you define a rule set using a selector with chained classes
|
||||||
|
# (a.k.a. adjoining classes).
|
||||||
|
ChainedClasses:
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
# Prefer hexadecimal color codes over color keywords.
|
||||||
|
# (e.g. `color: green` is a color keyword)
|
||||||
|
ColorKeyword:
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
# Prefer color literals (keywords or hexadecimal codes) to be used only in
|
||||||
|
# variable declarations. They should be referred to via variables everywhere
|
||||||
|
# else.
|
||||||
|
ColorVariable:
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
# Which form of comments to prefer in CSS.
|
||||||
|
Comment:
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
# Reports @debug statements (which you probably left behind accidentally).
|
||||||
|
DebugStatement:
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
# Rule sets should be ordered as follows:
|
||||||
|
# - @extend declarations
|
||||||
|
# - @include declarations without inner @content
|
||||||
|
# - properties, @include declarations with inner @content
|
||||||
|
# - nested rule sets.
|
||||||
|
DeclarationOrder:
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
# `scss-lint:disable` control comments should be preceded by a comment
|
||||||
|
# explaining why these linters are being disabled for this file.
|
||||||
|
# See https://github.com/brigade/scss-lint#disabling-linters-via-source for
|
||||||
|
# more information.
|
||||||
|
DisableLinterReason:
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
# Reports when you define the same property twice in a single rule set.
|
||||||
|
DuplicateProperty:
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
# Separate rule, function, and mixin declarations with empty lines.
|
||||||
|
EmptyLineBetweenBlocks:
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
# Reports when you have an empty rule set.
|
||||||
|
EmptyRule:
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
# Reports when you have an @extend directive.
|
||||||
|
ExtendDirective:
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
# Files should always have a final newline. This results in better diffs
|
||||||
|
# when adding lines to the file, since SCM systems such as git won't
|
||||||
|
# think that you touched the last line.
|
||||||
|
FinalNewline:
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
# HEX colors should use three-character values where possible.
|
||||||
|
HexLength:
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
# HEX color values should use lower-case colors to differentiate between
|
||||||
|
# letters and numbers, e.g. `#E3E3E3` vs. `#e3e3e3`.
|
||||||
|
HexNotation:
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
# Avoid using ID selectors.
|
||||||
|
IdSelector:
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
# The basenames of @imported SCSS partials should not begin with an
|
||||||
|
# underscore and should not include the filename extension.
|
||||||
|
ImportPath:
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
# Avoid using !important in properties. It is usually indicative of a
|
||||||
|
# misunderstanding of CSS specificity and can lead to brittle code.
|
||||||
|
ImportantRule:
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
# Indentation should always be done in increments of 2 spaces.
|
||||||
|
Indentation:
|
||||||
|
enabled: true
|
||||||
|
width: 2
|
||||||
|
|
||||||
|
# Don't write leading zeros for numeric values with a decimal point.
|
||||||
|
LeadingZero:
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
# Reports when you define the same selector twice in a single sheet.
|
||||||
|
MergeableSelector:
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
# Functions, mixins, variables, and placeholders should be declared
|
||||||
|
# with all lowercase letters and hyphens instead of underscores.
|
||||||
|
NameFormat:
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
# Avoid nesting selectors too deeply.
|
||||||
|
NestingDepth:
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
# Always use placeholder selectors in @extend.
|
||||||
|
PlaceholderInExtend:
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
# Sort properties in a strict order.
|
||||||
|
PropertySortOrder:
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
# Reports when you use an unknown or disabled CSS property
|
||||||
|
# (ignoring vendor-prefixed properties).
|
||||||
|
PropertySpelling:
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
# Configure which units are allowed for property values.
|
||||||
|
PropertyUnits:
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
# Pseudo-elements, like ::before, and ::first-letter, should be declared
|
||||||
|
# with two colons. Pseudo-classes, like :hover and :first-child, should
|
||||||
|
# be declared with one colon.
|
||||||
|
PseudoElement:
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
# Avoid qualifying elements in selectors (also known as "tag-qualifying").
|
||||||
|
QualifyingElement:
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
# Don't write selectors with a depth of applicability greater than 3.
|
||||||
|
SelectorDepth:
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
# Selectors should always use hyphenated-lowercase, rather than camelCase or
|
||||||
|
# snake_case.
|
||||||
|
SelectorFormat:
|
||||||
|
enabled: false
|
||||||
|
convention: hyphenated_lowercase
|
||||||
|
|
||||||
|
# Prefer the shortest shorthand form possible for properties that support it.
|
||||||
|
Shorthand:
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
# Each property should have its own line, except in the special case of
|
||||||
|
# single line rulesets.
|
||||||
|
SingleLinePerProperty:
|
||||||
|
enabled: true
|
||||||
|
allow_single_line_rule_sets: true
|
||||||
|
|
||||||
|
# Split selectors onto separate lines after each comma, and have each
|
||||||
|
# individual selector occupy a single line.
|
||||||
|
SingleLinePerSelector:
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
# Commas in lists should be followed by a space.
|
||||||
|
SpaceAfterComma:
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
# Properties should be formatted with a single space separating the colon
|
||||||
|
# from the property's value.
|
||||||
|
SpaceAfterPropertyColon:
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
# Properties should be formatted with no space between the name and the
|
||||||
|
# colon.
|
||||||
|
SpaceAfterPropertyName:
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
# Variables should be formatted with a single space separating the colon
|
||||||
|
# from the variable's value.
|
||||||
|
SpaceAfterVariableColon:
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
# Variables should be formatted with no space between the name and the
|
||||||
|
# colon.
|
||||||
|
SpaceAfterVariableName:
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
# Operators should be formatted with a single space on both sides of an
|
||||||
|
# infix operator.
|
||||||
|
SpaceAroundOperator:
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
# Opening braces should be preceded by a single space.
|
||||||
|
SpaceBeforeBrace:
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
# Parentheses should not be padded with spaces.
|
||||||
|
SpaceBetweenParens:
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
# Enforces that string literals should be written with a consistent form
|
||||||
|
# of quotes (single or double).
|
||||||
|
StringQuotes:
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
# Property values, @extend, @include, and @import directives, and variable
|
||||||
|
# declarations should always end with a semicolon.
|
||||||
|
TrailingSemicolon:
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
# Reports lines containing trailing whitespace.
|
||||||
|
TrailingWhitespace:
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
# Don't write trailing zeros for numeric values with a decimal point.
|
||||||
|
TrailingZero:
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
# Don't use the `all` keyword to specify transition properties.
|
||||||
|
TransitionAll:
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
# Numeric values should not contain unnecessary fractional portions.
|
||||||
|
UnnecessaryMantissa:
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
# Do not use parent selector references (&) when they would otherwise
|
||||||
|
# be unnecessary.
|
||||||
|
UnnecessaryParentReference:
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
# URLs should be valid and not contain protocols or domain names.
|
||||||
|
UrlFormat:
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
# URLs should always be enclosed within quotes.
|
||||||
|
UrlQuotes:
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
# Properties, like color and font, are easier to read and maintain
|
||||||
|
# when defined using variables rather than literals.
|
||||||
|
VariableForProperty:
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
# Avoid vendor prefixes. Or rather: don't write them yourself.
|
||||||
|
VendorPrefix:
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
# Omit length units on zero values, e.g. `0px` vs. `0`.
|
||||||
|
ZeroUnit:
|
||||||
|
enabled: true
|
57
.travis.yml
Normal file
57
.travis.yml
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
language: ruby
|
||||||
|
cache:
|
||||||
|
bundler: true
|
||||||
|
yarn: true
|
||||||
|
directories:
|
||||||
|
- node_modules
|
||||||
|
- public/assets
|
||||||
|
- public/packs-test
|
||||||
|
- tmp/cache/babel-loader
|
||||||
|
dist: trusty
|
||||||
|
sudo: required
|
||||||
|
|
||||||
|
notifications:
|
||||||
|
email: false
|
||||||
|
|
||||||
|
env:
|
||||||
|
global:
|
||||||
|
- LOCAL_DOMAIN=cb6e6126.ngrok.io
|
||||||
|
- LOCAL_HTTPS=true
|
||||||
|
- RAILS_ENV=test
|
||||||
|
- NOKOGIRI_USE_SYSTEM_LIBRARIES=true
|
||||||
|
- PARALLEL_TEST_PROCESSORS=2
|
||||||
|
- "PATH=$HOME:$PATH"
|
||||||
|
|
||||||
|
addons:
|
||||||
|
postgresql: 9.4
|
||||||
|
apt:
|
||||||
|
sources:
|
||||||
|
- trusty-media
|
||||||
|
packages:
|
||||||
|
- ffmpeg
|
||||||
|
- libprotobuf-dev
|
||||||
|
- protobuf-compiler
|
||||||
|
- libicu-dev
|
||||||
|
|
||||||
|
rvm:
|
||||||
|
- 2.3.4
|
||||||
|
- 2.4.2
|
||||||
|
|
||||||
|
services:
|
||||||
|
- redis-server
|
||||||
|
|
||||||
|
install:
|
||||||
|
- nvm install
|
||||||
|
- npm install -g yarn
|
||||||
|
- bundle install --path=vendor/bundle --without development production --retry=3 --jobs=16
|
||||||
|
- yarn install
|
||||||
|
|
||||||
|
before_script:
|
||||||
|
- bundle exec rake parallel:create parallel:load_schema parallel:prepare
|
||||||
|
- bundle exec rails assets:precompile
|
||||||
|
- ln -s /usr/bin/x86_64-linux-gnu-g++-6 "$HOME/g++"
|
||||||
|
|
||||||
|
script:
|
||||||
|
- travis_retry bundle exec parallel_test spec/ --group-by filesize --type rspec
|
||||||
|
- yarn test
|
||||||
|
- bundle exec i18n-tasks check-normalized && bundle exec i18n-tasks unused
|
|
@ -43,4 +43,4 @@ Gruntfile.js
|
||||||
|
|
||||||
# for specific ignore
|
# for specific ignore
|
||||||
!.svgo.yml
|
!.svgo.yml
|
||||||
!sass-lint/**/*.yml
|
|
||||||
|
|
1765
AUTHORS.md
1765
AUTHORS.md
File diff suppressed because it is too large
Load diff
24
Aptfile
24
Aptfile
|
@ -1,26 +1,10 @@
|
||||||
ffmpeg
|
ffmpeg
|
||||||
libicu[0-9][0-9]
|
libicu[0-9][0-9]
|
||||||
libicu-dev
|
libicu-dev
|
||||||
libidn12
|
libidn11
|
||||||
libidn-dev
|
libidn11-dev
|
||||||
libpq-dev
|
libpq-dev
|
||||||
|
libprotobuf-dev
|
||||||
libxdamage1
|
libxdamage1
|
||||||
libxfixes3
|
libxfixes3
|
||||||
zlib1g-dev
|
protobuf-compiler
|
||||||
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
|
|
||||||
libvpx[5-9]
|
|
||||||
libxcb-render0
|
|
||||||
libxcb-shm0
|
|
||||||
libxrender1
|
|
||||||
|
|
2498
CHANGELOG.md
2498
CHANGELOG.md
File diff suppressed because it is too large
Load diff
32
CODEOWNERS
Normal file
32
CODEOWNERS
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,46 +0,0 @@
|
||||||
# Contributor Covenant Code of Conduct
|
|
||||||
|
|
||||||
## Our Pledge
|
|
||||||
|
|
||||||
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
|
|
||||||
|
|
||||||
## Our Standards
|
|
||||||
|
|
||||||
Examples of behavior that contributes to creating a positive environment include:
|
|
||||||
|
|
||||||
* Using welcoming and inclusive language
|
|
||||||
* Being respectful of differing viewpoints and experiences
|
|
||||||
* Gracefully accepting constructive criticism
|
|
||||||
* Focusing on what is best for the community
|
|
||||||
* Showing empathy towards other community members
|
|
||||||
|
|
||||||
Examples of unacceptable behavior by participants include:
|
|
||||||
|
|
||||||
* The use of sexualized language or imagery and unwelcome sexual attention or advances
|
|
||||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
|
||||||
* Public or private harassment
|
|
||||||
* Publishing others' private information, such as a physical or electronic address, without explicit permission
|
|
||||||
* Other conduct which could reasonably be considered inappropriate in a professional setting
|
|
||||||
|
|
||||||
## Our Responsibilities
|
|
||||||
|
|
||||||
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
|
|
||||||
|
|
||||||
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
|
|
||||||
|
|
||||||
## Scope
|
|
||||||
|
|
||||||
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
|
|
||||||
|
|
||||||
## Enforcement
|
|
||||||
|
|
||||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at glitch-abuse@sitedethib.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
|
|
||||||
|
|
||||||
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
|
|
||||||
|
|
||||||
## Attribution
|
|
||||||
|
|
||||||
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]: http://contributor-covenant.org
|
|
||||||
[version]: http://contributor-covenant.org/version/1/4/
|
|
|
@ -27,59 +27,60 @@ See the guidelines below.
|
||||||
|
|
||||||
- - -
|
- - -
|
||||||
|
|
||||||
You should also try to follow the guidelines set out in the original `CONTRIBUTING.md` from `mastodon/mastodon`, reproduced below.
|
You should also try to follow the guidelines set out in the original `CONTRIBUTING.md` from `tootsuite/mastodon`, reproduced below.
|
||||||
|
|
||||||
<blockquote>
|
<blockquote>
|
||||||
|
|
||||||
CONTRIBUTING
|
CONTRIBUTING
|
||||||
=======
|
============
|
||||||
Contributing
|
|
||||||
|
|
||||||
Thank you for considering contributing to Mastodon 🐘
|
There are three ways in which you can contribute to this repository:
|
||||||
|
|
||||||
You can contribute in the following ways:
|
1. By improving the documentation
|
||||||
|
2. By working on the back-end application
|
||||||
|
3. By working on the front-end application
|
||||||
|
|
||||||
- Finding and reporting bugs
|
Choosing what to work on in a large open source project is not easy. The list of [GitHub issues](https://github.com/tootsuite/mastodon/issues) may provide some ideas, but not every feature request has been greenlit. Likewise, not every change or feature that resolves a personal itch will be merged into the main repository. Some communication ahead of time may be wise. If your addition creates a new feature or setting, or otherwise changes how things work in some substantial way, please remember to submit a correlating pull request to document your changes in the [documentation](http://github.com/tootsuite/documentation).
|
||||||
- Translating the Mastodon interface into various languages
|
|
||||||
- Contributing code to Mastodon by fixing bugs or implementing features
|
|
||||||
- Improving the documentation
|
|
||||||
|
|
||||||
If your contributions are accepted into Mastodon, you can request to be paid through [our OpenCollective](https://opencollective.com/mastodon).
|
Below are the guidelines for working on pull requests:
|
||||||
|
|
||||||
## Bug reports
|
## General
|
||||||
|
|
||||||
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.
|
- 2 spaces indentation
|
||||||
|
|
||||||
## Translations
|
|
||||||
|
|
||||||
You can submit translations via [Crowdin](https://crowdin.com/project/mastodon). They are periodically merged into the codebase.
|
|
||||||
|
|
||||||
[![Crowdin](https://d322cqt584bo4o.cloudfront.net/mastodon/localized.svg)](https://crowdin.com/project/mastodon)
|
|
||||||
|
|
||||||
## 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).
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
|Not ideal|Better|
|
|
||||||
|---|----|
|
|
||||||
|Fixed NoMethodError in RemovalWorker|Fix nil error when removing statuses caused by race condition|
|
|
||||||
|
|
||||||
It is not always possible to phrase every change in such a manner, but it is desired.
|
|
||||||
|
|
||||||
**The smaller the set of changes in the pull request is, the quicker it can be reviewed and merged.** Splitting tasks into multiple smaller pull requests is often preferable.
|
|
||||||
|
|
||||||
**Pull requests that do not pass automated checks may not be reviewed**. In particular, you need to keep in mind:
|
|
||||||
|
|
||||||
- Unit and integration tests (rspec, jest)
|
|
||||||
- Code style rules (rubocop, eslint)
|
|
||||||
- Normalization of locale files (i18n-tasks)
|
|
||||||
|
|
||||||
**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).
|
- No spelling mistakes
|
||||||
|
- No orthographic mistakes
|
||||||
|
- No Markdown syntax errors
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
- Ruby
|
||||||
|
- Node.js
|
||||||
|
- PostgreSQL
|
||||||
|
- Redis
|
||||||
|
- Nginx (optional)
|
||||||
|
|
||||||
|
## Back-end application
|
||||||
|
|
||||||
|
It is expected that you have a working development environment set up. The development environment includes [rubocop](https://github.com/bbatsov/rubocop), which checks your Ruby code for compliance with our style guide and best practices. Sublime Text, likely like other editors, has a [Rubocop plugin](https://github.com/pderichs/sublime_rubocop) that runs checks on files as you edit them. The codebase also has a test suite.
|
||||||
|
|
||||||
|
* The codebase is not perfect, at the time of writing, but it is expected that you do not introduce new code style violations
|
||||||
|
* The rspec test suite must pass
|
||||||
|
* To the extent that it is possible, verify your changes. In the best case, by adding new tests to the test suite. At the very least, by running the server or console and checking it manually
|
||||||
|
* If you are introducing new strings to the user interface, they must be using localization methods
|
||||||
|
|
||||||
|
If your code has syntax errors that won't let it run, it's a good sign that the pull request isn't ready for submission yet.
|
||||||
|
|
||||||
|
## Front-end application
|
||||||
|
|
||||||
|
It is expected that you have a working development environment set up (see back-end application section). This project includes an ESLint configuration file, with which you can lint your changes.
|
||||||
|
|
||||||
|
* Avoid grave ESLint violations
|
||||||
|
* Verify that your changes work
|
||||||
|
* If you are introducing new strings, they must be using localization methods
|
||||||
|
|
||||||
|
If the JavaScript or CSS assets won't compile due to a syntax error, it's a good sign that the pull request isn't ready for submission yet.
|
||||||
|
|
||||||
</blockquote>
|
</blockquote>
|
||||||
|
|
164
Dockerfile
164
Dockerfile
|
@ -1,96 +1,78 @@
|
||||||
# syntax=docker/dockerfile:1.4
|
FROM ruby:2.4.2-alpine3.6
|
||||||
# This needs to be bullseye-slim because the Ruby image is built on bullseye-slim
|
|
||||||
ARG NODE_VERSION="16.17.1-bullseye-slim"
|
|
||||||
|
|
||||||
FROM ghcr.io/moritzheiber/ruby-jemalloc:3.0.4-slim as ruby
|
LABEL maintainer="https://github.com/tootsuite/mastodon" \
|
||||||
FROM node:${NODE_VERSION} as build
|
description="A GNU Social-compatible microblogging server"
|
||||||
|
|
||||||
COPY --link --from=ruby /opt/ruby /opt/ruby
|
ENV UID=991 GID=991 \
|
||||||
|
RAILS_SERVE_STATIC_FILES=true \
|
||||||
|
RAILS_ENV=production NODE_ENV=production
|
||||||
|
|
||||||
ENV DEBIAN_FRONTEND="noninteractive" \
|
ARG YARN_VERSION=1.1.0
|
||||||
PATH="${PATH}:/opt/ruby/bin"
|
ARG YARN_DOWNLOAD_SHA256=171c1f9ee93c488c0d774ac6e9c72649047c3f896277d88d0f805266519430f3
|
||||||
|
ARG LIBICONV_VERSION=1.15
|
||||||
|
ARG LIBICONV_DOWNLOAD_SHA256=ccf536620a45458d26ba83887a983b96827001e92a13847b45e4925cc8913178
|
||||||
|
|
||||||
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
|
|
||||||
|
|
||||||
WORKDIR /opt/mastodon
|
|
||||||
COPY Gemfile* package.json yarn.lock /opt/mastodon/
|
|
||||||
|
|
||||||
RUN apt update && \
|
|
||||||
apt-get install -y --no-install-recommends build-essential \
|
|
||||||
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
|
|
||||||
|
|
||||||
FROM node:${NODE_VERSION}
|
|
||||||
|
|
||||||
ARG UID="991"
|
|
||||||
ARG GID="991"
|
|
||||||
|
|
||||||
COPY --link --from=ruby /opt/ruby /opt/ruby
|
|
||||||
|
|
||||||
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
|
|
||||||
|
|
||||||
ENV DEBIAN_FRONTEND="noninteractive" \
|
|
||||||
PATH="${PATH}:/opt/ruby/bin:/opt/mastodon/bin"
|
|
||||||
|
|
||||||
RUN apt-get update && \
|
|
||||||
echo "Etc/UTC" > /etc/localtime && \
|
|
||||||
groupadd -g "${GID}" mastodon && \
|
|
||||||
useradd -u "$UID" -g "${GID}" -m -d /opt/mastodon mastodon && \
|
|
||||||
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 --chown=mastodon:mastodon . /opt/mastodon
|
|
||||||
COPY --chown=mastodon:mastodon --from=build /opt/mastodon /opt/mastodon
|
|
||||||
|
|
||||||
ENV RAILS_ENV="production" \
|
|
||||||
NODE_ENV="production" \
|
|
||||||
RAILS_SERVE_STATIC_FILES="true" \
|
|
||||||
BIND="0.0.0.0"
|
|
||||||
|
|
||||||
# Set the run user
|
|
||||||
USER mastodon
|
|
||||||
WORKDIR /opt/mastodon
|
|
||||||
|
|
||||||
# Precompile assets
|
|
||||||
RUN OTP_SECRET=precompile_placeholder SECRET_KEY_BASE=precompile_placeholder rails assets:precompile && \
|
|
||||||
yarn cache clean
|
|
||||||
|
|
||||||
# Set the work dir and the container entry point
|
|
||||||
ENTRYPOINT ["/usr/bin/tini", "--"]
|
|
||||||
EXPOSE 3000 4000
|
EXPOSE 3000 4000
|
||||||
|
|
||||||
|
WORKDIR /mastodon
|
||||||
|
|
||||||
|
RUN apk -U upgrade \
|
||||||
|
&& apk add -t build-dependencies \
|
||||||
|
build-base \
|
||||||
|
icu-dev \
|
||||||
|
libidn-dev \
|
||||||
|
libressl \
|
||||||
|
libtool \
|
||||||
|
postgresql-dev \
|
||||||
|
protobuf-dev \
|
||||||
|
python \
|
||||||
|
&& apk add \
|
||||||
|
ca-certificates \
|
||||||
|
ffmpeg \
|
||||||
|
file \
|
||||||
|
git \
|
||||||
|
icu-libs \
|
||||||
|
imagemagick \
|
||||||
|
libidn \
|
||||||
|
libpq \
|
||||||
|
nodejs \
|
||||||
|
nodejs-npm \
|
||||||
|
protobuf \
|
||||||
|
su-exec \
|
||||||
|
tini \
|
||||||
|
&& update-ca-certificates \
|
||||||
|
&& mkdir -p /tmp/src /opt \
|
||||||
|
&& wget -O yarn.tar.gz "https://github.com/yarnpkg/yarn/releases/download/v$YARN_VERSION/yarn-v$YARN_VERSION.tar.gz" \
|
||||||
|
&& echo "$YARN_DOWNLOAD_SHA256 *yarn.tar.gz" | sha256sum -c - \
|
||||||
|
&& tar -xzf yarn.tar.gz -C /tmp/src \
|
||||||
|
&& rm yarn.tar.gz \
|
||||||
|
&& mv /tmp/src/yarn-v$YARN_VERSION /opt/yarn \
|
||||||
|
&& ln -s /opt/yarn/bin/yarn /usr/local/bin/yarn \
|
||||||
|
&& wget -O libiconv.tar.gz "http://ftp.gnu.org/pub/gnu/libiconv/libiconv-$LIBICONV_VERSION.tar.gz" \
|
||||||
|
&& echo "$LIBICONV_DOWNLOAD_SHA256 *libiconv.tar.gz" | sha256sum -c - \
|
||||||
|
&& tar -xzf libiconv.tar.gz -C /tmp/src \
|
||||||
|
&& rm libiconv.tar.gz \
|
||||||
|
&& cd /tmp/src/libiconv-$LIBICONV_VERSION \
|
||||||
|
&& ./configure --prefix=/usr/local \
|
||||||
|
&& make -j$(getconf _NPROCESSORS_ONLN)\
|
||||||
|
&& make install \
|
||||||
|
&& libtool --finish /usr/local/lib \
|
||||||
|
&& cd /mastodon \
|
||||||
|
&& rm -rf /tmp/* /var/cache/apk/*
|
||||||
|
|
||||||
|
COPY Gemfile Gemfile.lock package.json yarn.lock .yarnclean /mastodon/
|
||||||
|
|
||||||
|
RUN bundle config build.nokogiri --with-iconv-lib=/usr/local/lib --with-iconv-include=/usr/local/include \
|
||||||
|
&& bundle install -j$(getconf _NPROCESSORS_ONLN) --deployment --without test development \
|
||||||
|
&& yarn --pure-lockfile \
|
||||||
|
&& yarn cache clean
|
||||||
|
|
||||||
|
COPY . /mastodon
|
||||||
|
|
||||||
|
COPY docker_entrypoint.sh /usr/local/bin/run
|
||||||
|
|
||||||
|
RUN chmod +x /usr/local/bin/run
|
||||||
|
|
||||||
|
VOLUME /mastodon/public/system /mastodon/public/assets /mastodon/public/packs
|
||||||
|
|
||||||
|
ENTRYPOINT ["/usr/local/bin/run"]
|
||||||
|
|
|
@ -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
|
|
202
Gemfile
202
Gemfile
|
@ -1,159 +1,119 @@
|
||||||
# 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.3.0', '< 2.5.0'
|
||||||
|
|
||||||
gem 'pkg-config', '~> 1.4'
|
gem 'pkg-config', '~> 1.2'
|
||||||
gem 'rexml', '~> 3.2'
|
|
||||||
|
|
||||||
gem 'puma', '~> 5.6'
|
gem 'puma', '~> 3.10'
|
||||||
gem 'rails', '~> 6.1.7'
|
gem 'rails', '~> 5.1.4'
|
||||||
gem 'sprockets', '~> 3.7.2'
|
gem 'uglifier', '~> 3.2'
|
||||||
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', '~> 0.20'
|
||||||
gem 'makara', '~> 0.5'
|
gem 'pghero', '~> 1.7'
|
||||||
gem 'pghero', '~> 2.8'
|
gem 'dotenv-rails', '~> 2.2'
|
||||||
gem 'dotenv-rails', '~> 2.8'
|
|
||||||
|
|
||||||
gem 'aws-sdk-s3', '~> 1.114', require: false
|
gem 'aws-sdk', '~> 2.9'
|
||||||
gem 'fog-core', '<= 2.1.0'
|
gem 'fog-openstack', '~> 0.1'
|
||||||
gem 'fog-openstack', '~> 0.3', require: false
|
gem 'paperclip', '~> 5.1'
|
||||||
gem 'kt-paperclip', '~> 7.1'
|
gem 'paperclip-av-transcoder', '~> 0.6'
|
||||||
gem 'blurhash', '~> 0.1'
|
|
||||||
|
|
||||||
gem 'active_model_serializers', '~> 0.10'
|
gem 'active_model_serializers', '~> 0.10'
|
||||||
gem 'addressable', '~> 2.8'
|
gem 'addressable', '~> 2.5'
|
||||||
gem 'bootsnap', '~> 1.14.0', require: false
|
gem 'bootsnap'
|
||||||
gem 'browser'
|
gem 'browser'
|
||||||
gem 'charlock_holmes', '~> 0.7.7'
|
gem 'charlock_holmes', '~> 0.7.5'
|
||||||
gem 'chewy', '~> 7.2'
|
gem 'iso-639'
|
||||||
gem 'devise', '~> 4.8'
|
gem 'cld3', '~> 3.2.0'
|
||||||
gem 'devise-two-factor', '~> 4.0'
|
gem 'devise', '~> 4.2'
|
||||||
|
gem 'devise-two-factor', '~> 3.0'
|
||||||
group :pam_authentication, optional: true do
|
gem 'doorkeeper', '~> 4.2'
|
||||||
gem 'devise_pam_authenticatable2', '~> 9.2'
|
|
||||||
end
|
|
||||||
|
|
||||||
gem 'net-ldap', '~> 0.17'
|
|
||||||
gem 'omniauth-cas', '~> 2.0'
|
|
||||||
gem 'omniauth-saml', '~> 1.10'
|
|
||||||
gem 'gitlab-omniauth-openid-connect', '~>0.10.0', require: 'omniauth_openid_connect'
|
|
||||||
gem 'omniauth', '~> 1.9'
|
|
||||||
gem 'omniauth-rails_csrf_protection', '~> 0.1'
|
|
||||||
|
|
||||||
gem 'color_diff', '~> 0.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 'goldfinger', '~> 2.0'
|
||||||
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', '~> 2.2'
|
||||||
gem 'http_accept_language', '~> 2.1'
|
gem 'http_accept_language', '~> 2.1'
|
||||||
gem 'httplog', '~> 1.6.2'
|
gem 'httplog', '~> 0.99'
|
||||||
gem 'idn-ruby', require: 'idn'
|
gem 'idn-ruby', require: 'idn'
|
||||||
gem 'kaminari', '~> 1.2'
|
gem 'kaminari', '~> 1.0'
|
||||||
gem 'link_header', '~> 0.0'
|
gem 'link_header', '~> 0.0'
|
||||||
gem 'mime-types', '~> 3.4.1', require: 'mime/types/columnar'
|
gem 'mime-types', '~> 3.1'
|
||||||
gem 'nokogiri', '~> 1.13'
|
gem 'nokogiri', '~> 1.7'
|
||||||
gem 'nsa', '~> 0.2'
|
gem 'nsa', '~> 0.2'
|
||||||
gem 'oj', '~> 3.13'
|
gem 'oj', '~> 3.0'
|
||||||
gem 'ox', '~> 2.14'
|
gem 'ostatus2', '~> 2.0'
|
||||||
gem 'parslet'
|
gem 'ox', '~> 2.5'
|
||||||
gem 'posix-spawn'
|
gem 'pundit', '~> 1.1'
|
||||||
gem 'pundit', '~> 2.2'
|
gem 'rabl', '~> 0.13'
|
||||||
gem 'premailer-rails'
|
gem 'rack-attack', '~> 5.0'
|
||||||
gem 'rack-attack', '~> 6.6'
|
gem 'rack-cors', '~> 0.4', require: 'rack/cors'
|
||||||
gem 'rack-cors', '~> 1.1', require: 'rack/cors'
|
gem 'rack-timeout', '~> 0.4'
|
||||||
gem 'rails-i18n', '~> 6.0'
|
gem 'rails-i18n', '~> 5.0'
|
||||||
gem 'rails-settings-cached', '~> 0.6'
|
gem 'rails-settings-cached', '~> 0.6'
|
||||||
gem 'redcarpet', '~> 3.5'
|
gem 'redis', '~> 3.3', 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 'ruby-oembed', '~> 0.12', require: 'oembed'
|
||||||
gem 'sanitize', '~> 6.0'
|
gem 'sanitize', '~> 4.4'
|
||||||
gem 'scenic', '~> 1.6'
|
gem 'sidekiq', '~> 5.0'
|
||||||
gem 'sidekiq', '~> 6.5'
|
gem 'sidekiq-scheduler', '~> 2.1'
|
||||||
gem 'sidekiq-scheduler', '~> 4.0'
|
gem 'sidekiq-unique-jobs', '~> 5.0'
|
||||||
gem 'sidekiq-unique-jobs', '~> 7.1'
|
gem 'sidekiq-bulk', '~>0.1.1'
|
||||||
gem 'sidekiq-bulk', '~> 0.2.0'
|
gem 'simple-navigation', '~> 4.0'
|
||||||
gem 'simple-navigation', '~> 4.4'
|
gem 'simple_form', '~> 3.4'
|
||||||
gem 'simple_form', '~> 5.1'
|
gem 'sprockets-rails', '~> 3.2', require: 'sprockets/railtie'
|
||||||
gem 'sprockets-rails', '~> 3.4', require: 'sprockets/railtie'
|
gem 'strong_migrations'
|
||||||
gem 'stoplight', '~> 3.0.0'
|
gem 'twitter-text', '~> 1.14'
|
||||||
gem 'strong_migrations', '~> 0.7'
|
gem 'tzinfo-data', '~> 1.2017'
|
||||||
gem 'tty-prompt', '~> 0.23', require: false
|
gem 'webpacker', '~> 3.0'
|
||||||
gem 'twitter-text', '~> 3.1.0'
|
gem 'webpush'
|
||||||
gem 'tzinfo-data', '~> 1.2022'
|
|
||||||
gem 'webpacker', '~> 5.4'
|
|
||||||
gem 'webpush', github: 'ClearlyClaire/webpush', ref: 'f14a4d52e201128b1b00245d11b6de80d6cfdcd9'
|
|
||||||
gem 'webauthn', '~> 2.5'
|
|
||||||
|
|
||||||
gem 'json-ld'
|
gem 'json-ld-preloaded', '~> 2.2.1'
|
||||||
gem 'json-ld-preloaded', '~> 3.2'
|
gem 'rdf-normalize', '~> 0.3.1'
|
||||||
gem 'rdf-normalize', '~> 0.5'
|
|
||||||
|
|
||||||
group :development, :test do
|
group :development, :test do
|
||||||
gem 'fabrication', '~> 2.30'
|
gem 'fabrication', '~> 2.16'
|
||||||
gem 'fuubar', '~> 2.5'
|
gem 'fuubar', '~> 2.2'
|
||||||
gem 'i18n-tasks', '~> 1.0', require: false
|
gem 'i18n-tasks', '~> 0.9', require: false
|
||||||
gem 'pry-byebug', '~> 3.10'
|
|
||||||
gem 'pry-rails', '~> 0.3'
|
gem 'pry-rails', '~> 0.3'
|
||||||
gem 'rspec-rails', '~> 5.1'
|
gem 'rspec-rails', '~> 3.6'
|
||||||
end
|
|
||||||
|
|
||||||
group :production, :test do
|
|
||||||
gem 'private_address_check', '~> 0.5'
|
|
||||||
end
|
end
|
||||||
|
|
||||||
group :test do
|
group :test do
|
||||||
gem 'capybara', '~> 3.38'
|
gem 'capybara', '~> 2.14'
|
||||||
gem 'climate_control', '~> 0.2'
|
gem 'climate_control', '~> 0.2'
|
||||||
gem 'faker', '~> 2.23'
|
gem 'faker', '~> 1.7'
|
||||||
gem 'microformats', '~> 4.4'
|
gem 'microformats', '~> 4.0'
|
||||||
gem 'rails-controller-testing', '~> 1.0'
|
gem 'rails-controller-testing', '~> 1.0'
|
||||||
gem 'rspec-sidekiq', '~> 3.1'
|
gem 'rspec-sidekiq', '~> 3.0'
|
||||||
gem 'simplecov', '~> 0.21', require: false
|
gem 'simplecov', '~> 0.14', require: false
|
||||||
gem 'webmock', '~> 3.18'
|
gem 'webmock', '~> 3.0'
|
||||||
gem 'rspec_junit_formatter', '~> 0.6'
|
gem 'parallel_tests', '~> 2.14'
|
||||||
gem 'rack-test', '~> 2.0'
|
|
||||||
end
|
end
|
||||||
|
|
||||||
group :development do
|
group :development do
|
||||||
gem 'active_record_query_trace', '~> 1.8'
|
gem 'active_record_query_trace', '~> 1.5'
|
||||||
gem 'annotate', '~> 3.2'
|
gem 'annotate', '~> 2.7'
|
||||||
gem 'better_errors', '~> 2.9'
|
gem 'better_errors', '~> 2.1'
|
||||||
gem 'binding_of_caller', '~> 1.0'
|
gem 'binding_of_caller', '~> 0.7'
|
||||||
gem 'bullet', '~> 7.0'
|
gem 'bullet', '~> 5.5'
|
||||||
gem 'letter_opener', '~> 1.8'
|
gem 'letter_opener', '~> 1.4'
|
||||||
gem 'letter_opener_web', '~> 2.0'
|
gem 'letter_opener_web', '~> 1.3'
|
||||||
gem 'memory_profiler'
|
gem 'rubocop', require: false
|
||||||
gem 'rubocop', '~> 1.30', require: false
|
gem 'brakeman', '~> 4.0', require: false
|
||||||
gem 'rubocop-rails', '~> 2.15', require: false
|
gem 'bundler-audit', '~> 0.6', require: false
|
||||||
gem 'brakeman', '~> 5.4', require: false
|
gem 'scss_lint', '~> 0.53', require: false
|
||||||
gem 'bundler-audit', '~> 0.9', require: false
|
|
||||||
|
|
||||||
gem 'capistrano', '~> 3.17'
|
gem 'capistrano', '~> 3.8'
|
||||||
gem 'capistrano-rails', '~> 1.6'
|
gem 'capistrano-rails', '~> 1.2'
|
||||||
gem 'capistrano-rbenv', '~> 2.2'
|
gem 'capistrano-rbenv', '~> 2.1'
|
||||||
gem 'capistrano-yarn', '~> 2.0'
|
gem 'capistrano-yarn', '~> 2.0'
|
||||||
|
|
||||||
gem 'stackprof'
|
|
||||||
end
|
end
|
||||||
|
|
||||||
group :production do
|
group :production do
|
||||||
gem 'lograge', '~> 0.12'
|
gem 'lograge', '~> 0.5'
|
||||||
|
gem 'redis-rails', '~> 5.0'
|
||||||
end
|
end
|
||||||
|
|
||||||
gem 'concurrent-ruby', require: false
|
|
||||||
gem 'connection_pool', require: false
|
|
||||||
gem 'xorcist', '~> 1.1'
|
|
||||||
|
|
||||||
gem 'hcaptcha', '~> 7.1'
|
|
||||||
gem 'cocoon', '~> 1.2'
|
|
||||||
|
|
1228
Gemfile.lock
1228
Gemfile.lock
File diff suppressed because it is too large
Load diff
6
ISSUE_TEMPLATE.md
Normal file
6
ISSUE_TEMPLATE.md
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
[Issue text goes here].
|
||||||
|
|
||||||
|
* * * *
|
||||||
|
|
||||||
|
- [ ] I searched or browsed the repo’s other issues to ensure this is not a duplicate.
|
||||||
|
- [ ] This bug happens on a [tagged release](https://github.com/tootsuite/mastodon/releases) and not on `master` (If you're a user, don't worry about this).
|
14
Procfile
14
Procfile
|
@ -1,14 +1,2 @@
|
||||||
web: bin/heroku-web
|
web: bundle exec puma -C config/puma.rb
|
||||||
worker: bundle exec sidekiq
|
worker: bundle exec sidekiq
|
||||||
|
|
||||||
# For the streaming API, you need a separate app that shares Postgres and Redis:
|
|
||||||
#
|
|
||||||
# heroku create
|
|
||||||
# heroku buildpacks:add heroku/nodejs
|
|
||||||
# heroku config:set RUN_STREAMING=true
|
|
||||||
# heroku addons:attach <main-app>::DATABASE
|
|
||||||
# heroku addons:attach <main-app>::REDIS
|
|
||||||
#
|
|
||||||
# and let the main app use the separate app:
|
|
||||||
#
|
|
||||||
# heroku config:set STREAMING_API_BASE_URL=wss://<streaming-app>.herokuapp.com -a <main-app>
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
web: env PORT=3000 RAILS_ENV=development bundle exec puma -C config/puma.rb
|
web: PORT=3000 bundle exec puma -C config/puma.rb
|
||||||
sidekiq: env PORT=3000 RAILS_ENV=development bundle exec sidekiq
|
sidekiq: PORT=3000 bundle exec sidekiq
|
||||||
stream: env PORT=4000 yarn run start
|
stream: 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
|
||||||
|
|
|
@ -2,13 +2,9 @@
|
||||||
|
|
||||||
> Now with automated deploys!
|
> Now with automated deploys!
|
||||||
|
|
||||||
[![Build Status](https://img.shields.io/circleci/project/github/glitch-soc/mastodon.svg)][circleci]
|
[![Build Status](https://travis-ci.org/glitch-soc/mastodon.svg?branch=master)](https://travis-ci.org/glitch-soc/mastodon)
|
||||||
[![Code Climate](https://img.shields.io/codeclimate/maintainability/glitch-soc/mastodon.svg)][code_climate]
|
|
||||||
|
|
||||||
[circleci]: https://circleci.com/gh/glitch-soc/mastodon
|
So here's the deal: we all work on this code, and then it runs on dev.glitch.social and anyone who uses that does so absolutely at their own risk. can you dig it?
|
||||||
[code_climate]: https://codeclimate.com/github/glitch-soc/mastodon
|
|
||||||
|
|
||||||
So here's the deal: we all work on this code, and anyone who uses that does so absolutely at their own risk. can you dig it?
|
|
||||||
|
|
||||||
- You can view documentation for this project at [glitch-soc.github.io/docs/](https://glitch-soc.github.io/docs/).
|
- You can view documentation for this project at [glitch-soc.github.io/docs/](https://glitch-soc.github.io/docs/).
|
||||||
- And contributing guidelines are available [here](CONTRIBUTING.md) and [here](https://glitch-soc.github.io/docs/contributing/).
|
- And contributing guidelines are available [here](CONTRIBUTING.md) and [here](https://glitch-soc.github.io/docs/contributing/).
|
||||||
|
|
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 |
|
|
32
Vagrantfile
vendored
32
Vagrantfile
vendored
|
@ -12,7 +12,7 @@ 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_14.x | sudo bash -
|
curl -sL https://deb.nodesource.com/setup_6.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"]}
|
||||||
|
@ -33,24 +33,22 @@ sudo apt-get install \
|
||||||
redis-tools \
|
redis-tools \
|
||||||
postgresql \
|
postgresql \
|
||||||
postgresql-contrib \
|
postgresql-contrib \
|
||||||
|
protobuf-compiler \
|
||||||
yarn \
|
yarn \
|
||||||
libicu-dev \
|
libicu-dev \
|
||||||
libidn11-dev \
|
libidn11-dev \
|
||||||
|
libprotobuf-dev \
|
||||||
libreadline-dev \
|
libreadline-dev \
|
||||||
libpam0g-dev \
|
|
||||||
-y
|
-y
|
||||||
|
|
||||||
# Install rvm
|
# Install rvm
|
||||||
read RUBY_VERSION < .ruby-version
|
read RUBY_VERSION < .ruby-version
|
||||||
|
gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3
|
||||||
curl -sSL https://rvm.io/mpapis.asc | gpg --import
|
|
||||||
curl -sSL https://rvm.io/pkuczynski.asc | gpg --import
|
|
||||||
|
|
||||||
curl -sSL https://raw.githubusercontent.com/rvm/rvm/stable/binscripts/rvm-installer | bash -s stable --ruby=$RUBY_VERSION
|
curl -sSL https://raw.githubusercontent.com/rvm/rvm/stable/binscripts/rvm-installer | bash -s stable --ruby=$RUBY_VERSION
|
||||||
source /home/vagrant/.rvm/scripts/rvm
|
source /home/vagrant/.rvm/scripts/rvm
|
||||||
|
|
||||||
# Install Ruby
|
# Install Ruby
|
||||||
rvm reinstall ruby-$RUBY_VERSION --disable-binary
|
rvm install ruby-$RUBY_VERSION
|
||||||
|
|
||||||
# Configure database
|
# Configure database
|
||||||
sudo -u postgres createuser -U postgres vagrant -s
|
sudo -u postgres createuser -U postgres vagrant -s
|
||||||
|
@ -62,12 +60,10 @@ bundle install
|
||||||
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
|
||||||
|
@ -83,14 +79,11 @@ VAGRANTFILE_API_VERSION = "2"
|
||||||
|
|
||||||
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
|
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
|
||||||
|
|
||||||
config.vm.box = "ubuntu/bionic64"
|
config.vm.box = "ubuntu/trusty64"
|
||||||
|
|
||||||
config.vm.provider :virtualbox do |vb|
|
config.vm.provider :virtualbox do |vb|
|
||||||
vb.name = "mastodon"
|
vb.name = "mastodon"
|
||||||
vb.customize ["modifyvm", :id, "--memory", "4096"]
|
vb.customize ["modifyvm", :id, "--memory", "2048"]
|
||||||
# Increase the number of CPUs. Uncomment and adjust to
|
|
||||||
# increase performance
|
|
||||||
# vb.customize ["modifyvm", :id, "--cpus", "3"]
|
|
||||||
|
|
||||||
# Disable VirtualBox DNS proxy to skip long-delay IPv6 resolutions.
|
# Disable VirtualBox DNS proxy to skip long-delay IPv6 resolutions.
|
||||||
# https://github.com/mitchellh/vagrant/issues/1172
|
# https://github.com/mitchellh/vagrant/issues/1172
|
||||||
|
@ -103,22 +96,19 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
config.vm.hostname = "mastodon.dev"
|
||||||
|
|
||||||
# This uses the vagrant-hostsupdater plugin, and lets you
|
# This uses the vagrant-hostsupdater plugin, and lets you
|
||||||
# access the development site at http://mastodon.local.
|
# access the development site at http://mastodon.dev.
|
||||||
# If you change it, also change it in .env.vagrant before provisioning
|
|
||||||
# the vagrant server to update the development build.
|
|
||||||
#
|
|
||||||
# To install:
|
# To install:
|
||||||
# $ vagrant plugin install vagrant-hostsupdater
|
# $ vagrant plugin install vagrant-hostsupdater
|
||||||
config.vm.hostname = "mastodon.local"
|
|
||||||
|
|
||||||
if defined?(VagrantPlugins::HostsUpdater)
|
if defined?(VagrantPlugins::HostsUpdater)
|
||||||
config.vm.network :private_network, ip: "192.168.42.42", nictype: "virtio"
|
config.vm.network :private_network, ip: "192.168.42.42", nictype: "virtio"
|
||||||
config.hostsupdater.remove_on_suspend = false
|
config.hostsupdater.remove_on_suspend = false
|
||||||
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', 'vers=3', 'tcp', 'actimeo=1']
|
config.vm.synced_folder ".", "/vagrant", type: "nfs", mount_options: ['rw', 'vers=3', 'tcp']
|
||||||
else
|
else
|
||||||
config.vm.synced_folder ".", "/vagrant"
|
config.vm.synced_folder ".", "/vagrant"
|
||||||
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,75 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
class StatusesIndex < Chewy::Index
|
|
||||||
include FormattingHelper
|
|
||||||
|
|
||||||
settings index: { refresh_interval: '30s' }, analysis: {
|
|
||||||
filter: {
|
|
||||||
english_stop: {
|
|
||||||
type: 'stop',
|
|
||||||
stopwords: '_english_',
|
|
||||||
},
|
|
||||||
english_stemmer: {
|
|
||||||
type: 'stemmer',
|
|
||||||
language: 'english',
|
|
||||||
},
|
|
||||||
english_possessive_stemmer: {
|
|
||||||
type: 'stemmer',
|
|
||||||
language: 'possessive_english',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
analyzer: {
|
|
||||||
content: {
|
|
||||||
tokenizer: 'uax_url_email',
|
|
||||||
filter: %w(
|
|
||||||
english_possessive_stemmer
|
|
||||||
lowercase
|
|
||||||
asciifolding
|
|
||||||
cjk_width
|
|
||||||
english_stop
|
|
||||||
english_stemmer
|
|
||||||
),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
# We do not use delete_if option here because it would call a method that we
|
|
||||||
# expect to be called with crutches without crutches, causing n+1 queries
|
|
||||||
index_scope ::Status.unscoped.kept.without_reblogs.includes(:media_attachments, :preloadable_poll)
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
field :searchable_by, type: 'long', value: ->(status, crutches) { status.searchable_by(crutches) }
|
|
||||||
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,38 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class AboutController < ApplicationController
|
class AboutController < ApplicationController
|
||||||
include WebAppControllerConcern
|
before_action :set_body_classes
|
||||||
|
before_action :set_instance_presenter, only: [:show, :more, :terms]
|
||||||
skip_before_action :require_functional!
|
|
||||||
|
|
||||||
before_action :set_instance_presenter
|
|
||||||
|
|
||||||
def show
|
def show
|
||||||
expires_in 0, public: true unless user_signed_in?
|
serializable_resource = ActiveModelSerializers::SerializableResource.new(InitialStatePresenter.new(initial_state_params), serializer: InitialStateSerializer)
|
||||||
|
@initial_state_json = serializable_resource.to_json
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def more; end
|
||||||
|
|
||||||
|
def terms; end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def new_user
|
||||||
|
User.new.tap(&:build_account)
|
||||||
|
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
|
||||||
|
@body_classes = 'about-body'
|
||||||
|
end
|
||||||
|
|
||||||
|
def initial_state_params
|
||||||
|
{
|
||||||
|
settings: {},
|
||||||
|
token: current_session&.token,
|
||||||
|
}
|
||||||
|
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,111 +1,87 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class AccountsController < ApplicationController
|
class AccountsController < ApplicationController
|
||||||
PAGE_SIZE = 20
|
|
||||||
PAGE_SIZE_MAX = 200
|
|
||||||
|
|
||||||
include AccountControllerConcern
|
include AccountControllerConcern
|
||||||
include SignatureAuthentication
|
include SignatureVerification
|
||||||
|
|
||||||
before_action :require_account_signature!, if: -> { request.format == :json && authorized_fetch_mode? }
|
|
||||||
before_action :set_cache_headers
|
|
||||||
|
|
||||||
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?
|
@pinned_statuses = []
|
||||||
|
|
||||||
@rss_url = rss_url
|
if current_account && @account.blocking?(current_account)
|
||||||
|
@statuses = []
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
@pinned_statuses = cache_collection(@account.pinned_statuses, Status) if show_pinned_statuses?
|
||||||
|
@statuses = filtered_statuses.paginate_by_max_id(20, params[:max_id], params[:since_id])
|
||||||
|
@statuses = cache_collection(@statuses, Status)
|
||||||
|
@next_url = next_url unless @statuses.empty?
|
||||||
end
|
end
|
||||||
|
|
||||||
format.rss do
|
format.atom do
|
||||||
expires_in 1.minute, public: true
|
@entries = @account.stream_entries.where(hidden: false).with_includes.paginate_by_max_id(20, params[:max_id], params[:since_id])
|
||||||
|
render xml: OStatus::AtomSerializer.render(OStatus::AtomSerializer.new.feed(@account, @entries.reject { |entry| entry.status.nil? }))
|
||||||
limit = params[:limit].present? ? [params[:limit].to_i, PAGE_SIZE_MAX].min : PAGE_SIZE
|
|
||||||
@statuses = filtered_statuses.without_reblogs.limit(limit)
|
|
||||||
@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?)
|
render json: @account,
|
||||||
render_with_cache json: @account, content_type: 'application/activity+json', serializer: ActivityPub::ActorSerializer, adapter: ActivityPub::Adapter
|
serializer: ActivityPub::ActorSerializer,
|
||||||
|
adapter: ActivityPub::Adapter,
|
||||||
|
content_type: 'application/activity+json'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def show_pinned_statuses?
|
||||||
|
[replies_requested?, media_requested?, params[:max_id].present?, params[:since_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!(only_media_scope) if media_requested?
|
statuses.merge!(only_media_scope) if media_requested?
|
||||||
statuses.merge!(no_replies_scope) unless replies_requested?
|
statuses.merge!(no_replies_scope) unless replies_requested?
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def default_statuses
|
def default_statuses
|
||||||
@account.statuses.not_local_only.where(visibility: [:public, :unlisted])
|
@account.statuses.where(visibility: [:public, :unlisted])
|
||||||
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
|
||||||
Status.without_replies
|
Status.without_replies
|
||||||
end
|
end
|
||||||
|
|
||||||
def hashtag_scope
|
def set_account
|
||||||
tag = Tag.find_normalized(params[:tag])
|
@account = Account.find_local!(params[:username])
|
||||||
|
end
|
||||||
|
|
||||||
if tag
|
def next_url
|
||||||
Status.tagged_with(tag.id)
|
if media_requested?
|
||||||
|
short_account_media_url(@account, max_id: @statuses.last.id)
|
||||||
|
elsif replies_requested?
|
||||||
|
short_account_with_replies_url(@account, max_id: @statuses.last.id)
|
||||||
else
|
else
|
||||||
Status.none
|
short_account_url(@account, max_id: @statuses.last.id)
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def username_param
|
|
||||||
params[:username]
|
|
||||||
end
|
|
||||||
|
|
||||||
def skip_temporary_suspension_response?
|
|
||||||
request.format == :json
|
|
||||||
end
|
|
||||||
|
|
||||||
def rss_url
|
|
||||||
if tag_requested?
|
|
||||||
short_account_tag_url(@account, params[:tag], format: 'rss')
|
|
||||||
else
|
|
||||||
short_account_url(@account, format: 'rss')
|
|
||||||
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
|
|
||||||
|
|
||||||
def tag_requested?
|
|
||||||
request.path.split('.').first.end_with?(Addressable::URI.parse("/tagged/#{params[:tag]}").normalize)
|
|
||||||
end
|
|
||||||
|
|
||||||
def cached_filtered_status_page
|
|
||||||
cache_collection_paginated_by_id(
|
|
||||||
filtered_statuses,
|
|
||||||
Status,
|
|
||||||
PAGE_SIZE,
|
|
||||||
params_slice(:max_id, :min_id, :since_id)
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def params_slice(*keys)
|
|
||||||
params.slice(*keys).permit(*keys)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,17 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
class ActivityPub::BaseController < Api::BaseController
|
|
||||||
skip_before_action :require_authenticated_user!
|
|
||||||
skip_before_action :require_not_suspended!
|
|
||||||
skip_around_action :set_locale
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def set_cache_headers
|
|
||||||
response.headers['Vary'] = 'Signature' if authorized_fetch_mode?
|
|
||||||
end
|
|
||||||
|
|
||||||
def skip_temporary_suspension_response?
|
|
||||||
false
|
|
||||||
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
|
|
|
@ -1,74 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
class ActivityPub::CollectionsController < ActivityPub::BaseController
|
|
||||||
include SignatureVerification
|
|
||||||
include AccountOwnedConcern
|
|
||||||
|
|
||||||
before_action :require_account_signature!, if: :authorized_fetch_mode?
|
|
||||||
before_action :set_items
|
|
||||||
before_action :set_size
|
|
||||||
before_action :set_type
|
|
||||||
before_action :set_cache_headers
|
|
||||||
|
|
||||||
def show
|
|
||||||
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
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def set_items
|
|
||||||
case params[:id]
|
|
||||||
when 'featured'
|
|
||||||
@items = for_signed_account { cache_collection(@account.pinned_statuses.not_local_only, 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
|
|
||||||
|
|
||||||
def set_size
|
|
||||||
case params[:id]
|
|
||||||
when 'featured', 'devices', 'tags'
|
|
||||||
@size = @items.size
|
|
||||||
else
|
|
||||||
not_found
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def set_type
|
|
||||||
case params[:id]
|
|
||||||
when 'featured'
|
|
||||||
@type = :ordered
|
|
||||||
when 'devices', 'tags'
|
|
||||||
@type = :unordered
|
|
||||||
else
|
|
||||||
not_found
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def collection_presenter
|
|
||||||
ActivityPub::CollectionPresenter.new(
|
|
||||||
id: account_collection_url(@account, params[:id]),
|
|
||||||
type: @type,
|
|
||||||
size: @size,
|
|
||||||
items: @items
|
|
||||||
)
|
|
||||||
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
|
|
|
@ -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,76 +1,41 @@
|
||||||
# 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 AccountOwnedConcern
|
|
||||||
|
|
||||||
before_action :skip_unknown_actor_activity
|
before_action :set_account
|
||||||
before_action :require_actor_signature!
|
|
||||||
skip_before_action :authenticate_user!
|
|
||||||
|
|
||||||
def create
|
def create
|
||||||
upgrade_account
|
if signed_request_account
|
||||||
process_collection_synchronization
|
upgrade_account
|
||||||
process_payload
|
process_payload
|
||||||
head 202
|
head 202
|
||||||
|
else
|
||||||
|
[signature_verification_failure_reason, 401]
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def skip_unknown_actor_activity
|
def set_account
|
||||||
head 202 if unknown_affected_account?
|
@account = Account.find_local!(params[:account_username]) if params[:account_username]
|
||||||
end
|
|
||||||
|
|
||||||
def unknown_affected_account?
|
|
||||||
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?
|
|
||||||
rescue Oj::ParseError
|
|
||||||
false
|
|
||||||
end
|
|
||||||
|
|
||||||
def account_required?
|
|
||||||
params[:account_username].present?
|
|
||||||
end
|
|
||||||
|
|
||||||
def skip_temporary_suspension_response?
|
|
||||||
true
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def body
|
def body
|
||||||
return @body if defined?(@body)
|
@body ||= request.body.read
|
||||||
|
|
||||||
@body = request.body.read
|
|
||||||
@body.force_encoding('UTF-8') if @body.present?
|
|
||||||
|
|
||||||
request.body.rewind if request.body.respond_to?(:rewind)
|
|
||||||
|
|
||||||
@body
|
|
||||||
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)
|
ResolveRemoteAccountWorker.perform_async(signed_request_account.acct)
|
||||||
end
|
end
|
||||||
|
|
||||||
DeliveryFailureTracker.reset!(signed_request_actor.inbox_url)
|
Pubsubhubbub::UnsubscribeWorker.perform_async(signed_request_account.id) if signed_request_account.subscribed?
|
||||||
end
|
DeliveryFailureTracker.track_inverse_success!(signed_request_account)
|
||||||
|
|
||||||
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.force_encoding('UTF-8'))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,87 +1,27 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class ActivityPub::OutboxesController < ActivityPub::BaseController
|
class ActivityPub::OutboxesController < Api::BaseController
|
||||||
LIMIT = 20
|
before_action :set_account
|
||||||
|
|
||||||
include SignatureVerification
|
|
||||||
include AccountOwnedConcern
|
|
||||||
|
|
||||||
before_action :require_account_signature!, if: :authorized_fetch_mode?
|
|
||||||
before_action :set_statuses
|
|
||||||
before_action :set_cache_headers
|
|
||||||
|
|
||||||
def show
|
def show
|
||||||
if page_requested?
|
@statuses = @account.statuses.permitted_for(@account, current_account).paginate_by_max_id(20, params[:max_id], params[:since_id])
|
||||||
expires_in(1.minute, public: public_fetch_mode? && signed_request_account.nil?)
|
@statuses = cache_collection(@statuses, Status)
|
||||||
else
|
|
||||||
expires_in(3.minutes, public: public_fetch_mode?)
|
render json: outbox_presenter, serializer: ActivityPub::CollectionSerializer, adapter: ActivityPub::Adapter, content_type: 'application/activity+json'
|
||||||
end
|
|
||||||
render json: outbox_presenter, serializer: ActivityPub::OutboxSerializer, adapter: ActivityPub::Adapter, content_type: 'application/activity+json'
|
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def set_account
|
||||||
|
@account = Account.find_local!(params[:account_username])
|
||||||
|
end
|
||||||
|
|
||||||
def outbox_presenter
|
def outbox_presenter
|
||||||
if page_requested?
|
ActivityPub::CollectionPresenter.new(
|
||||||
ActivityPub::CollectionPresenter.new(
|
id: account_outbox_url(@account),
|
||||||
id: outbox_url(**page_params),
|
type: :ordered,
|
||||||
type: :ordered,
|
size: @account.statuses_count,
|
||||||
part_of: outbox_url,
|
items: @statuses
|
||||||
prev: prev_page,
|
|
||||||
next: next_page,
|
|
||||||
items: @statuses
|
|
||||||
)
|
|
||||||
else
|
|
||||||
ActivityPub::CollectionPresenter.new(
|
|
||||||
id: outbox_url,
|
|
||||||
type: :ordered,
|
|
||||||
size: @account.statuses_count,
|
|
||||||
first: outbox_url(page: true),
|
|
||||||
last: outbox_url(page: true, min_id: 0)
|
|
||||||
)
|
|
||||||
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
|
|
||||||
outbox_url(page: true, max_id: @statuses.last.id) if @statuses.size == LIMIT
|
|
||||||
end
|
|
||||||
|
|
||||||
def prev_page
|
|
||||||
outbox_url(page: true, min_id: @statuses.first.id) unless @statuses.empty?
|
|
||||||
end
|
|
||||||
|
|
||||||
def set_statuses
|
|
||||||
return unless page_requested?
|
|
||||||
|
|
||||||
@statuses = cache_collection_paginated_by_id(
|
|
||||||
AccountStatusesFilter.new(@account, signed_request_account).results,
|
|
||||||
Status,
|
|
||||||
LIMIT,
|
|
||||||
params_slice(:max_id, :min_id, :since_id)
|
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def page_requested?
|
|
||||||
truthy_param?(:page)
|
|
||||||
end
|
|
||||||
|
|
||||||
def page_params
|
|
||||||
{ page: true, max_id: params[:max_id], min_id: params[:min_id] }.compact
|
|
||||||
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,94 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
class ActivityPub::RepliesController < ActivityPub::BaseController
|
|
||||||
include SignatureVerification
|
|
||||||
include Authorization
|
|
||||||
include AccountOwnedConcern
|
|
||||||
|
|
||||||
DESCENDANTS_LIMIT = 60
|
|
||||||
|
|
||||||
before_action :require_account_signature!, if: :authorized_fetch_mode?
|
|
||||||
before_action :set_status
|
|
||||||
before_action :set_cache_headers
|
|
||||||
before_action :set_replies
|
|
||||||
|
|
||||||
def index
|
|
||||||
expires_in 0, public: public_fetch_mode?
|
|
||||||
render json: replies_collection_presenter, serializer: ActivityPub::CollectionSerializer, adapter: ActivityPub::Adapter, content_type: 'application/activity+json', skip_activities: true
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def pundit_user
|
|
||||||
signed_request_account
|
|
||||||
end
|
|
||||||
|
|
||||||
def set_status
|
|
||||||
@status = @account.statuses.find(params[:status_id])
|
|
||||||
authorize @status, :show?
|
|
||||||
rescue Mastodon::NotPermittedError
|
|
||||||
not_found
|
|
||||||
end
|
|
||||||
|
|
||||||
def set_replies
|
|
||||||
@replies = only_other_accounts? ? Status.where.not(account_id: @account.id).joins(:account).merge(Account.without_suspended) : @account.statuses
|
|
||||||
@replies = @replies.where(in_reply_to_id: @status.id, visibility: [:public, :unlisted])
|
|
||||||
@replies = @replies.paginate_by_min_id(DESCENDANTS_LIMIT, params[:min_id])
|
|
||||||
end
|
|
||||||
|
|
||||||
def replies_collection_presenter
|
|
||||||
page = ActivityPub::CollectionPresenter.new(
|
|
||||||
id: account_status_replies_url(@account, @status, page_params),
|
|
||||||
type: :unordered,
|
|
||||||
part_of: account_status_replies_url(@account, @status),
|
|
||||||
next: next_page,
|
|
||||||
items: @replies.map { |status| status.local? ? status : status.uri }
|
|
||||||
)
|
|
||||||
|
|
||||||
return page if page_requested?
|
|
||||||
|
|
||||||
ActivityPub::CollectionPresenter.new(
|
|
||||||
id: account_status_replies_url(@account, @status),
|
|
||||||
type: :unordered,
|
|
||||||
first: page
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def page_requested?
|
|
||||||
truthy_param?(:page)
|
|
||||||
end
|
|
||||||
|
|
||||||
def only_other_accounts?
|
|
||||||
truthy_param?(:only_other_accounts)
|
|
||||||
end
|
|
||||||
|
|
||||||
def next_page
|
|
||||||
if only_other_accounts?
|
|
||||||
# Only consider remote accounts
|
|
||||||
return nil if @replies.size < DESCENDANTS_LIMIT
|
|
||||||
|
|
||||||
account_status_replies_url(
|
|
||||||
@account,
|
|
||||||
@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
|
|
||||||
|
|
||||||
def page_params
|
|
||||||
params_slice(:only_other_accounts, :min_id).merge(page: true)
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,40 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
module Admin
|
|
||||||
class AccountActionsController < BaseController
|
|
||||||
before_action :set_account
|
|
||||||
|
|
||||||
def new
|
|
||||||
authorize @account, :show?
|
|
||||||
|
|
||||||
@account_action = Admin::AccountAction.new(type: params[:type], report_id: params[:report_id], send_email_notification: true, include_statuses: true)
|
|
||||||
@warning_presets = AccountWarningPreset.all
|
|
||||||
end
|
|
||||||
|
|
||||||
def create
|
|
||||||
authorize @account, :show?
|
|
||||||
|
|
||||||
account_action = Admin::AccountAction.new(resource_params)
|
|
||||||
account_action.target_account = @account
|
|
||||||
account_action.current_account = current_account
|
|
||||||
|
|
||||||
account_action.save!
|
|
||||||
|
|
||||||
if account_action.with_report?
|
|
||||||
redirect_to admin_reports_path
|
|
||||||
else
|
|
||||||
redirect_to admin_account_path(@account.id)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def set_account
|
|
||||||
@account = Account.find(params[:account_id])
|
|
||||||
end
|
|
||||||
|
|
||||||
def resource_params
|
|
||||||
params.require(:admin_account_action).permit(:type, :report_id, :warning_preset_id, :text, :send_email_notification, :include_statuses)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,42 +1,31 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
module Admin
|
class Admin::AccountModerationNotesController < Admin::BaseController
|
||||||
class AccountModerationNotesController < BaseController
|
def create
|
||||||
before_action :set_account_moderation_note, only: [:destroy]
|
@account_moderation_note = current_account.account_moderation_notes.new(resource_params)
|
||||||
|
if @account_moderation_note.save
|
||||||
def create
|
@target_account = @account_moderation_note.target_account
|
||||||
authorize AccountModerationNote, :create?
|
redirect_to admin_account_path(@target_account.id), notice: I18n.t('admin.account_moderation_notes.created_msg')
|
||||||
|
else
|
||||||
@account_moderation_note = current_account.account_moderation_notes.new(resource_params)
|
@account = @account_moderation_note.target_account
|
||||||
|
@moderation_notes = @account.targeted_moderation_notes.latest
|
||||||
if @account_moderation_note.save
|
render template: 'admin/accounts/show'
|
||||||
redirect_to admin_account_path(@account_moderation_note.target_account_id), notice: I18n.t('admin.account_moderation_notes.created_msg')
|
|
||||||
else
|
|
||||||
@account = @account_moderation_note.target_account
|
|
||||||
@moderation_notes = @account.targeted_moderation_notes.latest
|
|
||||||
@warnings = @account.strikes.custom.latest
|
|
||||||
|
|
||||||
render template: 'admin/accounts/show'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def destroy
|
|
||||||
authorize @account_moderation_note, :destroy?
|
|
||||||
@account_moderation_note.destroy!
|
|
||||||
redirect_to admin_account_path(@account_moderation_note.target_account_id), notice: I18n.t('admin.account_moderation_notes.destroyed_msg')
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def resource_params
|
|
||||||
params.require(:account_moderation_note).permit(
|
|
||||||
:content,
|
|
||||||
:target_account_id
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def set_account_moderation_note
|
|
||||||
@account_moderation_note = AccountModerationNote.find(params[:id])
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def destroy
|
||||||
|
@account_moderation_note = AccountModerationNote.find(params[:id])
|
||||||
|
@target_account = @account_moderation_note.target_account
|
||||||
|
@account_moderation_note.destroy
|
||||||
|
redirect_to admin_account_path(@target_account.id), notice: I18n.t('admin.account_moderation_notes.destroyed_msg')
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def resource_params
|
||||||
|
params.require(:account_moderation_note).permit(
|
||||||
|
:content,
|
||||||
|
:target_account_id
|
||||||
|
)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,135 +2,34 @@
|
||||||
|
|
||||||
module Admin
|
module Admin
|
||||||
class AccountsController < BaseController
|
class AccountsController < BaseController
|
||||||
before_action :set_account, except: [:index, :batch]
|
before_action :set_account, only: [:show, :subscribe, :unsubscribe, :redownload]
|
||||||
before_action :require_remote_account!, only: [:redownload]
|
before_action :require_remote_account!, only: [:subscribe, :unsubscribe, :redownload]
|
||||||
before_action :require_local_account!, only: [:enable, :memorialize, :approve, :reject]
|
|
||||||
|
|
||||||
def index
|
def 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?
|
|
||||||
|
|
||||||
@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
|
|
||||||
@domain_block = DomainBlock.rule_for(@account.domain)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def memorialize
|
def subscribe
|
||||||
authorize @account, :memorialize?
|
Pubsubhubbub::SubscribeWorker.perform_async(@account.id)
|
||||||
@account.memorialize!
|
|
||||||
log_action :memorialize, @account
|
|
||||||
redirect_to admin_account_path(@account.id), notice: I18n.t('admin.accounts.memorialized_msg', username: @account.acct)
|
|
||||||
end
|
|
||||||
|
|
||||||
def enable
|
|
||||||
authorize @account.user, :enable?
|
|
||||||
@account.user.enable!
|
|
||||||
log_action :enable, @account.user
|
|
||||||
redirect_to admin_account_path(@account.id), notice: I18n.t('admin.accounts.enabled_msg', username: @account.acct)
|
|
||||||
end
|
|
||||||
|
|
||||||
def approve
|
|
||||||
authorize @account.user, :approve?
|
|
||||||
@account.user.approve!
|
|
||||||
redirect_to admin_accounts_path(status: 'pending'), notice: I18n.t('admin.accounts.approved_msg', username: @account.acct)
|
|
||||||
end
|
|
||||||
|
|
||||||
def reject
|
|
||||||
authorize @account.user, :reject?
|
|
||||||
DeleteAccountService.new.call(@account, reserve_email: false, reserve_username: false)
|
|
||||||
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)
|
redirect_to admin_account_path(@account.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
def unsilence
|
def unsubscribe
|
||||||
authorize @account, :unsilence?
|
Pubsubhubbub::UnsubscribeWorker.perform_async(@account.id)
|
||||||
@account.unsilence!
|
redirect_to admin_account_path(@account.id)
|
||||||
log_action :unsilence, @account
|
|
||||||
redirect_to admin_account_path(@account.id), notice: I18n.t('admin.accounts.unsilenced_msg', username: @account.acct)
|
|
||||||
end
|
|
||||||
|
|
||||||
def unsuspend
|
|
||||||
authorize @account, :unsuspend?
|
|
||||||
@account.unsuspend!
|
|
||||||
Admin::UnsuspensionWorker.perform_async(@account.id)
|
|
||||||
log_action :unsuspend, @account
|
|
||||||
redirect_to admin_account_path(@account.id), notice: I18n.t('admin.accounts.unsuspended_msg', username: @account.acct)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def redownload
|
def redownload
|
||||||
authorize @account, :redownload?
|
@account.reset_avatar!
|
||||||
|
@account.reset_header!
|
||||||
@account.update!(last_webfingered_at: nil)
|
|
||||||
ResolveAccountService.new.call(@account)
|
|
||||||
|
|
||||||
redirect_to admin_account_path(@account.id), notice: I18n.t('admin.accounts.redownloaded_msg', username: @account.acct)
|
|
||||||
end
|
|
||||||
|
|
||||||
def remove_avatar
|
|
||||||
authorize @account, :remove_avatar?
|
|
||||||
|
|
||||||
@account.avatar = nil
|
|
||||||
@account.save!
|
@account.save!
|
||||||
|
|
||||||
log_action :remove_avatar, @account.user
|
redirect_to admin_account_path(@account.id)
|
||||||
|
|
||||||
redirect_to admin_account_path(@account.id), notice: I18n.t('admin.accounts.removed_avatar_msg', username: @account.acct)
|
|
||||||
end
|
|
||||||
|
|
||||||
def remove_header
|
|
||||||
authorize @account, :remove_header?
|
|
||||||
|
|
||||||
@account.header = nil
|
|
||||||
@account.save!
|
|
||||||
|
|
||||||
log_action :remove_header, @account.user
|
|
||||||
|
|
||||||
redirect_to admin_account_path(@account.id), notice: I18n.t('admin.accounts.removed_header_msg', username: @account.acct)
|
|
||||||
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
|
||||||
|
@ -143,30 +42,23 @@ module Admin
|
||||||
redirect_to admin_account_path(@account.id) if @account.local?
|
redirect_to admin_account_path(@account.id) if @account.local?
|
||||||
end
|
end
|
||||||
|
|
||||||
def require_local_account!
|
|
||||||
redirect_to admin_account_path(@account.id) unless @account.local? && @account.user.present?
|
|
||||||
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: [])
|
:silenced,
|
||||||
end
|
:recent,
|
||||||
|
:suspended,
|
||||||
def action_from_button
|
:username,
|
||||||
if params[:suspend]
|
:display_name,
|
||||||
'suspend'
|
:email,
|
||||||
elsif params[:approve]
|
:ip
|
||||||
'approve'
|
)
|
||||||
elsif params[:reject]
|
|
||||||
'reject'
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,22 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
module Admin
|
|
||||||
class ActionLogsController < BaseController
|
|
||||||
before_action :set_action_logs
|
|
||||||
|
|
||||||
def index
|
|
||||||
authorize :audit_log, :index?
|
|
||||||
@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
|
|
|
@ -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
|
|
|
@ -2,27 +2,8 @@
|
||||||
|
|
||||||
module Admin
|
module Admin
|
||||||
class BaseController < ApplicationController
|
class BaseController < ApplicationController
|
||||||
include Authorization
|
before_action :require_admin!
|
||||||
include AccountableConcern
|
|
||||||
|
|
||||||
layout 'admin'
|
layout 'admin'
|
||||||
|
|
||||||
before_action :set_pack
|
|
||||||
before_action :set_body_classes
|
|
||||||
after_action :verify_authorized
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def set_body_classes
|
|
||||||
@body_classes = 'admin'
|
|
||||||
end
|
|
||||||
|
|
||||||
def set_pack
|
|
||||||
use_pack 'admin'
|
|
||||||
end
|
|
||||||
|
|
||||||
def set_user
|
|
||||||
@user = Account.find(params[:account_id]).user || raise(ActiveRecord::RecordNotFound)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,49 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
module Admin
|
|
||||||
class ChangeEmailsController < BaseController
|
|
||||||
before_action :set_account
|
|
||||||
before_action :require_local_account!
|
|
||||||
|
|
||||||
def show
|
|
||||||
authorize @user, :change_email?
|
|
||||||
end
|
|
||||||
|
|
||||||
def update
|
|
||||||
authorize @user, :change_email?
|
|
||||||
|
|
||||||
new_email = resource_params.fetch(:unconfirmed_email)
|
|
||||||
|
|
||||||
if new_email != @user.email
|
|
||||||
@user.update!(
|
|
||||||
unconfirmed_email: new_email,
|
|
||||||
# Regenerate the confirmation token:
|
|
||||||
confirmation_token: nil
|
|
||||||
)
|
|
||||||
|
|
||||||
log_action :change_email, @user
|
|
||||||
|
|
||||||
@user.send_confirmation_instructions
|
|
||||||
end
|
|
||||||
|
|
||||||
redirect_to admin_account_path(@account.id), notice: I18n.t('admin.accounts.change_email.changed_msg')
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def set_account
|
|
||||||
@account = Account.find(params[:account_id])
|
|
||||||
@user = @account.user
|
|
||||||
end
|
|
||||||
|
|
||||||
def require_local_account!
|
|
||||||
redirect_to admin_account_path(@account.id) unless @account.local? && @account.user.present?
|
|
||||||
end
|
|
||||||
|
|
||||||
def resource_params
|
|
||||||
params.require(:user).permit(
|
|
||||||
:unconfirmed_email
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -2,34 +2,15 @@
|
||||||
|
|
||||||
module Admin
|
module Admin
|
||||||
class ConfirmationsController < BaseController
|
class ConfirmationsController < BaseController
|
||||||
before_action :set_user
|
|
||||||
before_action :check_confirmation, only: [:resend]
|
|
||||||
|
|
||||||
def create
|
def create
|
||||||
authorize @user, :confirm?
|
account_user.confirm
|
||||||
@user.confirm!
|
|
||||||
log_action :confirm, @user
|
|
||||||
redirect_to admin_accounts_path
|
|
||||||
end
|
|
||||||
|
|
||||||
def resend
|
|
||||||
authorize @user, :confirm?
|
|
||||||
|
|
||||||
@user.resend_confirmation_instructions
|
|
||||||
|
|
||||||
log_action :resend, @user
|
|
||||||
|
|
||||||
flash[:notice] = I18n.t('admin.accounts.resend_confirmation.success')
|
|
||||||
redirect_to admin_accounts_path
|
redirect_to admin_accounts_path
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def check_confirmation
|
def account_user
|
||||||
if @user.confirmed?
|
Account.find(params[:account_id]).user || raise(ActiveRecord::RecordNotFound)
|
||||||
flash[:error] = I18n.t('admin.accounts.resend_confirmation.already_confirmed')
|
|
||||||
redirect_to admin_accounts_path
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,52 +2,61 @@
|
||||||
|
|
||||||
module Admin
|
module Admin
|
||||||
class CustomEmojisController < BaseController
|
class CustomEmojisController < BaseController
|
||||||
def index
|
before_action :set_custom_emoji, except: [:index, :new, :create]
|
||||||
authorize :custom_emoji, :index?
|
|
||||||
|
|
||||||
@custom_emojis = filtered_custom_emojis.eager_load(:local_counterpart).page(params[:page])
|
def index
|
||||||
@form = Form::CustomEmojiBatch.new
|
@custom_emojis = filtered_custom_emojis.page(params[:page])
|
||||||
end
|
end
|
||||||
|
|
||||||
def new
|
def new
|
||||||
authorize :custom_emoji, :create?
|
|
||||||
|
|
||||||
@custom_emoji = CustomEmoji.new
|
@custom_emoji = CustomEmoji.new
|
||||||
end
|
end
|
||||||
|
|
||||||
def create
|
def create
|
||||||
authorize :custom_emoji, :create?
|
|
||||||
|
|
||||||
@custom_emoji = CustomEmoji.new(resource_params)
|
@custom_emoji = CustomEmoji.new(resource_params)
|
||||||
|
|
||||||
if @custom_emoji.save
|
if @custom_emoji.save
|
||||||
log_action :create, @custom_emoji
|
|
||||||
redirect_to admin_custom_emojis_path, notice: I18n.t('admin.custom_emojis.created_msg')
|
redirect_to admin_custom_emojis_path, notice: I18n.t('admin.custom_emojis.created_msg')
|
||||||
else
|
else
|
||||||
render :new
|
render :new
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def batch
|
def destroy
|
||||||
authorize :custom_emoji, :index?
|
@custom_emoji.destroy
|
||||||
|
redirect_to admin_custom_emojis_path, notice: I18n.t('admin.custom_emojis.destroyed_msg')
|
||||||
|
end
|
||||||
|
|
||||||
@form = Form::CustomEmojiBatch.new(form_custom_emoji_batch_params.merge(current_account: current_account, action: action_from_button))
|
def copy
|
||||||
@form.save
|
emoji = CustomEmoji.new(domain: nil, shortcode: @custom_emoji.shortcode, image: @custom_emoji.image)
|
||||||
rescue ActionController::ParameterMissing
|
|
||||||
flash[:alert] = I18n.t('admin.custom_emojis.no_emoji_selected')
|
if emoji.save
|
||||||
rescue Mastodon::NotPermittedError
|
flash[:notice] = I18n.t('admin.custom_emojis.copied_msg')
|
||||||
flash[:alert] = I18n.t('admin.custom_emojis.not_permitted')
|
else
|
||||||
rescue ActiveRecord::RecordInvalid => e
|
flash[:alert] = I18n.t('admin.custom_emojis.copy_failed_msg')
|
||||||
error_message = action_from_button == 'copy' ? 'admin.custom_emojis.batch_copy_error' : 'admin.custom_emojis.batch_error'
|
end
|
||||||
flash[:alert] = I18n.t(error_message, message: e.message)
|
|
||||||
ensure
|
redirect_to admin_custom_emojis_path(params[:page])
|
||||||
redirect_to admin_custom_emojis_path(filter_params)
|
end
|
||||||
|
|
||||||
|
def enable
|
||||||
|
@custom_emoji.update!(disabled: false)
|
||||||
|
redirect_to admin_custom_emojis_path, notice: I18n.t('admin.custom_emojis.enabled_msg')
|
||||||
|
end
|
||||||
|
|
||||||
|
def disable
|
||||||
|
@custom_emoji.update!(disabled: true)
|
||||||
|
redirect_to admin_custom_emojis_path, notice: I18n.t('admin.custom_emojis.disabled_msg')
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def set_custom_emoji
|
||||||
|
@custom_emoji = CustomEmoji.find(params[:id])
|
||||||
|
end
|
||||||
|
|
||||||
def resource_params
|
def resource_params
|
||||||
params.require(:custom_emoji).permit(:shortcode, :image, :visible_in_picker)
|
params.require(:custom_emoji).permit(:shortcode, :image)
|
||||||
end
|
end
|
||||||
|
|
||||||
def filtered_custom_emojis
|
def filtered_custom_emojis
|
||||||
|
@ -55,29 +64,10 @@ 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
|
)
|
||||||
if params[:update]
|
|
||||||
'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 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
module Admin
|
|
||||||
class DashboardController < BaseController
|
|
||||||
include Redisable
|
|
||||||
|
|
||||||
def index
|
|
||||||
authorize :dashboard, :index?
|
|
||||||
|
|
||||||
@system_checks = Admin::SystemCheck.perform(current_user)
|
|
||||||
@time_period = (29.days.ago.to_date...Time.now.utc.to_date)
|
|
||||||
@pending_users_count = User.pending.count
|
|
||||||
@pending_reports_count = Report.unresolved.count
|
|
||||||
@pending_tags_count = Tag.pending_review.count
|
|
||||||
@pending_appeals_count = Appeal.pending.count
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def redis_info
|
|
||||||
@redis_info ||= begin
|
|
||||||
if redis.is_a?(Redis::Namespace)
|
|
||||||
redis.redis.info
|
|
||||||
else
|
|
||||||
redis.info
|
|
||||||
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,77 +2,32 @@
|
||||||
|
|
||||||
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
|
def index
|
||||||
authorize :domain_block, :create?
|
@domain_blocks = DomainBlock.page(params[:page])
|
||||||
@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
|
end
|
||||||
|
|
||||||
def new
|
def new
|
||||||
authorize :domain_block, :create?
|
@domain_block = DomainBlock.new
|
||||||
@domain_block = DomainBlock.new(domain: params[:_domain])
|
|
||||||
end
|
|
||||||
|
|
||||||
def edit
|
|
||||||
authorize :domain_block, :create?
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def create
|
def create
|
||||||
authorize :domain_block, :create?
|
|
||||||
|
|
||||||
@domain_block = DomainBlock.new(resource_params)
|
@domain_block = DomainBlock.new(resource_params)
|
||||||
existing_domain_block = resource_params[:domain].present? ? DomainBlock.rule_for(resource_params[:domain]) : nil
|
|
||||||
|
|
||||||
if existing_domain_block.present? && !@domain_block.stricter_than?(existing_domain_block)
|
|
||||||
@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
|
|
||||||
@domain_block.errors.delete(:domain)
|
|
||||||
render :new
|
|
||||||
else
|
|
||||||
if existing_domain_block.present?
|
|
||||||
@domain_block = existing_domain_block
|
|
||||||
@domain_block.update(resource_params)
|
|
||||||
end
|
|
||||||
|
|
||||||
if @domain_block.save
|
|
||||||
DomainBlockWorker.perform_async(@domain_block.id)
|
|
||||||
log_action :create, @domain_block
|
|
||||||
redirect_to admin_instances_path(limited: '1'), notice: I18n.t('admin.domain_blocks.created_msg')
|
|
||||||
else
|
|
||||||
render :new
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def update
|
|
||||||
authorize :domain_block, :update?
|
|
||||||
|
|
||||||
@domain_block.update(update_params)
|
|
||||||
|
|
||||||
severity_changed = @domain_block.severity_changed?
|
|
||||||
|
|
||||||
if @domain_block.save
|
if @domain_block.save
|
||||||
DomainBlockWorker.perform_async(@domain_block.id, severity_changed)
|
DomainBlockWorker.perform_async(@domain_block.id)
|
||||||
log_action :update, @domain_block
|
redirect_to admin_domain_blocks_path, notice: I18n.t('admin.domain_blocks.created_msg')
|
||||||
redirect_to admin_instances_path(limited: '1'), notice: I18n.t('admin.domain_blocks.created_msg')
|
|
||||||
else
|
else
|
||||||
render :edit
|
render :new
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def show; end
|
||||||
|
|
||||||
def destroy
|
def destroy
|
||||||
authorize @domain_block, :destroy?
|
UnblockDomainService.new.call(@domain_block, retroactive_unblock?)
|
||||||
UnblockDomainService.new.call(@domain_block)
|
redirect_to admin_domain_blocks_path, notice: I18n.t('admin.domain_blocks.destroyed_msg')
|
||||||
log_action :destroy, @domain_block
|
|
||||||
redirect_to admin_instances_path(limited: '1'), notice: I18n.t('admin.domain_blocks.destroyed_msg')
|
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
@ -81,22 +36,12 @@ 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, :retroactive)
|
||||||
end
|
end
|
||||||
|
|
||||||
def form_domain_block_batch_params
|
def retroactive_unblock?
|
||||||
params.require(:form_domain_block_batch).permit(domain_blocks_attributes: [:enabled, :domain, :severity, :reject_media, :reject_reports, :private_comment, :public_comment, :obfuscate])
|
ActiveRecord::Type.lookup(:boolean).cast(resource_params[:retroactive])
|
||||||
end
|
|
||||||
|
|
||||||
def action_from_button
|
|
||||||
if params[:save]
|
|
||||||
'save'
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,56 +5,26 @@ module Admin
|
||||||
before_action :set_email_domain_block, only: [:show, :destroy]
|
before_action :set_email_domain_block, only: [:show, :destroy]
|
||||||
|
|
||||||
def index
|
def 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?
|
@email_domain_block = EmailDomainBlock.new
|
||||||
@email_domain_block = EmailDomainBlock.new(domain: params[:_domain])
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def create
|
def create
|
||||||
authorize :email_domain_block, :create?
|
|
||||||
|
|
||||||
@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
|
|
||||||
@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
|
||||||
|
@email_domain_block.destroy
|
||||||
|
redirect_to admin_email_domain_blocks_path, notice: I18n.t('admin.email_domain_blocks.destroyed_msg')
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
@ -63,27 +33,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
|
|
|
@ -2,74 +2,40 @@
|
||||||
|
|
||||||
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?
|
@instances = ordered_instances
|
||||||
preload_delivery_failures!
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def show
|
def resubscribe
|
||||||
authorize :instance, :show?
|
params.require(:by_domain)
|
||||||
@time_period = (6.days.ago.to_date...Time.now.utc.to_date)
|
Pubsubhubbub::SubscribeWorker.push_bulk(subscribeable_accounts.pluck(:id))
|
||||||
end
|
redirect_to admin_instances_path
|
||||||
|
|
||||||
def destroy
|
|
||||||
authorize :instance, :destroy?
|
|
||||||
Admin::DomainPurgeWorker.perform_async(@instance.domain)
|
|
||||||
log_action :destroy, @instance
|
|
||||||
redirect_to admin_instances_path, notice: I18n.t('admin.instances.destroyed_msg', domain: @instance.domain)
|
|
||||||
end
|
|
||||||
|
|
||||||
def clear_delivery_errors
|
|
||||||
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 { |account| Instance.new(account) }
|
||||||
|
end
|
||||||
|
|
||||||
|
def subscribeable_accounts
|
||||||
|
Account.with_followers.remote.where(domain: params[:by_domain])
|
||||||
end
|
end
|
||||||
|
|
||||||
def filter_params
|
def filter_params
|
||||||
params.slice(*InstanceFilter::KEYS).permit(*InstanceFilter::KEYS)
|
params.permit(
|
||||||
|
:domain_name
|
||||||
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,53 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
module Admin
|
|
||||||
class InvitesController < BaseController
|
|
||||||
def index
|
|
||||||
authorize :invite, :index?
|
|
||||||
|
|
||||||
@invites = filtered_invites.includes(user: :account).page(params[:page])
|
|
||||||
@invite = Invite.new
|
|
||||||
end
|
|
||||||
|
|
||||||
def create
|
|
||||||
authorize :invite, :create?
|
|
||||||
|
|
||||||
@invite = Invite.new(resource_params)
|
|
||||||
@invite.user = current_user
|
|
||||||
|
|
||||||
if @invite.save
|
|
||||||
redirect_to admin_invites_path
|
|
||||||
else
|
|
||||||
@invites = Invite.page(params[:page])
|
|
||||||
render :index
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def destroy
|
|
||||||
@invite = Invite.find(params[:id])
|
|
||||||
authorize @invite, :destroy?
|
|
||||||
@invite.expire!
|
|
||||||
redirect_to admin_invites_path
|
|
||||||
end
|
|
||||||
|
|
||||||
def deactivate_all
|
|
||||||
authorize :invite, :deactivate_all?
|
|
||||||
Invite.available.in_batches.update_all(expires_at: Time.now.utc)
|
|
||||||
redirect_to admin_invites_path
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def resource_params
|
|
||||||
params.require(:invite).permit(:max_uses, :expires_in)
|
|
||||||
end
|
|
||||||
|
|
||||||
def filtered_invites
|
|
||||||
InviteFilter.new(filter_params).results
|
|
||||||
end
|
|
||||||
|
|
||||||
def filter_params
|
|
||||||
params.slice(*InviteFilter::KEYS).permit(*InviteFilter::KEYS)
|
|
||||||
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
|
|
|
@ -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
|
|
|
@ -1,63 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
module Admin
|
|
||||||
class RelaysController < BaseController
|
|
||||||
before_action :set_relay, except: [:index, :new, :create]
|
|
||||||
before_action :require_signatures_enabled!, only: [:new, :create, :enable]
|
|
||||||
|
|
||||||
def index
|
|
||||||
authorize :relay, :update?
|
|
||||||
@relays = Relay.all
|
|
||||||
end
|
|
||||||
|
|
||||||
def new
|
|
||||||
authorize :relay, :update?
|
|
||||||
@relay = Relay.new
|
|
||||||
end
|
|
||||||
|
|
||||||
def create
|
|
||||||
authorize :relay, :update?
|
|
||||||
|
|
||||||
@relay = Relay.new(resource_params)
|
|
||||||
|
|
||||||
if @relay.save
|
|
||||||
@relay.enable!
|
|
||||||
redirect_to admin_relays_path
|
|
||||||
else
|
|
||||||
render action: :new
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def destroy
|
|
||||||
authorize :relay, :update?
|
|
||||||
@relay.destroy
|
|
||||||
redirect_to admin_relays_path
|
|
||||||
end
|
|
||||||
|
|
||||||
def enable
|
|
||||||
authorize :relay, :update?
|
|
||||||
@relay.enable!
|
|
||||||
redirect_to admin_relays_path
|
|
||||||
end
|
|
||||||
|
|
||||||
def disable
|
|
||||||
authorize :relay, :update?
|
|
||||||
@relay.disable!
|
|
||||||
redirect_to admin_relays_path
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def set_relay
|
|
||||||
@relay = Relay.find(params[:id])
|
|
||||||
end
|
|
||||||
|
|
||||||
def resource_params
|
|
||||||
params.require(:relay).permit(:inbox_url)
|
|
||||||
end
|
|
||||||
|
|
||||||
def require_signatures_enabled!
|
|
||||||
redirect_to admin_relays_path, alert: I18n.t('admin.relays.signatures_not_enabled') if authorized_fetch_mode?
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,60 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
module Admin
|
|
||||||
class ReportNotesController < BaseController
|
|
||||||
before_action :set_report_note, only: [:destroy]
|
|
||||||
|
|
||||||
def create
|
|
||||||
authorize :report_note, :create?
|
|
||||||
|
|
||||||
@report_note = current_account.report_notes.new(resource_params)
|
|
||||||
@report = @report_note.report
|
|
||||||
|
|
||||||
if @report_note.save
|
|
||||||
if params[:create_and_resolve]
|
|
||||||
@report.resolve!(current_account)
|
|
||||||
log_action :resolve, @report
|
|
||||||
elsif params[:create_and_unresolve]
|
|
||||||
@report.unresolve!
|
|
||||||
log_action :reopen, @report
|
|
||||||
end
|
|
||||||
|
|
||||||
redirect_to after_create_redirect_path, notice: I18n.t('admin.report_notes.created_msg')
|
|
||||||
else
|
|
||||||
@report_notes = @report.notes.includes(:account).order(id: :desc)
|
|
||||||
@action_logs = @report.history.includes(:target)
|
|
||||||
@form = Admin::StatusBatchAction.new
|
|
||||||
@statuses = @report.statuses.with_includes
|
|
||||||
|
|
||||||
render template: 'admin/reports/show'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def destroy
|
|
||||||
authorize @report_note, :destroy?
|
|
||||||
@report_note.destroy!
|
|
||||||
redirect_to admin_report_path(@report_note.report_id), notice: I18n.t('admin.report_notes.destroyed_msg')
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def after_create_redirect_path
|
|
||||||
if params[:create_and_resolve]
|
|
||||||
admin_reports_path
|
|
||||||
else
|
|
||||||
admin_report_path(@report)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def resource_params
|
|
||||||
params.require(:report_note).permit(
|
|
||||||
:content,
|
|
||||||
:report_id
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def set_report_note
|
|
||||||
@report_note = ReportNote.find(params[:id])
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
46
app/controllers/admin/reported_statuses_controller.rb
Normal file
46
app/controllers/admin/reported_statuses_controller.rb
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module Admin
|
||||||
|
class ReportedStatusesController < BaseController
|
||||||
|
include Authorization
|
||||||
|
|
||||||
|
before_action :set_report
|
||||||
|
before_action :set_status, only: [:update, :destroy]
|
||||||
|
|
||||||
|
def create
|
||||||
|
@form = Form::StatusBatch.new(form_status_batch_params)
|
||||||
|
flash[:alert] = t('admin.statuses.failed_to_execute') unless @form.save
|
||||||
|
|
||||||
|
redirect_to admin_report_path(@report)
|
||||||
|
end
|
||||||
|
|
||||||
|
def update
|
||||||
|
@status.update(status_params)
|
||||||
|
redirect_to admin_report_path(@report)
|
||||||
|
end
|
||||||
|
|
||||||
|
def destroy
|
||||||
|
authorize @status, :destroy?
|
||||||
|
RemovalWorker.perform_async(@status.id)
|
||||||
|
render json: @status
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def status_params
|
||||||
|
params.require(:status).permit(:sensitive)
|
||||||
|
end
|
||||||
|
|
||||||
|
def form_status_batch_params
|
||||||
|
params.require(:form_status_batch).permit(:action, status_ids: [])
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_report
|
||||||
|
@report = Report.find(params[:report_id])
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_status
|
||||||
|
@status = @report.statuses.find(params[: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
|
|
|
@ -5,56 +5,64 @@ module Admin
|
||||||
before_action :set_report, except: [:index]
|
before_action :set_report, except: [:index]
|
||||||
|
|
||||||
def index
|
def index
|
||||||
authorize :report, :index?
|
|
||||||
@reports = filtered_reports.page(params[:page])
|
@reports = filtered_reports.page(params[:page])
|
||||||
end
|
end
|
||||||
|
|
||||||
def show
|
def show
|
||||||
authorize @report, :show?
|
@form = Form::StatusBatch.new
|
||||||
|
|
||||||
@report_note = @report.notes.new
|
|
||||||
@report_notes = @report.notes.includes(:account).order(id: :desc)
|
|
||||||
@action_logs = @report.history.includes(:target)
|
|
||||||
@form = Admin::StatusBatchAction.new
|
|
||||||
@statuses = @report.statuses.with_includes
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def assign_to_self
|
def update
|
||||||
authorize @report, :update?
|
process_report
|
||||||
@report.update!(assigned_account_id: current_account.id)
|
|
||||||
log_action :assigned_to_self, @report
|
|
||||||
redirect_to admin_report_path(@report)
|
redirect_to admin_report_path(@report)
|
||||||
end
|
end
|
||||||
|
|
||||||
def unassign
|
|
||||||
authorize @report, :update?
|
|
||||||
@report.update!(assigned_account_id: nil)
|
|
||||||
log_action :unassigned, @report
|
|
||||||
redirect_to admin_report_path(@report)
|
|
||||||
end
|
|
||||||
|
|
||||||
def reopen
|
|
||||||
authorize @report, :update?
|
|
||||||
@report.unresolve!
|
|
||||||
log_action :reopen, @report
|
|
||||||
redirect_to admin_report_path(@report)
|
|
||||||
end
|
|
||||||
|
|
||||||
def resolve
|
|
||||||
authorize @report, :update?
|
|
||||||
@report.resolve!(current_account)
|
|
||||||
log_action :resolve, @report
|
|
||||||
redirect_to admin_reports_path, notice: I18n.t('admin.reports.resolved_msg')
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def process_report
|
||||||
|
case params[:outcome].to_s
|
||||||
|
when 'resolve'
|
||||||
|
@report.update(action_taken_by_current_attributes)
|
||||||
|
when 'suspend'
|
||||||
|
Admin::SuspensionWorker.perform_async(@report.target_account.id)
|
||||||
|
resolve_all_target_account_reports
|
||||||
|
when 'silence'
|
||||||
|
@report.target_account.update(silenced: true)
|
||||||
|
resolve_all_target_account_reports
|
||||||
|
else
|
||||||
|
raise ActiveRecord::RecordNotFound
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def action_taken_by_current_attributes
|
||||||
|
{ action_taken: true, action_taken_by_account_id: current_account.id }
|
||||||
|
end
|
||||||
|
|
||||||
|
def resolve_all_target_account_reports
|
||||||
|
unresolved_reports_for_target_account.update_all(
|
||||||
|
action_taken_by_current_attributes
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def unresolved_reports_for_target_account
|
||||||
|
Report.where(
|
||||||
|
target_account: @report.target_account
|
||||||
|
).unresolved
|
||||||
|
end
|
||||||
|
|
||||||
def filtered_reports
|
def filtered_reports
|
||||||
ReportFilter.new(filter_params).results.order(id: :desc).includes(:account, :target_account)
|
ReportFilter.new(filter_params).results.order(id: :desc).includes(
|
||||||
|
:account,
|
||||||
|
:target_account
|
||||||
|
)
|
||||||
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
|
||||||
|
|
|
@ -2,13 +2,17 @@
|
||||||
|
|
||||||
module Admin
|
module Admin
|
||||||
class ResetsController < BaseController
|
class ResetsController < BaseController
|
||||||
before_action :set_user
|
before_action :set_account
|
||||||
|
|
||||||
def create
|
def create
|
||||||
authorize @user, :reset_password?
|
@account.user.send_reset_password_instructions
|
||||||
@user.reset_password!
|
redirect_to admin_accounts_path
|
||||||
log_action :reset_password, @user
|
end
|
||||||
redirect_to admin_account_path(@user.account_id)
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def set_account
|
||||||
|
@account = Account.find(params[:account_id])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,67 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
module Admin
|
|
||||||
class RolesController < BaseController
|
|
||||||
before_action :set_role, except: [:index, :new, :create]
|
|
||||||
|
|
||||||
def index
|
|
||||||
authorize :user_role, :index?
|
|
||||||
|
|
||||||
@roles = UserRole.order(position: :desc).page(params[:page])
|
|
||||||
end
|
|
||||||
|
|
||||||
def new
|
|
||||||
authorize :user_role, :create?
|
|
||||||
|
|
||||||
@role = UserRole.new
|
|
||||||
end
|
|
||||||
|
|
||||||
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
|
|
|
@ -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
|
|
|
@ -1,9 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
class Admin::Settings::AppearanceController < Admin::SettingsController
|
|
||||||
private
|
|
||||||
|
|
||||||
def after_update_redirect_path
|
|
||||||
admin_settings_appearance_path
|
|
||||||
end
|
|
||||||
end
|
|
Some files were not shown because too many files have changed in this diff Show more
Reference in a new issue