Compare commits
59 Commits
cybrespace
...
cybrespace
Author | SHA1 | Date |
---|---|---|
khr | 1e31148c57 | |
Eugen Rochko | 887f9de6dc | |
ThibG | e625425c8f | |
ThibG | f13d08314e | |
Eugen Rochko | 13979a84f9 | |
Eugen Rochko | 82570019ba | |
Eugen Rochko | a1216e6315 | |
Eugen Rochko | 34de90c486 | |
Eugen Rochko | 442f335504 | |
Eugen Rochko | 58108b4481 | |
Eugen Rochko | cc0c1674f0 | |
ThibG | 49f49cf367 | |
Hugo Gameiro | ec20a5d53a | |
Eugen Rochko | 404dc97fb0 | |
Eugen Rochko | a2cda74ba3 | |
valerauko | 12bdd7dc5f | |
Renato "Lond" Cerqueira | 15dcb414bf | |
Alexandre Alapetite | 2c36d35784 | |
Dan Hunsaker | c0736c466c | |
Eugen Rochko | fa02f878fc | |
Eugen Rochko | ecc58c0f23 | |
Eugen Rochko | 6d4438a6ae | |
mayaeh | 01a8ab921e | |
ThibG | a3ef076160 | |
Eugen Rochko | cd8575aef6 | |
ThibG | 4ce6ed2021 | |
ThibG | 886ef1cc38 | |
ThibG | d06a724b1c | |
Eugen Rochko | f73b7e77da | |
Eugen Rochko | 63f168c3bf | |
Eugen Rochko | 0f436de035 | |
Eugen Rochko | 21fd335dd7 | |
Eugen Rochko | 4b2f254806 | |
Eugen Rochko | b3c29ece47 | |
Eugen Rochko | 330401bec0 | |
Eugen Rochko | 5ee4fd4606 | |
m.b | 430499fbe1 | |
Steven Tappert | 449e6e451f | |
LenPayne | d510ef7375 | |
nightpool | b9bab4e477 | |
khr | 9a49c503c8 | |
khr | 7c0624ccfa | |
khr | 6f6c4f2a50 | |
khr | 42954f329a | |
khr | a0ba41a78c | |
khr | 6695882f19 | |
khr | 35d7ae4974 | |
khr | f40d8a3c37 | |
khr | 9eab8a139f | |
khr | 7abb76fd6f | |
khr | a0bce3a23f | |
chr | 7dae98557c | |
Andrew | 87eb1df45a | |
khr | a01c7ce321 | |
khr | ebed61cf88 | |
Andrew | 99b3c18b28 | |
khr | df0855a91b | |
Andrew | bf7b956a99 | |
Andrew | 5834fc5aa1 |
|
@ -3,7 +3,7 @@ version: 2
|
||||||
aliases:
|
aliases:
|
||||||
- &defaults
|
- &defaults
|
||||||
docker:
|
docker:
|
||||||
- image: circleci/ruby:2.6-stretch-node
|
- image: circleci/ruby:2.5.1-stretch-node
|
||||||
environment: &ruby_environment
|
environment: &ruby_environment
|
||||||
BUNDLE_APP_CONFIG: ./.bundle/
|
BUNDLE_APP_CONFIG: ./.bundle/
|
||||||
DB_HOST: localhost
|
DB_HOST: localhost
|
||||||
|
@ -98,21 +98,21 @@ jobs:
|
||||||
<<: *defaults
|
<<: *defaults
|
||||||
<<: *install_steps
|
<<: *install_steps
|
||||||
|
|
||||||
install-ruby2.6:
|
|
||||||
<<: *defaults
|
|
||||||
<<: *install_ruby_dependencies
|
|
||||||
|
|
||||||
install-ruby2.5:
|
install-ruby2.5:
|
||||||
<<: *defaults
|
<<: *defaults
|
||||||
docker:
|
|
||||||
- image: circleci/ruby:2.5-stretch-node
|
|
||||||
environment: *ruby_environment
|
|
||||||
<<: *install_ruby_dependencies
|
<<: *install_ruby_dependencies
|
||||||
|
|
||||||
install-ruby2.4:
|
install-ruby2.4:
|
||||||
<<: *defaults
|
<<: *defaults
|
||||||
docker:
|
docker:
|
||||||
- image: circleci/ruby:2.4-stretch-node
|
- image: circleci/ruby:2.4.4-stretch-node
|
||||||
|
environment: *ruby_environment
|
||||||
|
<<: *install_ruby_dependencies
|
||||||
|
|
||||||
|
install-ruby2.3:
|
||||||
|
<<: *defaults
|
||||||
|
docker:
|
||||||
|
- image: circleci/ruby:2.3.7-stretch-node
|
||||||
environment: *ruby_environment
|
environment: *ruby_environment
|
||||||
<<: *install_ruby_dependencies
|
<<: *install_ruby_dependencies
|
||||||
|
|
||||||
|
@ -128,43 +128,43 @@ jobs:
|
||||||
- ./mastodon/public/assets
|
- ./mastodon/public/assets
|
||||||
- ./mastodon/public/packs-test/
|
- ./mastodon/public/packs-test/
|
||||||
|
|
||||||
test-ruby2.6:
|
|
||||||
<<: *defaults
|
|
||||||
docker:
|
|
||||||
- image: circleci/ruby:2.6-stretch-node
|
|
||||||
environment: *ruby_environment
|
|
||||||
- image: circleci/postgres:10.6-alpine
|
|
||||||
environment:
|
|
||||||
POSTGRES_USER: root
|
|
||||||
- image: circleci/redis:5-alpine
|
|
||||||
<<: *test_steps
|
|
||||||
|
|
||||||
test-ruby2.5:
|
test-ruby2.5:
|
||||||
<<: *defaults
|
<<: *defaults
|
||||||
docker:
|
docker:
|
||||||
- image: circleci/ruby:2.5-stretch-node
|
- image: circleci/ruby:2.5.1-stretch-node
|
||||||
environment: *ruby_environment
|
environment: *ruby_environment
|
||||||
- image: circleci/postgres:10.6-alpine
|
- image: circleci/postgres:10.3-alpine
|
||||||
environment:
|
environment:
|
||||||
POSTGRES_USER: root
|
POSTGRES_USER: root
|
||||||
- image: circleci/redis:5-alpine
|
- image: circleci/redis:4.0.9-alpine
|
||||||
<<: *test_steps
|
<<: *test_steps
|
||||||
|
|
||||||
test-ruby2.4:
|
test-ruby2.4:
|
||||||
<<: *defaults
|
<<: *defaults
|
||||||
docker:
|
docker:
|
||||||
- image: circleci/ruby:2.4-stretch-node
|
- image: circleci/ruby:2.4.4-stretch-node
|
||||||
environment: *ruby_environment
|
environment: *ruby_environment
|
||||||
- image: circleci/postgres:10.6-alpine
|
- image: circleci/postgres:10.3-alpine
|
||||||
environment:
|
environment:
|
||||||
POSTGRES_USER: root
|
POSTGRES_USER: root
|
||||||
- image: circleci/redis:5-alpine
|
- image: circleci/redis:4.0.9-alpine
|
||||||
|
<<: *test_steps
|
||||||
|
|
||||||
|
test-ruby2.3:
|
||||||
|
<<: *defaults
|
||||||
|
docker:
|
||||||
|
- image: circleci/ruby:2.3.7-stretch-node
|
||||||
|
environment: *ruby_environment
|
||||||
|
- image: circleci/postgres:10.3-alpine
|
||||||
|
environment:
|
||||||
|
POSTGRES_USER: root
|
||||||
|
- image: circleci/redis:4.0.9-alpine
|
||||||
<<: *test_steps
|
<<: *test_steps
|
||||||
|
|
||||||
test-webui:
|
test-webui:
|
||||||
<<: *defaults
|
<<: *defaults
|
||||||
docker:
|
docker:
|
||||||
- image: circleci/node:12.9-stretch
|
- image: circleci/node:8.11.1-stretch
|
||||||
steps:
|
steps:
|
||||||
- *attach_workspace
|
- *attach_workspace
|
||||||
- run: ./bin/retry yarn test:jest
|
- run: ./bin/retry yarn test:jest
|
||||||
|
@ -173,35 +173,30 @@ jobs:
|
||||||
<<: *defaults
|
<<: *defaults
|
||||||
steps:
|
steps:
|
||||||
- *attach_workspace
|
- *attach_workspace
|
||||||
- *install_system_dependencies
|
|
||||||
- run: bundle exec i18n-tasks check-normalized
|
- run: bundle exec i18n-tasks check-normalized
|
||||||
- run: bundle exec i18n-tasks unused -l en
|
- run: bundle exec i18n-tasks unused
|
||||||
|
- run: bundle exec i18n-tasks missing -t plural
|
||||||
- run: bundle exec i18n-tasks check-consistent-interpolations
|
- run: bundle exec i18n-tasks check-consistent-interpolations
|
||||||
- run: bundle exec rake repo:check_locales_files
|
|
||||||
|
|
||||||
workflows:
|
workflows:
|
||||||
version: 2
|
version: 2
|
||||||
build-and-test:
|
build-and-test:
|
||||||
jobs:
|
jobs:
|
||||||
- install
|
- install
|
||||||
- install-ruby2.6:
|
|
||||||
requires:
|
|
||||||
- install
|
|
||||||
- install-ruby2.5:
|
- install-ruby2.5:
|
||||||
requires:
|
requires:
|
||||||
- install
|
- install
|
||||||
- install-ruby2.6
|
|
||||||
- install-ruby2.4:
|
- install-ruby2.4:
|
||||||
requires:
|
requires:
|
||||||
- install
|
- install
|
||||||
- install-ruby2.6
|
- install-ruby2.5
|
||||||
|
- install-ruby2.3:
|
||||||
|
requires:
|
||||||
|
- install
|
||||||
|
- install-ruby2.5
|
||||||
- build:
|
- build:
|
||||||
requires:
|
requires:
|
||||||
- install-ruby2.6
|
- install-ruby2.5
|
||||||
- test-ruby2.6:
|
|
||||||
requires:
|
|
||||||
- install-ruby2.6
|
|
||||||
- build
|
|
||||||
- test-ruby2.5:
|
- test-ruby2.5:
|
||||||
requires:
|
requires:
|
||||||
- install-ruby2.5
|
- install-ruby2.5
|
||||||
|
@ -210,9 +205,13 @@ workflows:
|
||||||
requires:
|
requires:
|
||||||
- install-ruby2.4
|
- install-ruby2.4
|
||||||
- build
|
- build
|
||||||
|
- test-ruby2.3:
|
||||||
|
requires:
|
||||||
|
- install-ruby2.3
|
||||||
|
- build
|
||||||
- test-webui:
|
- test-webui:
|
||||||
requires:
|
requires:
|
||||||
- install
|
- install
|
||||||
- check-i18n:
|
- check-i18n:
|
||||||
requires:
|
requires:
|
||||||
- install-ruby2.6
|
- install-ruby2.5
|
||||||
|
|
|
@ -27,11 +27,11 @@ plugins:
|
||||||
enabled: true
|
enabled: true
|
||||||
eslint:
|
eslint:
|
||||||
enabled: true
|
enabled: true
|
||||||
channel: eslint-5
|
channel: eslint-4
|
||||||
rubocop:
|
rubocop:
|
||||||
enabled: true
|
enabled: true
|
||||||
channel: rubocop-0-71
|
channel: rubocop-0-54
|
||||||
sass-lint:
|
scss-lint:
|
||||||
enabled: true
|
enabled: true
|
||||||
exclude_patterns:
|
exclude_patterns:
|
||||||
- spec/
|
- spec/
|
||||||
|
|
|
@ -1,10 +0,0 @@
|
||||||
version: 1
|
|
||||||
|
|
||||||
update_configs:
|
|
||||||
- package_manager: "ruby:bundler"
|
|
||||||
directory: "/"
|
|
||||||
update_schedule: "weekly"
|
|
||||||
|
|
||||||
- package_manager: "javascript"
|
|
||||||
directory: "/"
|
|
||||||
update_schedule: "weekly"
|
|
56
.env.nanobox
56
.env.nanobox
|
@ -11,14 +11,24 @@ DB_NAME=gonano
|
||||||
DB_PASS=$DATA_DB_PASS
|
DB_PASS=$DATA_DB_PASS
|
||||||
DB_PORT=5432
|
DB_PORT=5432
|
||||||
|
|
||||||
# DATABASE_URL=postgresql://$DATA_DB_USER:$DATA_DB_PASS@$DATA_DB_HOST/gonano
|
DATABASE_URL=postgresql://$DATA_DB_USER:$DATA_DB_PASS@$DATA_DB_HOST/gonano
|
||||||
|
|
||||||
# Optional ElasticSearch configuration
|
# Optional ElasticSearch configuration
|
||||||
ES_ENABLED=true
|
ES_ENABLED=true
|
||||||
ES_HOST=$DATA_ELASTIC_HOST
|
ES_HOST=$DATA_ELASTIC_HOST
|
||||||
ES_PORT=9200
|
ES_PORT=9200
|
||||||
|
|
||||||
BIND=0.0.0.0
|
# Optimizations
|
||||||
|
LD_PRELOAD=/data/lib/libjemalloc.so
|
||||||
|
|
||||||
|
# ImageMagick optimizations
|
||||||
|
MAGICK_TEMPORARY_PATH=/app/tmp
|
||||||
|
MAGICK_MEMORY_LIMIT=128MiB
|
||||||
|
MAGICK_MAP_LIMIT=64MiB
|
||||||
|
MAGICK_TIME_LIMIT=15
|
||||||
|
MAGICK_AREA_LIMIT=16MP
|
||||||
|
MAGICK_WIDTH_LIMIT=8KP
|
||||||
|
MAGICK_HEIGHT_LIMIT=8KP
|
||||||
|
|
||||||
# Federation
|
# Federation
|
||||||
# Note: Changing LOCAL_DOMAIN at a later time will cause unwanted side effects, including breaking all existing federation.
|
# Note: Changing LOCAL_DOMAIN at a later time will cause unwanted side effects, including breaking all existing federation.
|
||||||
|
@ -74,7 +84,6 @@ SMTP_PORT=587
|
||||||
SMTP_LOGIN=$SMTP_LOGIN
|
SMTP_LOGIN=$SMTP_LOGIN
|
||||||
SMTP_PASSWORD=$SMTP_PASSWORD
|
SMTP_PASSWORD=$SMTP_PASSWORD
|
||||||
SMTP_FROM_ADDRESS=notifications@${APP_NAME}.nanoapp.io
|
SMTP_FROM_ADDRESS=notifications@${APP_NAME}.nanoapp.io
|
||||||
#SMTP_REPLY_TO=
|
|
||||||
#SMTP_DOMAIN= # defaults to LOCAL_DOMAIN
|
#SMTP_DOMAIN= # defaults to LOCAL_DOMAIN
|
||||||
#SMTP_DELIVERY_METHOD=smtp # delivery method can also be sendmail
|
#SMTP_DELIVERY_METHOD=smtp # delivery method can also be sendmail
|
||||||
#SMTP_AUTH_METHOD=plain
|
#SMTP_AUTH_METHOD=plain
|
||||||
|
@ -88,17 +97,9 @@ SMTP_FROM_ADDRESS=notifications@${APP_NAME}.nanoapp.io
|
||||||
# PAPERCLIP_ROOT_URL=/system
|
# PAPERCLIP_ROOT_URL=/system
|
||||||
|
|
||||||
# Optional asset host for multi-server setups
|
# Optional asset host for multi-server setups
|
||||||
# The asset host must allow cross origin request from WEB_DOMAIN or LOCAL_DOMAIN
|
|
||||||
# if WEB_DOMAIN is not set. For example, the server may have the
|
|
||||||
# following header field:
|
|
||||||
# Access-Control-Allow-Origin: https://example.com/
|
|
||||||
# CDN_HOST=https://assets.example.com
|
# CDN_HOST=https://assets.example.com
|
||||||
|
|
||||||
# S3 (optional)
|
# S3 (optional)
|
||||||
# The attachment host must allow cross origin request from WEB_DOMAIN or
|
|
||||||
# LOCAL_DOMAIN if WEB_DOMAIN is not set. For example, the server may have the
|
|
||||||
# following header field:
|
|
||||||
# Access-Control-Allow-Origin: https://192.168.1.123:9000/
|
|
||||||
# S3_ENABLED=true
|
# S3_ENABLED=true
|
||||||
# S3_BUCKET=
|
# S3_BUCKET=
|
||||||
# AWS_ACCESS_KEY_ID=
|
# AWS_ACCESS_KEY_ID=
|
||||||
|
@ -108,8 +109,6 @@ SMTP_FROM_ADDRESS=notifications@${APP_NAME}.nanoapp.io
|
||||||
# S3_HOSTNAME=192.168.1.123:9000
|
# S3_HOSTNAME=192.168.1.123:9000
|
||||||
|
|
||||||
# S3 (Minio Config (optional) Please check Minio instance for details)
|
# S3 (Minio Config (optional) Please check Minio instance for details)
|
||||||
# The attachment host must allow cross origin request - see the description
|
|
||||||
# above.
|
|
||||||
# S3_ENABLED=true
|
# S3_ENABLED=true
|
||||||
# S3_BUCKET=
|
# S3_BUCKET=
|
||||||
# AWS_ACCESS_KEY_ID=
|
# AWS_ACCESS_KEY_ID=
|
||||||
|
@ -120,30 +119,12 @@ SMTP_FROM_ADDRESS=notifications@${APP_NAME}.nanoapp.io
|
||||||
# S3_ENDPOINT=
|
# S3_ENDPOINT=
|
||||||
# S3_SIGNATURE_VERSION=
|
# S3_SIGNATURE_VERSION=
|
||||||
|
|
||||||
# Google Cloud Storage (optional)
|
|
||||||
# Use S3 compatible API. Since GCS does not support Multipart Upload,
|
|
||||||
# increase the value of S3_MULTIPART_THRESHOLD to disable Multipart Upload.
|
|
||||||
# The attachment host must allow cross origin request - see the description
|
|
||||||
# above.
|
|
||||||
# S3_ENABLED=true
|
|
||||||
# AWS_ACCESS_KEY_ID=
|
|
||||||
# AWS_SECRET_ACCESS_KEY=
|
|
||||||
# S3_REGION=
|
|
||||||
# S3_PROTOCOL=https
|
|
||||||
# S3_HOSTNAME=storage.googleapis.com
|
|
||||||
# S3_ENDPOINT=https://storage.googleapis.com
|
|
||||||
# S3_MULTIPART_THRESHOLD=52428801 # 50.megabytes
|
|
||||||
|
|
||||||
# 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=
|
||||||
|
@ -190,8 +171,8 @@ SMTP_FROM_ADDRESS=notifications@${APP_NAME}.nanoapp.io
|
||||||
# The pam environment variable "email" is provided by:
|
# The pam environment variable "email" is provided by:
|
||||||
# https://github.com/devkral/pam_email_extractor
|
# https://github.com/devkral/pam_email_extractor
|
||||||
# PAM_ENABLED=true
|
# PAM_ENABLED=true
|
||||||
# Fallback email domain for email address generation (LOCAL_DOMAIN by default)
|
# Fallback Suffix for email address generation (nil by default)
|
||||||
# PAM_EMAIL_DOMAIN=example.com
|
# PAM_DEFAULT_SUFFIX=pam
|
||||||
# Name of the pam service (pam "auth" section is evaluated)
|
# Name of the pam service (pam "auth" section is evaluated)
|
||||||
# PAM_DEFAULT_SERVICE=rpam
|
# 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)
|
# Name of the pam service used for checking if an user can register (pam "account" section is evaluated) (nil (disabled) by default)
|
||||||
|
@ -239,14 +220,7 @@ SMTP_FROM_ADDRESS=notifications@${APP_NAME}.nanoapp.io
|
||||||
# SAML_SECURITY_ASSUME_EMAIL_IS_VERIFIED=true
|
# SAML_SECURITY_ASSUME_EMAIL_IS_VERIFIED=true
|
||||||
# SAML_ATTRIBUTES_STATEMENTS_UID="urn:oid:0.9.2342.19200300.100.1.1"
|
# 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_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_FULL_NAME="urn:oid:2.5.4.42"
|
||||||
# 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_UID_ATTRIBUTE="urn:oid:0.9.2342.19200300.100.1.1"
|
||||||
# SAML_ATTRIBUTES_STATEMENTS_VERIFIED=
|
# SAML_ATTRIBUTES_STATEMENTS_VERIFIED=
|
||||||
# SAML_ATTRIBUTES_STATEMENTS_VERIFIED_EMAIL=
|
# SAML_ATTRIBUTES_STATEMENTS_VERIFIED_EMAIL=
|
||||||
|
|
||||||
# Use HTTP proxy for outgoing request (optional)
|
|
||||||
# http_proxy=http://gateway.local:8118
|
|
||||||
# Access control for hidden service.
|
|
||||||
# ALLOW_ACCESS_TO_HIDDEN_SERVICE=true
|
|
||||||
|
|
|
@ -10,7 +10,6 @@ DB_NAME=postgres
|
||||||
DB_PASS=
|
DB_PASS=
|
||||||
DB_PORT=5432
|
DB_PORT=5432
|
||||||
# Optional ElasticSearch configuration
|
# Optional ElasticSearch configuration
|
||||||
# You may also set ES_PREFIX to share the same cluster between multiple Mastodon servers (falls back to REDIS_NAMESPACE if not set)
|
|
||||||
# ES_ENABLED=true
|
# ES_ENABLED=true
|
||||||
# ES_HOST=es
|
# ES_HOST=es
|
||||||
# ES_PORT=9200
|
# ES_PORT=9200
|
||||||
|
@ -69,7 +68,6 @@ SMTP_PORT=587
|
||||||
SMTP_LOGIN=
|
SMTP_LOGIN=
|
||||||
SMTP_PASSWORD=
|
SMTP_PASSWORD=
|
||||||
SMTP_FROM_ADDRESS=notifications@example.com
|
SMTP_FROM_ADDRESS=notifications@example.com
|
||||||
#SMTP_REPLY_TO=
|
|
||||||
#SMTP_DOMAIN= # defaults to LOCAL_DOMAIN
|
#SMTP_DOMAIN= # defaults to LOCAL_DOMAIN
|
||||||
#SMTP_DELIVERY_METHOD=smtp # delivery method can also be sendmail
|
#SMTP_DELIVERY_METHOD=smtp # delivery method can also be sendmail
|
||||||
#SMTP_AUTH_METHOD=plain
|
#SMTP_AUTH_METHOD=plain
|
||||||
|
@ -115,20 +113,6 @@ SMTP_FROM_ADDRESS=notifications@example.com
|
||||||
# S3_ENDPOINT=
|
# S3_ENDPOINT=
|
||||||
# S3_SIGNATURE_VERSION=
|
# S3_SIGNATURE_VERSION=
|
||||||
|
|
||||||
# Google Cloud Storage (optional)
|
|
||||||
# Use S3 compatible API. Since GCS does not support Multipart Upload,
|
|
||||||
# increase the value of S3_MULTIPART_THRESHOLD to disable Multipart Upload.
|
|
||||||
# The attachment host must allow cross origin request - see the description
|
|
||||||
# above.
|
|
||||||
# S3_ENABLED=true
|
|
||||||
# AWS_ACCESS_KEY_ID=
|
|
||||||
# AWS_SECRET_ACCESS_KEY=
|
|
||||||
# S3_REGION=
|
|
||||||
# S3_PROTOCOL=https
|
|
||||||
# S3_HOSTNAME=storage.googleapis.com
|
|
||||||
# S3_ENDPOINT=https://storage.googleapis.com
|
|
||||||
# S3_MULTIPART_THRESHOLD=52428801 # 50.megabytes
|
|
||||||
|
|
||||||
# Swift (optional)
|
# Swift (optional)
|
||||||
# The attachment host must allow cross origin request - see the description
|
# The attachment host must allow cross origin request - see the description
|
||||||
# above.
|
# above.
|
||||||
|
@ -178,7 +162,7 @@ STREAMING_CLUSTER_NUM=1
|
||||||
# LDAP_BIND_DN=
|
# LDAP_BIND_DN=
|
||||||
# LDAP_PASSWORD=
|
# LDAP_PASSWORD=
|
||||||
# LDAP_UID=cn
|
# LDAP_UID=cn
|
||||||
# LDAP_SEARCH_FILTER=%{uid}=%{email}
|
# LDAP_SEARCH_FILTER="%{uid}=%{email}"
|
||||||
|
|
||||||
# PAM authentication (optional)
|
# PAM authentication (optional)
|
||||||
# PAM authentication uses for the email generation the "email" pam variable
|
# PAM authentication uses for the email generation the "email" pam variable
|
||||||
|
|
|
@ -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/*
|
||||||
|
|
204
.eslintrc.js
204
.eslintrc.js
|
@ -1,204 +0,0 @@
|
||||||
module.exports = {
|
|
||||||
root: true,
|
|
||||||
|
|
||||||
env: {
|
|
||||||
browser: true,
|
|
||||||
node: true,
|
|
||||||
es6: true,
|
|
||||||
jest: true,
|
|
||||||
},
|
|
||||||
|
|
||||||
globals: {
|
|
||||||
ATTACHMENT_HOST: false,
|
|
||||||
},
|
|
||||||
|
|
||||||
parser: 'babel-eslint',
|
|
||||||
|
|
||||||
plugins: [
|
|
||||||
'react',
|
|
||||||
'jsx-a11y',
|
|
||||||
'import',
|
|
||||||
'promise',
|
|
||||||
],
|
|
||||||
|
|
||||||
parserOptions: {
|
|
||||||
sourceType: 'module',
|
|
||||||
ecmaFeatures: {
|
|
||||||
experimentalObjectRestSpread: true,
|
|
||||||
jsx: true,
|
|
||||||
},
|
|
||||||
ecmaVersion: 2018,
|
|
||||||
},
|
|
||||||
|
|
||||||
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-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',
|
|
||||||
},
|
|
||||||
};
|
|
|
@ -0,0 +1,170 @@
|
||||||
|
---
|
||||||
|
root: true
|
||||||
|
|
||||||
|
env:
|
||||||
|
browser: true
|
||||||
|
node: true
|
||||||
|
es6: true
|
||||||
|
jest: true
|
||||||
|
|
||||||
|
globals:
|
||||||
|
ATTACHMENT_HOST: false
|
||||||
|
|
||||||
|
parser: babel-eslint
|
||||||
|
|
||||||
|
plugins:
|
||||||
|
- react
|
||||||
|
- jsx-a11y
|
||||||
|
- import
|
||||||
|
- promise
|
||||||
|
|
||||||
|
parserOptions:
|
||||||
|
sourceType: module
|
||||||
|
ecmaFeatures:
|
||||||
|
experimentalObjectRestSpread: true
|
||||||
|
jsx: true
|
||||||
|
ecmaVersion: 2018
|
||||||
|
|
||||||
|
settings:
|
||||||
|
import/extensions:
|
||||||
|
- .js
|
||||||
|
import/ignore:
|
||||||
|
- node_modules
|
||||||
|
- \\.(css|scss|json)$
|
||||||
|
|
||||||
|
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/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
|
|
@ -1,2 +0,0 @@
|
||||||
patreon: mastodon
|
|
||||||
open_collective: mastodon
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
plugins:
|
||||||
|
postcss-smart-import: {}
|
||||||
|
precss: {}
|
||||||
|
autoprefixer:
|
||||||
|
browsers:
|
||||||
|
- last 2 versions
|
||||||
|
- IE >= 11
|
||||||
|
- iOS >= 9
|
||||||
|
postcss-object-fit-images: {}
|
|
@ -1,6 +1,3 @@
|
||||||
require:
|
|
||||||
- rubocop-rails
|
|
||||||
|
|
||||||
AllCops:
|
AllCops:
|
||||||
TargetRubyVersion: 2.3
|
TargetRubyVersion: 2.3
|
||||||
Exclude:
|
Exclude:
|
||||||
|
@ -83,10 +80,7 @@ Rails/HttpStatus:
|
||||||
Rails/Exit:
|
Rails/Exit:
|
||||||
Exclude:
|
Exclude:
|
||||||
- 'lib/mastodon/*'
|
- 'lib/mastodon/*'
|
||||||
- 'lib/cli.rb'
|
- 'lib/cli'
|
||||||
|
|
||||||
Rails/HelperInstanceVariable:
|
|
||||||
Enabled: false
|
|
||||||
|
|
||||||
Style/ClassAndModuleChildren:
|
Style/ClassAndModuleChildren:
|
||||||
Enabled: false
|
Enabled: false
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
2.6.5
|
2.5.3
|
||||||
|
|
|
@ -1,37 +0,0 @@
|
||||||
# Linter Documentation:
|
|
||||||
# https://github.com/sasstools/sass-lint/tree/v1.13.1/docs/options
|
|
||||||
|
|
||||||
files:
|
|
||||||
include: app/javascript/styles/**/*.scss
|
|
||||||
ignore:
|
|
||||||
- app/javascript/styles/mastodon/reset.scss
|
|
||||||
|
|
||||||
rules:
|
|
||||||
# Disallows
|
|
||||||
no-color-literals: 0
|
|
||||||
no-css-comments: 0
|
|
||||||
no-duplicate-properties: 0
|
|
||||||
no-ids: 0
|
|
||||||
no-important: 0
|
|
||||||
no-mergeable-selectors: 0
|
|
||||||
no-misspelled-properties: 0
|
|
||||||
no-qualifying-elements: 0
|
|
||||||
no-transition-all: 0
|
|
||||||
no-vendor-prefixes: 0
|
|
||||||
|
|
||||||
# Nesting
|
|
||||||
force-element-nesting: 0
|
|
||||||
force-attribute-nesting: 0
|
|
||||||
force-pseudo-nesting: 0
|
|
||||||
|
|
||||||
# Name Formats
|
|
||||||
class-name-format: 0
|
|
||||||
leading-zero: 0
|
|
||||||
|
|
||||||
# Style Guide
|
|
||||||
attribute-quotes: 0
|
|
||||||
hex-length: 0
|
|
||||||
indentation: 0
|
|
||||||
nesting-depth: 0
|
|
||||||
property-sort-order: 0
|
|
||||||
quotes: 0
|
|
|
@ -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
|
|
@ -43,4 +43,4 @@ Gruntfile.js
|
||||||
|
|
||||||
# for specific ignore
|
# for specific ignore
|
||||||
!.svgo.yml
|
!.svgo.yml
|
||||||
!sass-lint/**/*.yml
|
|
||||||
|
|
442
AUTHORS.md
442
AUTHORS.md
|
@ -1,104 +1,86 @@
|
||||||
Authors
|
|
||||||
=======
|
|
||||||
|
|
||||||
Mastodon is available on [GitHub](https://github.com/tootsuite/mastodon)
|
Mastodon is available on [GitHub](https://github.com/tootsuite/mastodon)
|
||||||
and provided thanks to the work of the following contributors:
|
and provided thanks to the work of the following contributors:
|
||||||
|
|
||||||
* [Gargron](https://github.com/Gargron)
|
* [Gargron](https://github.com/Gargron)
|
||||||
* [ThibG](https://github.com/ThibG)
|
|
||||||
* [ykzts](https://github.com/ykzts)
|
* [ykzts](https://github.com/ykzts)
|
||||||
* [dependabot[bot]](https://github.com/apps/dependabot)
|
|
||||||
* [akihikodaki](https://github.com/akihikodaki)
|
* [akihikodaki](https://github.com/akihikodaki)
|
||||||
* [dependabot-preview[bot]](https://github.com/apps/dependabot-preview)
|
|
||||||
* [mjankowski](https://github.com/mjankowski)
|
* [mjankowski](https://github.com/mjankowski)
|
||||||
|
* [ThibG](https://github.com/ThibG)
|
||||||
* [unarist](https://github.com/unarist)
|
* [unarist](https://github.com/unarist)
|
||||||
|
* [m4sk1n](https://github.com/m4sk1n)
|
||||||
* [yiskah](https://github.com/yiskah)
|
* [yiskah](https://github.com/yiskah)
|
||||||
* [nolanlawson](https://github.com/nolanlawson)
|
* [nolanlawson](https://github.com/nolanlawson)
|
||||||
* [ysksn](https://github.com/ysksn)
|
|
||||||
* [abcang](https://github.com/abcang)
|
|
||||||
* [sorin-davidoi](https://github.com/sorin-davidoi)
|
* [sorin-davidoi](https://github.com/sorin-davidoi)
|
||||||
|
* [abcang](https://github.com/abcang)
|
||||||
* [lynlynlynx](https://github.com/lynlynlynx)
|
* [lynlynlynx](https://github.com/lynlynlynx)
|
||||||
* [mayaeh](https://github.com/mayaeh)
|
* [dependabot[bot]](https://github.com/apps/dependabot)
|
||||||
* [m4sk1n](mailto:me@m4sk.in)
|
|
||||||
* [Marcin Mikołajczak](mailto:me@m4sk.in)
|
|
||||||
* [Kjwon15](https://github.com/Kjwon15)
|
|
||||||
* [renatolond](https://github.com/renatolond)
|
|
||||||
* [alpaca-tc](https://github.com/alpaca-tc)
|
* [alpaca-tc](https://github.com/alpaca-tc)
|
||||||
* [jeroenpraat](https://github.com/jeroenpraat)
|
|
||||||
* [nclm](https://github.com/nclm)
|
* [nclm](https://github.com/nclm)
|
||||||
* [ineffyble](https://github.com/ineffyble)
|
* [ineffyble](https://github.com/ineffyble)
|
||||||
* [mabkenar](https://github.com/mabkenar)
|
* [renatolond](https://github.com/renatolond)
|
||||||
|
* [jeroenpraat](https://github.com/jeroenpraat)
|
||||||
|
* [mayaeh](https://github.com/mayaeh)
|
||||||
* [blackle](https://github.com/blackle)
|
* [blackle](https://github.com/blackle)
|
||||||
* [Quent-in](https://github.com/Quent-in)
|
* [Quent-in](https://github.com/Quent-in)
|
||||||
* [JantsoP](https://github.com/JantsoP)
|
* [JantsoP](https://github.com/JantsoP)
|
||||||
* [zunda](https://github.com/zunda)
|
|
||||||
* [nullkal](https://github.com/nullkal)
|
* [nullkal](https://github.com/nullkal)
|
||||||
* [yookoala](https://github.com/yookoala)
|
* [yookoala](https://github.com/yookoala)
|
||||||
* [Aditoo17](https://github.com/Aditoo17)
|
* [mabkenar](https://github.com/mabkenar)
|
||||||
* [Quenty31](https://github.com/Quenty31)
|
* [ysksn](https://github.com/ysksn)
|
||||||
* [marek-lach](https://github.com/marek-lach)
|
|
||||||
* [shuheiktgw](https://github.com/shuheiktgw)
|
* [shuheiktgw](https://github.com/shuheiktgw)
|
||||||
* [ashfurrow](https://github.com/ashfurrow)
|
* [ashfurrow](https://github.com/ashfurrow)
|
||||||
|
* [Kjwon15](https://github.com/Kjwon15)
|
||||||
|
* [zunda](https://github.com/zunda)
|
||||||
* [eramdam](https://github.com/eramdam)
|
* [eramdam](https://github.com/eramdam)
|
||||||
* [noellabo](https://github.com/noellabo)
|
|
||||||
* [takayamaki](https://github.com/takayamaki)
|
|
||||||
* [danhunsaker](https://github.com/danhunsaker)
|
|
||||||
* [masarakki](https://github.com/masarakki)
|
* [masarakki](https://github.com/masarakki)
|
||||||
|
* [takayamaki](https://github.com/takayamaki)
|
||||||
* [ticky](https://github.com/ticky)
|
* [ticky](https://github.com/ticky)
|
||||||
|
* [Quenty31](https://github.com/Quenty31)
|
||||||
|
* [danhunsaker](https://github.com/danhunsaker)
|
||||||
* [ThisIsMissEm](https://github.com/ThisIsMissEm)
|
* [ThisIsMissEm](https://github.com/ThisIsMissEm)
|
||||||
* [hcmiya](https://github.com/hcmiya)
|
* [hcmiya](https://github.com/hcmiya)
|
||||||
* [stephenburgess8](https://github.com/stephenburgess8)
|
* [stephenburgess8](https://github.com/stephenburgess8)
|
||||||
* [Wonderfall](https://github.com/Wonderfall)
|
* [Wonderfall](https://github.com/Wonderfall)
|
||||||
* [matteoaquila](https://github.com/matteoaquila)
|
* [matteoaquila](https://github.com/matteoaquila)
|
||||||
* [yukimochi](https://github.com/yukimochi)
|
|
||||||
* [palindromordnilap](https://github.com/palindromordnilap)
|
|
||||||
* [rkarabut](https://github.com/rkarabut)
|
* [rkarabut](https://github.com/rkarabut)
|
||||||
|
* [yukimochi](https://github.com/yukimochi)
|
||||||
* [Artoria2e5](https://github.com/Artoria2e5)
|
* [Artoria2e5](https://github.com/Artoria2e5)
|
||||||
* [nightpool](https://github.com/nightpool)
|
|
||||||
* [marrus-sh](https://github.com/marrus-sh)
|
* [marrus-sh](https://github.com/marrus-sh)
|
||||||
* [hinaloe](https://github.com/hinaloe)
|
|
||||||
* [krainboltgreene](https://github.com/krainboltgreene)
|
* [krainboltgreene](https://github.com/krainboltgreene)
|
||||||
* [pfigel](https://github.com/pfigel)
|
* [patf](https://github.com/patf)
|
||||||
* [Aldarone](https://github.com/Aldarone)
|
* [Aldarone](https://github.com/Aldarone)
|
||||||
* [BoFFire](https://github.com/BoFFire)
|
* [BoFFire](https://github.com/BoFFire)
|
||||||
* [clworld](https://github.com/clworld)
|
* [clworld](https://github.com/clworld)
|
||||||
* [MasterGroosha](https://github.com/MasterGroosha)
|
|
||||||
* [dracos](https://github.com/dracos)
|
* [dracos](https://github.com/dracos)
|
||||||
* [MaciekBaron](https://github.com/MaciekBaron)
|
|
||||||
* [SerCom_KC](mailto:sercom-kc@users.noreply.github.com)
|
* [SerCom_KC](mailto:sercom-kc@users.noreply.github.com)
|
||||||
* [Sylvhem](https://github.com/Sylvhem)
|
* [Sylvhem](https://github.com/Sylvhem)
|
||||||
* [MitarashiDango](https://github.com/MitarashiDango)
|
* [nightpool](https://github.com/nightpool)
|
||||||
|
* [MasterGroosha](https://github.com/MasterGroosha)
|
||||||
* [JeanGauthier](https://github.com/JeanGauthier)
|
* [JeanGauthier](https://github.com/JeanGauthier)
|
||||||
* [kschaper](https://github.com/kschaper)
|
* [kschaper](https://github.com/kschaper)
|
||||||
|
* [MaciekBaron](https://github.com/MaciekBaron)
|
||||||
|
* [MitarashiDango](mailto:mitarashidango@users.noreply.github.com)
|
||||||
* [beatrix-bitrot](https://github.com/beatrix-bitrot)
|
* [beatrix-bitrot](https://github.com/beatrix-bitrot)
|
||||||
* [angristan](https://github.com/angristan)
|
|
||||||
* [adbelle](https://github.com/adbelle)
|
* [adbelle](https://github.com/adbelle)
|
||||||
* [evanminto](https://github.com/evanminto)
|
* [evanminto](https://github.com/evanminto)
|
||||||
* [MightyPork](https://github.com/MightyPork)
|
* [MightyPork](https://github.com/MightyPork)
|
||||||
* [ashleyhull-versent](mailto:ashley.hull@versent.com.au)
|
|
||||||
* [yhirano55](https://github.com/yhirano55)
|
* [yhirano55](https://github.com/yhirano55)
|
||||||
* [rinsuki](https://github.com/rinsuki)
|
|
||||||
* [camponez](https://github.com/camponez)
|
* [camponez](https://github.com/camponez)
|
||||||
* [SerCom_KC](mailto:szescxz@gmail.com)
|
* [SerCom-KC](https://github.com/SerCom-KC)
|
||||||
* [aschmitz](https://github.com/aschmitz)
|
* [aschmitz](https://github.com/aschmitz)
|
||||||
* [trwnh](https://github.com/trwnh)
|
|
||||||
* [devkral](https://github.com/devkral)
|
* [devkral](https://github.com/devkral)
|
||||||
* [fpiesche](https://github.com/fpiesche)
|
* [fpiesche](https://github.com/fpiesche)
|
||||||
* [hugogameiro](https://github.com/hugogameiro)
|
|
||||||
* [gandaro](https://github.com/gandaro)
|
* [gandaro](https://github.com/gandaro)
|
||||||
* [johnsudaar](https://github.com/johnsudaar)
|
* [johnsudaar](https://github.com/johnsudaar)
|
||||||
* [ariasuni](https://github.com/ariasuni)
|
|
||||||
* [trebmuh](https://github.com/trebmuh)
|
* [trebmuh](https://github.com/trebmuh)
|
||||||
* [rmhasan](https://github.com/rmhasan)
|
* [Rakib Hasan](mailto:rmhasan@gmail.com)
|
||||||
* [kedamaDQ](https://github.com/kedamaDQ)
|
|
||||||
* [lindwurm](https://github.com/lindwurm)
|
* [lindwurm](https://github.com/lindwurm)
|
||||||
* [victorhck](mailto:victorhck@geeko.site)
|
* [victorhck](mailto:victorhck@geeko.site)
|
||||||
* [voidsatisfaction](https://github.com/voidsatisfaction)
|
* [voidsatisfaction](https://github.com/voidsatisfaction)
|
||||||
* [BenLubar](https://github.com/BenLubar)
|
|
||||||
* [hikari-no-yume](https://github.com/hikari-no-yume)
|
* [hikari-no-yume](https://github.com/hikari-no-yume)
|
||||||
|
* [angristan](https://github.com/angristan)
|
||||||
* [seefood](https://github.com/seefood)
|
* [seefood](https://github.com/seefood)
|
||||||
* [jackjennings](https://github.com/jackjennings)
|
* [jackjennings](https://github.com/jackjennings)
|
||||||
* [koyuawsmbrtn](https://github.com/koyuawsmbrtn)
|
|
||||||
* [spla](mailto:spla@mastodont.cat)
|
* [spla](mailto:spla@mastodont.cat)
|
||||||
* [expenses](https://github.com/expenses)
|
* [expenses](https://github.com/expenses)
|
||||||
* [walf443](https://github.com/walf443)
|
* [walf443](https://github.com/walf443)
|
||||||
|
@ -108,29 +90,26 @@ and provided thanks to the work of the following contributors:
|
||||||
* [xqus](https://github.com/xqus)
|
* [xqus](https://github.com/xqus)
|
||||||
* [pfm-eyesightjp](https://github.com/pfm-eyesightjp)
|
* [pfm-eyesightjp](https://github.com/pfm-eyesightjp)
|
||||||
* [fakenine](https://github.com/fakenine)
|
* [fakenine](https://github.com/fakenine)
|
||||||
* [Shleeble](https://github.com/Shleeble)
|
|
||||||
* [tsuwatch](https://github.com/tsuwatch)
|
* [tsuwatch](https://github.com/tsuwatch)
|
||||||
* [victorhck](https://github.com/victorhck)
|
* [victorhck](https://github.com/victorhck)
|
||||||
* [mkljczk](https://github.com/mkljczk)
|
|
||||||
* [manuelviens](https://github.com/manuelviens)
|
|
||||||
* [puckipedia](https://github.com/puckipedia)
|
* [puckipedia](https://github.com/puckipedia)
|
||||||
* [fvh-P](https://github.com/fvh-P)
|
* [fvh-P](https://github.com/fvh-P)
|
||||||
* [rtucker](https://github.com/rtucker)
|
* [contraexemplo](https://github.com/contraexemplo)
|
||||||
* [Anna e só](mailto:contraexemplos@gmail.com)
|
* [hugogameiro](https://github.com/hugogameiro)
|
||||||
* [kazu9su](https://github.com/kazu9su)
|
* [kazu9su](https://github.com/kazu9su)
|
||||||
* [Komic](https://github.com/Komic)
|
* [Komic](https://github.com/Komic)
|
||||||
* [lmorchard](https://github.com/lmorchard)
|
|
||||||
* [diomed](https://github.com/diomed)
|
* [diomed](https://github.com/diomed)
|
||||||
|
* [ariasuni](https://github.com/ariasuni)
|
||||||
* [Neetshin](mailto:neetshin@neetsh.in)
|
* [Neetshin](mailto:neetshin@neetsh.in)
|
||||||
* [rainyday](https://github.com/rainyday)
|
* [rainyday](https://github.com/rainyday)
|
||||||
* [ProgVal](https://github.com/ProgVal)
|
* [ProgVal](https://github.com/ProgVal)
|
||||||
* [valentin2105](https://github.com/valentin2105)
|
* [valentin2105](https://github.com/valentin2105)
|
||||||
* [yuntan](https://github.com/yuntan)
|
* [yuntan](https://github.com/yuntan)
|
||||||
|
* [ashleyhull-versent](https://github.com/ashleyhull-versent)
|
||||||
* [goofy-bz](mailto:goofy@babelzilla.org)
|
* [goofy-bz](mailto:goofy@babelzilla.org)
|
||||||
* [kadiix](https://github.com/kadiix)
|
* [kadiix](https://github.com/kadiix)
|
||||||
* [kodacs](https://github.com/kodacs)
|
* [kodacs](https://github.com/kodacs)
|
||||||
* [marcin mikołajczak](mailto:me@m4sk.in)
|
* [rtucker](https://github.com/rtucker)
|
||||||
* [JMendyk](https://github.com/JMendyk)
|
|
||||||
* [KScl](https://github.com/KScl)
|
* [KScl](https://github.com/KScl)
|
||||||
* [sterdev](https://github.com/sterdev)
|
* [sterdev](https://github.com/sterdev)
|
||||||
* [TheKinrar](https://github.com/TheKinrar)
|
* [TheKinrar](https://github.com/TheKinrar)
|
||||||
|
@ -140,139 +119,114 @@ and provided thanks to the work of the following contributors:
|
||||||
* [northerner](https://github.com/northerner)
|
* [northerner](https://github.com/northerner)
|
||||||
* [fhemberger](https://github.com/fhemberger)
|
* [fhemberger](https://github.com/fhemberger)
|
||||||
* [greysteil](https://github.com/greysteil)
|
* [greysteil](https://github.com/greysteil)
|
||||||
* [hencatsmith](https://github.com/hencatsmith)
|
* [hnrysmth](https://github.com/hnrysmth)
|
||||||
* [d6rkaiz](https://github.com/d6rkaiz)
|
* [d6rkaiz](https://github.com/d6rkaiz)
|
||||||
* [Reverite](https://github.com/Reverite)
|
* [JMendyk](https://github.com/JMendyk)
|
||||||
* [JohnD28](https://github.com/JohnD28)
|
* [JohnD28](https://github.com/JohnD28)
|
||||||
* [znz](https://github.com/znz)
|
* [znz](https://github.com/znz)
|
||||||
* [Naouak](https://github.com/Naouak)
|
* [Naouak](https://github.com/Naouak)
|
||||||
* [pawelngei](https://github.com/pawelngei)
|
|
||||||
* [reneklacan](https://github.com/reneklacan)
|
* [reneklacan](https://github.com/reneklacan)
|
||||||
* [ekiru](https://github.com/ekiru)
|
* [ekiru](https://github.com/ekiru)
|
||||||
* [tcitworld](https://github.com/tcitworld)
|
* [tcitworld](https://github.com/tcitworld)
|
||||||
* [geta6](https://github.com/geta6)
|
* [geta6](https://github.com/geta6)
|
||||||
* [happycoloredbanana](https://github.com/happycoloredbanana)
|
* [happycoloredbanana](https://github.com/happycoloredbanana)
|
||||||
|
* [kedamaDQ](https://github.com/kedamaDQ)
|
||||||
* [leopku](https://github.com/leopku)
|
* [leopku](https://github.com/leopku)
|
||||||
* [SansPseudoFix](https://github.com/SansPseudoFix)
|
* [SansPseudoFix](https://github.com/SansPseudoFix)
|
||||||
* [salvadorpla](https://github.com/salvadorpla)
|
|
||||||
* [tomfhowe](https://github.com/tomfhowe)
|
* [tomfhowe](https://github.com/tomfhowe)
|
||||||
* [noraworld](https://github.com/noraworld)
|
* [noraworld](https://github.com/noraworld)
|
||||||
* [theboss](https://github.com/theboss)
|
* [theboss](https://github.com/theboss)
|
||||||
* [nzws](https://github.com/nzws)
|
|
||||||
* [178inaba](https://github.com/178inaba)
|
* [178inaba](https://github.com/178inaba)
|
||||||
* [xgess](https://github.com/xgess)
|
|
||||||
* [alyssais](https://github.com/alyssais)
|
* [alyssais](https://github.com/alyssais)
|
||||||
* [aablinov](https://github.com/aablinov)
|
* [kodnaplakal](https://github.com/kodnaplakal)
|
||||||
* [stalker314314](https://github.com/stalker314314)
|
* [stalker314314](https://github.com/stalker314314)
|
||||||
* [cutls](https://github.com/cutls)
|
|
||||||
* [huertanix](https://github.com/huertanix)
|
* [huertanix](https://github.com/huertanix)
|
||||||
* [genesixx](https://github.com/genesixx)
|
* [genesixx](https://github.com/genesixx)
|
||||||
* [halkeye](https://github.com/halkeye)
|
* [halkeye](https://github.com/halkeye)
|
||||||
|
* [hinaloe](https://github.com/hinaloe)
|
||||||
* [treby](https://github.com/treby)
|
* [treby](https://github.com/treby)
|
||||||
|
* [Reverite](https://github.com/Reverite)
|
||||||
* [jpdevries](https://github.com/jpdevries)
|
* [jpdevries](https://github.com/jpdevries)
|
||||||
* [gdpelican](https://github.com/gdpelican)
|
* [H-C-F](https://github.com/H-C-F)
|
||||||
* [kmichl](https://github.com/kmichl)
|
|
||||||
* [Kurtis Rainbolt-Greene](mailto:me@kurtisrainboltgreene.name)
|
* [Kurtis Rainbolt-Greene](mailto:me@kurtisrainboltgreene.name)
|
||||||
* [saper](https://github.com/saper)
|
* [saper](https://github.com/saper)
|
||||||
* [Dar13](https://github.com/Dar13)
|
|
||||||
* [nevillepark](https://github.com/nevillepark)
|
* [nevillepark](https://github.com/nevillepark)
|
||||||
* [ornithocoder](https://github.com/ornithocoder)
|
* [ornithocoder](https://github.com/ornithocoder)
|
||||||
* [pwoolcoc](https://github.com/pwoolcoc)
|
|
||||||
* [pierreozoux](https://github.com/pierreozoux)
|
* [pierreozoux](https://github.com/pierreozoux)
|
||||||
* [qguv](https://github.com/qguv)
|
* [qguv](https://github.com/qguv)
|
||||||
* [Ram Lmn](mailto:ramlmn@users.noreply.github.com)
|
* [Ram Lmn](mailto:ramlmn@users.noreply.github.com)
|
||||||
* [aurelia-sl](https://github.com/aurelia-sl)
|
|
||||||
* [harukasan](https://github.com/harukasan)
|
* [harukasan](https://github.com/harukasan)
|
||||||
* [stamak](https://github.com/stamak)
|
* [stamak](https://github.com/stamak)
|
||||||
* [Technowix](https://github.com/Technowix)
|
* [Technowix](mailto:technowix@users.noreply.github.com)
|
||||||
* [Zoeille](https://github.com/Zoeille)
|
* [Eychics](https://github.com/Eychics)
|
||||||
* [Thor Harald Johansen](mailto:thj@thj.no)
|
* [Thor Harald Johansen](mailto:thj@thj.no)
|
||||||
* [0x70b1a5](https://github.com/0x70b1a5)
|
* [0x70b1a5](https://github.com/0x70b1a5)
|
||||||
* [gled-rs](https://github.com/gled-rs)
|
* [gled-rs](https://github.com/gled-rs)
|
||||||
* [Valentin_NC](mailto:valentin.ouvrard@nautile.sarl)
|
* [Valentin_NC](mailto:valentin.ouvrard@nautile.sarl)
|
||||||
* [R0ckweb](https://github.com/R0ckweb)
|
* [R0ckweb](https://github.com/R0ckweb)
|
||||||
* [unasuke](https://github.com/unasuke)
|
|
||||||
* [caasi](https://github.com/caasi)
|
* [caasi](https://github.com/caasi)
|
||||||
* [chr-1x](https://github.com/chr-1x)
|
|
||||||
* [esetomo](https://github.com/esetomo)
|
* [esetomo](https://github.com/esetomo)
|
||||||
* [foxiehkins](https://github.com/foxiehkins)
|
* [foxiehkins](https://github.com/foxiehkins)
|
||||||
* [hoodie](mailto:hoodiekitten@outlook.com)
|
* [hoodie](mailto:hoodiekitten@outlook.com)
|
||||||
* [luzi82](https://github.com/luzi82)
|
* [luzi82](https://github.com/luzi82)
|
||||||
* [duxovni](https://github.com/duxovni)
|
* [duxovni](https://github.com/duxovni)
|
||||||
* [slice](https://github.com/slice)
|
* [unsmell](https://github.com/unsmell)
|
||||||
* [tmm576](https://github.com/tmm576)
|
|
||||||
* [unsmell](mailto:unsmell@users.noreply.github.com)
|
|
||||||
* [valerauko](https://github.com/valerauko)
|
|
||||||
* [chriswmartin](https://github.com/chriswmartin)
|
* [chriswmartin](https://github.com/chriswmartin)
|
||||||
* [vahnj](https://github.com/vahnj)
|
* [vahnj](https://github.com/vahnj)
|
||||||
* [ikuradon](https://github.com/ikuradon)
|
* [ikuradon](https://github.com/ikuradon)
|
||||||
* [AndreLewin](https://github.com/AndreLewin)
|
* [AndreLewin](https://github.com/AndreLewin)
|
||||||
* [0xflotus](https://github.com/0xflotus)
|
* [rinsuki](https://github.com/rinsuki)
|
||||||
* [redtachyons](https://github.com/redtachyons)
|
* [redtachyons](https://github.com/redtachyons)
|
||||||
* [acid-chicken](https://github.com/acid-chicken)
|
|
||||||
* [thurloat](https://github.com/thurloat)
|
* [thurloat](https://github.com/thurloat)
|
||||||
* [aaribaud](https://github.com/aaribaud)
|
* [aaribaud](https://github.com/aaribaud)
|
||||||
* [pointlessone](https://github.com/pointlessone)
|
|
||||||
* [Andrew](mailto:andrewlchronister@gmail.com)
|
* [Andrew](mailto:andrewlchronister@gmail.com)
|
||||||
* [aurelien-reeves](https://github.com/aurelien-reeves)
|
|
||||||
* [AnaGelez](https://github.com/AnaGelez)
|
|
||||||
* [estuans](https://github.com/estuans)
|
* [estuans](https://github.com/estuans)
|
||||||
* [dissolve](https://github.com/dissolve)
|
* [dissolve](https://github.com/dissolve)
|
||||||
* [PurpleBooth](https://github.com/PurpleBooth)
|
* [PurpleBooth](https://github.com/PurpleBooth)
|
||||||
* [bradurani](https://github.com/bradurani)
|
* [bradurani](https://github.com/bradurani)
|
||||||
* [wavebeem](https://github.com/wavebeem)
|
* [wavebeem](https://github.com/wavebeem)
|
||||||
* [bruwalfas](https://github.com/bruwalfas)
|
* [bruwalfas](https://github.com/bruwalfas)
|
||||||
* [LottieVixen](https://github.com/LottieVixen)
|
* [foxsan48](https://github.com/foxsan48)
|
||||||
* [wchristian](https://github.com/wchristian)
|
* [wchristian](https://github.com/wchristian)
|
||||||
* [muffinista](https://github.com/muffinista)
|
* [muffinista](https://github.com/muffinista)
|
||||||
* [cdutson](https://github.com/cdutson)
|
* [cdutson](https://github.com/cdutson)
|
||||||
* [farlistener](https://github.com/farlistener)
|
* [farlistener](https://github.com/farlistener)
|
||||||
* [dariusk](https://github.com/dariusk)
|
|
||||||
* [DavidLibeau](https://github.com/DavidLibeau)
|
* [DavidLibeau](https://github.com/DavidLibeau)
|
||||||
* [ddevault](https://github.com/ddevault)
|
* [SirCmpwn](https://github.com/SirCmpwn)
|
||||||
* [Fjoerfoks](https://github.com/Fjoerfoks)
|
* [Fjoerfoks](https://github.com/Fjoerfoks)
|
||||||
* [fmauNeko](https://github.com/fmauNeko)
|
* [fmauNeko](https://github.com/fmauNeko)
|
||||||
* [gloaec](https://github.com/gloaec)
|
* [gloaec](https://github.com/gloaec)
|
||||||
* [Gomasy](https://github.com/Gomasy)
|
* [Gomasy](https://github.com/Gomasy)
|
||||||
* [unstabler](https://github.com/unstabler)
|
* [unstabler](https://github.com/unstabler)
|
||||||
* [potato4d](https://github.com/potato4d)
|
* [potato4d](https://github.com/potato4d)
|
||||||
* [Hanage999](https://github.com/Hanage999)
|
|
||||||
* [h-izumi](https://github.com/h-izumi)
|
* [h-izumi](https://github.com/h-izumi)
|
||||||
* [ErikXXon](https://github.com/ErikXXon)
|
* [ErikXXon](https://github.com/ErikXXon)
|
||||||
* [ian-kelling](https://github.com/ian-kelling)
|
* [ian-kelling](https://github.com/ian-kelling)
|
||||||
* [immae](https://github.com/immae)
|
* [immae](https://github.com/immae)
|
||||||
* [J0WI](https://github.com/J0WI)
|
|
||||||
* [foozmeat](https://github.com/foozmeat)
|
* [foozmeat](https://github.com/foozmeat)
|
||||||
* [jasonrhodes](https://github.com/jasonrhodes)
|
* [jasonrhodes](https://github.com/jasonrhodes)
|
||||||
* [Jason Snell](mailto:jason@newrelic.com)
|
* [Jason Snell](mailto:jason@newrelic.com)
|
||||||
* [jviide](https://github.com/jviide)
|
* [jviide](https://github.com/jviide)
|
||||||
* [YuleZ](https://github.com/YuleZ)
|
|
||||||
* [crakaC](https://github.com/crakaC)
|
* [crakaC](https://github.com/crakaC)
|
||||||
* [tkbky](https://github.com/tkbky)
|
* [tkbky](https://github.com/tkbky)
|
||||||
* [Kaylee](mailto:kaylee@codethat.sucks)
|
* [Kaylee](mailto:kaylee@codethat.sucks)
|
||||||
* [Kazhnuz](https://github.com/Kazhnuz)
|
* [Kazhnuz](https://github.com/Kazhnuz)
|
||||||
* [connyduck](https://github.com/connyduck)
|
* [connyduck](https://github.com/connyduck)
|
||||||
* [LindseyB](https://github.com/LindseyB)
|
* [Lindsey Bieda](mailto:lindseyb@users.noreply.github.com)
|
||||||
* [Lorenz Diener](mailto:halcyon@icosahedron.website)
|
* [Lorenz Diener](mailto:halcyon@icosahedron.website)
|
||||||
* [alimony](https://github.com/alimony)
|
* [alimony](https://github.com/alimony)
|
||||||
* [mig5](https://github.com/mig5)
|
* [mig5](https://github.com/mig5)
|
||||||
* [moritzheiber](https://github.com/moritzheiber)
|
|
||||||
* [ndarville](https://github.com/ndarville)
|
* [ndarville](https://github.com/ndarville)
|
||||||
* [Abzol](https://github.com/Abzol)
|
* [Abzol](https://github.com/Abzol)
|
||||||
* [PatOnTheBack](https://github.com/PatOnTheBack)
|
* [pwoolcoc](https://github.com/pwoolcoc)
|
||||||
* [xPaw](https://github.com/xPaw)
|
* [xPaw](https://github.com/xPaw)
|
||||||
* [petzah](https://github.com/petzah)
|
* [petzah](https://github.com/petzah)
|
||||||
* [ignisf](https://github.com/ignisf)
|
* [ignisf](https://github.com/ignisf)
|
||||||
* [raymestalez](https://github.com/raymestalez)
|
* [raymestalez](https://github.com/raymestalez)
|
||||||
* [remram44](https://github.com/remram44)
|
* [sascha-sl](https://github.com/sascha-sl)
|
||||||
* [sts10](https://github.com/sts10)
|
|
||||||
* [SuperSandro2000](https://github.com/SuperSandro2000)
|
|
||||||
* [u1-liquid](https://github.com/u1-liquid)
|
* [u1-liquid](https://github.com/u1-liquid)
|
||||||
* [rosylilly](https://github.com/rosylilly)
|
|
||||||
* [sim6](https://github.com/sim6)
|
* [sim6](https://github.com/sim6)
|
||||||
* [Sir-Boops](https://github.com/Sir-Boops)
|
|
||||||
* [stemid](https://github.com/stemid)
|
* [stemid](https://github.com/stemid)
|
||||||
* [sumdog](https://github.com/sumdog)
|
|
||||||
* [ThomasLeister](https://github.com/ThomasLeister)
|
* [ThomasLeister](https://github.com/ThomasLeister)
|
||||||
* [mcat-ee](https://github.com/mcat-ee)
|
* [mcat-ee](https://github.com/mcat-ee)
|
||||||
* [tototoshi](https://github.com/tototoshi)
|
* [tototoshi](https://github.com/tototoshi)
|
||||||
|
@ -289,49 +243,49 @@ and provided thanks to the work of the following contributors:
|
||||||
* [aus-social](https://github.com/aus-social)
|
* [aus-social](https://github.com/aus-social)
|
||||||
* [imbsky](https://github.com/imbsky)
|
* [imbsky](https://github.com/imbsky)
|
||||||
* [bsky](mailto:me@imbsky.net)
|
* [bsky](mailto:me@imbsky.net)
|
||||||
|
* [chr-1x](https://github.com/chr-1x)
|
||||||
* [codl](https://github.com/codl)
|
* [codl](https://github.com/codl)
|
||||||
* [cpsdqs](https://github.com/cpsdqs)
|
* [cpsdqs](https://github.com/cpsdqs)
|
||||||
* [barzamin](https://github.com/barzamin)
|
* [barzamin](https://github.com/barzamin)
|
||||||
* [fhalna](https://github.com/fhalna)
|
* [fhalna](https://github.com/fhalna)
|
||||||
* [highemerly](https://github.com/highemerly)
|
|
||||||
* [haoyayoi](https://github.com/haoyayoi)
|
* [haoyayoi](https://github.com/haoyayoi)
|
||||||
* [ik11235](https://github.com/ik11235)
|
* [ik11235](https://github.com/ik11235)
|
||||||
* [kawax](https://github.com/kawax)
|
* [kawax](https://github.com/kawax)
|
||||||
* [007lva](https://github.com/007lva)
|
* [007lva](https://github.com/007lva)
|
||||||
* [mbajur](https://github.com/mbajur)
|
|
||||||
* [matsurai25](https://github.com/matsurai25)
|
* [matsurai25](https://github.com/matsurai25)
|
||||||
* [mecab](https://github.com/mecab)
|
* [mecab](https://github.com/mecab)
|
||||||
* [nicobz25](https://github.com/nicobz25)
|
* [nicobz25](https://github.com/nicobz25)
|
||||||
* [oliverkeeble](https://github.com/oliverkeeble)
|
* [oliverkeeble](https://github.com/oliverkeeble)
|
||||||
* [partev](https://github.com/partev)
|
|
||||||
* [pinfort](https://github.com/pinfort)
|
* [pinfort](https://github.com/pinfort)
|
||||||
* [rbaumert](https://github.com/rbaumert)
|
* [rbaumert](https://github.com/rbaumert)
|
||||||
* [rhoio](https://github.com/rhoio)
|
* [rhoio](https://github.com/rhoio)
|
||||||
|
* [trwnh](https://github.com/trwnh)
|
||||||
* [usagi-f](https://github.com/usagi-f)
|
* [usagi-f](https://github.com/usagi-f)
|
||||||
* [vidarlee](https://github.com/vidarlee)
|
* [vidarlee](https://github.com/vidarlee)
|
||||||
* [vjackson725](https://github.com/vjackson725)
|
* [vjackson725](https://github.com/vjackson725)
|
||||||
* [wxcafe](https://github.com/wxcafe)
|
* [wxcafe](https://github.com/wxcafe)
|
||||||
* [新都心(Neet Shin)](mailto:nucx@dio-vox.com)
|
* [新都心(Neet Shin)](mailto:nucx@dio-vox.com)
|
||||||
* [clarfon](https://github.com/clarfon)
|
|
||||||
* [cygnan](https://github.com/cygnan)
|
* [cygnan](https://github.com/cygnan)
|
||||||
* [Awea](https://github.com/Awea)
|
* [Awea](https://github.com/Awea)
|
||||||
* [halcy](https://github.com/halcy)
|
* [halcy](https://github.com/halcy)
|
||||||
|
* [naaaaaaaaaaaf](https://github.com/naaaaaaaaaaaf)
|
||||||
|
* [NecroTechno](https://github.com/NecroTechno)
|
||||||
* [8398a7](https://github.com/8398a7)
|
* [8398a7](https://github.com/8398a7)
|
||||||
* [857b](https://github.com/857b)
|
* [857b](https://github.com/857b)
|
||||||
* [insom](https://github.com/insom)
|
* [insom](https://github.com/insom)
|
||||||
* [tachyons](https://github.com/tachyons)
|
* [Aditoo17](https://github.com/Aditoo17)
|
||||||
* [Esteth](https://github.com/Esteth)
|
|
||||||
* [unascribed](https://github.com/unascribed)
|
* [unascribed](https://github.com/unascribed)
|
||||||
* [Aguay-val](https://github.com/Aguay-val)
|
* [Aguay-val](https://github.com/Aguay-val)
|
||||||
|
* [Akihiko Odaki](mailto:nekomanma@pixiv.co.jp)
|
||||||
* [knu](https://github.com/knu)
|
* [knu](https://github.com/knu)
|
||||||
* [h3poteto](https://github.com/h3poteto)
|
* [h3poteto](https://github.com/h3poteto)
|
||||||
* [unleashed](https://github.com/unleashed)
|
* [unleashed](https://github.com/unleashed)
|
||||||
* [alxrcs](https://github.com/alxrcs)
|
* [alxrcs](https://github.com/alxrcs)
|
||||||
* [console-cowboy](https://github.com/console-cowboy)
|
* [console-cowboy](https://github.com/console-cowboy)
|
||||||
* [Alkarex](https://github.com/Alkarex)
|
* [pointlessone](https://github.com/pointlessone)
|
||||||
* [a2](https://github.com/a2)
|
* [a2](https://github.com/a2)
|
||||||
* [alfiedotwtf](https://github.com/alfiedotwtf)
|
|
||||||
* [0xa](https://github.com/0xa)
|
* [0xa](https://github.com/0xa)
|
||||||
|
* [palindromordnilap](https://github.com/palindromordnilap)
|
||||||
* [virtualpain](https://github.com/virtualpain)
|
* [virtualpain](https://github.com/virtualpain)
|
||||||
* [sapphirus](https://github.com/sapphirus)
|
* [sapphirus](https://github.com/sapphirus)
|
||||||
* [amandavisconti](https://github.com/amandavisconti)
|
* [amandavisconti](https://github.com/amandavisconti)
|
||||||
|
@ -340,13 +294,12 @@ and provided thanks to the work of the following contributors:
|
||||||
* [Andreas Drop](mailto:andy@remline.de)
|
* [Andreas Drop](mailto:andy@remline.de)
|
||||||
* [andi1984](https://github.com/andi1984)
|
* [andi1984](https://github.com/andi1984)
|
||||||
* [schas002](https://github.com/schas002)
|
* [schas002](https://github.com/schas002)
|
||||||
* [contraexemplo](https://github.com/contraexemplo)
|
|
||||||
* [abackstrom](https://github.com/abackstrom)
|
* [abackstrom](https://github.com/abackstrom)
|
||||||
* [armandfardeau](https://github.com/armandfardeau)
|
|
||||||
* [raboof](https://github.com/raboof)
|
|
||||||
* [jumbosushi](https://github.com/jumbosushi)
|
* [jumbosushi](https://github.com/jumbosushi)
|
||||||
* [ayumin](https://github.com/ayumin)
|
* [ayumin](https://github.com/ayumin)
|
||||||
|
* [BaptisteGelez](https://github.com/BaptisteGelez)
|
||||||
* [bzg](https://github.com/bzg)
|
* [bzg](https://github.com/bzg)
|
||||||
|
* [BenLubar](https://github.com/BenLubar)
|
||||||
* [benediktg](https://github.com/benediktg)
|
* [benediktg](https://github.com/benediktg)
|
||||||
* [blakebarnett](https://github.com/blakebarnett)
|
* [blakebarnett](https://github.com/blakebarnett)
|
||||||
* [bradj](https://github.com/bradj)
|
* [bradj](https://github.com/bradj)
|
||||||
|
@ -359,7 +312,7 @@ and provided thanks to the work of the following contributors:
|
||||||
* [DoubleMalt](https://github.com/DoubleMalt)
|
* [DoubleMalt](https://github.com/DoubleMalt)
|
||||||
* [Moosh-be](https://github.com/Moosh-be)
|
* [Moosh-be](https://github.com/Moosh-be)
|
||||||
* [Motoma](https://github.com/Motoma)
|
* [Motoma](https://github.com/Motoma)
|
||||||
* [Christopher Kolstad](mailto:christopher.kolstad@finn.no)
|
* [chriswk](https://github.com/chriswk)
|
||||||
* [csu](https://github.com/csu)
|
* [csu](https://github.com/csu)
|
||||||
* [kklleemm](https://github.com/kklleemm)
|
* [kklleemm](https://github.com/kklleemm)
|
||||||
* [colindean](https://github.com/colindean)
|
* [colindean](https://github.com/colindean)
|
||||||
|
@ -367,7 +320,6 @@ and provided thanks to the work of the following contributors:
|
||||||
* [multiple-creatures](https://github.com/multiple-creatures)
|
* [multiple-creatures](https://github.com/multiple-creatures)
|
||||||
* [watilde](https://github.com/watilde)
|
* [watilde](https://github.com/watilde)
|
||||||
* [daprice](https://github.com/daprice)
|
* [daprice](https://github.com/daprice)
|
||||||
* [da2x](https://github.com/da2x)
|
|
||||||
* [dar5hak](https://github.com/dar5hak)
|
* [dar5hak](https://github.com/dar5hak)
|
||||||
* [kant](https://github.com/kant)
|
* [kant](https://github.com/kant)
|
||||||
* [maxolasersquad](https://github.com/maxolasersquad)
|
* [maxolasersquad](https://github.com/maxolasersquad)
|
||||||
|
@ -376,7 +328,7 @@ and provided thanks to the work of the following contributors:
|
||||||
* [davefp](https://github.com/davefp)
|
* [davefp](https://github.com/davefp)
|
||||||
* [yipdw](https://github.com/yipdw)
|
* [yipdw](https://github.com/yipdw)
|
||||||
* [debanshuk](https://github.com/debanshuk)
|
* [debanshuk](https://github.com/debanshuk)
|
||||||
* [DerekNonGeneric](https://github.com/DerekNonGeneric)
|
* [Derek Lewis](mailto:derekcecillewis@gmail.com)
|
||||||
* [dblandin](https://github.com/dblandin)
|
* [dblandin](https://github.com/dblandin)
|
||||||
* [Drew Gates](mailto:aranaur@users.noreply.github.com)
|
* [Drew Gates](mailto:aranaur@users.noreply.github.com)
|
||||||
* [dtschust](https://github.com/dtschust)
|
* [dtschust](https://github.com/dtschust)
|
||||||
|
@ -384,17 +336,12 @@ and provided thanks to the work of the following contributors:
|
||||||
* [eai04191](https://github.com/eai04191)
|
* [eai04191](https://github.com/eai04191)
|
||||||
* [d3vgru](https://github.com/d3vgru)
|
* [d3vgru](https://github.com/d3vgru)
|
||||||
* [Elizafox](https://github.com/Elizafox)
|
* [Elizafox](https://github.com/Elizafox)
|
||||||
* [enewhuis](https://github.com/enewhuis)
|
|
||||||
* [ericblade](https://github.com/ericblade)
|
* [ericblade](https://github.com/ericblade)
|
||||||
* [mikoim](https://github.com/mikoim)
|
* [mikoim](https://github.com/mikoim)
|
||||||
* [espenronnevik](https://github.com/espenronnevik)
|
* [espenronnevik](https://github.com/espenronnevik)
|
||||||
* [fabianonline](https://github.com/fabianonline)
|
|
||||||
* [Finariel](https://github.com/Finariel)
|
* [Finariel](https://github.com/Finariel)
|
||||||
* [siuying](https://github.com/siuying)
|
* [siuying](https://github.com/siuying)
|
||||||
* [zoc](https://github.com/zoc)
|
|
||||||
* [fwenzel](https://github.com/fwenzel)
|
|
||||||
* [GenbuHase](https://github.com/GenbuHase)
|
* [GenbuHase](https://github.com/GenbuHase)
|
||||||
* [nilsding](https://github.com/nilsding)
|
|
||||||
* [hattori6789](https://github.com/hattori6789)
|
* [hattori6789](https://github.com/hattori6789)
|
||||||
* [algernon](https://github.com/algernon)
|
* [algernon](https://github.com/algernon)
|
||||||
* [Fastbyte01](https://github.com/Fastbyte01)
|
* [Fastbyte01](https://github.com/Fastbyte01)
|
||||||
|
@ -410,19 +357,17 @@ and provided thanks to the work of the following contributors:
|
||||||
* [suzukaze](https://github.com/suzukaze)
|
* [suzukaze](https://github.com/suzukaze)
|
||||||
* [Hiromi-Kai](https://github.com/Hiromi-Kai)
|
* [Hiromi-Kai](https://github.com/Hiromi-Kai)
|
||||||
* [hishamhm](https://github.com/hishamhm)
|
* [hishamhm](https://github.com/hishamhm)
|
||||||
* [Slaynash](https://github.com/Slaynash)
|
|
||||||
* [musashino205](https://github.com/musashino205)
|
* [musashino205](https://github.com/musashino205)
|
||||||
* [iwaim](https://github.com/iwaim)
|
* [iwaim](https://github.com/iwaim)
|
||||||
* [valrus](https://github.com/valrus)
|
* [valrus](https://github.com/valrus)
|
||||||
* [IMcD23](https://github.com/IMcD23)
|
* [IMcD23](https://github.com/IMcD23)
|
||||||
* [yi0713](https://github.com/yi0713)
|
* [yi0713](https://github.com/yi0713)
|
||||||
* [iblech](https://github.com/iblech)
|
* [iblech](https://github.com/iblech)
|
||||||
* [J Yeary](mailto:usbsnowcrash@users.noreply.github.com)
|
* [usbsnowcrash](https://github.com/usbsnowcrash)
|
||||||
* [jack-michaud](https://github.com/jack-michaud)
|
* [jack-michaud](https://github.com/jack-michaud)
|
||||||
* [Floppy](https://github.com/Floppy)
|
* [Floppy](https://github.com/Floppy)
|
||||||
* [loomchild](https://github.com/loomchild)
|
* [loomchild](https://github.com/loomchild)
|
||||||
* [jenkr55](https://github.com/jenkr55)
|
* [jenkr55](https://github.com/jenkr55)
|
||||||
* [hyenagirl64](https://github.com/hyenagirl64)
|
|
||||||
* [press5](https://github.com/press5)
|
* [press5](https://github.com/press5)
|
||||||
* [TrollDecker](https://github.com/TrollDecker)
|
* [TrollDecker](https://github.com/TrollDecker)
|
||||||
* [jmontane](https://github.com/jmontane)
|
* [jmontane](https://github.com/jmontane)
|
||||||
|
@ -430,19 +375,21 @@ and provided thanks to the work of the following contributors:
|
||||||
* [jguerder](https://github.com/jguerder)
|
* [jguerder](https://github.com/jguerder)
|
||||||
* [Jehops](https://github.com/Jehops)
|
* [Jehops](https://github.com/Jehops)
|
||||||
* [joshuap](https://github.com/joshuap)
|
* [joshuap](https://github.com/joshuap)
|
||||||
|
* [YuleZ](https://github.com/YuleZ)
|
||||||
* [Tiwy57](https://github.com/Tiwy57)
|
* [Tiwy57](https://github.com/Tiwy57)
|
||||||
* [xuv](https://github.com/xuv)
|
* [xuv](https://github.com/xuv)
|
||||||
* [Jnsll](https://github.com/Jnsll)
|
* [Jnsll](https://github.com/Jnsll)
|
||||||
* [j0k3r](https://github.com/j0k3r)
|
* [j0k3r](https://github.com/j0k3r)
|
||||||
* [KEINOS](https://github.com/KEINOS)
|
* [KEINOS](https://github.com/KEINOS)
|
||||||
* [futoase](https://github.com/futoase)
|
* [futoase](https://github.com/futoase)
|
||||||
* [pot8to](https://github.com/pot8to)
|
* [Pneumaticat](https://github.com/Pneumaticat)
|
||||||
* [Kit Redgrave](mailto:qwertyitis@gmail.com)
|
* [Kit Redgrave](mailto:qwertyitis@gmail.com)
|
||||||
* [Knut Erik](mailto:abjectio@users.noreply.github.com)
|
* [Knut Erik](mailto:abjectio@users.noreply.github.com)
|
||||||
* [mkody](https://github.com/mkody)
|
* [mkody](https://github.com/mkody)
|
||||||
* [k0ta0uchi](https://github.com/k0ta0uchi)
|
* [k0ta0uchi](https://github.com/k0ta0uchi)
|
||||||
* [KrzysiekJ](https://github.com/KrzysiekJ)
|
* [KrzysiekJ](https://github.com/KrzysiekJ)
|
||||||
* [Leo Wzukw](mailto:leowzukw@users.noreply.github.com)
|
* [leowzukw](https://github.com/leowzukw)
|
||||||
|
* [lmorchard](https://github.com/lmorchard)
|
||||||
* [Tak](https://github.com/Tak)
|
* [Tak](https://github.com/Tak)
|
||||||
* [cacheflow](https://github.com/cacheflow)
|
* [cacheflow](https://github.com/cacheflow)
|
||||||
* [ldidry](https://github.com/ldidry)
|
* [ldidry](https://github.com/ldidry)
|
||||||
|
@ -450,92 +397,83 @@ and provided thanks to the work of the following contributors:
|
||||||
* [lfuelling](https://github.com/lfuelling)
|
* [lfuelling](https://github.com/lfuelling)
|
||||||
* [Grabacr07](https://github.com/Grabacr07)
|
* [Grabacr07](https://github.com/Grabacr07)
|
||||||
* [mistermantas](https://github.com/mistermantas)
|
* [mistermantas](https://github.com/mistermantas)
|
||||||
* [MareenaKunjachan](https://github.com/MareenaKunjachan)
|
|
||||||
* [mareklach](https://github.com/mareklach)
|
* [mareklach](https://github.com/mareklach)
|
||||||
* [wirehack7](https://github.com/wirehack7)
|
* [wirehack7](https://github.com/wirehack7)
|
||||||
* [martymcguire](https://github.com/martymcguire)
|
* [martymcguire](https://github.com/martymcguire)
|
||||||
* [marvinkopf](https://github.com/marvinkopf)
|
* [marvinkopf](https://github.com/marvinkopf)
|
||||||
* [otsune](https://github.com/otsune)
|
* [otsune](https://github.com/otsune)
|
||||||
* [mbugowski](https://github.com/mbugowski)
|
|
||||||
* [Mathias B](mailto:10813340+mathias-b@users.noreply.github.com)
|
* [Mathias B](mailto:10813340+mathias-b@users.noreply.github.com)
|
||||||
* [madmath03](https://github.com/madmath03)
|
|
||||||
* [matt-auckland](https://github.com/matt-auckland)
|
* [matt-auckland](https://github.com/matt-auckland)
|
||||||
* [webroo](https://github.com/webroo)
|
* [webroo](https://github.com/webroo)
|
||||||
* [Matthias Beyer](mailto:mail@beyermatthias.de)
|
* [matthiasbeyer](https://github.com/matthiasbeyer)
|
||||||
* [Matthias Jouan](mailto:matthias.jouan@gmail.com)
|
* [mattjmattj](https://github.com/mattjmattj)
|
||||||
* [Matthieu Paret](mailto:matthieuparet69@gmail.com)
|
* [mtparet](https://github.com/mtparet)
|
||||||
* [Maxime BORGES](mailto:maxime.borges@gmail.com)
|
* [maximeborges](https://github.com/maximeborges)
|
||||||
* [Mayu Laierlence](mailto:minacle@live.com)
|
* [minacle](https://github.com/minacle)
|
||||||
* [Michael Deeb](mailto:michaeldeeb@me.com)
|
* [michaeljdeeb](https://github.com/michaeljdeeb)
|
||||||
* [Michael Vieira](mailto:dtox94@gmail.com)
|
* [Themimitoof](https://github.com/Themimitoof)
|
||||||
* [Michel](mailto:michel@cyweo.com)
|
* [cyweo](https://github.com/cyweo)
|
||||||
* [Midgard](mailto:m1dgard@users.noreply.github.com)
|
* [Midgard](mailto:m1dgard@users.noreply.github.com)
|
||||||
* [Mike Burns](mailto:mburns@thoughtbot.com)
|
* [mike-burns](https://github.com/mike-burns)
|
||||||
* [Milan](mailto:me@petabyteboy.de)
|
* [verymilan](https://github.com/verymilan)
|
||||||
* [Milan*](mailto:tchncs@vivaldi.net)
|
* [milmazz](https://github.com/milmazz)
|
||||||
* [Milton Mazzarri](mailto:milmazz@gmail.com)
|
* [premist](https://github.com/premist)
|
||||||
* [Minku Lee](mailto:premist@me.com)
|
* [Mnkai](https://github.com/Mnkai)
|
||||||
* [Minori Hiraoka](mailto:mnkai@users.noreply.github.com)
|
* [mitchhentges](https://github.com/mitchhentges)
|
||||||
* [Mitchell Hentges](mailto:mitch9654@gmail.com)
|
* [moritzheiber](https://github.com/moritzheiber)
|
||||||
* [Mostafa Ahangarha](mailto:ahangarha@users.noreply.github.com)
|
* [mouse-reeve](https://github.com/mouse-reeve)
|
||||||
* [Mouse Reeve](mailto:mousereeve@riseup.net)
|
* [Mozinet-fr](https://github.com/Mozinet-fr)
|
||||||
* [Mozinet](mailto:mozinet-fr@users.noreply.github.com)
|
* [lae](https://github.com/lae)
|
||||||
* [Musee U](mailto:lae@users.noreply.github.com)
|
* [Nanamachi](https://github.com/Nanamachi)
|
||||||
* [NOGISAKA Sadata](mailto:ngsksdt@gmail.com)
|
* [orinthe](https://github.com/orinthe)
|
||||||
* [Naf](mailto:uenok.htc@gmail.com)
|
* [Dar13](https://github.com/Dar13)
|
||||||
* [Nanamachi](mailto:town7.haruki@gmail.com)
|
* [ngerakines](https://github.com/ngerakines)
|
||||||
* [Nathaniel Ekoniak](mailto:nekoniak@ennate.tech)
|
* [vonneudeck](https://github.com/vonneudeck)
|
||||||
* [NecroTechno](mailto:necrotechno@riseup.net)
|
* [Ninetailed](https://github.com/Ninetailed)
|
||||||
* [Nick Gerakines](mailto:nick@gerakines.net)
|
* [k24](https://github.com/k24)
|
||||||
* [Nicolai von Neudeck](mailto:nicolai@vonneudeck.com)
|
* [noiob](https://github.com/noiob)
|
||||||
* [Ninetailed](mailto:ninetailed@gmail.com)
|
* [kwaio](https://github.com/kwaio)
|
||||||
* [Nishi, Keisuke](mailto:k24@users.noreply.github.com)
|
* [norayr](https://github.com/norayr)
|
||||||
* [Noiob](mailto:noiob@users.noreply.github.com)
|
* [joyeusenoelle](https://github.com/joyeusenoelle)
|
||||||
* [Nope Nope](mailto:hireme@kwaio.ninja)
|
* [OlivierNicole](https://github.com/OlivierNicole)
|
||||||
* [Norayr Chilingarian](mailto:norayr@arnet.am)
|
* [noppa](https://github.com/noppa)
|
||||||
* [Noëlle Anthony](mailto:noelle.d.anthony@gmail.com)
|
* [Otakan951](https://github.com/Otakan951)
|
||||||
* [N氏](mailto:uenok.htc@gmail.com)
|
* [fahy](https://github.com/fahy)
|
||||||
* [Olivier Nicole](mailto:olivierthnicole@gmail.com)
|
* [PatrickRWells](https://github.com/PatrickRWells)
|
||||||
* [Oskari Noppa](mailto:noppa@users.noreply.github.com)
|
* [Pangoraw](https://github.com/Pangoraw)
|
||||||
* [Otakan](mailto:otakan951@gmail.com)
|
* [peterkeen](https://github.com/peterkeen)
|
||||||
* [Padraig Fahy](mailto:tech@padraigfahy.com)
|
* [pgate](https://github.com/pgate)
|
||||||
* [PatrickRWells](mailto:32802366+patrickrwells@users.noreply.github.com)
|
* [remram44](https://github.com/remram44)
|
||||||
* [Paul](mailto:naydex.mc+github@gmail.com)
|
* [retokromer](https://github.com/retokromer)
|
||||||
* [Pete Keen](mailto:pete@petekeen.net)
|
* [rfwatson](https://github.com/rfwatson)
|
||||||
* [Pierre-Morgan Gate](mailto:pgate@users.noreply.github.com)
|
* [rfreebern](https://github.com/rfreebern)
|
||||||
* [Ratmir Karabut](mailto:rkarabut@sfmodern.ru)
|
|
||||||
* [Reto Kromer](mailto:retokromer@users.noreply.github.com)
|
|
||||||
* [Rob Watson](mailto:rfwatson@users.noreply.github.com)
|
|
||||||
* [Ryan Freebern](mailto:ryan@freebern.org)
|
|
||||||
* [Ryan Wade](mailto:ryan.wade@protonmail.com)
|
* [Ryan Wade](mailto:ryan.wade@protonmail.com)
|
||||||
* [Ryo Kajiwara](mailto:kfe-fecn6.prussian@s01.info)
|
* [sylph01](https://github.com/sylph01)
|
||||||
* [S.H](mailto:gamelinks007@gmail.com)
|
* [S-H-GAMELINKS](https://github.com/S-H-GAMELINKS)
|
||||||
* [Sadiq Saif](mailto:staticsafe@users.noreply.github.com)
|
* [staticsafe](https://github.com/staticsafe)
|
||||||
* [Sam Hewitt](mailto:hewittsamuel@gmail.com)
|
* [snwh](https://github.com/snwh)
|
||||||
* [Sasha Sorokin](mailto:dafri.nochiterov8@gmail.com)
|
* [sts10](https://github.com/sts10)
|
||||||
* [Satoshi KOJIMA](mailto:skoji@mac.com)
|
* [skoji](https://github.com/skoji)
|
||||||
* [ScienJus](mailto:i@scienjus.com)
|
* [ScienJus](https://github.com/ScienJus)
|
||||||
* [Scott Larkin](mailto:scott@codeclimate.com)
|
* [larkinscott](https://github.com/larkinscott)
|
||||||
* [Sebastian Hübner](mailto:imolein@users.noreply.github.com)
|
* [imolein](https://github.com/imolein)
|
||||||
* [Sebastian Morr](mailto:sebastian@morr.cc)
|
* [blinry](https://github.com/blinry)
|
||||||
* [Sergei Č](mailto:noiwex1911@gmail.com)
|
* [Noiwex](https://github.com/Noiwex)
|
||||||
* [Setuu](mailto:yuki764setuu@gmail.com)
|
* [yuki764](https://github.com/yuki764)
|
||||||
* [Shaun Gillies](mailto:me@shaungillies.net)
|
* [shnjp](https://github.com/shnjp)
|
||||||
* [Shin Adachi](mailto:shn@glucose.jp)
|
* [ernix](https://github.com/ernix)
|
||||||
* [Shin Kojima](mailto:shin@kojima.org)
|
* [rosylilly](https://github.com/rosylilly)
|
||||||
* [Shouko Yu](mailto:imshouko@gmail.com)
|
* [shouko](https://github.com/shouko)
|
||||||
* [Sina Mashek](mailto:sina@mashek.xyz)
|
* [Sina Mashek](mailto:sina@mashek.xyz)
|
||||||
* [Soshi Kato](mailto:mail@sossii.com)
|
* [sossii](https://github.com/sossii)
|
||||||
* [Spanky](mailto:2788886+spankyworks@users.noreply.github.com)
|
* [Spanky](mailto:2788886+spankyworks@users.noreply.github.com)
|
||||||
* [StefOfficiel](mailto:pichard.stephane@free.fr)
|
* [StefOfficiel](mailto:pichard.stephane@free.fr)
|
||||||
* [Steven Tappert](mailto:admin@dark-it.net)
|
|
||||||
* [Svetlozar Todorov](mailto:svetlik@users.noreply.github.com)
|
* [Svetlozar Todorov](mailto:svetlik@users.noreply.github.com)
|
||||||
* [Sébastien Santoro](mailto:dereckson@espace-win.org)
|
* [Sébastien Santoro](mailto:dereckson@espace-win.org)
|
||||||
* [Tad Thorley](mailto:phaedryx@users.noreply.github.com)
|
* [Tad Thorley](mailto:phaedryx@users.noreply.github.com)
|
||||||
* [Takayoshi Nishida](mailto:takayoshi.nishida@gmail.com)
|
* [Takayoshi Nishida](mailto:takayoshi.nishida@gmail.com)
|
||||||
* [Takayuki KUSANO](mailto:github@tkusano.jp)
|
* [Takayuki KUSANO](mailto:github@tkusano.jp)
|
||||||
* [TakesxiSximada](mailto:takesxi.sximada@gmail.com)
|
* [TakesxiSximada](mailto:takesxi.sximada@gmail.com)
|
||||||
* [Tao Bror Bojlén](mailto:brortao@users.noreply.github.com)
|
|
||||||
* [TheInventrix](mailto:theinventrix@users.noreply.github.com)
|
* [TheInventrix](mailto:theinventrix@users.noreply.github.com)
|
||||||
* [Thomas Alberola](mailto:thomas@needacoffee.fr)
|
* [Thomas Alberola](mailto:thomas@needacoffee.fr)
|
||||||
* [Toby Deshane](mailto:fortyseven@users.noreply.github.com)
|
* [Toby Deshane](mailto:fortyseven@users.noreply.github.com)
|
||||||
|
@ -545,12 +483,10 @@ and provided thanks to the work of the following contributors:
|
||||||
* [Treyssat-Vincent Nino](mailto:treyssatvincent@users.noreply.github.com)
|
* [Treyssat-Vincent Nino](mailto:treyssatvincent@users.noreply.github.com)
|
||||||
* [Udo Kramer](mailto:optik@fluffel.io)
|
* [Udo Kramer](mailto:optik@fluffel.io)
|
||||||
* [Una](mailto:una@unascribed.com)
|
* [Una](mailto:una@unascribed.com)
|
||||||
* [Ushitora Anqou](mailto:ushitora@anqou.net)
|
|
||||||
* [Ushitora Anqou](mailto:ushitora_anqou@yahoo.co.jp)
|
* [Ushitora Anqou](mailto:ushitora_anqou@yahoo.co.jp)
|
||||||
* [Valentin Lorentz](mailto:progval+git@progval.net)
|
* [Valentin Lorentz](mailto:progval+git@progval.net)
|
||||||
* [Vladimir Mincev](mailto:vladimir@canicinteractive.com)
|
* [Vladimir Mincev](mailto:vladimir@canicinteractive.com)
|
||||||
* [Waldir Pimenta](mailto:waldyrious@gmail.com)
|
* [Waldir Pimenta](mailto:waldyrious@gmail.com)
|
||||||
* [Wenceslao Páez Chávez](mailto:wcpaez@gmail.com)
|
|
||||||
* [Wesley Ellis](mailto:tahnok@gmail.com)
|
* [Wesley Ellis](mailto:tahnok@gmail.com)
|
||||||
* [Wiktor](mailto:wiktor@metacode.biz)
|
* [Wiktor](mailto:wiktor@metacode.biz)
|
||||||
* [Wonderfall](mailto:wonderfall@schrodinger.io)
|
* [Wonderfall](mailto:wonderfall@schrodinger.io)
|
||||||
|
@ -561,7 +497,6 @@ and provided thanks to the work of the following contributors:
|
||||||
* [YaQ](mailto:i_k_o_m_a_7@yahoo.co.jp)
|
* [YaQ](mailto:i_k_o_m_a_7@yahoo.co.jp)
|
||||||
* [Yanaken](mailto:yanakend@gmail.com)
|
* [Yanaken](mailto:yanakend@gmail.com)
|
||||||
* [Yann Klis](mailto:yann.klis@gmail.com)
|
* [Yann Klis](mailto:yann.klis@gmail.com)
|
||||||
* [Yağızhan](mailto:35808275+yagizhan49@users.noreply.github.com)
|
|
||||||
* [Yeechan Lu](mailto:wz.bluesnow@gmail.com)
|
* [Yeechan Lu](mailto:wz.bluesnow@gmail.com)
|
||||||
* [Yusuke Abe](mailto:moonset20@gmail.com)
|
* [Yusuke Abe](mailto:moonset20@gmail.com)
|
||||||
* [Zachary Spector](mailto:logicaldash@gmail.com)
|
* [Zachary Spector](mailto:logicaldash@gmail.com)
|
||||||
|
@ -575,39 +510,31 @@ and provided thanks to the work of the following contributors:
|
||||||
* [chrolis](mailto:chrolis@users.noreply.github.com)
|
* [chrolis](mailto:chrolis@users.noreply.github.com)
|
||||||
* [cormo](mailto:cormorant2+github@gmail.com)
|
* [cormo](mailto:cormorant2+github@gmail.com)
|
||||||
* [d0p1](mailto:dopi-sama@hush.com)
|
* [d0p1](mailto:dopi-sama@hush.com)
|
||||||
* [dxwc](mailto:dxwc@users.noreply.github.com)
|
|
||||||
* [evilny0](mailto:evilny0@moomoocamp.net)
|
* [evilny0](mailto:evilny0@moomoocamp.net)
|
||||||
* [febrezo](mailto:felixbrezo@gmail.com)
|
* [febrezo](mailto:felixbrezo@gmail.com)
|
||||||
* [fsubal](mailto:fsubal@users.noreply.github.com)
|
* [fsubal](mailto:fsubal@users.noreply.github.com)
|
||||||
* [fusshi-](mailto:dikky1218@users.noreply.github.com)
|
* [fusshi-](mailto:dikky1218@users.noreply.github.com)
|
||||||
* [gentaro](mailto:gentaroooo@gmail.com)
|
* [gentaro](mailto:gentaroooo@gmail.com)
|
||||||
* [gol-cha](mailto:info@mevo.xyz)
|
|
||||||
* [hakoai](mailto:hk--76@qa2.so-net.ne.jp)
|
* [hakoai](mailto:hk--76@qa2.so-net.ne.jp)
|
||||||
* [haosbvnker](mailto:github@chaosbunker.com)
|
* [haosbvnker](mailto:github@chaosbunker.com)
|
||||||
* [ichi_i](mailto:51489410+ichi-i@users.noreply.github.com)
|
|
||||||
* [isati](mailto:phil@juchnowi.cz)
|
* [isati](mailto:phil@juchnowi.cz)
|
||||||
* [jacob](mailto:jacobherringtondeveloper@gmail.com)
|
* [jacob](mailto:jacobherringtondeveloper@gmail.com)
|
||||||
* [jenn kaplan](mailto:me@jkap.io)
|
* [jenn kaplan](mailto:me@jkap.io)
|
||||||
* [jirayudech](mailto:jirayudech@gmail.com)
|
* [jirayudech](mailto:jirayudech@gmail.com)
|
||||||
* [jomo](mailto:github@jomo.tv)
|
|
||||||
* [jooops](mailto:joops@autistici.org)
|
* [jooops](mailto:joops@autistici.org)
|
||||||
* [jukper](mailto:jukkaperanto@gmail.com)
|
* [jukper](mailto:jukkaperanto@gmail.com)
|
||||||
* [jumoru](mailto:jumoru@mailbox.org)
|
* [jumoru](mailto:jumoru@mailbox.org)
|
||||||
* [karlyeurl](mailto:karl.yeurl@gmail.com)
|
* [karlyeurl](mailto:karl.yeurl@gmail.com)
|
||||||
* [kedama](mailto:32974885+kedamadq@users.noreply.github.com)
|
* [kedama](mailto:32974885+kedamadq@users.noreply.github.com)
|
||||||
* [kodai](mailto:shirafuta.kodai@gmail.com)
|
|
||||||
* [kuro5hin](mailto:rusty@kuro5hin.org)
|
* [kuro5hin](mailto:rusty@kuro5hin.org)
|
||||||
* [luzpaz](mailto:luzpaz@users.noreply.github.com)
|
* [luzpaz](mailto:luzpaz@users.noreply.github.com)
|
||||||
* [maxypy](mailto:maxime@mpigou.fr)
|
* [maxypy](mailto:maxime@mpigou.fr)
|
||||||
* [mhe](mailto:mail@marcus-herrmann.com)
|
* [mhe](mailto:mail@marcus-herrmann.com)
|
||||||
* [mike castleman](mailto:m@mlcastle.net)
|
|
||||||
* [mimikun](mailto:dzdzble_effort_311@outlook.jp)
|
* [mimikun](mailto:dzdzble_effort_311@outlook.jp)
|
||||||
* [mohemohe](mailto:mohemohe@users.noreply.github.com)
|
|
||||||
* [mshrtkch](mailto:mshrtkch@users.noreply.github.com)
|
* [mshrtkch](mailto:mshrtkch@users.noreply.github.com)
|
||||||
* [muan](mailto:muan@github.com)
|
* [muan](mailto:muan@github.com)
|
||||||
* [namelessGonbai](mailto:43787036+namelessgonbai@users.noreply.github.com)
|
|
||||||
* [neetshin](mailto:neetshin@neetsh.in)
|
* [neetshin](mailto:neetshin@neetsh.in)
|
||||||
* [nzws](mailto:git-yuzu@svk.jp)
|
* [nightpool](mailto:nightpool@users.noreply.github.com)
|
||||||
* [rch850](mailto:rich850@gmail.com)
|
* [rch850](mailto:rich850@gmail.com)
|
||||||
* [roikale](mailto:roikale@users.noreply.github.com)
|
* [roikale](mailto:roikale@users.noreply.github.com)
|
||||||
* [rysiekpl](mailto:rysiek@hackerspace.pl)
|
* [rysiekpl](mailto:rysiek@hackerspace.pl)
|
||||||
|
@ -620,8 +547,6 @@ and provided thanks to the work of the following contributors:
|
||||||
* [tateisu](mailto:tateisu@gmail.com)
|
* [tateisu](mailto:tateisu@gmail.com)
|
||||||
* [tmyt](mailto:shigure@refy.net)
|
* [tmyt](mailto:shigure@refy.net)
|
||||||
* [trevDev()](mailto:trev@trevdev.ca)
|
* [trevDev()](mailto:trev@trevdev.ca)
|
||||||
* [tsia](mailto:github@tsia.de)
|
|
||||||
* [umonaca](mailto:53662960+umonaca@users.noreply.github.com)
|
|
||||||
* [utam0k](mailto:k0ma@utam0k.jp)
|
* [utam0k](mailto:k0ma@utam0k.jp)
|
||||||
* [vpzomtrrfrt](mailto:vpzomtrrfrt@gmail.com)
|
* [vpzomtrrfrt](mailto:vpzomtrrfrt@gmail.com)
|
||||||
* [walfie](mailto:walfington@gmail.com)
|
* [walfie](mailto:walfington@gmail.com)
|
||||||
|
@ -630,137 +555,12 @@ and provided thanks to the work of the following contributors:
|
||||||
* [yoshipc](mailto:yoooo@yoshipc.net)
|
* [yoshipc](mailto:yoooo@yoshipc.net)
|
||||||
* [Özcan Zafer AYAN](mailto:ozcanzaferayan@gmail.com)
|
* [Özcan Zafer AYAN](mailto:ozcanzaferayan@gmail.com)
|
||||||
* [ばん](mailto:detteiu0321@gmail.com)
|
* [ばん](mailto:detteiu0321@gmail.com)
|
||||||
* [ふるふる](mailto:frfs@users.noreply.github.com)
|
* [みたらしだんご](mailto:mitarashidango@users.noreply.github.com)
|
||||||
* [りんすき](mailto:6533808+rinsuki@users.noreply.github.com)
|
* [りんすき](mailto:6533808+rinsuki@users.noreply.github.com)
|
||||||
* [ヨイツの賢狼ホロ | 3rd style](mailto:horo@yoitsu.moe)
|
* [ヨイツの賢狼ホロ | 3rd style](mailto:horo@yoitsu.moe)
|
||||||
* [唐宗勛](mailto:tangzongxun@hotmail.com)
|
|
||||||
* [猫吸血鬼ディフリス / 猫ロキP](mailto:deflis@gmail.com)
|
* [猫吸血鬼ディフリス / 猫ロキP](mailto:deflis@gmail.com)
|
||||||
* [艮 鮟鱇](mailto:ushitora_anqou@yahoo.co.jp)
|
* [艮 鮟鱇](mailto:ushitora_anqou@yahoo.co.jp)
|
||||||
* [西小倉宏信](mailto:nishiko@mindia.jp)
|
* [西小倉宏信](mailto:nishiko@mindia.jp)
|
||||||
* [雨宮美羽](mailto:k737566@gmail.com)
|
* [雨宮美羽](mailto:k737566@gmail.com)
|
||||||
|
|
||||||
This document is provided for informational purposes only. Since it is only updated once per release, the version you are looking at may be currently out of date. To see the full list of contributors, consider looking at the [git history](https://github.com/tootsuite/mastodon/graphs/contributors) instead.
|
This document is provided for informational purposes only. Since it is only updated once per release, the version you are looking at may be currently out of date. To see the full list of contributors, consider looking at the [git history](https://github.com/tootsuite/mastodon/graphs/contributors) instead.
|
||||||
|
|
||||||
## Translators
|
|
||||||
|
|
||||||
Following people have contributed to translation of Mastodon:
|
|
||||||
|
|
||||||
- Zoltán Gera (*Hungarian*)
|
|
||||||
- Kristijan Tkalec (*Slovenian*)
|
|
||||||
- Evert Prants (*Estonian*)
|
|
||||||
- borys_sh (*Ukrainian*)
|
|
||||||
- ButterflyOfFire (*Arabic; French*)
|
|
||||||
- Osoitz (*Basque*)
|
|
||||||
- oɹʇuʞ (*Spanish, Argentina*)
|
|
||||||
- koyu (*German*)
|
|
||||||
- Jeroen (*Dutch*)
|
|
||||||
- Muha Aliss (*Turkish*)
|
|
||||||
- 唐宗勛 (*Chinese Simplified*)
|
|
||||||
- Jeong Arm (*Korean; Esperanto; Japanese*)
|
|
||||||
- Oguz Ersen (*Turkish*)
|
|
||||||
- spla (*Catalan*)
|
|
||||||
- Ramdziana F Y (*Indonesian*)
|
|
||||||
- Aditoo17 (*Czech*)
|
|
||||||
- Xosé M. (*Galician*)
|
|
||||||
- Roboron (*Spanish*)
|
|
||||||
- Alix Rossi (*Corsican; French*)
|
|
||||||
- Maya Minatsuki (*Japanese*)
|
|
||||||
- Masoud Abkenar (*Persian*)
|
|
||||||
- Thai Localization (*Thai*)
|
|
||||||
- Marek Ľach (*Slovak; Polish*)
|
|
||||||
- d5Ziif3K (*Ukrainian*)
|
|
||||||
- lamnatos (*Greek*)
|
|
||||||
- Emyn Nant Nefydd (*Welsh*)
|
|
||||||
- Diluns (*Occitan*)
|
|
||||||
- atarashiako (*Chinese Simplified*)
|
|
||||||
- 101010 (*Polish*)
|
|
||||||
- Yi-Jyun Pan (*Chinese Traditional*)
|
|
||||||
- silkevicious (*Italian*)
|
|
||||||
- FédiQuébec (*French*)
|
|
||||||
- Jaz-Michael King (*Welsh*)
|
|
||||||
- christalleras (*Norwegian Nynorsk*)
|
|
||||||
- tykayn (*French*)
|
|
||||||
- Alessandro Levati (*Italian*)
|
|
||||||
- carolinagiorno (*Portuguese, Brazilian*)
|
|
||||||
- taoxvx (*Danish*)
|
|
||||||
- sabri (*Spanish*)
|
|
||||||
- Sasha Sorokin (*Russian*)
|
|
||||||
- shioko (*Chinese Simplified*)
|
|
||||||
- Evgeny Petrov (*Russian*)
|
|
||||||
- ariasuni (*French; Esperanto*)
|
|
||||||
- Tiago Epifânio (*Portuguese*)
|
|
||||||
- dxwc (*Bengali*)
|
|
||||||
- liffon (*Swedish*)
|
|
||||||
- Vanege (*Esperanto*)
|
|
||||||
- Johan Schiff (*Swedish*)
|
|
||||||
- kat (*Ukrainian; Russian*)
|
|
||||||
- oti4500 (*Hungarian; Ukrainian*)
|
|
||||||
- Juan José Salvador Piedra (*Spanish*)
|
|
||||||
- diazepan (*Spanish*)
|
|
||||||
- SHeija (*Finnish*)
|
|
||||||
- Jack R (*Spanish*)
|
|
||||||
- Saederup92 (*Danish*)
|
|
||||||
- Stasiek Michalski (*Polish*)
|
|
||||||
- Dewi (*Breton; French*)
|
|
||||||
- cybergene (*Japanese*)
|
|
||||||
- AW Unad (*Indonesian*)
|
|
||||||
- Andrea Lo Iacono (*Italian*)
|
|
||||||
- Ray (*Spanish*)
|
|
||||||
- Unmual (*Spanish*)
|
|
||||||
- Ryo (*Korean*)
|
|
||||||
- juanda097 (*Spanish*)
|
|
||||||
- Anunnakey (*Macedonian*)
|
|
||||||
- Cutls (*Japanese*)
|
|
||||||
- erikstl (*Esperanto*)
|
|
||||||
- ruine (*Japanese*)
|
|
||||||
- MadeInSteak (*Finnish*)
|
|
||||||
- Sokratis Alichanidis (*Greek*)
|
|
||||||
- dragnucs2 (*Arabic*)
|
|
||||||
- frumble (*German*)
|
|
||||||
- Rikard Linde (*Swedish*)
|
|
||||||
- PPNplus (*Thai*)
|
|
||||||
- arethsu (*Swedish*)
|
|
||||||
- EPEMA YT (*German*)
|
|
||||||
- Rhys Harrison (*Esperanto*)
|
|
||||||
- KEINOS (*Japanese*)
|
|
||||||
- filippodb (*Italian*)
|
|
||||||
- JzshAC (*Chinese Simplified*)
|
|
||||||
- Rintan1 (*Japanese*)
|
|
||||||
- Antillion (*Spanish*)
|
|
||||||
- hiphipvargas (*Portuguese*)
|
|
||||||
- Ch. (*Korean*)
|
|
||||||
- tctovsli (*Norwegian Nynorsk*)
|
|
||||||
- vjasiegd (*Polish*)
|
|
||||||
- SamitiMed (*Thai*)
|
|
||||||
- umelard (*Hebrew*)
|
|
||||||
- 硫酸鶏 (*Japanese*)
|
|
||||||
- Adrián Lattes (*Spanish*)
|
|
||||||
- Hinaloe (*Japanese*)
|
|
||||||
- Renato "Lond" Cerqueira (*Portuguese, Brazilian*)
|
|
||||||
- parnikkapore (*Thai*)
|
|
||||||
- Marcin Mikołajczak (*Polish*)
|
|
||||||
- 森の子リスのミーコの大冒険 (*Japanese*)
|
|
||||||
- Marcepanek_ (*Polish*)
|
|
||||||
- Sahak Petrosyan (*Armenian*)
|
|
||||||
- Daniel Dimitrov (*Bulgarian*)
|
|
||||||
- Hugh Liu (*Chinese Simplified*)
|
|
||||||
- Rakino (*Chinese Simplified*)
|
|
||||||
- hussama (*Portuguese, Brazilian*)
|
|
||||||
- ThibG (*French*)
|
|
||||||
- SnDer (*Dutch*)
|
|
||||||
- PifyZ (*French*)
|
|
||||||
- eichkat3r (*German*)
|
|
||||||
- Karol Kosek (*Polish*)
|
|
||||||
- Akarshan Biswas (*Bengali*)
|
|
||||||
- Tradjincal (*French*)
|
|
||||||
- Steven Tappert (*German*)
|
|
||||||
- sergioaraujo1 (*Portuguese, Brazilian*)
|
|
||||||
- mmokhi (*Persian*)
|
|
||||||
- fedot (*Russian*)
|
|
||||||
- skaaarrr (*German*)
|
|
||||||
- JackXu (*Chinese Simplified*)
|
|
||||||
- Lukas Fülling (*German*)
|
|
||||||
- Zoé Bőle (*German*)
|
|
||||||
- Dremski (*Bulgarian*)
|
|
||||||
- tamaina (*Japanese*)
|
|
||||||
- OpenAlgeria (*Arabic*)
|
|
||||||
|
|
1021
CHANGELOG.md
1021
CHANGELOG.md
File diff suppressed because it is too large
Load Diff
|
@ -1,7 +1,7 @@
|
||||||
Contributing
|
Contributing
|
||||||
============
|
============
|
||||||
|
|
||||||
Thank you for considering contributing to Mastodon 🐘
|
Thank you for considering contributing to Mastodon 🐘
|
||||||
|
|
||||||
You can contribute in the following ways:
|
You can contribute in the following ways:
|
||||||
|
|
||||||
|
@ -10,17 +10,15 @@ You can contribute in the following ways:
|
||||||
- Contributing code to Mastodon by fixing bugs or implementing features
|
- Contributing code to Mastodon by fixing bugs or implementing features
|
||||||
- Improving the documentation
|
- Improving the documentation
|
||||||
|
|
||||||
If your contributions are accepted into Mastodon, you can request to be paid through [our OpenCollective](https://opencollective.com/mastodon).
|
|
||||||
|
|
||||||
## Bug reports
|
## Bug reports
|
||||||
|
|
||||||
Bug reports and feature suggestions can be submitted to [GitHub Issues](https://github.com/tootsuite/mastodon/issues). Please make sure that you are not submitting duplicates, and that a similar report or request has not already been resolved or rejected in the past using the search function. Please also use descriptive, concise titles.
|
Bug reports and feature suggestions can be submitted to [GitHub Issues](https://github.com/tootsuite/mastodon/issues). Please make sure that you are not submitting duplicates, and that a similar report or request has not already been resolved or rejected in the past using the search function. Please also use descriptive, concise titles.
|
||||||
|
|
||||||
## Translations
|
## Translations
|
||||||
|
|
||||||
You can submit translations via [Crowdin](https://crowdin.com/project/mastodon). They are periodically merged into the codebase.
|
You can submit translations via [Weblate](https://weblate.joinmastodon.org/). They are periodically merged into the codebase.
|
||||||
|
|
||||||
[![Crowdin](https://d322cqt584bo4o.cloudfront.net/mastodon/localized.svg)][crowdin]
|
[![Mastodon translation statistics by language](https://weblate.joinmastodon.org/widgets/mastodon/-/multi-auto.svg)](https://weblate.joinmastodon.org/)
|
||||||
|
|
||||||
## Pull requests
|
## Pull requests
|
||||||
|
|
||||||
|
|
185
Dockerfile
185
Dockerfile
|
@ -1,125 +1,86 @@
|
||||||
FROM ubuntu:18.04 as build-dep
|
FROM node:8.12.0-alpine as node
|
||||||
|
FROM ruby:2.4.5-alpine3.8
|
||||||
|
|
||||||
# Use bash for the shell
|
LABEL maintainer="https://github.com/tootsuite/mastodon" \
|
||||||
SHELL ["bash", "-c"]
|
description="Your self-hosted, globally interconnected microblogging community"
|
||||||
|
|
||||||
# Install Node
|
|
||||||
ENV NODE_VER="12.11.1"
|
|
||||||
RUN echo "Etc/UTC" > /etc/localtime && \
|
|
||||||
apt update && \
|
|
||||||
apt -y install wget python && \
|
|
||||||
cd ~ && \
|
|
||||||
wget https://nodejs.org/download/release/v$NODE_VER/node-v$NODE_VER-linux-x64.tar.gz && \
|
|
||||||
tar xf node-v$NODE_VER-linux-x64.tar.gz && \
|
|
||||||
rm node-v$NODE_VER-linux-x64.tar.gz && \
|
|
||||||
mv node-v$NODE_VER-linux-x64 /opt/node
|
|
||||||
|
|
||||||
# Install jemalloc
|
|
||||||
ENV JE_VER="5.2.1"
|
|
||||||
RUN apt update && \
|
|
||||||
apt -y install make autoconf gcc g++ && \
|
|
||||||
cd ~ && \
|
|
||||||
wget https://github.com/jemalloc/jemalloc/archive/$JE_VER.tar.gz && \
|
|
||||||
tar xf $JE_VER.tar.gz && \
|
|
||||||
cd jemalloc-$JE_VER && \
|
|
||||||
./autogen.sh && \
|
|
||||||
./configure --prefix=/opt/jemalloc && \
|
|
||||||
make -j$(nproc) > /dev/null && \
|
|
||||||
make install_bin install_include install_lib
|
|
||||||
|
|
||||||
# Install ruby
|
|
||||||
ENV RUBY_VER="2.6.5"
|
|
||||||
ENV CPPFLAGS="-I/opt/jemalloc/include"
|
|
||||||
ENV LDFLAGS="-L/opt/jemalloc/lib/"
|
|
||||||
RUN apt update && \
|
|
||||||
apt -y install build-essential \
|
|
||||||
bison libyaml-dev libgdbm-dev libreadline-dev \
|
|
||||||
libncurses5-dev libffi-dev zlib1g-dev libssl-dev && \
|
|
||||||
cd ~ && \
|
|
||||||
wget https://cache.ruby-lang.org/pub/ruby/${RUBY_VER%.*}/ruby-$RUBY_VER.tar.gz && \
|
|
||||||
tar xf ruby-$RUBY_VER.tar.gz && \
|
|
||||||
cd ruby-$RUBY_VER && \
|
|
||||||
./configure --prefix=/opt/ruby \
|
|
||||||
--with-jemalloc \
|
|
||||||
--with-shared \
|
|
||||||
--disable-install-doc && \
|
|
||||||
ln -s /opt/jemalloc/lib/* /usr/lib/ && \
|
|
||||||
make -j$(nproc) > /dev/null && \
|
|
||||||
make install
|
|
||||||
|
|
||||||
ENV PATH="${PATH}:/opt/ruby/bin:/opt/node/bin"
|
|
||||||
|
|
||||||
RUN npm install -g yarn && \
|
|
||||||
gem install bundler && \
|
|
||||||
apt update && \
|
|
||||||
apt -y install git libicu-dev libidn11-dev \
|
|
||||||
libpq-dev libprotobuf-dev protobuf-compiler
|
|
||||||
|
|
||||||
COPY Gemfile* package.json yarn.lock /opt/mastodon/
|
|
||||||
|
|
||||||
RUN cd /opt/mastodon && \
|
|
||||||
bundle install -j$(nproc) --deployment --without development test && \
|
|
||||||
yarn install --pure-lockfile
|
|
||||||
|
|
||||||
FROM ubuntu:18.04
|
|
||||||
|
|
||||||
# Copy over all the langs needed for runtime
|
|
||||||
COPY --from=build-dep /opt/node /opt/node
|
|
||||||
COPY --from=build-dep /opt/ruby /opt/ruby
|
|
||||||
COPY --from=build-dep /opt/jemalloc /opt/jemalloc
|
|
||||||
|
|
||||||
# Add more PATHs to the PATH
|
|
||||||
ENV PATH="${PATH}:/opt/ruby/bin:/opt/node/bin:/opt/mastodon/bin"
|
|
||||||
|
|
||||||
# Create the mastodon user
|
|
||||||
ARG UID=991
|
ARG UID=991
|
||||||
ARG GID=991
|
ARG GID=991
|
||||||
RUN apt update && \
|
|
||||||
echo "Etc/UTC" > /etc/localtime && \
|
|
||||||
ln -s /opt/jemalloc/lib/* /usr/lib/ && \
|
|
||||||
apt install -y whois wget && \
|
|
||||||
addgroup --gid $GID mastodon && \
|
|
||||||
useradd -m -u $UID -g $GID -d /opt/mastodon mastodon && \
|
|
||||||
echo "mastodon:`head /dev/urandom | tr -dc A-Za-z0-9 | head -c 24 | mkpasswd -s -m sha-256`" | chpasswd
|
|
||||||
|
|
||||||
# Install mastodon runtime deps
|
ENV PATH=/mastodon/bin:$PATH \
|
||||||
RUN apt -y --no-install-recommends install \
|
RAILS_SERVE_STATIC_FILES=true \
|
||||||
libssl1.1 libpq5 imagemagick ffmpeg \
|
RAILS_ENV=production \
|
||||||
libicu60 libprotobuf10 libidn11 libyaml-0-2 \
|
NODE_ENV=production
|
||||||
file ca-certificates tzdata libreadline7 && \
|
|
||||||
apt -y install gcc && \
|
|
||||||
ln -s /opt/mastodon /mastodon && \
|
|
||||||
gem install bundler && \
|
|
||||||
rm -rf /var/cache && \
|
|
||||||
rm -rf /var/lib/apt/lists/*
|
|
||||||
|
|
||||||
# Add tini
|
ARG LIBICONV_VERSION=1.15
|
||||||
ENV TINI_VERSION="0.18.0"
|
ARG LIBICONV_DOWNLOAD_SHA256=ccf536620a45458d26ba83887a983b96827001e92a13847b45e4925cc8913178
|
||||||
ENV TINI_SUM="12d20136605531b09a2c2dac02ccee85e1b874eb322ef6baf7561cd93f93c855"
|
|
||||||
ADD https://github.com/krallin/tini/releases/download/v${TINI_VERSION}/tini /tini
|
|
||||||
RUN echo "$TINI_SUM tini" | sha256sum -c -
|
|
||||||
RUN chmod +x /tini
|
|
||||||
|
|
||||||
# Copy over mastodon source, and dependencies from building, and set permissions
|
EXPOSE 3000 4000
|
||||||
COPY --chown=mastodon:mastodon . /opt/mastodon
|
|
||||||
COPY --from=build-dep --chown=mastodon:mastodon /opt/mastodon /opt/mastodon
|
|
||||||
|
|
||||||
# Run mastodon services in prod mode
|
WORKDIR /mastodon
|
||||||
ENV RAILS_ENV="production"
|
|
||||||
ENV NODE_ENV="production"
|
|
||||||
|
|
||||||
# Tell rails to serve static files
|
COPY --from=node /usr/local/bin/node /usr/local/bin/node
|
||||||
ENV RAILS_SERVE_STATIC_FILES="true"
|
COPY --from=node /usr/local/lib/node_modules /usr/local/lib/node_modules
|
||||||
ENV BIND="0.0.0.0"
|
COPY --from=node /usr/local/bin/npm /usr/local/bin/npm
|
||||||
|
COPY --from=node /opt/yarn-* /opt/yarn
|
||||||
|
|
||||||
|
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 \
|
||||||
|
protobuf \
|
||||||
|
tini \
|
||||||
|
tzdata \
|
||||||
|
&& update-ca-certificates \
|
||||||
|
&& ln -s /opt/yarn/bin/yarn /usr/local/bin/yarn \
|
||||||
|
&& ln -s /opt/yarn/bin/yarnpkg /usr/local/bin/yarnpkg \
|
||||||
|
&& mkdir -p /tmp/src /opt \
|
||||||
|
&& wget -O libiconv.tar.gz "https://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 install --pure-lockfile --ignore-engines \
|
||||||
|
&& yarn cache clean
|
||||||
|
|
||||||
|
RUN addgroup -g ${GID} mastodon && adduser -h /mastodon -s /bin/sh -D -G mastodon -u ${UID} mastodon \
|
||||||
|
&& mkdir -p /mastodon/public/system /mastodon/public/assets /mastodon/public/packs \
|
||||||
|
&& chown -R mastodon:mastodon /mastodon/public
|
||||||
|
|
||||||
|
COPY . /mastodon
|
||||||
|
|
||||||
|
RUN chown -R mastodon:mastodon /mastodon
|
||||||
|
|
||||||
|
VOLUME /mastodon/public/system
|
||||||
|
|
||||||
# Set the run user
|
|
||||||
USER mastodon
|
USER mastodon
|
||||||
|
|
||||||
# Precompile assets
|
RUN OTP_SECRET=precompile_placeholder SECRET_KEY_BASE=precompile_placeholder bundle exec rails assets:precompile
|
||||||
RUN cd ~ && \
|
|
||||||
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 ["/sbin/tini", "--"]
|
||||||
WORKDIR /opt/mastodon
|
|
||||||
ENTRYPOINT ["/tini", "--"]
|
|
||||||
|
|
110
Gemfile
110
Gemfile
|
@ -1,38 +1,37 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
source 'https://rubygems.org'
|
source 'https://rubygems.org'
|
||||||
ruby '>= 2.4.0', '< 2.7.0'
|
ruby '>= 2.3.0', '< 2.6.0'
|
||||||
|
|
||||||
gem 'pkg-config', '~> 1.3'
|
gem 'pkg-config', '~> 1.3'
|
||||||
|
|
||||||
gem 'puma', '~> 4.2'
|
gem 'puma', '~> 3.12'
|
||||||
gem 'rails', '~> 5.2.3'
|
gem 'rails', '~> 5.2.1'
|
||||||
gem 'thor', '~> 0.20'
|
gem 'thor', '~> 0.20'
|
||||||
|
|
||||||
gem 'hamlit-rails', '~> 0.2'
|
gem 'hamlit-rails', '~> 0.2'
|
||||||
gem 'pg', '~> 1.1'
|
gem 'pg', '~> 1.1'
|
||||||
gem 'makara', '~> 0.4'
|
gem 'makara', '~> 0.4'
|
||||||
gem 'pghero', '~> 2.3'
|
gem 'pghero', '~> 2.2'
|
||||||
gem 'dotenv-rails', '~> 2.7'
|
gem 'dotenv-rails', '~> 2.5'
|
||||||
|
|
||||||
gem 'aws-sdk-s3', '~> 1.48', require: false
|
gem 'aws-sdk-s3', '~> 1.23', require: false
|
||||||
gem 'fog-core', '<= 2.1.0'
|
gem 'fog-core', '<= 2.1.0'
|
||||||
gem 'fog-openstack', '~> 0.3', require: false
|
gem 'fog-openstack', '~> 0.3', require: false
|
||||||
gem 'paperclip', '~> 6.0'
|
gem 'paperclip', '~> 6.0'
|
||||||
gem 'paperclip-av-transcoder', '~> 0.6'
|
gem 'paperclip-av-transcoder', '~> 0.6'
|
||||||
gem 'streamio-ffmpeg', '~> 3.0'
|
gem 'streamio-ffmpeg', '~> 3.0'
|
||||||
gem 'blurhash', '~> 0.1'
|
|
||||||
|
|
||||||
gem 'active_model_serializers', '~> 0.10'
|
gem 'active_model_serializers', '~> 0.10'
|
||||||
gem 'addressable', '~> 2.7'
|
gem 'addressable', '~> 2.5'
|
||||||
gem 'bootsnap', '~> 1.4', require: false
|
gem 'bootsnap', '~> 1.3', require: false
|
||||||
gem 'browser'
|
gem 'browser'
|
||||||
gem 'charlock_holmes', '~> 0.7.6'
|
gem 'charlock_holmes', '~> 0.7.6'
|
||||||
gem 'iso-639'
|
gem 'iso-639'
|
||||||
gem 'chewy', '~> 5.1'
|
gem 'chewy', '~> 5.0'
|
||||||
gem 'cld3', '~> 3.2.4'
|
gem 'cld3', '~> 3.2.0'
|
||||||
gem 'devise', '~> 4.7'
|
gem 'devise', '~> 4.5'
|
||||||
gem 'devise-two-factor', '~> 3.1'
|
gem 'devise-two-factor', '~> 3.0'
|
||||||
|
|
||||||
group :pam_authentication, optional: true do
|
group :pam_authentication, optional: true do
|
||||||
gem 'devise_pam_authenticatable2', '~> 9.2'
|
gem 'devise_pam_authenticatable2', '~> 9.2'
|
||||||
|
@ -41,70 +40,64 @@ end
|
||||||
gem 'net-ldap', '~> 0.10'
|
gem 'net-ldap', '~> 0.10'
|
||||||
gem 'omniauth-cas', '~> 1.1'
|
gem 'omniauth-cas', '~> 1.1'
|
||||||
gem 'omniauth-saml', '~> 1.10'
|
gem 'omniauth-saml', '~> 1.10'
|
||||||
gem 'omniauth', '~> 1.9'
|
gem 'omniauth', '~> 1.2'
|
||||||
|
|
||||||
gem 'discard', '~> 1.1'
|
gem 'doorkeeper', '~> 5.0'
|
||||||
gem 'doorkeeper', '~> 5.2'
|
|
||||||
gem 'fast_blank', '~> 1.0'
|
gem 'fast_blank', '~> 1.0'
|
||||||
gem 'fastimage'
|
gem 'fastimage'
|
||||||
gem 'goldfinger', '~> 2.1'
|
gem 'goldfinger', '~> 2.1'
|
||||||
gem 'hiredis', '~> 0.6'
|
gem 'hiredis', '~> 0.6'
|
||||||
gem 'redis-namespace', '~> 1.5'
|
gem 'redis-namespace', '~> 1.5'
|
||||||
gem 'health_check', git: 'https://github.com/ianheggie/health_check', ref: '0b799ead604f900ed50685e9b2d469cd2befba5b'
|
|
||||||
gem 'htmlentities', '~> 4.3'
|
gem 'htmlentities', '~> 4.3'
|
||||||
gem 'http', '~> 3.3'
|
gem 'http', '~> 3.3'
|
||||||
gem 'http_accept_language', '~> 2.1'
|
gem 'http_accept_language', '~> 2.1'
|
||||||
gem 'http_parser.rb', '~> 0.6', git: 'https://github.com/tmm1/http_parser.rb', ref: '54b17ba8c7d8d20a16dfc65d1775241833219cf2', submodules: true
|
gem 'http_parser.rb', '~> 0.6', git: 'https://github.com/tmm1/http_parser.rb', ref: '54b17ba8c7d8d20a16dfc65d1775241833219cf2'
|
||||||
gem 'httplog', '~> 1.3'
|
gem 'httplog', '~> 1.1'
|
||||||
gem 'idn-ruby', require: 'idn'
|
gem 'idn-ruby', require: 'idn'
|
||||||
gem 'kaminari', '~> 1.1'
|
gem 'kaminari', '~> 1.1'
|
||||||
gem 'link_header', '~> 0.0'
|
gem 'link_header', '~> 0.0'
|
||||||
gem 'mime-types', '~> 3.3', require: 'mime/types/columnar'
|
gem 'mime-types', '~> 3.2', require: 'mime/types/columnar'
|
||||||
gem 'nilsimsa', git: 'https://github.com/witgo/nilsimsa', ref: 'fd184883048b922b176939f851338d0a4971a532'
|
gem 'nokogiri', '~> 1.8'
|
||||||
gem 'nokogiri', '~> 1.10'
|
|
||||||
gem 'nsa', '~> 0.2'
|
gem 'nsa', '~> 0.2'
|
||||||
gem 'oj', '~> 3.9'
|
gem 'oj', '~> 3.7'
|
||||||
gem 'ostatus2', '~> 2.0'
|
gem 'ostatus2', '~> 2.0'
|
||||||
gem 'ox', '~> 2.11'
|
gem 'ox', '~> 2.10'
|
||||||
gem 'parslet'
|
|
||||||
gem 'parallel', '~> 1.17'
|
|
||||||
gem 'posix-spawn', git: 'https://github.com/rtomayko/posix-spawn', ref: '58465d2e213991f8afb13b984854a49fcdcc980c'
|
gem 'posix-spawn', git: 'https://github.com/rtomayko/posix-spawn', ref: '58465d2e213991f8afb13b984854a49fcdcc980c'
|
||||||
gem 'pundit', '~> 2.1'
|
gem 'pundit', '~> 2.0'
|
||||||
gem 'premailer-rails'
|
gem 'premailer-rails'
|
||||||
gem 'rack-attack', '~> 6.1'
|
gem 'rack-attack', '~> 5.4'
|
||||||
gem 'rack-cors', '~> 1.0', require: 'rack/cors'
|
gem 'rack-cors', '~> 1.0', require: 'rack/cors'
|
||||||
gem 'rails-i18n', '~> 5.1'
|
gem 'rails-i18n', '~> 5.1'
|
||||||
gem 'rails-settings-cached', '~> 0.6'
|
gem 'rails-settings-cached', '~> 0.6'
|
||||||
gem 'redis', '~> 4.1', require: ['redis', 'redis/connection/hiredis']
|
gem 'redis', '~> 4.0', 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', '~> 0.10'
|
gem 'rqrcode', '~> 0.10'
|
||||||
gem 'ruby-progressbar', '~> 1.10'
|
gem 'sanitize', '~> 5.0'
|
||||||
gem 'sanitize', '~> 5.1'
|
|
||||||
gem 'sidekiq', '~> 5.2'
|
gem 'sidekiq', '~> 5.2'
|
||||||
gem 'sidekiq-scheduler', '~> 3.0'
|
gem 'sidekiq-scheduler', '~> 3.0'
|
||||||
gem 'sidekiq-unique-jobs', '~> 6.0'
|
gem 'sidekiq-unique-jobs', '~> 5.0'
|
||||||
gem 'sidekiq-bulk', '~>0.2.0'
|
gem 'sidekiq-bulk', '~>0.1.1'
|
||||||
gem 'simple-navigation', '~> 4.1'
|
gem 'simple-navigation', '~> 4.0'
|
||||||
gem 'simple_form', '~> 4.1'
|
gem 'simple_form', '~> 4.0'
|
||||||
gem 'sprockets-rails', '~> 3.2', require: 'sprockets/railtie'
|
gem 'sprockets-rails', '~> 3.2', require: 'sprockets/railtie'
|
||||||
gem 'stoplight', '~> 2.1.3'
|
gem 'stoplight', '~> 2.1.3'
|
||||||
gem 'strong_migrations', '~> 0.4'
|
gem 'strong_migrations', '~> 0.3'
|
||||||
gem 'tty-command', '~> 0.9', require: false
|
gem 'tty-command', '~> 0.8', require: false
|
||||||
gem 'tty-prompt', '~> 0.19', require: false
|
gem 'tty-prompt', '~> 0.17', require: false
|
||||||
gem 'twitter-text', '~> 1.14'
|
gem 'twitter-text', '~> 1.14'
|
||||||
gem 'tzinfo-data', '~> 1.2019'
|
gem 'tzinfo-data', '~> 1.2018'
|
||||||
gem 'webpacker', '~> 4.0'
|
gem 'webpacker', '~> 3.5'
|
||||||
gem 'webpush'
|
gem 'webpush'
|
||||||
|
|
||||||
gem 'json-ld', git: 'https://github.com/ruby-rdf/json-ld.git', ref: 'e742697a0906e74e8bb777ef98137bc3955d981d'
|
gem 'json-ld', '~> 2.2'
|
||||||
gem 'json-ld-preloaded', '~> 3.0'
|
gem 'json-ld-preloaded', '~> 2.2'
|
||||||
gem 'rdf-normalize', '~> 0.3'
|
gem 'rdf-normalize', '~> 0.3'
|
||||||
|
|
||||||
group :development, :test do
|
group :development, :test do
|
||||||
gem 'fabrication', '~> 2.20'
|
gem 'fabrication', '~> 2.20'
|
||||||
gem 'fuubar', '~> 2.4'
|
gem 'fuubar', '~> 2.3'
|
||||||
gem 'i18n-tasks', '~> 0.9', require: false
|
gem 'i18n-tasks', '~> 0.9', require: false
|
||||||
gem 'pry-byebug', '~> 3.7'
|
gem 'pry-byebug', '~> 3.6'
|
||||||
gem 'pry-rails', '~> 0.3'
|
gem 'pry-rails', '~> 0.3'
|
||||||
gem 'rspec-rails', '~> 3.8'
|
gem 'rspec-rails', '~> 3.8'
|
||||||
end
|
end
|
||||||
|
@ -114,30 +107,30 @@ group :production, :test do
|
||||||
end
|
end
|
||||||
|
|
||||||
group :test do
|
group :test do
|
||||||
gem 'capybara', '~> 3.29'
|
gem 'capybara', '~> 3.10'
|
||||||
gem 'climate_control', '~> 0.2'
|
gem 'climate_control', '~> 0.2'
|
||||||
gem 'faker', '~> 2.5'
|
gem 'faker', '~> 1.9'
|
||||||
gem 'microformats', '~> 4.1'
|
gem 'microformats', '~> 4.0'
|
||||||
gem 'rails-controller-testing', '~> 1.0'
|
gem 'rails-controller-testing', '~> 1.0'
|
||||||
gem 'rspec-sidekiq', '~> 3.0'
|
gem 'rspec-sidekiq', '~> 3.0'
|
||||||
gem 'simplecov', '~> 0.17', require: false
|
gem 'simplecov', '~> 0.16', require: false
|
||||||
gem 'webmock', '~> 3.7'
|
gem 'webmock', '~> 3.4'
|
||||||
gem 'parallel_tests', '~> 2.29'
|
gem 'parallel_tests', '~> 2.26'
|
||||||
end
|
end
|
||||||
|
|
||||||
group :development do
|
group :development do
|
||||||
gem 'active_record_query_trace', '~> 1.6'
|
gem 'active_record_query_trace', '~> 1.5'
|
||||||
gem 'annotate', '~> 2.7'
|
gem 'annotate', '~> 2.7'
|
||||||
gem 'better_errors', '~> 2.5'
|
gem 'better_errors', '~> 2.5'
|
||||||
gem 'binding_of_caller', '~> 0.7'
|
gem 'binding_of_caller', '~> 0.7'
|
||||||
gem 'bullet', '~> 6.0'
|
gem 'bullet', '~> 5.7'
|
||||||
gem 'letter_opener', '~> 1.7'
|
gem 'letter_opener', '~> 1.4'
|
||||||
gem 'letter_opener_web', '~> 1.3'
|
gem 'letter_opener_web', '~> 1.3'
|
||||||
gem 'memory_profiler'
|
gem 'memory_profiler'
|
||||||
gem 'rubocop', '~> 0.74', require: false
|
gem 'rubocop', '~> 0.60', require: false
|
||||||
gem 'rubocop-rails', '~> 2.3', require: false
|
gem 'brakeman', '~> 4.3', require: false
|
||||||
gem 'brakeman', '~> 4.6', require: false
|
|
||||||
gem 'bundler-audit', '~> 0.6', require: false
|
gem 'bundler-audit', '~> 0.6', require: false
|
||||||
|
gem 'scss_lint', '~> 0.57', require: false
|
||||||
|
|
||||||
gem 'capistrano', '~> 3.11'
|
gem 'capistrano', '~> 3.11'
|
||||||
gem 'capistrano-rails', '~> 1.4'
|
gem 'capistrano-rails', '~> 1.4'
|
||||||
|
@ -149,9 +142,6 @@ group :development do
|
||||||
end
|
end
|
||||||
|
|
||||||
group :production do
|
group :production do
|
||||||
gem 'lograge', '~> 0.11'
|
gem 'lograge', '~> 0.10'
|
||||||
gem 'redis-rails', '~> 5.0'
|
gem 'redis-rails', '~> 5.0'
|
||||||
end
|
end
|
||||||
|
|
||||||
gem 'concurrent-ruby', require: false
|
|
||||||
gem 'connection_pool', require: false
|
|
||||||
|
|
636
Gemfile.lock
636
Gemfile.lock
File diff suppressed because it is too large
Load Diff
14
Procfile
14
Procfile
|
@ -1,14 +1,2 @@
|
||||||
web: if [ "$RUN_STREAMING" != "true" ]; then BIND=0.0.0.0 bundle exec puma -C config/puma.rb; else BIND=0.0.0.0 node ./streaming; fi
|
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>
|
|
||||||
|
|
12
README.md
12
README.md
|
@ -4,13 +4,13 @@
|
||||||
[![GitHub release](https://img.shields.io/github/release/tootsuite/mastodon.svg)][releases]
|
[![GitHub release](https://img.shields.io/github/release/tootsuite/mastodon.svg)][releases]
|
||||||
[![Build Status](https://img.shields.io/circleci/project/github/tootsuite/mastodon.svg)][circleci]
|
[![Build Status](https://img.shields.io/circleci/project/github/tootsuite/mastodon.svg)][circleci]
|
||||||
[![Code Climate](https://img.shields.io/codeclimate/maintainability/tootsuite/mastodon.svg)][code_climate]
|
[![Code Climate](https://img.shields.io/codeclimate/maintainability/tootsuite/mastodon.svg)][code_climate]
|
||||||
[![Crowdin](https://d322cqt584bo4o.cloudfront.net/mastodon/localized.svg)][crowdin]
|
[![Translation status](https://weblate.joinmastodon.org/widgets/mastodon/-/svg-badge.svg)][weblate]
|
||||||
[![Docker Pulls](https://img.shields.io/docker/pulls/tootsuite/mastodon.svg)][docker]
|
[![Docker Pulls](https://img.shields.io/docker/pulls/tootsuite/mastodon.svg)][docker]
|
||||||
|
|
||||||
[releases]: https://github.com/tootsuite/mastodon/releases
|
[releases]: https://github.com/tootsuite/mastodon/releases
|
||||||
[circleci]: https://circleci.com/gh/tootsuite/mastodon
|
[circleci]: https://circleci.com/gh/tootsuite/mastodon
|
||||||
[code_climate]: https://codeclimate.com/github/tootsuite/mastodon
|
[code_climate]: https://codeclimate.com/github/tootsuite/mastodon
|
||||||
[crowdin]: https://crowdin.com/project/mastodon
|
[weblate]: https://weblate.joinmastodon.org/engage/mastodon/
|
||||||
[docker]: https://hub.docker.com/r/tootsuite/mastodon/
|
[docker]: https://hub.docker.com/r/tootsuite/mastodon/
|
||||||
|
|
||||||
Mastodon is a **free, open-source social network server** based on ActivityPub. Follow friends and discover new ones. Publish anything you want: links, pictures, text, video. All servers of Mastodon are interoperable as a federated network, i.e. users on one server can seamlessly communicate with users from another one. This includes non-Mastodon software that also implements ActivityPub!
|
Mastodon is a **free, open-source social network server** based on ActivityPub. Follow friends and discover new ones. Publish anything you want: links, pictures, text, video. All servers of Mastodon are interoperable as a federated network, i.e. users on one server can seamlessly communicate with users from another one. This includes non-Mastodon software that also implements ActivityPub!
|
||||||
|
@ -21,7 +21,7 @@ Click below to **learn more** in a video:
|
||||||
|
|
||||||
[youtube_demo]: https://www.youtube.com/watch?v=IPSbNdBmWKE
|
[youtube_demo]: https://www.youtube.com/watch?v=IPSbNdBmWKE
|
||||||
|
|
||||||
## Navigation
|
## Navigation
|
||||||
|
|
||||||
- [Project homepage 🐘](https://joinmastodon.org)
|
- [Project homepage 🐘](https://joinmastodon.org)
|
||||||
- [Support the development via Patreon][patreon]
|
- [Support the development via Patreon][patreon]
|
||||||
|
@ -55,7 +55,7 @@ Private posts, locked accounts, phrase filtering, muting, blocking and all sorts
|
||||||
|
|
||||||
**OAuth2 and a straightforward REST API**
|
**OAuth2 and a straightforward REST API**
|
||||||
|
|
||||||
Mastodon acts as an OAuth2 provider so 3rd party apps can use the REST and Streaming APIs, resulting in a rich app ecosystem with a lot of choices!
|
Mastodon acts as an OAuth2 provider so 3rd party apps can use the REST and Streaming APIs, resulting in a rich app ecosystem with a lot of choice!
|
||||||
|
|
||||||
## Deployment
|
## Deployment
|
||||||
|
|
||||||
|
@ -80,13 +80,13 @@ A **Vagrant** configuration is included for development purposes.
|
||||||
|
|
||||||
Mastodon is **free, open source software** licensed under **AGPLv3**.
|
Mastodon is **free, open source software** licensed under **AGPLv3**.
|
||||||
|
|
||||||
You can open issues for bugs you've found or features you think are missing. You can also submit pull requests to this repository, or submit translations using Weblate. To get started, take a look at [CONTRIBUTING.md](CONTRIBUTING.md). If your contributions are accepted into Mastodon, you can request to be paid through [our OpenCollective](https://opencollective.com/mastodon).
|
You can open issues for bugs you've found or features you think are missing. You can also submit pull requests to this repository, or submit translations using Weblate. To get started, take a look at [CONTRIBUTING.md](CONTRIBUTING.md)
|
||||||
|
|
||||||
**IRC channel**: #mastodon on irc.freenode.net
|
**IRC channel**: #mastodon on irc.freenode.net
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
Copyright (C) 2016-2019 Eugen Rochko & other Mastodon contributors (see [AUTHORS.md](AUTHORS.md))
|
Copyright (C) 2016-2018 Eugen Rochko & other Mastodon contributors (see [AUTHORS.md](AUTHORS.md))
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
|
|
@ -44,18 +44,7 @@ sudo apt-get install \
|
||||||
|
|
||||||
# Install rvm
|
# Install rvm
|
||||||
read RUBY_VERSION < .ruby-version
|
read RUBY_VERSION < .ruby-version
|
||||||
|
gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3
|
||||||
gpg_command="gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 7D2BAF1CF37B13E2069D6956105BD0E739499BDB"
|
|
||||||
$($gpg_command)
|
|
||||||
if [ $? -ne 0 ];then
|
|
||||||
echo "GPG command failed, This prevented RVM from installing."
|
|
||||||
echo "Retrying once..." && $($gpg_command)
|
|
||||||
if [ $? -ne 0 ];then
|
|
||||||
echo "GPG failed for the second time, please ensure network connectivity."
|
|
||||||
echo "Exiting..." && exit 1
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
|
|
9
app.json
9
app.json
|
@ -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"
|
||||||
|
|
|
@ -1,43 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
class AccountsIndex < Chewy::Index
|
|
||||||
settings index: { refresh_interval: '5m' }, 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,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
define_type ::Account.searchable.includes(:account_stat), delete_if: ->(account) { account.destroyed? || !account.searchable? } do
|
|
||||||
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.local.count }
|
|
||||||
field :followers_count, type: 'long', value: ->(account) { account.followers.local.count }
|
|
||||||
field :last_status_at, type: 'date', value: ->(account) { account.last_status_at || account.created_at }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -31,31 +31,31 @@ class StatusesIndex < Chewy::Index
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
define_type ::Status.unscoped.kept.without_reblogs.includes(:media_attachments), delete_if: ->(status) { status.searchable_by.empty? } do
|
define_type ::Status.unscoped.without_reblogs do
|
||||||
crutch :mentions do |collection|
|
crutch :mentions do |collection|
|
||||||
data = ::Mention.where(status_id: collection.map(&:id)).where(account: Account.local).pluck(:status_id, :account_id)
|
data = ::Mention.where(status_id: collection.map(&:id)).pluck(:status_id, :account_id)
|
||||||
data.each.with_object({}) { |(id, name), result| (result[id] ||= []).push(name) }
|
data.each.with_object({}) { |(id, name), result| (result[id] ||= []).push(name) }
|
||||||
end
|
end
|
||||||
|
|
||||||
crutch :favourites do |collection|
|
crutch :favourites do |collection|
|
||||||
data = ::Favourite.where(status_id: collection.map(&:id)).where(account: Account.local).pluck(:status_id, :account_id)
|
data = ::Favourite.where(status_id: collection.map(&:id)).pluck(:status_id, :account_id)
|
||||||
data.each.with_object({}) { |(id, name), result| (result[id] ||= []).push(name) }
|
data.each.with_object({}) { |(id, name), result| (result[id] ||= []).push(name) }
|
||||||
end
|
end
|
||||||
|
|
||||||
crutch :reblogs do |collection|
|
crutch :reblogs do |collection|
|
||||||
data = ::Status.where(reblog_of_id: collection.map(&:id)).where(account: Account.local).pluck(:reblog_of_id, :account_id)
|
data = ::Status.where(reblog_of_id: collection.map(&:id)).pluck(:reblog_of_id, :account_id)
|
||||||
data.each.with_object({}) { |(id, name), result| (result[id] ||= []).push(name) }
|
data.each.with_object({}) { |(id, name), result| (result[id] ||= []).push(name) }
|
||||||
end
|
end
|
||||||
|
|
||||||
root date_detection: false do
|
root date_detection: false do
|
||||||
field :id, type: 'long'
|
|
||||||
field :account_id, type: 'long'
|
field :account_id, type: 'long'
|
||||||
|
|
||||||
field :text, type: 'text', value: ->(status) { [status.spoiler_text, Formatter.instance.plaintext(status)].concat(status.media_attachments.map(&:description)).concat(status.preloadable_poll ? status.preloadable_poll.options : []).join("\n\n") } do
|
field :text, type: 'text', value: ->(status) { [status.spoiler_text, Formatter.instance.plaintext(status)].join("\n\n") } do
|
||||||
field :stemmed, type: 'text', analyzer: 'content'
|
field :stemmed, type: 'text', analyzer: 'content'
|
||||||
end
|
end
|
||||||
|
|
||||||
field :searchable_by, type: 'long', value: ->(status, crutches) { status.searchable_by(crutches) }
|
field :searchable_by, type: 'long', value: ->(status, crutches) { status.searchable_by(crutches) }
|
||||||
|
field :created_at, type: 'date'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,37 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
class TagsIndex < Chewy::Index
|
|
||||||
settings index: { refresh_interval: '15m' }, 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,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
define_type ::Tag.listable, delete_if: ->(tag) { tag.destroyed? || !tag.listable? } do
|
|
||||||
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) { tag.history.reduce(0) { |total, day| total + day[:accounts].to_i } }
|
|
||||||
field :last_status_at, type: 'date', value: ->(tag) { tag.last_status_at || tag.created_at }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,64 +1,42 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class AboutController < ApplicationController
|
class AboutController < ApplicationController
|
||||||
layout 'public'
|
before_action :set_body_classes
|
||||||
|
before_action :set_instance_presenter, only: [:show, :more, :terms]
|
||||||
|
|
||||||
before_action :require_open_federation!, only: [:show, :more]
|
def show
|
||||||
before_action :set_body_classes, only: :show
|
serializable_resource = ActiveModelSerializers::SerializableResource.new(InitialStatePresenter.new(initial_state_params), serializer: InitialStateSerializer)
|
||||||
before_action :set_instance_presenter
|
@initial_state_json = serializable_resource.to_json
|
||||||
before_action :set_expires_in, only: [:show, :more, :terms]
|
|
||||||
|
|
||||||
skip_before_action :require_functional!, only: [:more, :terms]
|
|
||||||
|
|
||||||
def show; end
|
|
||||||
|
|
||||||
def more
|
|
||||||
flash.now[:notice] = I18n.t('about.instance_actor_flash') if params[:instance_actor]
|
|
||||||
|
|
||||||
toc_generator = TOCGenerator.new(@instance_presenter.site_extended_description)
|
|
||||||
|
|
||||||
@contents = toc_generator.html
|
|
||||||
@table_of_contents = toc_generator.toc
|
|
||||||
@blocks = DomainBlock.with_user_facing_limitations.by_severity if display_blocks?
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def terms; end
|
def more
|
||||||
|
render layout: 'public'
|
||||||
|
end
|
||||||
|
|
||||||
helper_method :display_blocks?
|
def terms
|
||||||
helper_method :display_blocks_rationale?
|
render layout: 'public'
|
||||||
helper_method :public_fetch_mode?
|
end
|
||||||
helper_method :new_user
|
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def require_open_federation!
|
|
||||||
not_found if whitelist_mode?
|
|
||||||
end
|
|
||||||
|
|
||||||
def display_blocks?
|
|
||||||
Setting.show_domain_blocks == 'all' || (Setting.show_domain_blocks == 'users' && user_signed_in?)
|
|
||||||
end
|
|
||||||
|
|
||||||
def display_blocks_rationale?
|
|
||||||
Setting.show_domain_blocks_rationale == 'all' || (Setting.show_domain_blocks_rationale == 'users' && user_signed_in?)
|
|
||||||
end
|
|
||||||
|
|
||||||
def new_user
|
def new_user
|
||||||
User.new.tap do |user|
|
User.new.tap(&:build_account)
|
||||||
user.build_account
|
|
||||||
user.build_invite_request
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
helper_method :new_user
|
||||||
|
|
||||||
def set_instance_presenter
|
def set_instance_presenter
|
||||||
@instance_presenter = InstancePresenter.new
|
@instance_presenter = InstancePresenter.new
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_body_classes
|
def set_body_classes
|
||||||
@hide_navbar = true
|
@body_classes = 'with-modals'
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_expires_in
|
def initial_state_params
|
||||||
expires_in 0, public: true
|
{
|
||||||
|
settings: { known_fediverse: Setting.show_known_fediverse_at_about_page },
|
||||||
|
token: current_session&.token,
|
||||||
|
}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -4,22 +4,15 @@ class AccountsController < ApplicationController
|
||||||
PAGE_SIZE = 20
|
PAGE_SIZE = 20
|
||||||
|
|
||||||
include AccountControllerConcern
|
include AccountControllerConcern
|
||||||
include SignatureAuthentication
|
|
||||||
|
|
||||||
before_action :set_cache_headers
|
before_action :set_cache_headers
|
||||||
before_action :set_body_classes
|
|
||||||
|
|
||||||
skip_around_action :set_locale, if: -> { [:json, :rss].include?(request.format) }
|
|
||||||
skip_before_action :require_functional!
|
|
||||||
|
|
||||||
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?
|
@body_classes = 'with-modals'
|
||||||
|
|
||||||
@pinned_statuses = []
|
@pinned_statuses = []
|
||||||
@endorsed_accounts = @account.endorsed_accounts.to_a.sample(4)
|
@endorsed_accounts = @account.endorsed_accounts.to_a.sample(4)
|
||||||
@featured_hashtags = @account.featured_tags.order(statuses_count: :desc)
|
|
||||||
|
|
||||||
if current_account && @account.blocking?(current_account)
|
if current_account && @account.blocking?(current_account)
|
||||||
@statuses = []
|
@statuses = []
|
||||||
|
@ -29,7 +22,6 @@ class AccountsController < ApplicationController
|
||||||
@pinned_statuses = cache_collection(@account.pinned_statuses, Status) if show_pinned_statuses?
|
@pinned_statuses = cache_collection(@account.pinned_statuses, Status) if show_pinned_statuses?
|
||||||
@statuses = filtered_status_page(params)
|
@statuses = filtered_status_page(params)
|
||||||
@statuses = cache_collection(@statuses, Status)
|
@statuses = cache_collection(@statuses, Status)
|
||||||
@rss_url = rss_url
|
|
||||||
|
|
||||||
unless @statuses.empty?
|
unless @statuses.empty?
|
||||||
@older_url = older_url if @statuses.last.id > filtered_statuses.last.id
|
@older_url = older_url if @statuses.last.id > filtered_statuses.last.id
|
||||||
|
@ -37,34 +29,34 @@ class AccountsController < ApplicationController
|
||||||
end
|
end
|
||||||
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(PAGE_SIZE, params[:max_id], params[:since_id])
|
||||||
|
render xml: OStatus::AtomSerializer.render(OStatus::AtomSerializer.new.feed(@account, @entries.reject { |entry| entry.status.nil? }))
|
||||||
|
end
|
||||||
|
|
||||||
@statuses = filtered_statuses.without_reblogs.without_replies.limit(PAGE_SIZE)
|
format.rss do
|
||||||
@statuses = cache_collection(@statuses, Status)
|
@statuses = cache_collection(default_statuses.without_reblogs.without_replies.limit(PAGE_SIZE), Status)
|
||||||
render xml: RSS::AccountSerializer.render(@account, @statuses, params[:tag])
|
render xml: RSS::AccountSerializer.render(@account, @statuses)
|
||||||
end
|
end
|
||||||
|
|
||||||
format.json do
|
format.json do
|
||||||
expires_in 3.minutes, public: !(authorized_fetch_mode? && signed_request_account.present?)
|
skip_session!
|
||||||
render_with_cache json: @account, content_type: 'application/activity+json', serializer: ActivityPub::ActorSerializer, adapter: ActivityPub::Adapter, fields: restrict_fields_to
|
|
||||||
|
render_cached_json(['activitypub', 'actor', @account], content_type: 'application/activity+json') do
|
||||||
|
ActiveModelSerializers::SerializableResource.new(@account, serializer: ActivityPub::ActorSerializer, adapter: ActivityPub::Adapter)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def set_body_classes
|
|
||||||
@body_classes = 'with-modals'
|
|
||||||
end
|
|
||||||
|
|
||||||
def show_pinned_statuses?
|
def show_pinned_statuses?
|
||||||
[replies_requested?, media_requested?, tag_requested?, params[:max_id].present?, params[:min_id].present?].none?
|
[replies_requested?, media_requested?, params[:max_id].present?, params[:min_id].present?].none?
|
||||||
end
|
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
|
||||||
|
@ -86,29 +78,12 @@ class AccountsController < ApplicationController
|
||||||
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])
|
||||||
|
|
||||||
if tag
|
|
||||||
Status.tagged_with(tag.id)
|
|
||||||
else
|
|
||||||
Status.none
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def username_param
|
|
||||||
params[:username]
|
|
||||||
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
|
||||||
|
|
||||||
def older_url
|
def older_url
|
||||||
|
::Rails.logger.info("older: max_id #{@statuses.last.id}, url #{pagination_url(max_id: @statuses.last.id)}")
|
||||||
pagination_url(max_id: @statuses.last.id)
|
pagination_url(max_id: @statuses.last.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -117,9 +92,7 @@ class AccountsController < ApplicationController
|
||||||
end
|
end
|
||||||
|
|
||||||
def pagination_url(max_id: nil, min_id: nil)
|
def pagination_url(max_id: nil, min_id: nil)
|
||||||
if tag_requested?
|
if media_requested?
|
||||||
short_account_tag_url(@account, params[:tag], max_id: max_id, min_id: min_id)
|
|
||||||
elsif media_requested?
|
|
||||||
short_account_media_url(@account, max_id: max_id, min_id: min_id)
|
short_account_media_url(@account, max_id: max_id, min_id: min_id)
|
||||||
elsif replies_requested?
|
elsif replies_requested?
|
||||||
short_account_with_replies_url(@account, max_id: max_id, min_id: min_id)
|
short_account_with_replies_url(@account, max_id: max_id, min_id: min_id)
|
||||||
|
@ -129,15 +102,11 @@ class AccountsController < ApplicationController
|
||||||
end
|
end
|
||||||
|
|
||||||
def media_requested?
|
def media_requested?
|
||||||
request.path.ends_with?('/media') && !tag_requested?
|
request.path.ends_with?('/media')
|
||||||
end
|
end
|
||||||
|
|
||||||
def replies_requested?
|
def replies_requested?
|
||||||
request.path.ends_with?('/with_replies') && !tag_requested?
|
request.path.ends_with?('/with_replies')
|
||||||
end
|
|
||||||
|
|
||||||
def tag_requested?
|
|
||||||
request.path.split('.').first.ends_with?(Addressable::URI.parse("/tagged/#{params[:tag]}").normalize)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def filtered_status_page(params)
|
def filtered_status_page(params)
|
||||||
|
@ -147,12 +116,4 @@ class AccountsController < ApplicationController
|
||||||
filtered_statuses.paginate_by_max_id(PAGE_SIZE, params[:max_id], params[:since_id]).to_a
|
filtered_statuses.paginate_by_max_id(PAGE_SIZE, params[:max_id], params[:since_id]).to_a
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def restrict_fields_to
|
|
||||||
if signed_request_account.present? || public_fetch_mode?
|
|
||||||
# Return all fields
|
|
||||||
else
|
|
||||||
%i(id type preferred_username inbox public_key endpoints)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,11 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
class ActivityPub::BaseController < Api::BaseController
|
|
||||||
skip_before_action :require_authenticated_user!
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def set_cache_headers
|
|
||||||
response.headers['Vary'] = 'Signature' if authorized_fetch_mode?
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,21 +1,26 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class ActivityPub::CollectionsController < ActivityPub::BaseController
|
class ActivityPub::CollectionsController < Api::BaseController
|
||||||
include SignatureVerification
|
include SignatureVerification
|
||||||
include AccountOwnedConcern
|
|
||||||
|
|
||||||
before_action :require_signature!, if: :authorized_fetch_mode?
|
before_action :set_account
|
||||||
before_action :set_size
|
before_action :set_size
|
||||||
before_action :set_statuses
|
before_action :set_statuses
|
||||||
before_action :set_cache_headers
|
|
||||||
|
|
||||||
def show
|
def show
|
||||||
expires_in 3.minutes, public: public_fetch_mode?
|
render json: collection_presenter,
|
||||||
render_with_cache json: collection_presenter, content_type: 'application/activity+json', serializer: ActivityPub::CollectionSerializer, adapter: ActivityPub::Adapter, skip_activities: true
|
serializer: ActivityPub::CollectionSerializer,
|
||||||
|
adapter: ActivityPub::Adapter,
|
||||||
|
content_type: 'application/activity+json',
|
||||||
|
skip_activities: true
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def set_account
|
||||||
|
@account = Account.find_local!(params[:account_username])
|
||||||
|
end
|
||||||
|
|
||||||
def set_statuses
|
def set_statuses
|
||||||
@statuses = scope_for_collection
|
@statuses = scope_for_collection
|
||||||
@statuses = cache_collection(@statuses, Status)
|
@statuses = cache_collection(@statuses, Status)
|
||||||
|
@ -26,18 +31,18 @@ class ActivityPub::CollectionsController < ActivityPub::BaseController
|
||||||
when 'featured'
|
when 'featured'
|
||||||
@account.pinned_statuses.count
|
@account.pinned_statuses.count
|
||||||
else
|
else
|
||||||
raise ActiveRecord::RecordNotFound
|
raise ActiveRecord::NotFound
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def scope_for_collection
|
def scope_for_collection
|
||||||
case params[:id]
|
case params[:id]
|
||||||
when 'featured'
|
when 'featured'
|
||||||
return Status.none if @account.blocking?(signed_request_account)
|
@account.statuses.permitted_for(@account, signed_request_account).tap do |scope|
|
||||||
|
scope.merge!(@account.pinned_statuses)
|
||||||
@account.pinned_statuses
|
end
|
||||||
else
|
else
|
||||||
raise ActiveRecord::RecordNotFound
|
raise ActiveRecord::NotFound
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -1,45 +1,28 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class ActivityPub::InboxesController < ActivityPub::BaseController
|
class ActivityPub::InboxesController < Api::BaseController
|
||||||
include SignatureVerification
|
include SignatureVerification
|
||||||
include JsonLdHelper
|
|
||||||
include AccountOwnedConcern
|
|
||||||
|
|
||||||
before_action :skip_unknown_actor_delete
|
before_action :set_account
|
||||||
before_action :require_signature!
|
|
||||||
|
|
||||||
def create
|
def create
|
||||||
upgrade_account
|
if signed_request_account
|
||||||
process_payload
|
upgrade_account
|
||||||
head 202
|
process_payload
|
||||||
|
head 202
|
||||||
|
else
|
||||||
|
render plain: signature_verification_failure_reason, status: 401
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def skip_unknown_actor_delete
|
def set_account
|
||||||
head 202 if unknown_deleted_account?
|
@account = Account.find_local!(params[:account_username]) if params[:account_username]
|
||||||
end
|
|
||||||
|
|
||||||
def unknown_deleted_account?
|
|
||||||
json = Oj.load(body, mode: :strict)
|
|
||||||
json.is_a?(Hash) && json['type'] == 'Delete' && json['actor'].present? && json['actor'] == value_or_id(json['object']) && !Account.where(uri: json['actor']).exists?
|
|
||||||
rescue Oj::ParseError
|
|
||||||
false
|
|
||||||
end
|
|
||||||
|
|
||||||
def account_required?
|
|
||||||
params[:account_username].present?
|
|
||||||
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
|
||||||
|
@ -48,10 +31,11 @@ class ActivityPub::InboxesController < ActivityPub::BaseController
|
||||||
ResolveAccountWorker.perform_async(signed_request_account.acct)
|
ResolveAccountWorker.perform_async(signed_request_account.acct)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
Pubsubhubbub::UnsubscribeWorker.perform_async(signed_request_account.id) if signed_request_account.subscribed?
|
||||||
DeliveryFailureTracker.track_inverse_success!(signed_request_account)
|
DeliveryFailureTracker.track_inverse_success!(signed_request_account)
|
||||||
end
|
end
|
||||||
|
|
||||||
def process_payload
|
def process_payload
|
||||||
ActivityPub::ProcessingWorker.perform_async(signed_request_account.id, body, @account&.id)
|
ActivityPub::ProcessingWorker.perform_async(signed_request_account.id, body.force_encoding('UTF-8'), @account&.id)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,22 +1,23 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class ActivityPub::OutboxesController < ActivityPub::BaseController
|
class ActivityPub::OutboxesController < Api::BaseController
|
||||||
LIMIT = 20
|
LIMIT = 20
|
||||||
|
|
||||||
include SignatureVerification
|
include SignatureVerification
|
||||||
include AccountOwnedConcern
|
|
||||||
|
|
||||||
before_action :require_signature!, if: :authorized_fetch_mode?
|
before_action :set_account
|
||||||
before_action :set_statuses
|
before_action :set_statuses
|
||||||
before_action :set_cache_headers
|
|
||||||
|
|
||||||
def show
|
def show
|
||||||
expires_in(page_requested? ? 0 : 3.minutes, public: public_fetch_mode?)
|
|
||||||
render json: outbox_presenter, serializer: ActivityPub::OutboxSerializer, adapter: ActivityPub::Adapter, content_type: 'application/activity+json'
|
render json: outbox_presenter, serializer: ActivityPub::OutboxSerializer, adapter: ActivityPub::Adapter, content_type: 'application/activity+json'
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def set_account
|
||||||
|
@account = Account.find_local!(params[:account_username])
|
||||||
|
end
|
||||||
|
|
||||||
def outbox_presenter
|
def outbox_presenter
|
||||||
if page_requested?
|
if page_requested?
|
||||||
ActivityPub::CollectionPresenter.new(
|
ActivityPub::CollectionPresenter.new(
|
||||||
|
|
|
@ -1,71 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
class ActivityPub::RepliesController < ActivityPub::BaseController
|
|
||||||
include SignatureAuthentication
|
|
||||||
include Authorization
|
|
||||||
include AccountOwnedConcern
|
|
||||||
|
|
||||||
DESCENDANTS_LIMIT = 60
|
|
||||||
|
|
||||||
before_action :require_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 set_status
|
|
||||||
@status = @account.statuses.find(params[:status_id])
|
|
||||||
authorize @status, :show?
|
|
||||||
rescue Mastodon::NotPermittedError
|
|
||||||
raise ActiveRecord::RecordNotFound
|
|
||||||
end
|
|
||||||
|
|
||||||
def set_replies
|
|
||||||
@replies = page_params[:only_other_accounts] ? Status.where.not(account_id: @account.id) : @account.statuses
|
|
||||||
@replies = @replies.where(in_reply_to_id: @status.id, visibility: [:public, :unlisted])
|
|
||||||
@replies = @replies.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?
|
|
||||||
params[:page] == 'true'
|
|
||||||
end
|
|
||||||
|
|
||||||
def next_page
|
|
||||||
only_other_accounts = !(@replies&.last&.account_id == @account.id && @replies.size == DESCENDANTS_LIMIT)
|
|
||||||
account_status_replies_url(
|
|
||||||
@account,
|
|
||||||
@status,
|
|
||||||
page: true,
|
|
||||||
min_id: only_other_accounts && !page_params[:only_other_accounts] ? nil : @replies&.last&.id,
|
|
||||||
only_other_accounts: only_other_accounts
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def page_params
|
|
||||||
params_slice(:only_other_accounts, :min_id).merge(page: true)
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,36 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
module Admin
|
|
||||||
class AccountActionsController < BaseController
|
|
||||||
before_action :set_account
|
|
||||||
|
|
||||||
def new
|
|
||||||
@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
|
|
||||||
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
|
|
|
@ -14,7 +14,6 @@ module Admin
|
||||||
else
|
else
|
||||||
@account = @account_moderation_note.target_account
|
@account = @account_moderation_note.target_account
|
||||||
@moderation_notes = @account.targeted_moderation_notes.latest
|
@moderation_notes = @account.targeted_moderation_notes.latest
|
||||||
@warnings = @account.targeted_account_warnings.latest.custom
|
|
||||||
|
|
||||||
render template: 'admin/accounts/show'
|
render template: 'admin/accounts/show'
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,9 +2,9 @@
|
||||||
|
|
||||||
module Admin
|
module Admin
|
||||||
class AccountsController < BaseController
|
class AccountsController < BaseController
|
||||||
before_action :set_account, only: [:show, :redownload, :remove_avatar, :remove_header, :enable, :unsilence, :unsuspend, :memorialize, :approve, :reject]
|
before_action :set_account, only: [:show, :subscribe, :unsubscribe, :redownload, :remove_avatar, :enable, :disable, :memorialize]
|
||||||
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]
|
before_action :require_local_account!, only: [:enable, :disable, :memorialize]
|
||||||
|
|
||||||
def index
|
def index
|
||||||
authorize :account, :index?
|
authorize :account, :index?
|
||||||
|
@ -13,10 +13,20 @@ module Admin
|
||||||
|
|
||||||
def show
|
def show
|
||||||
authorize @account, :show?
|
authorize @account, :show?
|
||||||
|
|
||||||
@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.targeted_account_warnings.latest.custom
|
end
|
||||||
|
|
||||||
|
def subscribe
|
||||||
|
authorize @account, :subscribe?
|
||||||
|
Pubsubhubbub::SubscribeWorker.perform_async(@account.id)
|
||||||
|
redirect_to admin_account_path(@account.id)
|
||||||
|
end
|
||||||
|
|
||||||
|
def unsubscribe
|
||||||
|
authorize @account, :unsubscribe?
|
||||||
|
Pubsubhubbub::UnsubscribeWorker.perform_async(@account.id)
|
||||||
|
redirect_to admin_account_path(@account.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
def memorialize
|
def memorialize
|
||||||
|
@ -33,37 +43,19 @@ module Admin
|
||||||
redirect_to admin_account_path(@account.id)
|
redirect_to admin_account_path(@account.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
def approve
|
def disable
|
||||||
authorize @account.user, :approve?
|
authorize @account.user, :disable?
|
||||||
@account.user.approve!
|
@account.user.disable!
|
||||||
redirect_to admin_pending_accounts_path
|
log_action :disable, @account.user
|
||||||
end
|
|
||||||
|
|
||||||
def reject
|
|
||||||
authorize @account.user, :reject?
|
|
||||||
SuspendAccountService.new.call(@account, reserve_email: false, reserve_username: false)
|
|
||||||
redirect_to admin_pending_accounts_path
|
|
||||||
end
|
|
||||||
|
|
||||||
def unsilence
|
|
||||||
authorize @account, :unsilence?
|
|
||||||
@account.unsilence!
|
|
||||||
log_action :unsilence, @account
|
|
||||||
redirect_to admin_account_path(@account.id)
|
|
||||||
end
|
|
||||||
|
|
||||||
def unsuspend
|
|
||||||
authorize @account, :unsuspend?
|
|
||||||
@account.unsuspend!
|
|
||||||
log_action :unsuspend, @account
|
|
||||||
redirect_to admin_account_path(@account.id)
|
redirect_to admin_account_path(@account.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
def redownload
|
def redownload
|
||||||
authorize @account, :redownload?
|
authorize @account, :redownload?
|
||||||
|
|
||||||
@account.update!(last_webfingered_at: nil)
|
@account.reset_avatar!
|
||||||
ResolveAccountService.new.call(@account)
|
@account.reset_header!
|
||||||
|
@account.save!
|
||||||
|
|
||||||
redirect_to admin_account_path(@account.id)
|
redirect_to admin_account_path(@account.id)
|
||||||
end
|
end
|
||||||
|
@ -79,17 +71,6 @@ module Admin
|
||||||
redirect_to admin_account_path(@account.id)
|
redirect_to admin_account_path(@account.id)
|
||||||
end
|
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)
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def set_account
|
def set_account
|
||||||
|
@ -113,10 +94,8 @@ module Admin
|
||||||
:local,
|
:local,
|
||||||
:remote,
|
:remote,
|
||||||
:by_domain,
|
:by_domain,
|
||||||
:active,
|
|
||||||
:pending,
|
|
||||||
:disabled,
|
|
||||||
:silenced,
|
:silenced,
|
||||||
|
:alphabetic,
|
||||||
:suspended,
|
:suspended,
|
||||||
:username,
|
:username,
|
||||||
:display_name,
|
:display_name,
|
||||||
|
|
|
@ -15,9 +15,5 @@ module Admin
|
||||||
def set_body_classes
|
def set_body_classes
|
||||||
@body_classes = 'admin'
|
@body_classes = 'admin'
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_user
|
|
||||||
@user = Account.find(params[:account_id]).user || raise(ActiveRecord::RecordNotFound)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -25,6 +25,10 @@ module Admin
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def set_user
|
||||||
|
@user = Account.find(params[:account_id]).user || raise(ActiveRecord::RecordNotFound)
|
||||||
|
end
|
||||||
|
|
||||||
def check_confirmation
|
def check_confirmation
|
||||||
if @user.confirmed?
|
if @user.confirmed?
|
||||||
flash[:error] = I18n.t('admin.accounts.resend_confirmation.already_confirmed')
|
flash[:error] = I18n.t('admin.accounts.resend_confirmation.already_confirmed')
|
||||||
|
|
|
@ -2,20 +2,16 @@
|
||||||
|
|
||||||
module Admin
|
module Admin
|
||||||
class CustomEmojisController < BaseController
|
class CustomEmojisController < BaseController
|
||||||
include ObfuscateFilename
|
before_action :set_custom_emoji, except: [:index, :new, :create]
|
||||||
|
before_action :set_filter_params
|
||||||
obfuscate_filename [:custom_emoji, :image]
|
|
||||||
|
|
||||||
def index
|
def index
|
||||||
authorize :custom_emoji, :index?
|
authorize :custom_emoji, :index?
|
||||||
|
|
||||||
@custom_emojis = filtered_custom_emojis.eager_load(:local_counterpart).page(params[:page])
|
@custom_emojis = filtered_custom_emojis.eager_load(:local_counterpart).page(params[:page])
|
||||||
@form = Form::CustomEmojiBatch.new
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def new
|
def new
|
||||||
authorize :custom_emoji, :create?
|
authorize :custom_emoji, :create?
|
||||||
|
|
||||||
@custom_emoji = CustomEmoji.new
|
@custom_emoji = CustomEmoji.new
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -32,17 +28,69 @@ module Admin
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def batch
|
def update
|
||||||
@form = Form::CustomEmojiBatch.new(form_custom_emoji_batch_params.merge(current_account: current_account, action: action_from_button))
|
authorize @custom_emoji, :update?
|
||||||
@form.save
|
|
||||||
rescue ActionController::ParameterMissing
|
if @custom_emoji.update(resource_params)
|
||||||
flash[:alert] = I18n.t('admin.accounts.no_account_selected')
|
log_action :update, @custom_emoji
|
||||||
ensure
|
flash[:notice] = I18n.t('admin.custom_emojis.updated_msg')
|
||||||
redirect_to admin_custom_emojis_path(filter_params)
|
else
|
||||||
|
flash[:alert] = I18n.t('admin.custom_emojis.update_failed_msg')
|
||||||
|
end
|
||||||
|
redirect_to admin_custom_emojis_path(page: params[:page], **@filter_params)
|
||||||
|
end
|
||||||
|
|
||||||
|
def destroy
|
||||||
|
authorize @custom_emoji, :destroy?
|
||||||
|
@custom_emoji.destroy!
|
||||||
|
log_action :destroy, @custom_emoji
|
||||||
|
flash[:notice] = I18n.t('admin.custom_emojis.destroyed_msg')
|
||||||
|
redirect_to admin_custom_emojis_path(page: params[:page], **@filter_params)
|
||||||
|
end
|
||||||
|
|
||||||
|
def copy
|
||||||
|
authorize @custom_emoji, :copy?
|
||||||
|
|
||||||
|
emoji = CustomEmoji.find_or_initialize_by(domain: nil,
|
||||||
|
shortcode: @custom_emoji.shortcode)
|
||||||
|
emoji.image = @custom_emoji.image
|
||||||
|
|
||||||
|
if emoji.save
|
||||||
|
log_action :create, emoji
|
||||||
|
flash[:notice] = I18n.t('admin.custom_emojis.copied_msg')
|
||||||
|
else
|
||||||
|
flash[:alert] = I18n.t('admin.custom_emojis.copy_failed_msg')
|
||||||
|
end
|
||||||
|
|
||||||
|
redirect_to admin_custom_emojis_path(page: params[:page], **@filter_params)
|
||||||
|
end
|
||||||
|
|
||||||
|
def enable
|
||||||
|
authorize @custom_emoji, :enable?
|
||||||
|
@custom_emoji.update!(disabled: false)
|
||||||
|
log_action :enable, @custom_emoji
|
||||||
|
flash[:notice] = I18n.t('admin.custom_emojis.enabled_msg')
|
||||||
|
redirect_to admin_custom_emojis_path(page: params[:page], **@filter_params)
|
||||||
|
end
|
||||||
|
|
||||||
|
def disable
|
||||||
|
authorize @custom_emoji, :disable?
|
||||||
|
@custom_emoji.update!(disabled: true)
|
||||||
|
log_action :disable, @custom_emoji
|
||||||
|
flash[:notice] = I18n.t('admin.custom_emojis.disabled_msg')
|
||||||
|
redirect_to admin_custom_emojis_path(page: params[:page], **@filter_params)
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def set_custom_emoji
|
||||||
|
@custom_emoji = CustomEmoji.find(params[:id])
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_filter_params
|
||||||
|
@filter_params = filter_params.to_hash.symbolize_keys
|
||||||
|
end
|
||||||
|
|
||||||
def resource_params
|
def resource_params
|
||||||
params.require(:custom_emoji).permit(:shortcode, :image, :visible_in_picker)
|
params.require(:custom_emoji).permit(:shortcode, :image, :visible_in_picker)
|
||||||
end
|
end
|
||||||
|
@ -52,29 +100,12 @@ module Admin
|
||||||
end
|
end
|
||||||
|
|
||||||
def filter_params
|
def filter_params
|
||||||
params.slice(:local, :remote, :by_domain, :shortcode, :page).permit(:local, :remote, :by_domain, :shortcode, :page)
|
params.permit(
|
||||||
end
|
:local,
|
||||||
|
:remote,
|
||||||
def action_from_button
|
:by_domain,
|
||||||
if params[:update]
|
:shortcode
|
||||||
'update'
|
)
|
||||||
elsif params[:list]
|
|
||||||
'list'
|
|
||||||
elsif params[:unlist]
|
|
||||||
'unlist'
|
|
||||||
elsif params[:enable]
|
|
||||||
'enable'
|
|
||||||
elsif params[:disable]
|
|
||||||
'disable'
|
|
||||||
elsif params[:copy]
|
|
||||||
'copy'
|
|
||||||
elsif params[:delete]
|
|
||||||
'delete'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def form_custom_emoji_batch_params
|
|
||||||
params.require(:form_custom_emoji_batch).permit(:action, :category_id, :category_name, custom_emoji_ids: [])
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,13 +5,12 @@ module Admin
|
||||||
class DashboardController < BaseController
|
class DashboardController < BaseController
|
||||||
def index
|
def index
|
||||||
@users_count = User.count
|
@users_count = User.count
|
||||||
@pending_users_count = User.pending.count
|
|
||||||
@registrations_week = Redis.current.get("activity:accounts:local:#{current_week}") || 0
|
@registrations_week = Redis.current.get("activity:accounts:local:#{current_week}") || 0
|
||||||
@logins_week = Redis.current.pfcount("activity:logins:#{current_week}")
|
@logins_week = Redis.current.pfcount("activity:logins:#{current_week}")
|
||||||
@interactions_week = Redis.current.get("activity:interactions:#{current_week}") || 0
|
@interactions_week = Redis.current.get("activity:interactions:#{current_week}") || 0
|
||||||
@relay_enabled = Relay.enabled.exists?
|
@relay_enabled = Relay.enabled.exists?
|
||||||
@single_user_mode = Rails.configuration.x.single_user_mode
|
@single_user_mode = Rails.configuration.x.single_user_mode
|
||||||
@registrations_enabled = Setting.registrations_mode != 'none'
|
@registrations_enabled = Setting.open_registrations
|
||||||
@deletions_enabled = Setting.open_deletion
|
@deletions_enabled = Setting.open_deletion
|
||||||
@invites_enabled = Setting.min_invite_role == 'user'
|
@invites_enabled = Setting.min_invite_role == 'user'
|
||||||
@search_enabled = Chewy.enabled?
|
@search_enabled = Chewy.enabled?
|
||||||
|
@ -20,7 +19,7 @@ module Admin
|
||||||
@redis_version = redis_info['redis_version']
|
@redis_version = redis_info['redis_version']
|
||||||
@reports_count = Report.unresolved.count
|
@reports_count = Report.unresolved.count
|
||||||
@queue_backlog = Sidekiq::Stats.new.enqueued
|
@queue_backlog = Sidekiq::Stats.new.enqueued
|
||||||
@recent_users = User.confirmed.recent.includes(:account).limit(8)
|
@recent_users = User.confirmed.recent.includes(:account).limit(4)
|
||||||
@database_size = ActiveRecord::Base.connection.execute('SELECT pg_database_size(current_database())').first['pg_database_size']
|
@database_size = ActiveRecord::Base.connection.execute('SELECT pg_database_size(current_database())').first['pg_database_size']
|
||||||
@redis_size = redis_info['used_memory']
|
@redis_size = redis_info['used_memory']
|
||||||
@ldap_enabled = ENV['LDAP_ENABLED'] == 'true'
|
@ldap_enabled = ENV['LDAP_ENABLED'] == 'true'
|
||||||
|
@ -28,14 +27,7 @@ module Admin
|
||||||
@saml_enabled = ENV['SAML_ENABLED'] == 'true'
|
@saml_enabled = ENV['SAML_ENABLED'] == 'true'
|
||||||
@pam_enabled = ENV['PAM_ENABLED'] == 'true'
|
@pam_enabled = ENV['PAM_ENABLED'] == 'true'
|
||||||
@hidden_service = ENV['ALLOW_ACCESS_TO_HIDDEN_SERVICE'] == 'true'
|
@hidden_service = ENV['ALLOW_ACCESS_TO_HIDDEN_SERVICE'] == 'true'
|
||||||
@trending_hashtags = TrendingTags.get(10, filtered: false)
|
@trending_hashtags = TrendingTags.get(7)
|
||||||
@pending_tags_count = Tag.pending_review.count
|
|
||||||
@authorized_fetch = authorized_fetch_mode?
|
|
||||||
@whitelist_enabled = whitelist_mode?
|
|
||||||
@profile_directory = Setting.profile_directory
|
|
||||||
@timeline_preview = Setting.timeline_preview
|
|
||||||
@spam_check_enabled = Setting.spam_check_enabled
|
|
||||||
@trends_enabled = Setting.trends
|
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
@ -45,13 +37,7 @@ module Admin
|
||||||
end
|
end
|
||||||
|
|
||||||
def redis_info
|
def redis_info
|
||||||
@redis_info ||= begin
|
@redis_info ||= Redis.current.info
|
||||||
if Redis.current.is_a?(Redis::Namespace)
|
|
||||||
Redis.current.redis.info
|
|
||||||
else
|
|
||||||
Redis.current.info
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
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,56 +2,29 @@
|
||||||
|
|
||||||
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 index
|
||||||
|
authorize :domain_block, :index?
|
||||||
|
@domain_blocks = DomainBlock.page(params[:page])
|
||||||
|
end
|
||||||
|
|
||||||
def new
|
def new
|
||||||
authorize :domain_block, :create?
|
authorize :domain_block, :create?
|
||||||
@domain_block = DomainBlock.new(domain: params[:_domain])
|
@domain_block = DomainBlock.new
|
||||||
end
|
|
||||||
|
|
||||||
def edit
|
|
||||||
authorize :domain_block, :create?
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def create
|
def create
|
||||||
authorize :domain_block, :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[:domain].clear
|
|
||||||
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, :create?
|
|
||||||
|
|
||||||
@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 :create, @domain_block
|
log_action :create, @domain_block
|
||||||
redirect_to admin_instances_path(limited: '1'), notice: I18n.t('admin.domain_blocks.created_msg')
|
redirect_to admin_domain_blocks_path, notice: I18n.t('admin.domain_blocks.created_msg')
|
||||||
else
|
else
|
||||||
render :edit
|
render :new
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -61,9 +34,9 @@ module Admin
|
||||||
|
|
||||||
def destroy
|
def destroy
|
||||||
authorize @domain_block, :destroy?
|
authorize @domain_block, :destroy?
|
||||||
UnblockDomainService.new.call(@domain_block)
|
UnblockDomainService.new.call(@domain_block, retroactive_unblock?)
|
||||||
log_action :destroy, @domain_block
|
log_action :destroy, @domain_block
|
||||||
redirect_to admin_instances_path(limited: '1'), notice: I18n.t('admin.domain_blocks.destroyed_msg')
|
redirect_to admin_domain_blocks_path, notice: I18n.t('admin.domain_blocks.destroyed_msg')
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
@ -72,12 +45,12 @@ module Admin
|
||||||
@domain_block = DomainBlock.find(params[:id])
|
@domain_block = DomainBlock.find(params[:id])
|
||||||
end
|
end
|
||||||
|
|
||||||
def update_params
|
def resource_params
|
||||||
params.require(:domain_block).permit(:severity, :reject_media, :reject_reports, :private_comment, :public_comment)
|
params.require(:domain_block).permit(:domain, :severity, :reject_media, :reject_reports, :retroactive)
|
||||||
end
|
end
|
||||||
|
|
||||||
def resource_params
|
def retroactive_unblock?
|
||||||
params.require(:domain_block).permit(:domain, :severity, :reject_media, :reject_reports, :private_comment, :public_comment)
|
ActiveRecord::Type.lookup(:boolean).cast(resource_params[:retroactive])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,18 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
module Admin
|
|
||||||
class FollowersController < BaseController
|
|
||||||
before_action :set_account
|
|
||||||
|
|
||||||
PER_PAGE = 40
|
|
||||||
|
|
||||||
def index
|
|
||||||
authorize :account, :index?
|
|
||||||
@followers = @account.followers.local.recent.page(params[:page]).per(PER_PAGE)
|
|
||||||
end
|
|
||||||
|
|
||||||
def set_account
|
|
||||||
@account = Account.find(params[:account_id])
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -2,53 +2,22 @@
|
||||||
|
|
||||||
module Admin
|
module Admin
|
||||||
class InstancesController < BaseController
|
class InstancesController < BaseController
|
||||||
before_action :set_domain_block, only: :show
|
|
||||||
before_action :set_domain_allow, only: :show
|
|
||||||
before_action :set_instance, only: :show
|
|
||||||
|
|
||||||
def index
|
def index
|
||||||
authorize :instance, :index?
|
authorize :instance, :index?
|
||||||
|
|
||||||
@instances = ordered_instances
|
@instances = ordered_instances
|
||||||
end
|
end
|
||||||
|
|
||||||
def show
|
def resubscribe
|
||||||
authorize :instance, :show?
|
authorize :instance, :resubscribe?
|
||||||
|
params.require(:by_domain)
|
||||||
@following_count = Follow.where(account: Account.where(domain: params[:id])).count
|
Pubsubhubbub::SubscribeWorker.push_bulk(subscribeable_accounts.pluck(:id))
|
||||||
@followers_count = Follow.where(target_account: Account.where(domain: params[:id])).count
|
redirect_to admin_instances_path
|
||||||
@reports_count = Report.where(target_account: Account.where(domain: params[:id])).count
|
|
||||||
@blocks_count = Block.where(target_account: Account.where(domain: params[:id])).count
|
|
||||||
@available = DeliveryFailureTracker.available?(Account.select(:shared_inbox_url).where(domain: params[:id]).first&.shared_inbox_url)
|
|
||||||
@media_storage = MediaAttachment.where(account: Account.where(domain: params[:id])).sum(:file_file_size)
|
|
||||||
@private_comment = @domain_block&.private_comment
|
|
||||||
@public_comment = @domain_block&.public_comment
|
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def set_domain_block
|
|
||||||
@domain_block = DomainBlock.rule_for(params[:id])
|
|
||||||
end
|
|
||||||
|
|
||||||
def set_domain_allow
|
|
||||||
@domain_allow = DomainAllow.rule_for(params[:id])
|
|
||||||
end
|
|
||||||
|
|
||||||
def set_instance
|
|
||||||
resource = Account.by_domain_accounts.find_by(domain: params[:id])
|
|
||||||
resource ||= @domain_block
|
|
||||||
resource ||= @domain_allow
|
|
||||||
|
|
||||||
if resource
|
|
||||||
@instance = Instance.new(resource)
|
|
||||||
else
|
|
||||||
not_found
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def filtered_instances
|
def filtered_instances
|
||||||
InstanceFilter.new(whitelist_mode? ? { allowed: true } : filter_params).results
|
InstanceFilter.new(filter_params).results
|
||||||
end
|
end
|
||||||
|
|
||||||
def paginated_instances
|
def paginated_instances
|
||||||
|
@ -58,11 +27,17 @@ module Admin
|
||||||
helper_method :paginated_instances
|
helper_method :paginated_instances
|
||||||
|
|
||||||
def ordered_instances
|
def ordered_instances
|
||||||
paginated_instances.map { |resource| Instance.new(resource) }
|
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.permit(:limited, :by_domain)
|
params.permit(
|
||||||
|
:domain_name
|
||||||
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,52 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
module Admin
|
|
||||||
class PendingAccountsController < BaseController
|
|
||||||
before_action :set_accounts, only: :index
|
|
||||||
|
|
||||||
def index
|
|
||||||
@form = Form::AccountBatch.new
|
|
||||||
end
|
|
||||||
|
|
||||||
def batch
|
|
||||||
@form = Form::AccountBatch.new(form_account_batch_params.merge(current_account: current_account, action: action_from_button))
|
|
||||||
@form.save
|
|
||||||
rescue ActionController::ParameterMissing
|
|
||||||
flash[:alert] = I18n.t('admin.accounts.no_account_selected')
|
|
||||||
ensure
|
|
||||||
redirect_to admin_pending_accounts_path(current_params)
|
|
||||||
end
|
|
||||||
|
|
||||||
def approve_all
|
|
||||||
Form::AccountBatch.new(current_account: current_account, account_ids: User.pending.pluck(:account_id), action: 'approve').save
|
|
||||||
redirect_to admin_pending_accounts_path(current_params)
|
|
||||||
end
|
|
||||||
|
|
||||||
def reject_all
|
|
||||||
Form::AccountBatch.new(current_account: current_account, account_ids: User.pending.pluck(:account_id), action: 'reject').save
|
|
||||||
redirect_to admin_pending_accounts_path(current_params)
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def set_accounts
|
|
||||||
@accounts = Account.joins(:user).merge(User.pending.recent).includes(user: :invite_request).page(params[:page])
|
|
||||||
end
|
|
||||||
|
|
||||||
def form_account_batch_params
|
|
||||||
params.require(:form_account_batch).permit(:action, account_ids: [])
|
|
||||||
end
|
|
||||||
|
|
||||||
def action_from_button
|
|
||||||
if params[:approve]
|
|
||||||
'approve'
|
|
||||||
elsif params[:reject]
|
|
||||||
'reject'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def current_params
|
|
||||||
params.slice(:page).permit(:page)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -3,7 +3,6 @@
|
||||||
module Admin
|
module Admin
|
||||||
class RelaysController < BaseController
|
class RelaysController < BaseController
|
||||||
before_action :set_relay, except: [:index, :new, :create]
|
before_action :set_relay, except: [:index, :new, :create]
|
||||||
before_action :require_signatures_enabled!, only: [:new, :create, :enable]
|
|
||||||
|
|
||||||
def index
|
def index
|
||||||
authorize :relay, :update?
|
authorize :relay, :update?
|
||||||
|
@ -12,7 +11,7 @@ module Admin
|
||||||
|
|
||||||
def new
|
def new
|
||||||
authorize :relay, :update?
|
authorize :relay, :update?
|
||||||
@relay = Relay.new
|
@relay = Relay.new(inbox_url: Relay::PRESET_RELAY)
|
||||||
end
|
end
|
||||||
|
|
||||||
def create
|
def create
|
||||||
|
@ -55,9 +54,5 @@ module Admin
|
||||||
def resource_params
|
def resource_params
|
||||||
params.require(:relay).permit(:inbox_url)
|
params.require(:relay).permit(:inbox_url)
|
||||||
end
|
end
|
||||||
|
|
||||||
def require_signatures_enabled!
|
|
||||||
redirect_to admin_relays_path, alert: I18n.t('admin.relays.signatures_not_enabled') if authorized_fetch_mode?
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,10 +5,10 @@ module Admin
|
||||||
before_action :set_report_note, only: [:destroy]
|
before_action :set_report_note, only: [:destroy]
|
||||||
|
|
||||||
def create
|
def create
|
||||||
authorize :report_note, :create?
|
authorize ReportNote, :create?
|
||||||
|
|
||||||
@report_note = current_account.report_notes.new(resource_params)
|
@report_note = current_account.report_notes.new(resource_params)
|
||||||
@report = @report_note.report
|
@report = @report_note.report
|
||||||
|
|
||||||
if @report_note.save
|
if @report_note.save
|
||||||
if params[:create_and_resolve]
|
if params[:create_and_resolve]
|
||||||
|
@ -26,8 +26,9 @@ module Admin
|
||||||
|
|
||||||
redirect_to admin_report_path(@report), notice: I18n.t('admin.report_notes.created_msg')
|
redirect_to admin_report_path(@report), notice: I18n.t('admin.report_notes.created_msg')
|
||||||
else
|
else
|
||||||
@report_notes = (@report.notes.latest + @report.history + @report.target_account.targeted_account_warnings.latest.custom).sort_by(&:created_at)
|
@report_notes = @report.notes.latest
|
||||||
@form = Form::StatusBatch.new
|
@report_history = @report.history
|
||||||
|
@form = Form::StatusBatch.new
|
||||||
|
|
||||||
render template: 'admin/reports/show'
|
render template: 'admin/reports/show'
|
||||||
end
|
end
|
||||||
|
|
|
@ -10,10 +10,6 @@ module Admin
|
||||||
@form = Form::StatusBatch.new(form_status_batch_params.merge(current_account: current_account, action: action_from_button))
|
@form = Form::StatusBatch.new(form_status_batch_params.merge(current_account: current_account, action: action_from_button))
|
||||||
flash[:alert] = I18n.t('admin.statuses.failed_to_execute') unless @form.save
|
flash[:alert] = I18n.t('admin.statuses.failed_to_execute') unless @form.save
|
||||||
|
|
||||||
redirect_to admin_report_path(@report)
|
|
||||||
rescue ActionController::ParameterMissing
|
|
||||||
flash[:alert] = I18n.t('admin.statuses.no_status_selected')
|
|
||||||
|
|
||||||
redirect_to admin_report_path(@report)
|
redirect_to admin_report_path(@report)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -13,42 +13,75 @@ module Admin
|
||||||
authorize @report, :show?
|
authorize @report, :show?
|
||||||
|
|
||||||
@report_note = @report.notes.new
|
@report_note = @report.notes.new
|
||||||
@report_notes = (@report.notes.latest + @report.history + @report.target_account.targeted_account_warnings.latest.custom).sort_by(&:created_at)
|
@report_notes = (@report.notes.latest + @report.history).sort_by(&:created_at)
|
||||||
@form = Form::StatusBatch.new
|
@form = Form::StatusBatch.new
|
||||||
end
|
end
|
||||||
|
|
||||||
def assign_to_self
|
def update
|
||||||
authorize @report, :update?
|
authorize @report, :update?
|
||||||
@report.update!(assigned_account_id: current_account.id)
|
process_report
|
||||||
log_action :assigned_to_self, @report
|
|
||||||
redirect_to admin_report_path(@report)
|
|
||||||
end
|
|
||||||
|
|
||||||
def unassign
|
if @report.action_taken?
|
||||||
authorize @report, :update?
|
redirect_to admin_reports_path, notice: I18n.t('admin.reports.resolved_msg')
|
||||||
@report.update!(assigned_account_id: nil)
|
else
|
||||||
log_action :unassigned, @report
|
redirect_to admin_report_path(@report)
|
||||||
redirect_to admin_report_path(@report)
|
end
|
||||||
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
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def process_report
|
||||||
|
case params[:outcome].to_s
|
||||||
|
when 'assign_to_self'
|
||||||
|
@report.update!(assigned_account_id: current_account.id)
|
||||||
|
log_action :assigned_to_self, @report
|
||||||
|
when 'unassign'
|
||||||
|
@report.update!(assigned_account_id: nil)
|
||||||
|
log_action :unassigned, @report
|
||||||
|
when 'reopen'
|
||||||
|
@report.unresolve!
|
||||||
|
log_action :reopen, @report
|
||||||
|
when 'resolve'
|
||||||
|
@report.resolve!(current_account)
|
||||||
|
log_action :resolve, @report
|
||||||
|
when 'disable'
|
||||||
|
@report.resolve!(current_account)
|
||||||
|
@report.target_account.user.disable!
|
||||||
|
|
||||||
|
log_action :resolve, @report
|
||||||
|
log_action :disable, @report.target_account.user
|
||||||
|
|
||||||
|
resolve_all_target_account_reports
|
||||||
|
when 'silence'
|
||||||
|
@report.resolve!(current_account)
|
||||||
|
@report.target_account.update!(silenced: true)
|
||||||
|
|
||||||
|
log_action :resolve, @report
|
||||||
|
log_action :silence, @report.target_account
|
||||||
|
|
||||||
|
resolve_all_target_account_reports
|
||||||
|
else
|
||||||
|
raise ActiveRecord::RecordNotFound
|
||||||
|
end
|
||||||
|
|
||||||
|
@report.reload
|
||||||
|
end
|
||||||
|
|
||||||
|
def resolve_all_target_account_reports
|
||||||
|
unresolved_reports_for_target_account.update_all(action_taken: true, action_taken_by_account_id: current_account.id)
|
||||||
|
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
|
||||||
|
|
|
@ -10,5 +10,11 @@ module Admin
|
||||||
log_action :reset_password, @user
|
log_action :reset_password, @user
|
||||||
redirect_to admin_accounts_path
|
redirect_to admin_accounts_path
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def set_user
|
||||||
|
@user = Account.find(params[:account_id]).user || raise(ActiveRecord::RecordNotFound)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -17,5 +17,11 @@ module Admin
|
||||||
log_action :demote, @user
|
log_action :demote, @user
|
||||||
redirect_to admin_account_path(@user.account_id)
|
redirect_to admin_account_path(@user.account_id)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def set_user
|
||||||
|
@user = Account.find(params[:account_id]).user || raise(ActiveRecord::RecordNotFound)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,29 +2,83 @@
|
||||||
|
|
||||||
module Admin
|
module Admin
|
||||||
class SettingsController < BaseController
|
class SettingsController < BaseController
|
||||||
|
ADMIN_SETTINGS = %w(
|
||||||
|
site_contact_username
|
||||||
|
site_contact_email
|
||||||
|
site_title
|
||||||
|
site_short_description
|
||||||
|
site_description
|
||||||
|
site_extended_description
|
||||||
|
site_terms
|
||||||
|
open_registrations
|
||||||
|
closed_registrations_message
|
||||||
|
open_deletion
|
||||||
|
timeline_preview
|
||||||
|
show_staff_badge
|
||||||
|
bootstrap_timeline_accounts
|
||||||
|
theme
|
||||||
|
thumbnail
|
||||||
|
hero
|
||||||
|
mascot
|
||||||
|
min_invite_role
|
||||||
|
activity_api_enabled
|
||||||
|
peers_api_enabled
|
||||||
|
show_known_fediverse_at_about_page
|
||||||
|
preview_sensitive_media
|
||||||
|
custom_css
|
||||||
|
).freeze
|
||||||
|
|
||||||
|
BOOLEAN_SETTINGS = %w(
|
||||||
|
open_registrations
|
||||||
|
open_deletion
|
||||||
|
timeline_preview
|
||||||
|
show_staff_badge
|
||||||
|
activity_api_enabled
|
||||||
|
peers_api_enabled
|
||||||
|
show_known_fediverse_at_about_page
|
||||||
|
preview_sensitive_media
|
||||||
|
).freeze
|
||||||
|
|
||||||
|
UPLOAD_SETTINGS = %w(
|
||||||
|
thumbnail
|
||||||
|
hero
|
||||||
|
mascot
|
||||||
|
).freeze
|
||||||
|
|
||||||
def edit
|
def edit
|
||||||
authorize :settings, :show?
|
authorize :settings, :show?
|
||||||
|
|
||||||
@admin_settings = Form::AdminSettings.new
|
@admin_settings = Form::AdminSettings.new
|
||||||
end
|
end
|
||||||
|
|
||||||
def update
|
def update
|
||||||
authorize :settings, :update?
|
authorize :settings, :update?
|
||||||
|
|
||||||
@admin_settings = Form::AdminSettings.new(settings_params)
|
settings_params.each do |key, value|
|
||||||
|
if UPLOAD_SETTINGS.include?(key)
|
||||||
if @admin_settings.save
|
upload = SiteUpload.where(var: key).first_or_initialize(var: key)
|
||||||
flash[:notice] = I18n.t('generic.changes_saved_msg')
|
upload.update(file: value)
|
||||||
redirect_to edit_admin_settings_path
|
else
|
||||||
else
|
setting = Setting.where(var: key).first_or_initialize(var: key)
|
||||||
render :edit
|
setting.update(value: value_for_update(key, value))
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
flash[:notice] = I18n.t('generic.changes_saved_msg')
|
||||||
|
redirect_to edit_admin_settings_path
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def settings_params
|
def settings_params
|
||||||
params.require(:form_admin_settings).permit(*Form::AdminSettings::KEYS)
|
params.require(:form_admin_settings).permit(ADMIN_SETTINGS)
|
||||||
|
end
|
||||||
|
|
||||||
|
def value_for_update(key, value)
|
||||||
|
if BOOLEAN_SETTINGS.include?(key)
|
||||||
|
value == '1'
|
||||||
|
else
|
||||||
|
value
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module Admin
|
||||||
|
class SilencesController < BaseController
|
||||||
|
before_action :set_account
|
||||||
|
|
||||||
|
def create
|
||||||
|
authorize @account, :silence?
|
||||||
|
@account.update!(silenced: true)
|
||||||
|
log_action :silence, @account
|
||||||
|
redirect_to admin_accounts_path
|
||||||
|
end
|
||||||
|
|
||||||
|
def destroy
|
||||||
|
authorize @account, :unsilence?
|
||||||
|
@account.update!(silenced: false)
|
||||||
|
log_action :unsilence, @account
|
||||||
|
redirect_to admin_accounts_path
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def set_account
|
||||||
|
@account = Account.find(params[:account_id])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -22,15 +22,6 @@ module Admin
|
||||||
@form = Form::StatusBatch.new
|
@form = Form::StatusBatch.new
|
||||||
end
|
end
|
||||||
|
|
||||||
def show
|
|
||||||
authorize :status, :index?
|
|
||||||
|
|
||||||
@statuses = @account.statuses.where(id: params[:id])
|
|
||||||
authorize @statuses.first, :show?
|
|
||||||
|
|
||||||
@form = Form::StatusBatch.new
|
|
||||||
end
|
|
||||||
|
|
||||||
def create
|
def create
|
||||||
authorize :status, :update?
|
authorize :status, :update?
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,60 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module Admin
|
||||||
|
class SuspensionsController < BaseController
|
||||||
|
before_action :set_account
|
||||||
|
|
||||||
|
def new
|
||||||
|
@suspension = Form::AdminSuspensionConfirmation.new(report_id: params[:report_id])
|
||||||
|
end
|
||||||
|
|
||||||
|
def create
|
||||||
|
authorize @account, :suspend?
|
||||||
|
|
||||||
|
@suspension = Form::AdminSuspensionConfirmation.new(suspension_params)
|
||||||
|
|
||||||
|
if suspension_params[:acct] == @account.acct
|
||||||
|
resolve_report! if suspension_params[:report_id].present?
|
||||||
|
perform_suspend!
|
||||||
|
mark_reports_resolved!
|
||||||
|
redirect_to admin_accounts_path
|
||||||
|
else
|
||||||
|
flash.now[:alert] = I18n.t('admin.suspensions.bad_acct_msg')
|
||||||
|
render :new
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def destroy
|
||||||
|
authorize @account, :unsuspend?
|
||||||
|
@account.unsuspend!
|
||||||
|
log_action :unsuspend, @account
|
||||||
|
redirect_to admin_accounts_path
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def set_account
|
||||||
|
@account = Account.find(params[:account_id])
|
||||||
|
end
|
||||||
|
|
||||||
|
def suspension_params
|
||||||
|
params.require(:form_admin_suspension_confirmation).permit(:acct, :report_id)
|
||||||
|
end
|
||||||
|
|
||||||
|
def resolve_report!
|
||||||
|
report = Report.find(suspension_params[:report_id])
|
||||||
|
report.resolve!(current_account)
|
||||||
|
log_action :resolve, report
|
||||||
|
end
|
||||||
|
|
||||||
|
def perform_suspend!
|
||||||
|
@account.suspend!
|
||||||
|
Admin::SuspensionWorker.perform_async(@account.id)
|
||||||
|
log_action :suspend, @account
|
||||||
|
end
|
||||||
|
|
||||||
|
def mark_reports_resolved!
|
||||||
|
Report.where(target_account: @account).unresolved.update_all(action_taken: true, action_taken_by_account_id: current_account.id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,103 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
module Admin
|
|
||||||
class TagsController < BaseController
|
|
||||||
before_action :set_tag, except: [:index, :batch, :approve_all, :reject_all]
|
|
||||||
before_action :set_usage_by_domain, except: [:index, :batch, :approve_all, :reject_all]
|
|
||||||
before_action :set_counters, except: [:index, :batch, :approve_all, :reject_all]
|
|
||||||
|
|
||||||
def index
|
|
||||||
authorize :tag, :index?
|
|
||||||
|
|
||||||
@tags = filtered_tags.page(params[:page])
|
|
||||||
@form = Form::TagBatch.new
|
|
||||||
end
|
|
||||||
|
|
||||||
def batch
|
|
||||||
@form = Form::TagBatch.new(form_tag_batch_params.merge(current_account: current_account, action: action_from_button))
|
|
||||||
@form.save
|
|
||||||
rescue ActionController::ParameterMissing
|
|
||||||
flash[:alert] = I18n.t('admin.accounts.no_account_selected')
|
|
||||||
ensure
|
|
||||||
redirect_to admin_tags_path(filter_params)
|
|
||||||
end
|
|
||||||
|
|
||||||
def approve_all
|
|
||||||
Form::TagBatch.new(current_account: current_account, tag_ids: Tag.pending_review.pluck(:id), action: 'approve').save
|
|
||||||
redirect_to admin_tags_path(filter_params)
|
|
||||||
end
|
|
||||||
|
|
||||||
def reject_all
|
|
||||||
Form::TagBatch.new(current_account: current_account, tag_ids: Tag.pending_review.pluck(:id), action: 'reject').save
|
|
||||||
redirect_to admin_tags_path(filter_params)
|
|
||||||
end
|
|
||||||
|
|
||||||
def show
|
|
||||||
authorize @tag, :show?
|
|
||||||
end
|
|
||||||
|
|
||||||
def update
|
|
||||||
authorize @tag, :update?
|
|
||||||
|
|
||||||
if @tag.update(tag_params.merge(reviewed_at: Time.now.utc))
|
|
||||||
redirect_to admin_tag_path(@tag.id), notice: I18n.t('admin.tags.updated_msg')
|
|
||||||
else
|
|
||||||
render :show
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def set_tag
|
|
||||||
@tag = Tag.find(params[:id])
|
|
||||||
end
|
|
||||||
|
|
||||||
def set_usage_by_domain
|
|
||||||
@usage_by_domain = @tag.statuses
|
|
||||||
.with_public_visibility
|
|
||||||
.excluding_silenced_accounts
|
|
||||||
.where(Status.arel_table[:id].gteq(Mastodon::Snowflake.id_at(Time.now.utc.beginning_of_day)))
|
|
||||||
.joins(:account)
|
|
||||||
.group('accounts.domain')
|
|
||||||
.reorder('statuses_count desc')
|
|
||||||
.pluck('accounts.domain, count(*) AS statuses_count')
|
|
||||||
end
|
|
||||||
|
|
||||||
def set_counters
|
|
||||||
@accounts_today = @tag.history.first[:accounts]
|
|
||||||
@accounts_week = Redis.current.pfcount(*current_week_days.map { |day| "activity:tags:#{@tag.id}:#{day}:accounts" })
|
|
||||||
end
|
|
||||||
|
|
||||||
def filtered_tags
|
|
||||||
TagFilter.new(filter_params).results
|
|
||||||
end
|
|
||||||
|
|
||||||
def filter_params
|
|
||||||
params.slice(:directory, :reviewed, :unreviewed, :pending_review, :page, :popular, :active, :name).permit(:directory, :reviewed, :unreviewed, :pending_review, :page, :popular, :active, :name)
|
|
||||||
end
|
|
||||||
|
|
||||||
def tag_params
|
|
||||||
params.require(:tag).permit(:name, :trendable, :usable, :listable)
|
|
||||||
end
|
|
||||||
|
|
||||||
def current_week_days
|
|
||||||
now = Time.now.utc.beginning_of_day.to_date
|
|
||||||
|
|
||||||
(Date.commercial(now.cwyear, now.cweek)..now).map do |date|
|
|
||||||
date.to_time(:utc).beginning_of_day.to_i
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def form_tag_batch_params
|
|
||||||
params.require(:form_tag_batch).permit(:action, tag_ids: [])
|
|
||||||
end
|
|
||||||
|
|
||||||
def action_from_button
|
|
||||||
if params[:approve]
|
|
||||||
'approve'
|
|
||||||
elsif params[:reject]
|
|
||||||
'reject'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -2,19 +2,18 @@
|
||||||
|
|
||||||
module Admin
|
module Admin
|
||||||
class TwoFactorAuthenticationsController < BaseController
|
class TwoFactorAuthenticationsController < BaseController
|
||||||
before_action :set_target_user
|
before_action :set_user
|
||||||
|
|
||||||
def destroy
|
def destroy
|
||||||
authorize @user, :disable_2fa?
|
authorize @user, :disable_2fa?
|
||||||
@user.disable_two_factor!
|
@user.disable_two_factor!
|
||||||
log_action :disable_2fa, @user
|
log_action :disable_2fa, @user
|
||||||
UserMailer.two_factor_disabled(@user).deliver_later!
|
|
||||||
redirect_to admin_accounts_path
|
redirect_to admin_accounts_path
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def set_target_user
|
def set_user
|
||||||
@user = User.find(params[:user_id])
|
@user = User.find(params[:user_id])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,58 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
module Admin
|
|
||||||
class WarningPresetsController < BaseController
|
|
||||||
before_action :set_warning_preset, except: [:index, :create]
|
|
||||||
|
|
||||||
def index
|
|
||||||
authorize :account_warning_preset, :index?
|
|
||||||
|
|
||||||
@warning_presets = AccountWarningPreset.all
|
|
||||||
@warning_preset = AccountWarningPreset.new
|
|
||||||
end
|
|
||||||
|
|
||||||
def create
|
|
||||||
authorize :account_warning_preset, :create?
|
|
||||||
|
|
||||||
@warning_preset = AccountWarningPreset.new(warning_preset_params)
|
|
||||||
|
|
||||||
if @warning_preset.save
|
|
||||||
redirect_to admin_warning_presets_path
|
|
||||||
else
|
|
||||||
@warning_presets = AccountWarningPreset.all
|
|
||||||
render :index
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def edit
|
|
||||||
authorize @warning_preset, :update?
|
|
||||||
end
|
|
||||||
|
|
||||||
def update
|
|
||||||
authorize @warning_preset, :update?
|
|
||||||
|
|
||||||
if @warning_preset.update(warning_preset_params)
|
|
||||||
redirect_to admin_warning_presets_path
|
|
||||||
else
|
|
||||||
render :edit
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def destroy
|
|
||||||
authorize @warning_preset, :destroy?
|
|
||||||
|
|
||||||
@warning_preset.destroy!
|
|
||||||
redirect_to admin_warning_presets_path
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def set_warning_preset
|
|
||||||
@warning_preset = AccountWarningPreset.find(params[:id])
|
|
||||||
end
|
|
||||||
|
|
||||||
def warning_preset_params
|
|
||||||
params.require(:account_warning_preset).permit(:text)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -7,15 +7,10 @@ class Api::BaseController < ApplicationController
|
||||||
include RateLimitHeaders
|
include RateLimitHeaders
|
||||||
|
|
||||||
skip_before_action :store_current_location
|
skip_before_action :store_current_location
|
||||||
skip_before_action :require_functional!
|
skip_before_action :check_user_permissions
|
||||||
|
|
||||||
before_action :require_authenticated_user!, if: :disallow_unauthenticated_api_access?
|
|
||||||
before_action :set_cache_headers
|
|
||||||
|
|
||||||
protect_from_forgery with: :null_session
|
protect_from_forgery with: :null_session
|
||||||
|
|
||||||
skip_around_action :set_locale
|
|
||||||
|
|
||||||
rescue_from ActiveRecord::RecordInvalid, Mastodon::ValidationError do |e|
|
rescue_from ActiveRecord::RecordInvalid, Mastodon::ValidationError do |e|
|
||||||
render json: { error: e.to_s }, status: 422
|
render json: { error: e.to_s }, status: 422
|
||||||
end
|
end
|
||||||
|
@ -36,14 +31,6 @@ class Api::BaseController < ApplicationController
|
||||||
render json: { error: 'This action is not allowed' }, status: 403
|
render json: { error: 'This action is not allowed' }, status: 403
|
||||||
end
|
end
|
||||||
|
|
||||||
rescue_from Mastodon::RaceConditionError do
|
|
||||||
render json: { error: 'There was a temporary problem serving your request, please try again' }, status: 503
|
|
||||||
end
|
|
||||||
|
|
||||||
rescue_from ActionController::ParameterMissing do |e|
|
|
||||||
render json: { error: e.to_s }, status: 400
|
|
||||||
end
|
|
||||||
|
|
||||||
def doorkeeper_unauthorized_render_options(error: nil)
|
def doorkeeper_unauthorized_render_options(error: nil)
|
||||||
{ json: { error: (error.try(:description) || 'Not authorized') } }
|
{ json: { error: (error.try(:description) || 'Not authorized') } }
|
||||||
end
|
end
|
||||||
|
@ -80,21 +67,13 @@ class Api::BaseController < ApplicationController
|
||||||
nil
|
nil
|
||||||
end
|
end
|
||||||
|
|
||||||
def require_authenticated_user!
|
|
||||||
render json: { error: 'This API requires an authenticated user' }, status: 401 unless current_user
|
|
||||||
end
|
|
||||||
|
|
||||||
def require_user!
|
def require_user!
|
||||||
if !current_user
|
if current_user && !current_user.disabled?
|
||||||
render json: { error: 'This method requires an authenticated user' }, status: 422
|
|
||||||
elsif current_user.disabled?
|
|
||||||
render json: { error: 'Your login is currently disabled' }, status: 403
|
|
||||||
elsif !current_user.confirmed?
|
|
||||||
render json: { error: 'Your login is missing a confirmed e-mail address' }, status: 403
|
|
||||||
elsif !current_user.approved?
|
|
||||||
render json: { error: 'Your login is currently pending approval' }, status: 403
|
|
||||||
else
|
|
||||||
set_user_activity
|
set_user_activity
|
||||||
|
elsif current_user
|
||||||
|
render json: { error: 'Your login is currently disabled' }, status: 403
|
||||||
|
else
|
||||||
|
render json: { error: 'This method requires an authenticated user' }, status: 422
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -105,12 +84,4 @@ class Api::BaseController < ApplicationController
|
||||||
def authorize_if_got_token!(*scopes)
|
def authorize_if_got_token!(*scopes)
|
||||||
doorkeeper_authorize!(*scopes) if doorkeeper_token
|
doorkeeper_authorize!(*scopes) if doorkeeper_token
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_cache_headers
|
|
||||||
response.headers['Cache-Control'] = 'no-cache, no-store, max-age=0, must-revalidate'
|
|
||||||
end
|
|
||||||
|
|
||||||
def disallow_unauthenticated_api_access?
|
|
||||||
authorized_fetch_mode?
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,21 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
class Api::ProofsController < Api::BaseController
|
|
||||||
include AccountOwnedConcern
|
|
||||||
|
|
||||||
before_action :set_provider
|
|
||||||
|
|
||||||
def index
|
|
||||||
render json: @account, serializer: @provider.serializer_class
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def set_provider
|
|
||||||
@provider = ProofProvider.find(params[:provider]) || raise(ActiveRecord::RecordNotFound)
|
|
||||||
end
|
|
||||||
|
|
||||||
def username_param
|
|
||||||
params[:username]
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -0,0 +1,73 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class Api::PushController < Api::BaseController
|
||||||
|
include SignatureVerification
|
||||||
|
|
||||||
|
def update
|
||||||
|
response, status = process_push_request
|
||||||
|
render plain: response, status: status
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def process_push_request
|
||||||
|
case hub_mode
|
||||||
|
when 'subscribe'
|
||||||
|
Pubsubhubbub::SubscribeService.new.call(account_from_topic, hub_callback, hub_secret, hub_lease_seconds, verified_domain)
|
||||||
|
when 'unsubscribe'
|
||||||
|
Pubsubhubbub::UnsubscribeService.new.call(account_from_topic, hub_callback)
|
||||||
|
else
|
||||||
|
["Unknown mode: #{hub_mode}", 422]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def hub_mode
|
||||||
|
params['hub.mode']
|
||||||
|
end
|
||||||
|
|
||||||
|
def hub_topic
|
||||||
|
params['hub.topic']
|
||||||
|
end
|
||||||
|
|
||||||
|
def hub_callback
|
||||||
|
params['hub.callback']
|
||||||
|
end
|
||||||
|
|
||||||
|
def hub_lease_seconds
|
||||||
|
params['hub.lease_seconds']
|
||||||
|
end
|
||||||
|
|
||||||
|
def hub_secret
|
||||||
|
params['hub.secret']
|
||||||
|
end
|
||||||
|
|
||||||
|
def account_from_topic
|
||||||
|
if hub_topic.present? && local_domain? && account_feed_path?
|
||||||
|
Account.find_local(hub_topic_params[:username])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def hub_topic_params
|
||||||
|
@_hub_topic_params ||= Rails.application.routes.recognize_path(hub_topic_uri.path)
|
||||||
|
end
|
||||||
|
|
||||||
|
def hub_topic_uri
|
||||||
|
@_hub_topic_uri ||= Addressable::URI.parse(hub_topic).normalize
|
||||||
|
end
|
||||||
|
|
||||||
|
def local_domain?
|
||||||
|
TagManager.instance.web_domain?(hub_topic_domain)
|
||||||
|
end
|
||||||
|
|
||||||
|
def verified_domain
|
||||||
|
return signed_request_account.domain if signed_request_account
|
||||||
|
end
|
||||||
|
|
||||||
|
def hub_topic_domain
|
||||||
|
hub_topic_uri.host + (hub_topic_uri.port ? ":#{hub_topic_uri.port}" : '')
|
||||||
|
end
|
||||||
|
|
||||||
|
def account_feed_path?
|
||||||
|
hub_topic_params[:controller] == 'accounts' && hub_topic_params[:action] == 'show' && hub_topic_params[:format] == 'atom'
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,37 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class Api::SalmonController < Api::BaseController
|
||||||
|
include SignatureVerification
|
||||||
|
|
||||||
|
before_action :set_account
|
||||||
|
respond_to :txt
|
||||||
|
|
||||||
|
def update
|
||||||
|
if verify_payload?
|
||||||
|
process_salmon
|
||||||
|
head 202
|
||||||
|
elsif payload.present?
|
||||||
|
render plain: signature_verification_failure_reason, status: 401
|
||||||
|
else
|
||||||
|
head 400
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def set_account
|
||||||
|
@account = Account.find(params[:id])
|
||||||
|
end
|
||||||
|
|
||||||
|
def payload
|
||||||
|
@_payload ||= request.body.read
|
||||||
|
end
|
||||||
|
|
||||||
|
def verify_payload?
|
||||||
|
payload.present? && VerifySalmonService.new.call(payload)
|
||||||
|
end
|
||||||
|
|
||||||
|
def process_salmon
|
||||||
|
SalmonWorker.perform_async(@account.id, payload.force_encoding('UTF-8'))
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,51 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class Api::SubscriptionsController < Api::BaseController
|
||||||
|
before_action :set_account
|
||||||
|
respond_to :txt
|
||||||
|
|
||||||
|
def show
|
||||||
|
if subscription.valid?(params['hub.topic'])
|
||||||
|
@account.update(subscription_expires_at: future_expires)
|
||||||
|
render plain: encoded_challenge, status: 200
|
||||||
|
else
|
||||||
|
head 404
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def update
|
||||||
|
if subscription.verify(body, request.headers['HTTP_X_HUB_SIGNATURE'])
|
||||||
|
ProcessingWorker.perform_async(@account.id, body.force_encoding('UTF-8'))
|
||||||
|
end
|
||||||
|
|
||||||
|
head 200
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def subscription
|
||||||
|
@_subscription ||= @account.subscription(
|
||||||
|
api_subscription_url(@account.id)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def body
|
||||||
|
@_body ||= request.body.read
|
||||||
|
end
|
||||||
|
|
||||||
|
def encoded_challenge
|
||||||
|
HTMLEntities.new.encode(params['hub.challenge'])
|
||||||
|
end
|
||||||
|
|
||||||
|
def future_expires
|
||||||
|
Time.now.utc + lease_seconds_or_default
|
||||||
|
end
|
||||||
|
|
||||||
|
def lease_seconds_or_default
|
||||||
|
(params['hub.lease_seconds'] || 1.day).to_i.seconds
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_account
|
||||||
|
@account = Account.find(params[:id])
|
||||||
|
end
|
||||||
|
end
|
|
@ -21,7 +21,7 @@ class Api::V1::Accounts::CredentialsController < Api::BaseController
|
||||||
private
|
private
|
||||||
|
|
||||||
def account_params
|
def account_params
|
||||||
params.permit(:display_name, :note, :avatar, :header, :locked, :bot, :discoverable, fields_attributes: [:name, :value])
|
params.permit(:display_name, :note, :avatar, :header, :locked, :bot, fields_attributes: [:name, :value])
|
||||||
end
|
end
|
||||||
|
|
||||||
def user_settings_params
|
def user_settings_params
|
||||||
|
|
|
@ -19,17 +19,13 @@ class Api::V1::Accounts::FollowerAccountsController < Api::BaseController
|
||||||
end
|
end
|
||||||
|
|
||||||
def load_accounts
|
def load_accounts
|
||||||
return [] if hide_results?
|
return [] if @account.user_hides_network? && current_account.id != @account.id
|
||||||
|
|
||||||
default_accounts.merge(paginated_follows).to_a
|
default_accounts.merge(paginated_follows).to_a
|
||||||
end
|
end
|
||||||
|
|
||||||
def hide_results?
|
|
||||||
(@account.user_hides_network? && current_account.id != @account.id) || (current_account && @account.blocking?(current_account))
|
|
||||||
end
|
|
||||||
|
|
||||||
def default_accounts
|
def default_accounts
|
||||||
Account.includes(:active_relationships, :account_stat).references(:active_relationships)
|
Account.includes(:active_relationships).references(:active_relationships)
|
||||||
end
|
end
|
||||||
|
|
||||||
def paginated_follows
|
def paginated_follows
|
||||||
|
|
|
@ -19,17 +19,13 @@ class Api::V1::Accounts::FollowingAccountsController < Api::BaseController
|
||||||
end
|
end
|
||||||
|
|
||||||
def load_accounts
|
def load_accounts
|
||||||
return [] if hide_results?
|
return [] if @account.user_hides_network? && current_account.id != @account.id
|
||||||
|
|
||||||
default_accounts.merge(paginated_follows).to_a
|
default_accounts.merge(paginated_follows).to_a
|
||||||
end
|
end
|
||||||
|
|
||||||
def hide_results?
|
|
||||||
(@account.user_hides_network? && current_account.id != @account.id) || (current_account && @account.blocking?(current_account))
|
|
||||||
end
|
|
||||||
|
|
||||||
def default_accounts
|
def default_accounts
|
||||||
Account.includes(:passive_relationships, :account_stat).references(:passive_relationships)
|
Account.includes(:passive_relationships).references(:passive_relationships)
|
||||||
end
|
end
|
||||||
|
|
||||||
def paginated_follows
|
def paginated_follows
|
||||||
|
|
|
@ -1,19 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
class Api::V1::Accounts::IdentityProofsController < Api::BaseController
|
|
||||||
before_action :require_user!
|
|
||||||
before_action :set_account
|
|
||||||
|
|
||||||
respond_to :json
|
|
||||||
|
|
||||||
def index
|
|
||||||
@proofs = @account.identity_proofs.active
|
|
||||||
render json: @proofs, each_serializer: REST::IdentityProofSerializer
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def set_account
|
|
||||||
@account = Account.find(params[:account_id])
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -16,11 +16,10 @@ class Api::V1::Accounts::SearchController < Api::BaseController
|
||||||
def account_search
|
def account_search
|
||||||
AccountSearchService.new.call(
|
AccountSearchService.new.call(
|
||||||
params[:q],
|
params[:q],
|
||||||
|
limit_param(DEFAULT_ACCOUNTS_LIMIT),
|
||||||
current_account,
|
current_account,
|
||||||
limit: limit_param(DEFAULT_ACCOUNTS_LIMIT),
|
|
||||||
resolve: truthy_param?(:resolve),
|
resolve: truthy_param?(:resolve),
|
||||||
following: truthy_param?(:following),
|
following: truthy_param?(:following)
|
||||||
offset: params[:offset]
|
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class Api::V1::Accounts::StatusesController < Api::BaseController
|
class Api::V1::Accounts::StatusesController < Api::BaseController
|
||||||
before_action -> { authorize_if_got_token! :read, :'read:statuses' }
|
before_action -> { doorkeeper_authorize! :read, :'read:statuses' }
|
||||||
before_action :set_account
|
before_action :set_account
|
||||||
|
after_action :insert_pagination_headers
|
||||||
after_action :insert_pagination_headers, unless: -> { truthy_param?(:pinned) }
|
|
||||||
|
|
||||||
respond_to :json
|
respond_to :json
|
||||||
|
|
||||||
|
@ -29,13 +28,15 @@ class Api::V1::Accounts::StatusesController < Api::BaseController
|
||||||
|
|
||||||
def account_statuses
|
def account_statuses
|
||||||
statuses = truthy_param?(:pinned) ? pinned_scope : permitted_account_statuses
|
statuses = truthy_param?(:pinned) ? pinned_scope : permitted_account_statuses
|
||||||
|
statuses = statuses.paginate_by_id(
|
||||||
|
limit_param(DEFAULT_STATUSES_LIMIT),
|
||||||
|
params_slice(:max_id, :since_id, :min_id)
|
||||||
|
)
|
||||||
|
|
||||||
statuses.merge!(only_media_scope) if truthy_param?(:only_media)
|
statuses.merge!(only_media_scope) if truthy_param?(:only_media)
|
||||||
statuses.merge!(no_replies_scope) if truthy_param?(:exclude_replies)
|
statuses.merge!(no_replies_scope) if truthy_param?(:exclude_replies)
|
||||||
statuses.merge!(no_reblogs_scope) if truthy_param?(:exclude_reblogs)
|
|
||||||
statuses.merge!(hashtag_scope) if params[:tagged].present?
|
|
||||||
|
|
||||||
statuses.paginate_by_id(limit_param(DEFAULT_STATUSES_LIMIT), params_slice(:max_id, :since_id, :min_id))
|
statuses
|
||||||
end
|
end
|
||||||
|
|
||||||
def permitted_account_statuses
|
def permitted_account_statuses
|
||||||
|
@ -51,14 +52,12 @@ class Api::V1::Accounts::StatusesController < Api::BaseController
|
||||||
# Also, Avoid getting slow by not narrowing down by `statuses.account_id`.
|
# Also, Avoid getting slow by not narrowing down by `statuses.account_id`.
|
||||||
# When narrowing down by `statuses.account_id`, `index_statuses_20180106` will be used
|
# When narrowing down by `statuses.account_id`, `index_statuses_20180106` will be used
|
||||||
# and the table will be joined by `Merge Semi Join`, so the query will be slow.
|
# and the table will be joined by `Merge Semi Join`, so the query will be slow.
|
||||||
@account.statuses.joins(:media_attachments).merge(@account.media_attachments).permitted_for(@account, current_account)
|
Status.joins(:media_attachments).merge(@account.media_attachments).permitted_for(@account, current_account)
|
||||||
.paginate_by_max_id(limit_param(DEFAULT_STATUSES_LIMIT), params[:max_id], params[:since_id])
|
.paginate_by_max_id(limit_param(DEFAULT_STATUSES_LIMIT), params[:max_id], params[:since_id])
|
||||||
.reorder(id: :desc).distinct(:id).pluck(:id)
|
.reorder(id: :desc).distinct(:id).pluck(:id)
|
||||||
end
|
end
|
||||||
|
|
||||||
def pinned_scope
|
def pinned_scope
|
||||||
return Status.none if @account.blocking?(current_account)
|
|
||||||
|
|
||||||
@account.pinned_statuses
|
@account.pinned_statuses
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -66,20 +65,6 @@ class Api::V1::Accounts::StatusesController < Api::BaseController
|
||||||
Status.without_replies
|
Status.without_replies
|
||||||
end
|
end
|
||||||
|
|
||||||
def no_reblogs_scope
|
|
||||||
Status.without_reblogs
|
|
||||||
end
|
|
||||||
|
|
||||||
def hashtag_scope
|
|
||||||
tag = Tag.find_normalized(params[:tagged])
|
|
||||||
|
|
||||||
if tag
|
|
||||||
Status.tagged_with(tag.id)
|
|
||||||
else
|
|
||||||
Status.none
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def pagination_params(core_params)
|
def pagination_params(core_params)
|
||||||
params.slice(:limit, :only_media, :exclude_replies).permit(:limit, :only_media, :exclude_replies).merge(core_params)
|
params.slice(:limit, :only_media, :exclude_replies).permit(:limit, :only_media, :exclude_replies).merge(core_params)
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,18 +1,14 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class Api::V1::AccountsController < Api::BaseController
|
class Api::V1::AccountsController < Api::BaseController
|
||||||
before_action -> { authorize_if_got_token! :read, :'read:accounts' }, except: [:create, :follow, :unfollow, :block, :unblock, :mute, :unmute]
|
before_action -> { authorize_if_got_token! :read, :'read:accounts' }, except: [:follow, :unfollow, :block, :unblock, :mute, :unmute]
|
||||||
before_action -> { doorkeeper_authorize! :follow, :'write:follows' }, only: [:follow, :unfollow]
|
before_action -> { doorkeeper_authorize! :follow, :'write:follows' }, only: [:follow, :unfollow]
|
||||||
before_action -> { doorkeeper_authorize! :follow, :'write:mutes' }, only: [:mute, :unmute]
|
before_action -> { doorkeeper_authorize! :follow, :'write:mutes' }, only: [:mute, :unmute]
|
||||||
before_action -> { doorkeeper_authorize! :follow, :'write:blocks' }, only: [:block, :unblock]
|
before_action -> { doorkeeper_authorize! :follow, :'write:blocks' }, only: [:block, :unblock]
|
||||||
before_action -> { doorkeeper_authorize! :write, :'write:accounts' }, only: [:create]
|
|
||||||
|
|
||||||
before_action :require_user!, except: [:show, :create]
|
before_action :require_user!, except: [:show]
|
||||||
before_action :set_account, except: [:create]
|
before_action :set_account
|
||||||
before_action :check_account_suspension, only: [:show]
|
before_action :check_account_suspension, only: [:show]
|
||||||
before_action :check_enabled_registrations, only: [:create]
|
|
||||||
|
|
||||||
skip_before_action :require_authenticated_user!, only: :create
|
|
||||||
|
|
||||||
respond_to :json
|
respond_to :json
|
||||||
|
|
||||||
|
@ -20,20 +16,10 @@ class Api::V1::AccountsController < Api::BaseController
|
||||||
render json: @account, serializer: REST::AccountSerializer
|
render json: @account, serializer: REST::AccountSerializer
|
||||||
end
|
end
|
||||||
|
|
||||||
def create
|
|
||||||
token = AppSignUpService.new.call(doorkeeper_token.application, account_params)
|
|
||||||
response = Doorkeeper::OAuth::TokenResponse.new(token)
|
|
||||||
|
|
||||||
headers.merge!(response.headers)
|
|
||||||
|
|
||||||
self.response_body = Oj.dump(response.body)
|
|
||||||
self.status = response.status
|
|
||||||
end
|
|
||||||
|
|
||||||
def follow
|
def follow
|
||||||
FollowService.new.call(current_user.account, @account, reblogs: truthy_param?(:reblogs))
|
FollowService.new.call(current_user.account, @account, reblogs: truthy_param?(:reblogs))
|
||||||
|
|
||||||
options = @account.locked? || current_user.account.silenced? ? {} : { following_map: { @account.id => { reblogs: truthy_param?(:reblogs) } }, requested_map: { @account.id => false } }
|
options = @account.locked? ? {} : { following_map: { @account.id => { reblogs: truthy_param?(:reblogs) } }, requested_map: { @account.id => false } }
|
||||||
|
|
||||||
render json: @account, serializer: REST::RelationshipSerializer, relationships: relationships(options)
|
render json: @account, serializer: REST::RelationshipSerializer, relationships: relationships(options)
|
||||||
end
|
end
|
||||||
|
@ -76,16 +62,4 @@ class Api::V1::AccountsController < Api::BaseController
|
||||||
def check_account_suspension
|
def check_account_suspension
|
||||||
gone if @account.suspended?
|
gone if @account.suspended?
|
||||||
end
|
end
|
||||||
|
|
||||||
def account_params
|
|
||||||
params.permit(:username, :email, :password, :agreement, :locale, :reason)
|
|
||||||
end
|
|
||||||
|
|
||||||
def check_enabled_registrations
|
|
||||||
forbidden if single_user_mode? || !allowed_registrations?
|
|
||||||
end
|
|
||||||
|
|
||||||
def allowed_registrations?
|
|
||||||
Setting.registrations_mode != 'none'
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,32 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
class Api::V1::Admin::AccountActionsController < Api::BaseController
|
|
||||||
before_action -> { doorkeeper_authorize! :'admin:write', :'admin:write:accounts' }
|
|
||||||
before_action :require_staff!
|
|
||||||
before_action :set_account
|
|
||||||
|
|
||||||
def create
|
|
||||||
account_action = Admin::AccountAction.new(resource_params)
|
|
||||||
account_action.target_account = @account
|
|
||||||
account_action.current_account = current_account
|
|
||||||
account_action.save!
|
|
||||||
|
|
||||||
render_empty
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def set_account
|
|
||||||
@account = Account.find(params[:account_id])
|
|
||||||
end
|
|
||||||
|
|
||||||
def resource_params
|
|
||||||
params.permit(
|
|
||||||
:type,
|
|
||||||
:report_id,
|
|
||||||
:warning_preset_id,
|
|
||||||
:text,
|
|
||||||
:send_email_notification
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,128 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
class Api::V1::Admin::AccountsController < Api::BaseController
|
|
||||||
include Authorization
|
|
||||||
include AccountableConcern
|
|
||||||
|
|
||||||
LIMIT = 100
|
|
||||||
|
|
||||||
before_action -> { doorkeeper_authorize! :'admin:read', :'admin:read:accounts' }, only: [:index, :show]
|
|
||||||
before_action -> { doorkeeper_authorize! :'admin:write', :'admin:write:accounts' }, except: [:index, :show]
|
|
||||||
before_action :require_staff!
|
|
||||||
before_action :set_accounts, only: :index
|
|
||||||
before_action :set_account, except: :index
|
|
||||||
before_action :require_local_account!, only: [:enable, :approve, :reject]
|
|
||||||
|
|
||||||
after_action :insert_pagination_headers, only: :index
|
|
||||||
|
|
||||||
FILTER_PARAMS = %i(
|
|
||||||
local
|
|
||||||
remote
|
|
||||||
by_domain
|
|
||||||
active
|
|
||||||
pending
|
|
||||||
disabled
|
|
||||||
silenced
|
|
||||||
suspended
|
|
||||||
username
|
|
||||||
display_name
|
|
||||||
email
|
|
||||||
ip
|
|
||||||
staff
|
|
||||||
).freeze
|
|
||||||
|
|
||||||
PAGINATION_PARAMS = (%i(limit) + FILTER_PARAMS).freeze
|
|
||||||
|
|
||||||
def index
|
|
||||||
authorize :account, :index?
|
|
||||||
render json: @accounts, each_serializer: REST::Admin::AccountSerializer
|
|
||||||
end
|
|
||||||
|
|
||||||
def show
|
|
||||||
authorize @account, :show?
|
|
||||||
render json: @account, serializer: REST::Admin::AccountSerializer
|
|
||||||
end
|
|
||||||
|
|
||||||
def enable
|
|
||||||
authorize @account.user, :enable?
|
|
||||||
@account.user.enable!
|
|
||||||
log_action :enable, @account.user
|
|
||||||
render json: @account, serializer: REST::Admin::AccountSerializer
|
|
||||||
end
|
|
||||||
|
|
||||||
def approve
|
|
||||||
authorize @account.user, :approve?
|
|
||||||
@account.user.approve!
|
|
||||||
render json: @account, serializer: REST::Admin::AccountSerializer
|
|
||||||
end
|
|
||||||
|
|
||||||
def reject
|
|
||||||
authorize @account.user, :reject?
|
|
||||||
SuspendAccountService.new.call(@account, reserve_email: false, reserve_username: false)
|
|
||||||
render json: @account, serializer: REST::Admin::AccountSerializer
|
|
||||||
end
|
|
||||||
|
|
||||||
def unsilence
|
|
||||||
authorize @account, :unsilence?
|
|
||||||
@account.unsilence!
|
|
||||||
log_action :unsilence, @account
|
|
||||||
render json: @account, serializer: REST::Admin::AccountSerializer
|
|
||||||
end
|
|
||||||
|
|
||||||
def unsuspend
|
|
||||||
authorize @account, :unsuspend?
|
|
||||||
@account.unsuspend!
|
|
||||||
log_action :unsuspend, @account
|
|
||||||
render json: @account, serializer: REST::Admin::AccountSerializer
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def set_accounts
|
|
||||||
@accounts = filtered_accounts.order(id: :desc).includes(user: [:invite_request, :invite]).paginate_by_id(limit_param(LIMIT), params_slice(:max_id, :since_id, :min_id))
|
|
||||||
end
|
|
||||||
|
|
||||||
def set_account
|
|
||||||
@account = Account.find(params[:id])
|
|
||||||
end
|
|
||||||
|
|
||||||
def filtered_accounts
|
|
||||||
AccountFilter.new(filter_params).results
|
|
||||||
end
|
|
||||||
|
|
||||||
def filter_params
|
|
||||||
params.permit(*FILTER_PARAMS)
|
|
||||||
end
|
|
||||||
|
|
||||||
def insert_pagination_headers
|
|
||||||
set_pagination_headers(next_path, prev_path)
|
|
||||||
end
|
|
||||||
|
|
||||||
def next_path
|
|
||||||
api_v1_admin_accounts_url(pagination_params(max_id: pagination_max_id)) if records_continue?
|
|
||||||
end
|
|
||||||
|
|
||||||
def prev_path
|
|
||||||
api_v1_admin_accounts_url(pagination_params(min_id: pagination_since_id)) unless @accounts.empty?
|
|
||||||
end
|
|
||||||
|
|
||||||
def pagination_max_id
|
|
||||||
@accounts.last.id
|
|
||||||
end
|
|
||||||
|
|
||||||
def pagination_since_id
|
|
||||||
@accounts.first.id
|
|
||||||
end
|
|
||||||
|
|
||||||
def records_continue?
|
|
||||||
@accounts.size == limit_param(LIMIT)
|
|
||||||
end
|
|
||||||
|
|
||||||
def pagination_params(core_params)
|
|
||||||
params.slice(*PAGINATION_PARAMS).permit(*PAGINATION_PARAMS).merge(core_params)
|
|
||||||
end
|
|
||||||
|
|
||||||
def require_local_account!
|
|
||||||
forbidden unless @account.local? && @account.user.present?
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,108 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
class Api::V1::Admin::ReportsController < Api::BaseController
|
|
||||||
include Authorization
|
|
||||||
include AccountableConcern
|
|
||||||
|
|
||||||
LIMIT = 100
|
|
||||||
|
|
||||||
before_action -> { doorkeeper_authorize! :'admin:read', :'admin:read:reports' }, only: [:index, :show]
|
|
||||||
before_action -> { doorkeeper_authorize! :'admin:write', :'admin:write:reports' }, except: [:index, :show]
|
|
||||||
before_action :require_staff!
|
|
||||||
before_action :set_reports, only: :index
|
|
||||||
before_action :set_report, except: :index
|
|
||||||
|
|
||||||
after_action :insert_pagination_headers, only: :index
|
|
||||||
|
|
||||||
FILTER_PARAMS = %i(
|
|
||||||
resolved
|
|
||||||
account_id
|
|
||||||
target_account_id
|
|
||||||
).freeze
|
|
||||||
|
|
||||||
PAGINATION_PARAMS = (%i(limit) + FILTER_PARAMS).freeze
|
|
||||||
|
|
||||||
def index
|
|
||||||
authorize :report, :index?
|
|
||||||
render json: @reports, each_serializer: REST::Admin::ReportSerializer
|
|
||||||
end
|
|
||||||
|
|
||||||
def show
|
|
||||||
authorize @report, :show?
|
|
||||||
render json: @report, serializer: REST::Admin::ReportSerializer
|
|
||||||
end
|
|
||||||
|
|
||||||
def assign_to_self
|
|
||||||
authorize @report, :update?
|
|
||||||
@report.update!(assigned_account_id: current_account.id)
|
|
||||||
log_action :assigned_to_self, @report
|
|
||||||
render json: @report, serializer: REST::Admin::ReportSerializer
|
|
||||||
end
|
|
||||||
|
|
||||||
def unassign
|
|
||||||
authorize @report, :update?
|
|
||||||
@report.update!(assigned_account_id: nil)
|
|
||||||
log_action :unassigned, @report
|
|
||||||
render json: @report, serializer: REST::Admin::ReportSerializer
|
|
||||||
end
|
|
||||||
|
|
||||||
def reopen
|
|
||||||
authorize @report, :update?
|
|
||||||
@report.unresolve!
|
|
||||||
log_action :reopen, @report
|
|
||||||
render json: @report, serializer: REST::Admin::ReportSerializer
|
|
||||||
end
|
|
||||||
|
|
||||||
def resolve
|
|
||||||
authorize @report, :update?
|
|
||||||
@report.resolve!(current_account)
|
|
||||||
log_action :resolve, @report
|
|
||||||
render json: @report, serializer: REST::Admin::ReportSerializer
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def set_reports
|
|
||||||
@reports = filtered_reports.order(id: :desc).with_accounts.paginate_by_id(limit_param(LIMIT), params_slice(:max_id, :since_id, :min_id))
|
|
||||||
end
|
|
||||||
|
|
||||||
def set_report
|
|
||||||
@report = Report.find(params[:id])
|
|
||||||
end
|
|
||||||
|
|
||||||
def filtered_reports
|
|
||||||
ReportFilter.new(filter_params).results
|
|
||||||
end
|
|
||||||
|
|
||||||
def filter_params
|
|
||||||
params.permit(*FILTER_PARAMS)
|
|
||||||
end
|
|
||||||
|
|
||||||
def insert_pagination_headers
|
|
||||||
set_pagination_headers(next_path, prev_path)
|
|
||||||
end
|
|
||||||
|
|
||||||
def next_path
|
|
||||||
api_v1_admin_reports_url(pagination_params(max_id: pagination_max_id)) if records_continue?
|
|
||||||
end
|
|
||||||
|
|
||||||
def prev_path
|
|
||||||
api_v1_admin_reports_url(pagination_params(min_id: pagination_since_id)) unless @reports.empty?
|
|
||||||
end
|
|
||||||
|
|
||||||
def pagination_max_id
|
|
||||||
@reports.last.id
|
|
||||||
end
|
|
||||||
|
|
||||||
def pagination_since_id
|
|
||||||
@reports.first.id
|
|
||||||
end
|
|
||||||
|
|
||||||
def records_continue?
|
|
||||||
@reports.size == limit_param(LIMIT)
|
|
||||||
end
|
|
||||||
|
|
||||||
def pagination_params(core_params)
|
|
||||||
params.slice(*PAGINATION_PARAMS).permit(*PAGINATION_PARAMS).merge(core_params)
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -6,6 +6,6 @@ class Api::V1::Apps::CredentialsController < Api::BaseController
|
||||||
respond_to :json
|
respond_to :json
|
||||||
|
|
||||||
def show
|
def show
|
||||||
render json: doorkeeper_token.application, serializer: REST::ApplicationSerializer, fields: %i(name website vapid_key)
|
render json: doorkeeper_token.application, serializer: REST::StatusSerializer::ApplicationSerializer
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class Api::V1::AppsController < Api::BaseController
|
class Api::V1::AppsController < Api::BaseController
|
||||||
skip_before_action :require_authenticated_user!
|
|
||||||
|
|
||||||
def create
|
def create
|
||||||
@app = Doorkeeper::Application.create!(application_options)
|
@app = Doorkeeper::Application.create!(application_options)
|
||||||
render json: @app, serializer: REST::ApplicationSerializer
|
render json: @app, serializer: REST::ApplicationSerializer
|
||||||
|
|
|
@ -19,7 +19,7 @@ class Api::V1::BlocksController < Api::BaseController
|
||||||
end
|
end
|
||||||
|
|
||||||
def paginated_blocks
|
def paginated_blocks
|
||||||
@paginated_blocks ||= Block.eager_load(target_account: :account_stat)
|
@paginated_blocks ||= Block.eager_load(:target_account)
|
||||||
.where(account: current_account)
|
.where(account: current_account)
|
||||||
.paginate_by_max_id(
|
.paginate_by_max_id(
|
||||||
limit_param(DEFAULT_ACCOUNTS_LIMIT),
|
limit_param(DEFAULT_ACCOUNTS_LIMIT),
|
||||||
|
|
|
@ -3,10 +3,7 @@
|
||||||
class Api::V1::CustomEmojisController < Api::BaseController
|
class Api::V1::CustomEmojisController < Api::BaseController
|
||||||
respond_to :json
|
respond_to :json
|
||||||
|
|
||||||
skip_before_action :set_cache_headers
|
|
||||||
|
|
||||||
def index
|
def index
|
||||||
expires_in 3.minutes, public: true
|
render json: CustomEmoji.local.where(disabled: false), each_serializer: REST::CustomEmojiSerializer
|
||||||
render_with_cache(each_serializer: REST::CustomEmojiSerializer) { CustomEmoji.listed.includes(:category) }
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,30 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
class Api::V1::DirectoriesController < Api::BaseController
|
|
||||||
before_action :require_enabled!
|
|
||||||
before_action :set_accounts
|
|
||||||
|
|
||||||
def show
|
|
||||||
render json: @accounts, each_serializer: REST::AccountSerializer
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def require_enabled!
|
|
||||||
return not_found unless Setting.profile_directory
|
|
||||||
end
|
|
||||||
|
|
||||||
def set_accounts
|
|
||||||
@accounts = accounts_scope.offset(params[:offset]).limit(limit_param(DEFAULT_ACCOUNTS_LIMIT))
|
|
||||||
end
|
|
||||||
|
|
||||||
def accounts_scope
|
|
||||||
Account.discoverable.tap do |scope|
|
|
||||||
scope.merge!(Account.local) if truthy_param?(:local)
|
|
||||||
scope.merge!(Account.by_recent_status) if params[:order].blank? || params[:order] == 'active'
|
|
||||||
scope.merge!(Account.order(id: :desc)) if params[:order] == 'new'
|
|
||||||
scope.merge!(Account.not_excluded_by_account(current_account)) if current_account
|
|
||||||
scope.merge!(Account.not_domain_blocked_by_account(current_account)) if current_account && !truthy_param?(:local)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -27,7 +27,7 @@ class Api::V1::EndorsementsController < Api::BaseController
|
||||||
end
|
end
|
||||||
|
|
||||||
def endorsed_accounts
|
def endorsed_accounts
|
||||||
current_account.endorsed_accounts.includes(:account_stat)
|
current_account.endorsed_accounts
|
||||||
end
|
end
|
||||||
|
|
||||||
def insert_pagination_headers
|
def insert_pagination_headers
|
||||||
|
|
|
@ -1,20 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
class Api::V1::FeaturedTags::SuggestionsController < Api::BaseController
|
|
||||||
before_action -> { doorkeeper_authorize! :read, :'read:accounts' }, only: :index
|
|
||||||
|
|
||||||
before_action :require_user!
|
|
||||||
before_action :set_most_used_tags, only: :index
|
|
||||||
|
|
||||||
respond_to :json
|
|
||||||
|
|
||||||
def index
|
|
||||||
render json: @most_used_tags, each_serializer: REST::TagSerializer
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def set_most_used_tags
|
|
||||||
@most_used_tags = Tag.most_used(current_account).where.not(id: current_account.featured_tags).limit(10)
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,40 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
class Api::V1::FeaturedTagsController < Api::BaseController
|
|
||||||
before_action -> { doorkeeper_authorize! :read, :'read:accounts' }, only: :index
|
|
||||||
before_action -> { doorkeeper_authorize! :write, :'write:accounts' }, except: :index
|
|
||||||
|
|
||||||
before_action :require_user!
|
|
||||||
before_action :set_featured_tags, only: :index
|
|
||||||
before_action :set_featured_tag, except: [:index, :create]
|
|
||||||
|
|
||||||
def index
|
|
||||||
render json: @featured_tags, each_serializer: REST::FeaturedTagSerializer
|
|
||||||
end
|
|
||||||
|
|
||||||
def create
|
|
||||||
@featured_tag = current_account.featured_tags.new(featured_tag_params)
|
|
||||||
@featured_tag.reset_data
|
|
||||||
@featured_tag.save!
|
|
||||||
render json: @featured_tag, serializer: REST::FeaturedTagSerializer
|
|
||||||
end
|
|
||||||
|
|
||||||
def destroy
|
|
||||||
@featured_tag.destroy!
|
|
||||||
render_empty
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def set_featured_tag
|
|
||||||
@featured_tag = current_account.featured_tags.find(params[:id])
|
|
||||||
end
|
|
||||||
|
|
||||||
def set_featured_tags
|
|
||||||
@featured_tags = current_account.featured_tags.order(statuses_count: :desc)
|
|
||||||
end
|
|
||||||
|
|
||||||
def featured_tag_params
|
|
||||||
params.permit(:name)
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -14,12 +14,12 @@ class Api::V1::FollowRequestsController < Api::BaseController
|
||||||
def authorize
|
def authorize
|
||||||
AuthorizeFollowService.new.call(account, current_account)
|
AuthorizeFollowService.new.call(account, current_account)
|
||||||
NotifyService.new.call(current_account, Follow.find_by(account: account, target_account: current_account))
|
NotifyService.new.call(current_account, Follow.find_by(account: account, target_account: current_account))
|
||||||
render json: account, serializer: REST::RelationshipSerializer, relationships: relationships
|
render_empty
|
||||||
end
|
end
|
||||||
|
|
||||||
def reject
|
def reject
|
||||||
RejectFollowService.new.call(account, current_account)
|
RejectFollowService.new.call(account, current_account)
|
||||||
render json: account, serializer: REST::RelationshipSerializer, relationships: relationships
|
render_empty
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
@ -28,16 +28,12 @@ class Api::V1::FollowRequestsController < Api::BaseController
|
||||||
Account.find(params[:id])
|
Account.find(params[:id])
|
||||||
end
|
end
|
||||||
|
|
||||||
def relationships(**options)
|
|
||||||
AccountRelationshipsPresenter.new([params[:id]], current_user.account_id, options)
|
|
||||||
end
|
|
||||||
|
|
||||||
def load_accounts
|
def load_accounts
|
||||||
default_accounts.merge(paginated_follow_requests).to_a
|
default_accounts.merge(paginated_follow_requests).to_a
|
||||||
end
|
end
|
||||||
|
|
||||||
def default_accounts
|
def default_accounts
|
||||||
Account.includes(:follow_requests, :account_stat).references(:follow_requests)
|
Account.includes(:follow_requests).references(:follow_requests)
|
||||||
end
|
end
|
||||||
|
|
||||||
def paginated_follow_requests
|
def paginated_follow_requests
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class Api::V1::FollowsController < Api::BaseController
|
||||||
|
before_action -> { doorkeeper_authorize! :follow, :'write:follows' }
|
||||||
|
before_action :require_user!
|
||||||
|
|
||||||
|
respond_to :json
|
||||||
|
|
||||||
|
def create
|
||||||
|
raise ActiveRecord::RecordNotFound if follow_params[:uri].blank?
|
||||||
|
|
||||||
|
@account = FollowService.new.call(current_user.account, target_uri).try(:target_account)
|
||||||
|
|
||||||
|
if @account.nil?
|
||||||
|
username, domain = target_uri.split('@')
|
||||||
|
@account = Account.find_remote!(username, domain)
|
||||||
|
end
|
||||||
|
|
||||||
|
render json: @account, serializer: REST::AccountSerializer
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def target_uri
|
||||||
|
follow_params[:uri].strip.gsub(/\A@/, '')
|
||||||
|
end
|
||||||
|
|
||||||
|
def follow_params
|
||||||
|
params.permit(:uri)
|
||||||
|
end
|
||||||
|
end
|
|
@ -3,14 +3,10 @@
|
||||||
class Api::V1::Instances::ActivityController < Api::BaseController
|
class Api::V1::Instances::ActivityController < Api::BaseController
|
||||||
before_action :require_enabled_api!
|
before_action :require_enabled_api!
|
||||||
|
|
||||||
skip_before_action :set_cache_headers
|
|
||||||
skip_before_action :require_authenticated_user!, unless: :whitelist_mode?
|
|
||||||
|
|
||||||
respond_to :json
|
respond_to :json
|
||||||
|
|
||||||
def show
|
def show
|
||||||
expires_in 1.day, public: true
|
render_cached_json('api:v1:instances:activity:show', expires_in: 1.day) { activity }
|
||||||
render_with_cache json: :activity, expires_in: 1.day
|
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
@ -35,6 +31,6 @@ class Api::V1::Instances::ActivityController < Api::BaseController
|
||||||
end
|
end
|
||||||
|
|
||||||
def require_enabled_api!
|
def require_enabled_api!
|
||||||
head 404 unless Setting.activity_api_enabled && !whitelist_mode?
|
head 404 unless Setting.activity_api_enabled
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,19 +3,15 @@
|
||||||
class Api::V1::Instances::PeersController < Api::BaseController
|
class Api::V1::Instances::PeersController < Api::BaseController
|
||||||
before_action :require_enabled_api!
|
before_action :require_enabled_api!
|
||||||
|
|
||||||
skip_before_action :set_cache_headers
|
|
||||||
skip_before_action :require_authenticated_user!, unless: :whitelist_mode?
|
|
||||||
|
|
||||||
respond_to :json
|
respond_to :json
|
||||||
|
|
||||||
def index
|
def index
|
||||||
expires_in 1.day, public: true
|
render_cached_json('api:v1:instances:peers:index', expires_in: 1.day) { Account.remote.domains }
|
||||||
render_with_cache(expires_in: 1.day) { Account.remote.domains }
|
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def require_enabled_api!
|
def require_enabled_api!
|
||||||
head 404 unless Setting.peers_api_enabled && !whitelist_mode?
|
head 404 unless Setting.peers_api_enabled
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,11 +3,9 @@
|
||||||
class Api::V1::InstancesController < Api::BaseController
|
class Api::V1::InstancesController < Api::BaseController
|
||||||
respond_to :json
|
respond_to :json
|
||||||
|
|
||||||
skip_before_action :set_cache_headers
|
|
||||||
skip_before_action :require_authenticated_user!, unless: :whitelist_mode?
|
|
||||||
|
|
||||||
def show
|
def show
|
||||||
expires_in 3.minutes, public: true
|
render_cached_json('api:v1:instances', expires_in: 5.minutes) do
|
||||||
render_with_cache json: {}, serializer: REST::InstanceSerializer, root: 'instance'
|
ActiveModelSerializers::SerializableResource.new({}, serializer: REST::InstanceSerializer)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -37,9 +37,9 @@ class Api::V1::Lists::AccountsController < Api::BaseController
|
||||||
|
|
||||||
def load_accounts
|
def load_accounts
|
||||||
if unlimited?
|
if unlimited?
|
||||||
@list.accounts.includes(:account_stat).all
|
@list.accounts.all
|
||||||
else
|
else
|
||||||
@list.accounts.includes(:account_stat).paginate_by_max_id(limit_param(DEFAULT_ACCOUNTS_LIMIT), params[:max_id], params[:since_id])
|
@list.accounts.paginate_by_max_id(limit_param(DEFAULT_ACCOUNTS_LIMIT), params[:max_id], params[:since_id])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -1,44 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
class Api::V1::MarkersController < Api::BaseController
|
|
||||||
before_action -> { doorkeeper_authorize! :read, :'read:statuses' }, only: [:index]
|
|
||||||
before_action -> { doorkeeper_authorize! :write, :'write:statuses' }, except: [:index]
|
|
||||||
|
|
||||||
before_action :require_user!
|
|
||||||
|
|
||||||
def index
|
|
||||||
@markers = current_user.markers.where(timeline: Array(params[:timeline])).each_with_object({}) { |marker, h| h[marker.timeline] = marker }
|
|
||||||
render json: serialize_map(@markers)
|
|
||||||
end
|
|
||||||
|
|
||||||
def create
|
|
||||||
Marker.transaction do
|
|
||||||
@markers = {}
|
|
||||||
|
|
||||||
resource_params.each_pair do |timeline, timeline_params|
|
|
||||||
@markers[timeline] = current_user.markers.find_or_initialize_by(timeline: timeline)
|
|
||||||
@markers[timeline].update!(timeline_params)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
render json: serialize_map(@markers)
|
|
||||||
rescue ActiveRecord::StaleObjectError
|
|
||||||
render json: { error: 'Conflict during update, please try again' }, status: 409
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def serialize_map(map)
|
|
||||||
serialized = {}
|
|
||||||
|
|
||||||
map.each_pair do |key, value|
|
|
||||||
serialized[key] = ActiveModelSerializers::SerializableResource.new(value, serializer: REST::MarkerSerializer).as_json
|
|
||||||
end
|
|
||||||
|
|
||||||
Oj.dump(serialized)
|
|
||||||
end
|
|
||||||
|
|
||||||
def resource_params
|
|
||||||
params.slice(*Marker::TIMELINES).permit(*Marker::TIMELINES.map { |timeline| { timeline.to_sym => [:last_read_id] } })
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -44,7 +44,7 @@ class Api::V1::NotificationsController < Api::BaseController
|
||||||
end
|
end
|
||||||
|
|
||||||
def browserable_account_notifications
|
def browserable_account_notifications
|
||||||
current_account.notifications.browserable(exclude_types, from_account)
|
current_account.notifications.browserable(exclude_types)
|
||||||
end
|
end
|
||||||
|
|
||||||
def target_statuses_from_notifications
|
def target_statuses_from_notifications
|
||||||
|
@ -81,10 +81,6 @@ class Api::V1::NotificationsController < Api::BaseController
|
||||||
val
|
val
|
||||||
end
|
end
|
||||||
|
|
||||||
def from_account
|
|
||||||
params[:account_id]
|
|
||||||
end
|
|
||||||
|
|
||||||
def pagination_params(core_params)
|
def pagination_params(core_params)
|
||||||
params.slice(:limit, :exclude_types).permit(:limit, exclude_types: []).merge(core_params)
|
params.slice(:limit, :exclude_types).permit(:limit, exclude_types: []).merge(core_params)
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,29 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
class Api::V1::Polls::VotesController < Api::BaseController
|
|
||||||
include Authorization
|
|
||||||
|
|
||||||
before_action -> { doorkeeper_authorize! :write, :'write:statuses' }
|
|
||||||
before_action :require_user!
|
|
||||||
before_action :set_poll
|
|
||||||
|
|
||||||
respond_to :json
|
|
||||||
|
|
||||||
def create
|
|
||||||
VoteService.new.call(current_account, @poll, vote_params[:choices])
|
|
||||||
render json: @poll, serializer: REST::PollSerializer
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def set_poll
|
|
||||||
@poll = Poll.attached.find(params[:poll_id])
|
|
||||||
authorize @poll.status, :show?
|
|
||||||
rescue Mastodon::NotPermittedError
|
|
||||||
raise ActiveRecord::RecordNotFound
|
|
||||||
end
|
|
||||||
|
|
||||||
def vote_params
|
|
||||||
params.permit(choices: [])
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,28 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
class Api::V1::PollsController < Api::BaseController
|
|
||||||
include Authorization
|
|
||||||
|
|
||||||
before_action -> { authorize_if_got_token! :read, :'read:statuses' }, only: :show
|
|
||||||
before_action :set_poll
|
|
||||||
before_action :refresh_poll
|
|
||||||
|
|
||||||
respond_to :json
|
|
||||||
|
|
||||||
def show
|
|
||||||
render json: @poll, serializer: REST::PollSerializer, include_results: true
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def set_poll
|
|
||||||
@poll = Poll.attached.find(params[:id])
|
|
||||||
authorize @poll.status, :show?
|
|
||||||
rescue Mastodon::NotPermittedError
|
|
||||||
raise ActiveRecord::RecordNotFound
|
|
||||||
end
|
|
||||||
|
|
||||||
def refresh_poll
|
|
||||||
ActivityPub::FetchRemotePollService.new.call(@poll, current_account) if user_signed_in? && @poll.possibly_stale?
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,12 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
class Api::V1::PreferencesController < Api::BaseController
|
|
||||||
before_action -> { doorkeeper_authorize! :read, :'read:accounts' }
|
|
||||||
before_action :require_user!
|
|
||||||
|
|
||||||
respond_to :json
|
|
||||||
|
|
||||||
def index
|
|
||||||
render json: current_account, serializer: REST::PreferencesSerializer
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -51,6 +51,6 @@ class Api::V1::Push::SubscriptionsController < Api::BaseController
|
||||||
|
|
||||||
def data_params
|
def data_params
|
||||||
return {} if params[:data].blank?
|
return {} if params[:data].blank?
|
||||||
params.require(:data).permit(alerts: [:follow, :favourite, :reblog, :mention, :poll])
|
params.require(:data).permit(alerts: [:follow, :favourite, :reblog, :mention])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -21,7 +21,7 @@ class Api::V1::ReportsController < Api::BaseController
|
||||||
private
|
private
|
||||||
|
|
||||||
def reported_status_ids
|
def reported_status_ids
|
||||||
reported_account.statuses.with_discarded.find(status_ids).pluck(:id)
|
reported_account.statuses.find(status_ids).pluck(:id)
|
||||||
end
|
end
|
||||||
|
|
||||||
def status_ids
|
def status_ids
|
||||||
|
|
|
@ -1,77 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
class Api::V1::ScheduledStatusesController < Api::BaseController
|
|
||||||
include Authorization
|
|
||||||
|
|
||||||
before_action -> { doorkeeper_authorize! :read, :'read:statuses' }, except: [:update, :destroy]
|
|
||||||
before_action -> { doorkeeper_authorize! :write, :'write:statuses' }, only: [:update, :destroy]
|
|
||||||
|
|
||||||
before_action :set_statuses, only: :index
|
|
||||||
before_action :set_status, except: :index
|
|
||||||
|
|
||||||
after_action :insert_pagination_headers, only: :index
|
|
||||||
|
|
||||||
def index
|
|
||||||
render json: @statuses, each_serializer: REST::ScheduledStatusSerializer
|
|
||||||
end
|
|
||||||
|
|
||||||
def show
|
|
||||||
render json: @status, serializer: REST::ScheduledStatusSerializer
|
|
||||||
end
|
|
||||||
|
|
||||||
def update
|
|
||||||
@status.update!(scheduled_status_params)
|
|
||||||
render json: @status, serializer: REST::ScheduledStatusSerializer
|
|
||||||
end
|
|
||||||
|
|
||||||
def destroy
|
|
||||||
@status.destroy!
|
|
||||||
render_empty
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def set_statuses
|
|
||||||
@statuses = current_account.scheduled_statuses.paginate_by_id(limit_param(DEFAULT_STATUSES_LIMIT), params_slice(:max_id, :since_id, :min_id))
|
|
||||||
end
|
|
||||||
|
|
||||||
def set_status
|
|
||||||
@status = current_account.scheduled_statuses.find(params[:id])
|
|
||||||
end
|
|
||||||
|
|
||||||
def scheduled_status_params
|
|
||||||
params.permit(:scheduled_at)
|
|
||||||
end
|
|
||||||
|
|
||||||
def pagination_params(core_params)
|
|
||||||
params.slice(:limit).permit(:limit).merge(core_params)
|
|
||||||
end
|
|
||||||
|
|
||||||
def insert_pagination_headers
|
|
||||||
set_pagination_headers(next_path, prev_path)
|
|
||||||
end
|
|
||||||
|
|
||||||
def next_path
|
|
||||||
if records_continue?
|
|
||||||
api_v1_scheduled_statuses_url pagination_params(max_id: pagination_max_id)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def prev_path
|
|
||||||
unless @statuses.empty?
|
|
||||||
api_v1_scheduled_statuses_url pagination_params(min_id: pagination_since_id)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def records_continue?
|
|
||||||
@statuses.size == limit_param(DEFAULT_STATUSES_LIMIT)
|
|
||||||
end
|
|
||||||
|
|
||||||
def pagination_max_id
|
|
||||||
@statuses.last.id
|
|
||||||
end
|
|
||||||
|
|
||||||
def pagination_since_id
|
|
||||||
@statuses.first.id
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class Api::V1::SearchController < Api::BaseController
|
||||||
|
include Authorization
|
||||||
|
|
||||||
|
RESULTS_LIMIT = 5
|
||||||
|
|
||||||
|
before_action -> { doorkeeper_authorize! :read, :'read:search' }
|
||||||
|
before_action :require_user!
|
||||||
|
|
||||||
|
respond_to :json
|
||||||
|
|
||||||
|
def index
|
||||||
|
@search = Search.new(search)
|
||||||
|
render json: @search, serializer: REST::SearchSerializer
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def search
|
||||||
|
search_results.tap do |search|
|
||||||
|
search[:statuses].keep_if do |status|
|
||||||
|
begin
|
||||||
|
authorize status, :show?
|
||||||
|
rescue Mastodon::NotPermittedError
|
||||||
|
false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def search_results
|
||||||
|
SearchService.new.call(
|
||||||
|
params[:q],
|
||||||
|
RESULTS_LIMIT,
|
||||||
|
truthy_param?(:resolve),
|
||||||
|
current_account
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
|
@ -22,7 +22,7 @@ class Api::V1::Statuses::FavouritedByAccountsController < Api::BaseController
|
||||||
|
|
||||||
def default_accounts
|
def default_accounts
|
||||||
Account
|
Account
|
||||||
.includes(:favourites, :account_stat)
|
.includes(:favourites)
|
||||||
.references(:favourites)
|
.references(:favourites)
|
||||||
.where(favourites: { status_id: @status.id })
|
.where(favourites: { status_id: @status.id })
|
||||||
end
|
end
|
||||||
|
|
|
@ -21,11 +21,11 @@ class Api::V1::Statuses::RebloggedByAccountsController < Api::BaseController
|
||||||
end
|
end
|
||||||
|
|
||||||
def default_accounts
|
def default_accounts
|
||||||
Account.includes(:statuses, :account_stat).references(:statuses)
|
Account.includes(:statuses).references(:statuses)
|
||||||
end
|
end
|
||||||
|
|
||||||
def paginated_statuses
|
def paginated_statuses
|
||||||
Status.where(reblog_of_id: @status.id).where(visibility: [:public, :unlisted]).paginate_by_max_id(
|
Status.where(reblog_of_id: @status.id).paginate_by_max_id(
|
||||||
limit_param(DEFAULT_ACCOUNTS_LIMIT),
|
limit_param(DEFAULT_ACCOUNTS_LIMIT),
|
||||||
params[:max_id],
|
params[:max_id],
|
||||||
params[:since_id]
|
params[:since_id]
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue