Compare commits
	
		
			4 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 661f3f26b0 | ||
|  | 2d2e3651ee | ||
|  | 951e997b26 | ||
|  | fa3f78e4bf | 
| @@ -1,3 +1,4 @@ | |||||||
| https://github.com/heroku/heroku-buildpack-apt | https://github.com/heroku/heroku-buildpack-apt | ||||||
| https://github.com/Scalingo/ffmpeg-buildpack | https://github.com/Scalingo/ffmpeg-buildpack | ||||||
|  | https://github.com/Scalingo/nodejs-buildpack | ||||||
| https://github.com/Scalingo/ruby-buildpack | https://github.com/Scalingo/ruby-buildpack | ||||||
|   | |||||||
| @@ -72,12 +72,11 @@ aliases: | |||||||
|         - run: |         - run: | ||||||
|             name: Set bundler settings |             name: Set bundler settings | ||||||
|             command: | |             command: | | ||||||
|               bundle config --local clean 'true' |               bundle config clean 'true' | ||||||
|               bundle config --local deployment 'true' |               bundle config deployment 'true' | ||||||
|               bundle config --local with 'pam_authentication' |               bundle config with 'pam_authentication' | ||||||
|               bundle config --local without 'development production' |               bundle config without 'development production' | ||||||
|               bundle config --local frozen 'true' |               bundle config frozen 'true' | ||||||
|               bundle config --local path $BUNDLE_PATH |  | ||||||
|         - run: |         - run: | ||||||
|             name: Install bundler dependencies |             name: Install bundler dependencies | ||||||
|             command: bundle check || (bundle install && bundle clean) |             command: bundle check || (bundle install && bundle clean) | ||||||
| @@ -129,13 +128,6 @@ jobs: | |||||||
|         environment: *ruby_environment |         environment: *ruby_environment | ||||||
|     <<: *install_ruby_dependencies |     <<: *install_ruby_dependencies | ||||||
|  |  | ||||||
|   install-ruby3.0: |  | ||||||
|     <<: *defaults |  | ||||||
|     docker: |  | ||||||
|       - image: circleci/ruby:3.0-buster-node |  | ||||||
|         environment: *ruby_environment |  | ||||||
|     <<: *install_ruby_dependencies |  | ||||||
|  |  | ||||||
|   build: |   build: | ||||||
|     <<: *defaults |     <<: *defaults | ||||||
|     steps: |     steps: | ||||||
| @@ -167,45 +159,8 @@ jobs: | |||||||
|           name: Create database |           name: Create database | ||||||
|           command: ./bin/rails db:create |           command: ./bin/rails db:create | ||||||
|       - run: |       - run: | ||||||
|           command: ./bin/rails db:migrate VERSION=20171010025614 |           name: Run migrations | ||||||
|           name: Run migrations up to v2.0.0 |  | ||||||
|       - run: |  | ||||||
|           command: ./bin/rails tests:migrations:populate_v2 |  | ||||||
|           name: Populate database with test data |  | ||||||
|       - run: |  | ||||||
|           command: ./bin/rails db:migrate |           command: ./bin/rails db:migrate | ||||||
|           name: Run all remaining migrations |  | ||||||
|  |  | ||||||
|   test-two-step-migrations: |  | ||||||
|     <<: *defaults |  | ||||||
|     docker: |  | ||||||
|       - image: circleci/ruby:2.7-buster-node |  | ||||||
|         environment: *ruby_environment |  | ||||||
|       - image: circleci/postgres:12.2 |  | ||||||
|         environment: |  | ||||||
|           POSTGRES_USER: root |  | ||||||
|           POSTGRES_HOST_AUTH_METHOD: trust |  | ||||||
|       - image: circleci/redis:5-alpine |  | ||||||
|     steps: |  | ||||||
|       - *attach_workspace |  | ||||||
|       - *install_system_dependencies |  | ||||||
|       - run: |  | ||||||
|           command: ./bin/rails db:create |  | ||||||
|           name: Create database |  | ||||||
|       - run: |  | ||||||
|           command: ./bin/rails db:migrate VERSION=20171010025614 |  | ||||||
|           name: Run migrations up to v2.0.0 |  | ||||||
|       - run: |  | ||||||
|           command: ./bin/rails tests:migrations:populate_v2 |  | ||||||
|           name: Populate database with test data |  | ||||||
|       - run: |  | ||||||
|           command: ./bin/rails db:migrate |  | ||||||
|           name: Run all pre-deployment migrations |  | ||||||
|           evironment: |  | ||||||
|             SKIP_POST_DEPLOYMENT_MIGRATIONS: true |  | ||||||
|       - run: |  | ||||||
|           command: ./bin/rails db:migrate |  | ||||||
|           name: Run all post-deployment remaining migrations |  | ||||||
|  |  | ||||||
|   test-ruby2.7: |   test-ruby2.7: | ||||||
|     <<: *defaults |     <<: *defaults | ||||||
| @@ -231,18 +186,6 @@ jobs: | |||||||
|       - image: circleci/redis:5-alpine |       - image: circleci/redis:5-alpine | ||||||
|     <<: *test_steps |     <<: *test_steps | ||||||
|  |  | ||||||
|   test-ruby3.0: |  | ||||||
|     <<: *defaults |  | ||||||
|     docker: |  | ||||||
|       - image: circleci/ruby:3.0-buster-node |  | ||||||
|         environment: *ruby_environment |  | ||||||
|       - image: circleci/postgres:12.2 |  | ||||||
|         environment: |  | ||||||
|           POSTGRES_USER: root |  | ||||||
|           POSTGRES_HOST_AUTH_METHOD: trust |  | ||||||
|       - image: circleci/redis:5-alpine |  | ||||||
|     <<: *test_steps |  | ||||||
|  |  | ||||||
|   test-webui: |   test-webui: | ||||||
|     <<: *defaults |     <<: *defaults | ||||||
|     docker: |     docker: | ||||||
| @@ -253,6 +196,24 @@ jobs: | |||||||
|           name: Run jest |           name: Run jest | ||||||
|           command: yarn test:jest |           command: yarn test:jest | ||||||
|  |  | ||||||
|  |   check-i18n: | ||||||
|  |     <<: *defaults | ||||||
|  |     steps: | ||||||
|  |       - *attach_workspace | ||||||
|  |       - *install_system_dependencies | ||||||
|  |       - run: | ||||||
|  |           name: Check locale file normalization | ||||||
|  |           command: bundle exec i18n-tasks check-normalized | ||||||
|  |       - run: | ||||||
|  |           name: Check for unused strings | ||||||
|  |           command: bundle exec i18n-tasks unused -l en | ||||||
|  |       - run: | ||||||
|  |           name: Check for wrong string interpolations | ||||||
|  |           command: bundle exec i18n-tasks check-consistent-interpolations | ||||||
|  |       - run: | ||||||
|  |           name: Check that all required locale files exist | ||||||
|  |           command: bundle exec rake repo:check_locales_files | ||||||
|  |  | ||||||
| workflows: | workflows: | ||||||
|   version: 2 |   version: 2 | ||||||
|   build-and-test: |   build-and-test: | ||||||
| @@ -265,19 +226,12 @@ workflows: | |||||||
|           requires: |           requires: | ||||||
|             - install |             - install | ||||||
|             - install-ruby2.7 |             - install-ruby2.7 | ||||||
|       - install-ruby3.0: |  | ||||||
|           requires: |  | ||||||
|             - install |  | ||||||
|             - install-ruby2.7 |  | ||||||
|       - build: |       - build: | ||||||
|           requires: |           requires: | ||||||
|             - install-ruby2.7 |             - install-ruby2.7 | ||||||
|       - test-migrations: |       - test-migrations: | ||||||
|           requires: |           requires: | ||||||
|             - install-ruby2.7 |             - install-ruby2.7 | ||||||
|       - test-two-step-migrations: |  | ||||||
|           requires: |  | ||||||
|             - install-ruby2.7 |  | ||||||
|       - test-ruby2.7: |       - test-ruby2.7: | ||||||
|           requires: |           requires: | ||||||
|             - install-ruby2.7 |             - install-ruby2.7 | ||||||
| @@ -286,10 +240,9 @@ workflows: | |||||||
|           requires: |           requires: | ||||||
|             - install-ruby2.6 |             - install-ruby2.6 | ||||||
|             - build |             - build | ||||||
|       - test-ruby3.0: |  | ||||||
|           requires: |  | ||||||
|             - install-ruby3.0 |  | ||||||
|             - build |  | ||||||
|       - test-webui: |       - test-webui: | ||||||
|           requires: |           requires: | ||||||
|             - install |             - install | ||||||
|  |       - check-i18n: | ||||||
|  |           requires: | ||||||
|  |             - install-ruby2.7 | ||||||
|   | |||||||
| @@ -27,10 +27,10 @@ plugins: | |||||||
|     enabled: true |     enabled: true | ||||||
|   eslint: |   eslint: | ||||||
|     enabled: true |     enabled: true | ||||||
|     channel: eslint-7 |     channel: eslint-6 | ||||||
|   rubocop: |   rubocop: | ||||||
|     enabled: true |     enabled: true | ||||||
|     channel: rubocop-1-9-1 |     channel: rubocop-0-82 | ||||||
|   sass-lint: |   sass-lint: | ||||||
|     enabled: true |     enabled: true | ||||||
| exclude_patterns: | exclude_patterns: | ||||||
|   | |||||||
| @@ -1,23 +0,0 @@ | |||||||
| version = 1 |  | ||||||
|  |  | ||||||
| test_patterns = ["app/javascript/mastodon/**/__tests__/**"] |  | ||||||
|  |  | ||||||
| exclude_patterns = [ |  | ||||||
|     "db/migrate/**", |  | ||||||
|     "db/post_migrate/**" |  | ||||||
| ] |  | ||||||
|  |  | ||||||
| [[analyzers]] |  | ||||||
| name = "ruby" |  | ||||||
| enabled = true |  | ||||||
|  |  | ||||||
| [[analyzers]] |  | ||||||
| name = "javascript" |  | ||||||
| enabled = true |  | ||||||
|  |  | ||||||
|   [analyzers.meta] |  | ||||||
|   environment = [ |  | ||||||
|     "browser", |  | ||||||
|     "jest", |  | ||||||
|     "nodejs" |  | ||||||
|   ] |  | ||||||
							
								
								
									
										28
									
								
								.dependabot/config.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								.dependabot/config.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,28 @@ | |||||||
|  | version: 1 | ||||||
|  |  | ||||||
|  | update_configs: | ||||||
|  |   - package_manager: "ruby:bundler" | ||||||
|  |     directory: "/" | ||||||
|  |     update_schedule: "weekly" | ||||||
|  |     # Supported update schedule: live daily weekly monthly | ||||||
|  |     version_requirement_updates: "auto" | ||||||
|  |     # Supported version requirements: auto widen_ranges increase_versions increase_versions_if_necessary | ||||||
|  |     allowed_updates: | ||||||
|  |       - match: | ||||||
|  |           dependency_type: "all" | ||||||
|  |           # Supported dependency types: all indirect direct production development | ||||||
|  |           update_type: "all" | ||||||
|  |           # Supported update types: all security | ||||||
|  |  | ||||||
|  |   - package_manager: "javascript" | ||||||
|  |     directory: "/" | ||||||
|  |     update_schedule: "weekly" | ||||||
|  |     # Supported update schedule: live daily weekly monthly | ||||||
|  |     version_requirement_updates: "auto" | ||||||
|  |     # Supported version requirements: auto widen_ranges increase_versions increase_versions_if_necessary | ||||||
|  |     allowed_updates: | ||||||
|  |       - match: | ||||||
|  |           dependency_type: "all" | ||||||
|  |           # Supported dependency types: all indirect direct production development | ||||||
|  |           update_type: "all" | ||||||
|  |           # Supported update types: all security | ||||||
| @@ -1,10 +1,6 @@ | |||||||
| .bundle | .bundle | ||||||
| .env | .env | ||||||
| .env.* | .env.* | ||||||
| .git |  | ||||||
| .gitattributes |  | ||||||
| .gitignore |  | ||||||
| .github |  | ||||||
| public/system | public/system | ||||||
| public/assets | public/assets | ||||||
| public/packs | public/packs | ||||||
| @@ -17,4 +13,3 @@ vendor/bundle | |||||||
| postgres | postgres | ||||||
| redis | redis | ||||||
| elasticsearch | elasticsearch | ||||||
| chart |  | ||||||
|   | |||||||
| @@ -1,66 +1,262 @@ | |||||||
| # This is a sample configuration file. You can generate your configuration | # Service dependencies | ||||||
| # with the `rake mastodon:setup` interactive setup wizard, but to customize | # You may set REDIS_URL instead for more advanced options | ||||||
| # your setup even further, you'll need to edit it manually. This sample does | # You may also set REDIS_NAMESPACE to share Redis between multiple Mastodon servers | ||||||
| # not demonstrate all available configuration options. Please look at | REDIS_HOST=redis | ||||||
| # https://docs.joinmastodon.org/admin/config/ for the full documentation. |  | ||||||
|  |  | ||||||
| # Note that this file accepts slightly different syntax depending on whether |  | ||||||
| # you are using `docker-compose` or not. In particular, if you use |  | ||||||
| # `docker-compose`, the value of each declared variable will be taken verbatim, |  | ||||||
| # including surrounding quotes. |  | ||||||
| # See: https://github.com/mastodon/mastodon/issues/16895 |  | ||||||
|  |  | ||||||
| # Federation |  | ||||||
| # ---------- |  | ||||||
| # This identifies your server and cannot be changed safely later |  | ||||||
| # ---------- |  | ||||||
| LOCAL_DOMAIN=example.com |  | ||||||
|  |  | ||||||
| # Redis |  | ||||||
| # ----- |  | ||||||
| REDIS_HOST=localhost |  | ||||||
| REDIS_PORT=6379 | REDIS_PORT=6379 | ||||||
|  | # You may set DATABASE_URL instead for more advanced options | ||||||
| # PostgreSQL | DB_HOST=db | ||||||
| # ---------- | DB_USER=postgres | ||||||
| DB_HOST=/var/run/postgresql | DB_NAME=postgres | ||||||
| DB_USER=mastodon |  | ||||||
| DB_NAME=mastodon_production |  | ||||||
| DB_PASS= | DB_PASS= | ||||||
| DB_PORT=5432 | DB_PORT=5432 | ||||||
|  | # Optional ElasticSearch configuration | ||||||
|  | # You may also set ES_PREFIX to share the same cluster between multiple Mastodon servers (falls back to REDIS_NAMESPACE if not set) | ||||||
|  | # ES_ENABLED=true | ||||||
|  | # ES_HOST=es | ||||||
|  | # ES_PORT=9200 | ||||||
|  |  | ||||||
| # ElasticSearch (optional) | # Federation | ||||||
| # ------------------------ | # Note: Changing LOCAL_DOMAIN at a later time will cause unwanted side effects, including breaking all existing federation. | ||||||
| ES_ENABLED=true | # LOCAL_DOMAIN should *NOT* contain the protocol part of the domain e.g https://example.com. | ||||||
| ES_HOST=localhost | LOCAL_DOMAIN=example.com | ||||||
| ES_PORT=9200 |  | ||||||
|  |  | ||||||
| # Secrets | # Changing LOCAL_HTTPS in production is no longer supported. (Mastodon will always serve https:// links) | ||||||
| # ------- |  | ||||||
| # Make sure to use `rake secret` to generate secrets | # Use this only if you need to run mastodon on a different domain than the one used for federation. | ||||||
| # ------- | # You can read more about this option on https://github.com/tootsuite/documentation/blob/master/Running-Mastodon/Serving_a_different_domain.md | ||||||
|  | # DO *NOT* USE THIS UNLESS YOU KNOW *EXACTLY* WHAT YOU ARE DOING. | ||||||
|  | # WEB_DOMAIN=mastodon.example.com | ||||||
|  |  | ||||||
|  | # Use this if you want to have several aliases handler@example1.com | ||||||
|  | # handler@example2.com etc. for the same user. LOCAL_DOMAIN should not | ||||||
|  | # be added. Comma separated values | ||||||
|  | # ALTERNATE_DOMAINS=example1.com,example2.com | ||||||
|  |  | ||||||
|  | # Application secrets | ||||||
|  | # Generate each with the `RAILS_ENV=production bundle exec rake secret` task (`docker-compose run --rm web bundle exec rake secret` if you use docker compose) | ||||||
| SECRET_KEY_BASE= | SECRET_KEY_BASE= | ||||||
| OTP_SECRET= | OTP_SECRET= | ||||||
|  |  | ||||||
| # Web Push | # VAPID keys (used for push notifications | ||||||
| # -------- | # You can generate the keys using the following command (first is the private key, second is the public one) | ||||||
| # Generate with `rake mastodon:webpush:generate_vapid_key` | # You should only generate this once per instance. If you later decide to change it, all push subscription will | ||||||
| # -------- | # be invalidated, requiring the users to access the website again to resubscribe. | ||||||
|  | # | ||||||
|  | # Generate with `RAILS_ENV=production bundle exec rake mastodon:webpush:generate_vapid_key` task (`docker-compose run --rm web bundle exec rake mastodon:webpush:generate_vapid_key` if you use docker compose) | ||||||
|  | # | ||||||
|  | # For more information visit https://rossta.net/blog/using-the-web-push-api-with-vapid.html | ||||||
| VAPID_PRIVATE_KEY= | VAPID_PRIVATE_KEY= | ||||||
| VAPID_PUBLIC_KEY= | VAPID_PUBLIC_KEY= | ||||||
|  |  | ||||||
| # Sending mail | # Registrations | ||||||
| # ------------ | # Single user mode will disable registrations and redirect frontpage to the first profile | ||||||
|  | # SINGLE_USER_MODE=true | ||||||
|  | # Prevent registrations with following e-mail domains | ||||||
|  | # EMAIL_DOMAIN_BLACKLIST=example1.com|example2.de|etc | ||||||
|  | # Only allow registrations with the following e-mail domains | ||||||
|  | # EMAIL_DOMAIN_WHITELIST=example1.com|example2.de|etc | ||||||
|  |  | ||||||
|  | # Optionally change default language | ||||||
|  | # DEFAULT_LOCALE=de | ||||||
|  |  | ||||||
|  | # E-mail configuration | ||||||
|  | # Note: Mailgun and SparkPost (https://sparkpo.st/smtp) each have good free tiers | ||||||
|  | # If you want to use an SMTP server without authentication (e.g local Postfix relay) | ||||||
|  | # then set SMTP_AUTH_METHOD and SMTP_OPENSSL_VERIFY_MODE to 'none' and | ||||||
|  | # *comment* SMTP_LOGIN and SMTP_PASSWORD (leaving them blank is not enough). | ||||||
| SMTP_SERVER=smtp.mailgun.org | SMTP_SERVER=smtp.mailgun.org | ||||||
| SMTP_PORT=587 | SMTP_PORT=587 | ||||||
| SMTP_LOGIN= | SMTP_LOGIN= | ||||||
| SMTP_PASSWORD= | SMTP_PASSWORD= | ||||||
| SMTP_FROM_ADDRESS=notificatons@example.com | SMTP_FROM_ADDRESS=notifications@example.com | ||||||
|  | #SMTP_REPLY_TO= | ||||||
|  | #SMTP_DOMAIN= # defaults to LOCAL_DOMAIN | ||||||
|  | #SMTP_DELIVERY_METHOD=smtp # delivery method can also be sendmail | ||||||
|  | #SMTP_AUTH_METHOD=plain | ||||||
|  | #SMTP_CA_FILE=/etc/ssl/certs/ca-certificates.crt | ||||||
|  | #SMTP_OPENSSL_VERIFY_MODE=peer | ||||||
|  | #SMTP_ENABLE_STARTTLS_AUTO=true | ||||||
|  | #SMTP_TLS=true | ||||||
|  |  | ||||||
| # File storage (optional) | # Optional user upload path and URL (images, avatars). Default is :rails_root/public/system. If you set this variable, you are responsible for making your HTTP server (eg. nginx) serve these files. | ||||||
| # ----------------------- | # PAPERCLIP_ROOT_PATH=/var/lib/mastodon/public-system | ||||||
| S3_ENABLED=true | # PAPERCLIP_ROOT_URL=/system | ||||||
| S3_BUCKET=files.example.com |  | ||||||
| AWS_ACCESS_KEY_ID= | # Optional asset host for multi-server setups | ||||||
| AWS_SECRET_ACCESS_KEY= | # The asset host must allow cross origin request from WEB_DOMAIN or LOCAL_DOMAIN | ||||||
| S3_ALIAS_HOST=files.example.com | # 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 | ||||||
|  |  | ||||||
|  | # S3 (optional) | ||||||
|  | # The attachment host must allow cross origin request from WEB_DOMAIN or | ||||||
|  | # LOCAL_DOMAIN if WEB_DOMAIN is not set. For example, the server may have the | ||||||
|  | # following header field: | ||||||
|  | # Access-Control-Allow-Origin: https://192.168.1.123:9000/ | ||||||
|  | # S3_ENABLED=true | ||||||
|  | # S3_BUCKET= | ||||||
|  | # AWS_ACCESS_KEY_ID= | ||||||
|  | # AWS_SECRET_ACCESS_KEY= | ||||||
|  | # S3_REGION= | ||||||
|  | # S3_PROTOCOL=http | ||||||
|  | # S3_HOSTNAME=192.168.1.123:9000 | ||||||
|  |  | ||||||
|  | # S3 (Minio Config (optional) Please check Minio instance for details) | ||||||
|  | # The attachment host must allow cross origin request - see the description | ||||||
|  | # above. | ||||||
|  | # S3_ENABLED=true | ||||||
|  | # S3_BUCKET= | ||||||
|  | # AWS_ACCESS_KEY_ID= | ||||||
|  | # AWS_SECRET_ACCESS_KEY= | ||||||
|  | # S3_REGION= | ||||||
|  | # S3_PROTOCOL=https | ||||||
|  | # S3_HOSTNAME= | ||||||
|  | # S3_ENDPOINT= | ||||||
|  | # S3_SIGNATURE_VERSION= | ||||||
|  |  | ||||||
|  | # 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) | ||||||
|  | # The attachment host must allow cross origin request - see the description | ||||||
|  | # above. | ||||||
|  | # SWIFT_ENABLED=true | ||||||
|  | # SWIFT_USERNAME= | ||||||
|  | # For Keystone V3, the value for SWIFT_TENANT should be the project name | ||||||
|  | # SWIFT_TENANT= | ||||||
|  | # SWIFT_PASSWORD= | ||||||
|  | # Some OpenStack V3 providers require PROJECT_ID (optional) | ||||||
|  | # SWIFT_PROJECT_ID= | ||||||
|  | # Keystone V2 and V3 URLs are supported. Use a V3 URL if possible to avoid | ||||||
|  | # issues with token rate-limiting during high load. | ||||||
|  | # SWIFT_AUTH_URL= | ||||||
|  | # SWIFT_CONTAINER= | ||||||
|  | # SWIFT_OBJECT_URL= | ||||||
|  | # SWIFT_REGION= | ||||||
|  | # Defaults to 'default' | ||||||
|  | # SWIFT_DOMAIN_NAME= | ||||||
|  | # Defaults to 60 seconds. Set to 0 to disable | ||||||
|  | # SWIFT_CACHE_TTL= | ||||||
|  |  | ||||||
|  | # Optional alias for S3 (e.g. to serve files on a custom domain, possibly using Cloudfront or Cloudflare) | ||||||
|  | # S3_ALIAS_HOST= | ||||||
|  |  | ||||||
|  | # Streaming API integration | ||||||
|  | # STREAMING_API_BASE_URL= | ||||||
|  |  | ||||||
|  | # Advanced settings | ||||||
|  | # If you need to use pgBouncer, you need to disable prepared statements: | ||||||
|  | # PREPARED_STATEMENTS=false | ||||||
|  |  | ||||||
|  | # Cluster number setting for streaming API server. | ||||||
|  | # If you comment out following line, cluster number will be `numOfCpuCores - 1`. | ||||||
|  | STREAMING_CLUSTER_NUM=1 | ||||||
|  |  | ||||||
|  | # Docker mastodon user | ||||||
|  | # If you use Docker, you may want to assign UID/GID manually. | ||||||
|  | # UID=1000 | ||||||
|  | # GID=1000 | ||||||
|  |  | ||||||
|  | # LDAP authentication (optional) | ||||||
|  | # LDAP_ENABLED=true | ||||||
|  | # LDAP_HOST=localhost | ||||||
|  | # LDAP_PORT=389 | ||||||
|  | # LDAP_METHOD=simple_tls | ||||||
|  | # LDAP_BASE= | ||||||
|  | # LDAP_BIND_DN= | ||||||
|  | # LDAP_PASSWORD= | ||||||
|  | # LDAP_UID=cn | ||||||
|  | # LDAP_MAIL=mail | ||||||
|  | # LDAP_SEARCH_FILTER=(|(%{uid}=%{email})(%{mail}=%{email})) | ||||||
|  | # LDAP_UID_CONVERSION_ENABLED=true | ||||||
|  | # LDAP_UID_CONVERSION_SEARCH=., - | ||||||
|  | # LDAP_UID_CONVERSION_REPLACE=_ | ||||||
|  |  | ||||||
|  | # PAM authentication (optional) | ||||||
|  | # PAM authentication uses for the email generation the "email" pam variable | ||||||
|  | # and optional as fallback PAM_DEFAULT_SUFFIX | ||||||
|  | # The pam environment variable "email" is provided by: | ||||||
|  | # https://github.com/devkral/pam_email_extractor | ||||||
|  | # PAM_ENABLED=true | ||||||
|  | # Fallback email domain for email address generation (LOCAL_DOMAIN by default) | ||||||
|  | # PAM_EMAIL_DOMAIN=example.com | ||||||
|  | # Name of the pam service (pam "auth" section is evaluated) | ||||||
|  | # PAM_DEFAULT_SERVICE=rpam | ||||||
|  | # Name of the pam service used for checking if an user can register (pam "account" section is evaluated) (nil (disabled) by default) | ||||||
|  | # PAM_CONTROLLED_SERVICE=rpam | ||||||
|  |  | ||||||
|  | # Global OAuth settings (optional) : | ||||||
|  | # If you have only one strategy, you may want to enable this | ||||||
|  | # OAUTH_REDIRECT_AT_SIGN_IN=true | ||||||
|  |  | ||||||
|  | # Optional CAS authentication (cf. omniauth-cas) : | ||||||
|  | # CAS_ENABLED=true | ||||||
|  | # CAS_URL=https://sso.myserver.com/ | ||||||
|  | # CAS_HOST=sso.myserver.com/ | ||||||
|  | # CAS_PORT=443 | ||||||
|  | # CAS_SSL=true | ||||||
|  | # CAS_VALIDATE_URL= | ||||||
|  | # CAS_CALLBACK_URL= | ||||||
|  | # CAS_LOGOUT_URL= | ||||||
|  | # CAS_LOGIN_URL= | ||||||
|  | # CAS_UID_FIELD='user' | ||||||
|  | # CAS_CA_PATH= | ||||||
|  | # CAS_DISABLE_SSL_VERIFICATION=false | ||||||
|  | # CAS_UID_KEY='user' | ||||||
|  | # CAS_NAME_KEY='name' | ||||||
|  | # CAS_EMAIL_KEY='email' | ||||||
|  | # CAS_NICKNAME_KEY='nickname' | ||||||
|  | # CAS_FIRST_NAME_KEY='firstname' | ||||||
|  | # CAS_LAST_NAME_KEY='lastname' | ||||||
|  | # CAS_LOCATION_KEY='location' | ||||||
|  | # CAS_IMAGE_KEY='image' | ||||||
|  | # CAS_PHONE_KEY='phone' | ||||||
|  |  | ||||||
|  | # Optional SAML authentication (cf. omniauth-saml) | ||||||
|  | # SAML_ENABLED=true | ||||||
|  | # SAML_ACS_URL=http://localhost:3000/auth/auth/saml/callback | ||||||
|  | # SAML_ISSUER=https://example.com | ||||||
|  | # SAML_IDP_SSO_TARGET_URL=https://idp.testshib.org/idp/profile/SAML2/Redirect/SSO | ||||||
|  | # SAML_IDP_CERT= | ||||||
|  | # SAML_IDP_CERT_FINGERPRINT= | ||||||
|  | # SAML_NAME_IDENTIFIER_FORMAT= | ||||||
|  | # SAML_CERT= | ||||||
|  | # SAML_PRIVATE_KEY= | ||||||
|  | # SAML_SECURITY_WANT_ASSERTION_SIGNED=true | ||||||
|  | # SAML_SECURITY_WANT_ASSERTION_ENCRYPTED=true | ||||||
|  | # SAML_SECURITY_ASSUME_EMAIL_IS_VERIFIED=true | ||||||
|  | # SAML_ATTRIBUTES_STATEMENTS_UID="urn:oid:0.9.2342.19200300.100.1.1" | ||||||
|  | # SAML_ATTRIBUTES_STATEMENTS_EMAIL="urn:oid:1.3.6.1.4.1.5923.1.1.1.6" | ||||||
|  | # SAML_ATTRIBUTES_STATEMENTS_FULL_NAME="urn:oid:2.16.840.1.113730.3.1.241" | ||||||
|  | # SAML_ATTRIBUTES_STATEMENTS_FIRST_NAME="urn:oid:2.5.4.42" | ||||||
|  | # SAML_ATTRIBUTES_STATEMENTS_LAST_NAME="urn:oid:2.5.4.4" | ||||||
|  | # SAML_UID_ATTRIBUTE="urn:oid:0.9.2342.19200300.100.1.1" | ||||||
|  | # SAML_ATTRIBUTES_STATEMENTS_VERIFIED= | ||||||
|  | # SAML_ATTRIBUTES_STATEMENTS_VERIFIED_EMAIL= | ||||||
|  |  | ||||||
|  | # Use HTTP proxy for outgoing request (optional) | ||||||
|  | # http_proxy=http://gateway.local:8118 | ||||||
|  | # Access control for hidden service. | ||||||
|  | # ALLOW_ACCESS_TO_HIDDEN_SERVICE=true | ||||||
|  |  | ||||||
|  | # Authorized fetch mode (optional) | ||||||
|  | # Require remote servers to authentify when fetching toots, see | ||||||
|  | # https://docs.joinmastodon.org/admin/config/#authorized_fetch | ||||||
|  | # AUTHORIZED_FETCH=true | ||||||
|  |  | ||||||
|  | # Whitelist mode (optional) | ||||||
|  | # Only allow federation with whitelisted domains, see | ||||||
|  | # https://docs.joinmastodon.org/admin/config/#whitelist_mode | ||||||
|  | # WHITELIST_MODE=true | ||||||
|   | |||||||
| @@ -1,4 +1,3 @@ | |||||||
| VAGRANT=true | VAGRANT=true | ||||||
| LOCAL_DOMAIN=mastodon.local | LOCAL_DOMAIN=mastodon.local | ||||||
| BIND=0.0.0.0 | BIND=0.0.0.0 | ||||||
| DB_HOST=/var/run/postgresql/ |  | ||||||
|   | |||||||
| @@ -199,11 +199,6 @@ module.exports = { | |||||||
|     'import/no-unresolved': 'error', |     'import/no-unresolved': 'error', | ||||||
|     'import/no-webpack-loader-syntax': 'error', |     'import/no-webpack-loader-syntax': 'error', | ||||||
|  |  | ||||||
|     'promise/catch-or-return': [ |     'promise/catch-or-return': 'error', | ||||||
|       'error', |  | ||||||
|       { |  | ||||||
|         allowFinally: true, |  | ||||||
|       }, |  | ||||||
|     ], |  | ||||||
|   }, |   }, | ||||||
| }; | }; | ||||||
|   | |||||||
							
								
								
									
										2
									
								
								.github/CODEOWNERS
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/CODEOWNERS
									
									
									
									
										vendored
									
									
								
							| @@ -1,4 +1,4 @@ | |||||||
| # CODEOWNERS for mastodon/mastodon | # CODEOWNERS for tootsuite/mastodon | ||||||
|  |  | ||||||
| # Translators | # Translators | ||||||
| # To add translator, copy these lines, replace `fr` with appropriate language code and replace `@żelipapą` with user's GitHub nickname preceded by `@` sign or e-mail address. | # To add translator, copy these lines, replace `fr` with appropriate language code and replace `@żelipapą` with user's GitHub nickname preceded by `@` sign or e-mail address. | ||||||
|   | |||||||
							
								
								
									
										1
									
								
								.github/FUNDING.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.github/FUNDING.yml
									
									
									
									
										vendored
									
									
								
							| @@ -1,3 +1,2 @@ | |||||||
| patreon: mastodon | patreon: mastodon | ||||||
| open_collective: mastodon | open_collective: mastodon | ||||||
| github: [Gargron] |  | ||||||
|   | |||||||
							
								
								
									
										2
									
								
								.github/ISSUE_TEMPLATE/bug_report.md
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/ISSUE_TEMPLATE/bug_report.md
									
									
									
									
										vendored
									
									
								
							| @@ -1,7 +1,7 @@ | |||||||
| --- | --- | ||||||
| name: Bug Report | name: Bug Report | ||||||
| about: If something isn't working as expected | about: If something isn't working as expected | ||||||
| labels: bug |  | ||||||
| --- | --- | ||||||
|  |  | ||||||
| <!-- Make sure that you are submitting a new bug that was not previously reported or already fixed --> | <!-- Make sure that you are submitting a new bug that was not previously reported or already fixed --> | ||||||
|   | |||||||
							
								
								
									
										1
									
								
								.github/ISSUE_TEMPLATE/feature_request.md
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.github/ISSUE_TEMPLATE/feature_request.md
									
									
									
									
										vendored
									
									
								
							| @@ -1,6 +1,7 @@ | |||||||
| --- | --- | ||||||
| name: Feature Request | name: Feature Request | ||||||
| about: I have a suggestion | about: I have a suggestion | ||||||
|  |  | ||||||
| --- | --- | ||||||
|  |  | ||||||
| <!-- Please use a concise and distinct title for the issue --> | <!-- Please use a concise and distinct title for the issue --> | ||||||
|   | |||||||
							
								
								
									
										22
									
								
								.github/dependabot.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										22
									
								
								.github/dependabot.yml
									
									
									
									
										vendored
									
									
								
							| @@ -1,22 +0,0 @@ | |||||||
| # To get started with Dependabot version updates, you'll need to specify which |  | ||||||
| # package ecosystems to update and where the package manifests are located. |  | ||||||
| # Please see the documentation for all configuration options: |  | ||||||
| # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates |  | ||||||
|  |  | ||||||
| version: 2 |  | ||||||
| updates: |  | ||||||
|   - package-ecosystem: npm |  | ||||||
|     directory: "/" |  | ||||||
|     schedule: |  | ||||||
|       interval: weekly |  | ||||||
|     open-pull-requests-limit: 99 |  | ||||||
|     allow: |  | ||||||
|       - dependency-type: direct |  | ||||||
|  |  | ||||||
|   - package-ecosystem: bundler |  | ||||||
|     directory: "/" |  | ||||||
|     schedule: |  | ||||||
|       interval: weekly |  | ||||||
|     open-pull-requests-limit: 99 |  | ||||||
|     allow: |  | ||||||
|       - dependency-type: direct |  | ||||||
							
								
								
									
										34
									
								
								.github/workflows/build-image.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										34
									
								
								.github/workflows/build-image.yml
									
									
									
									
										vendored
									
									
								
							| @@ -1,34 +0,0 @@ | |||||||
| name: Build container image |  | ||||||
| on: |  | ||||||
|   workflow_dispatch: |  | ||||||
|   push: |  | ||||||
|     branches: |  | ||||||
|       - "main" |  | ||||||
|     tags: |  | ||||||
|       - "*" |  | ||||||
| jobs: |  | ||||||
|   build-image: |  | ||||||
|     runs-on: ubuntu-latest |  | ||||||
|     steps: |  | ||||||
|       - uses: actions/checkout@v2 |  | ||||||
|       - uses: docker/setup-buildx-action@v1 |  | ||||||
|       - uses: docker/login-action@v1 |  | ||||||
|         with: |  | ||||||
|           username: ${{ secrets.DOCKERHUB_USERNAME }} |  | ||||||
|           password: ${{ secrets.DOCKERHUB_TOKEN }} |  | ||||||
|       - uses: docker/metadata-action@v3 |  | ||||||
|         id: meta |  | ||||||
|         with: |  | ||||||
|           images: tootsuite/mastodon |  | ||||||
|           flavor: | |  | ||||||
|             latest=auto |  | ||||||
|           tags: | |  | ||||||
|             type=edge,branch=main |  | ||||||
|             type=semver,pattern={{ raw }} |  | ||||||
|       - uses: docker/build-push-action@v2 |  | ||||||
|         with: |  | ||||||
|           context: . |  | ||||||
|           push: true |  | ||||||
|           tags: ${{ steps.meta.outputs.tags }} |  | ||||||
|           cache-from: type=registry,ref=tootsuite/mastodon:latest |  | ||||||
|           cache-to: type=inline |  | ||||||
							
								
								
									
										34
									
								
								.github/workflows/check-i18n.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										34
									
								
								.github/workflows/check-i18n.yml
									
									
									
									
										vendored
									
									
								
							| @@ -1,34 +0,0 @@ | |||||||
| name: Check i18n |  | ||||||
|  |  | ||||||
| on: |  | ||||||
|   push: |  | ||||||
|     branches: [ main ] |  | ||||||
|   pull_request: |  | ||||||
|     branches: [ main ] |  | ||||||
|  |  | ||||||
| env: |  | ||||||
|   RAILS_ENV: test |  | ||||||
|  |  | ||||||
| jobs: |  | ||||||
|   check-i18n: |  | ||||||
|     runs-on: ubuntu-latest |  | ||||||
|  |  | ||||||
|     steps: |  | ||||||
|     - uses: actions/checkout@v2 |  | ||||||
|     - name: Install system dependencies |  | ||||||
|       run: | |  | ||||||
|         sudo apt-get update |  | ||||||
|         sudo apt-get install -y libicu-dev libidn11-dev libprotobuf-dev protobuf-compiler |  | ||||||
|     - name: Set up Ruby |  | ||||||
|       uses: ruby/setup-ruby@v1 |  | ||||||
|       with: |  | ||||||
|         ruby-version: '2.7' |  | ||||||
|         bundler-cache: true |  | ||||||
|     - name: Check locale file normalization |  | ||||||
|       run: bundle exec i18n-tasks check-normalized |  | ||||||
|     - name: Check for unused strings |  | ||||||
|       run: bundle exec i18n-tasks unused -l en |  | ||||||
|     - name: Check for wrong string interpolations |  | ||||||
|       run: bundle exec i18n-tasks check-consistent-interpolations |  | ||||||
|     - name: Check that all required locale files exist |  | ||||||
|       run: bundle exec rake repo:check_locales_files |  | ||||||
							
								
								
									
										25
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										25
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -17,34 +17,31 @@ | |||||||
| /log/* | /log/* | ||||||
| !/log/.keep | !/log/.keep | ||||||
| /tmp | /tmp | ||||||
| /coverage | coverage | ||||||
| /public/system | public/system | ||||||
| /public/assets | public/assets | ||||||
| /public/packs | public/packs | ||||||
| /public/packs-test | public/packs-test | ||||||
| .env | .env | ||||||
| .env.production | .env.production | ||||||
| .env.development | .env.development | ||||||
| /node_modules/ | node_modules/ | ||||||
| /build/ | build/ | ||||||
|  |  | ||||||
| # Ignore Vagrant files | # Ignore Vagrant files | ||||||
| .vagrant/ | .vagrant/ | ||||||
|  |  | ||||||
| # Ignore Capistrano customizations | # Ignore Capistrano customizations | ||||||
| /config/deploy/* | config/deploy/* | ||||||
|  |  | ||||||
| # Ignore IDE files | # Ignore IDE files | ||||||
| .vscode/ | .vscode/ | ||||||
| .idea/ | .idea/ | ||||||
|  |  | ||||||
| # Ignore postgres + redis + elasticsearch volume optionally created by docker-compose | # Ignore postgres + redis + elasticsearch volume optionally created by docker-compose | ||||||
| /postgres | postgres | ||||||
| /redis | redis | ||||||
| /elasticsearch | elasticsearch | ||||||
|  |  | ||||||
| # ignore Helm dependency charts |  | ||||||
| /chart/charts/*.tgz |  | ||||||
|  |  | ||||||
| # Ignore Apple files | # Ignore Apple files | ||||||
| .DS_Store | .DS_Store | ||||||
|   | |||||||
							
								
								
									
										192
									
								
								.rubocop.yml
									
									
									
									
									
								
							
							
						
						
									
										192
									
								
								.rubocop.yml
									
									
									
									
									
								
							| @@ -2,8 +2,7 @@ require: | |||||||
|   - rubocop-rails |   - rubocop-rails | ||||||
|  |  | ||||||
| AllCops: | AllCops: | ||||||
|   TargetRubyVersion: 2.5 |   TargetRubyVersion: 2.4 | ||||||
|   NewCops: disable |  | ||||||
|   Exclude: |   Exclude: | ||||||
|   - 'spec/**/*' |   - 'spec/**/*' | ||||||
|   - 'db/**/*' |   - 'db/**/*' | ||||||
| @@ -26,68 +25,26 @@ Layout/AccessModifierIndentation: | |||||||
| Layout/EmptyLineAfterMagicComment: | Layout/EmptyLineAfterMagicComment: | ||||||
|   Enabled: false |   Enabled: false | ||||||
|  |  | ||||||
| Layout/EmptyLineAfterGuardClause: |  | ||||||
|   Enabled: false |  | ||||||
|  |  | ||||||
| Layout/EmptyLinesAroundAttributeAccessor: |  | ||||||
|   Enabled: true |  | ||||||
|  |  | ||||||
| Layout/HashAlignment: |  | ||||||
|   Enabled: false |  | ||||||
|   # EnforcedHashRocketStyle: table |  | ||||||
|   # EnforcedColonStyle: table |  | ||||||
|  |  | ||||||
| Layout/SpaceAroundMethodCallOperator: |  | ||||||
|   Enabled: true |  | ||||||
|  |  | ||||||
| Layout/SpaceInsideHashLiteralBraces: | Layout/SpaceInsideHashLiteralBraces: | ||||||
|   EnforcedStyle: space |   EnforcedStyle: space | ||||||
|  |  | ||||||
| Lint/DeprecatedOpenSSLConstant: |  | ||||||
|   Enabled: true |  | ||||||
|  |  | ||||||
| Lint/DuplicateElsifCondition: |  | ||||||
|   Enabled: true |  | ||||||
|  |  | ||||||
| Lint/MixedRegexpCaptureTypes: |  | ||||||
|   Enabled: true |  | ||||||
|  |  | ||||||
| Lint/RaiseException: |  | ||||||
|   Enabled: true |  | ||||||
|  |  | ||||||
| Lint/StructNewOverride: |  | ||||||
|   Enabled: true |  | ||||||
|  |  | ||||||
| Lint/UselessAccessModifier: |  | ||||||
|   ContextCreatingMethods: |  | ||||||
|     - class_methods |  | ||||||
|  |  | ||||||
| Metrics/AbcSize: | Metrics/AbcSize: | ||||||
|   Max: 100 |   Max: 100 | ||||||
|   Exclude: |  | ||||||
|     - 'lib/mastodon/*_cli.rb' |  | ||||||
|  |  | ||||||
| Metrics/BlockLength: | Metrics/BlockLength: | ||||||
|   Max: 55 |   Max: 35 | ||||||
|   Exclude: |   Exclude: | ||||||
|     - 'lib/tasks/**/*' |     - 'lib/tasks/**/*' | ||||||
|     - 'lib/mastodon/*_cli.rb' |  | ||||||
|  |  | ||||||
| Metrics/BlockNesting: | Metrics/BlockNesting: | ||||||
|   Max: 3 |   Max: 3 | ||||||
|   Exclude: |  | ||||||
|     - 'lib/mastodon/*_cli.rb' |  | ||||||
|  |  | ||||||
| Metrics/ClassLength: | Metrics/ClassLength: | ||||||
|   CountComments: false |   CountComments: false | ||||||
|   Max: 400 |   Max: 300 | ||||||
|   Exclude: |  | ||||||
|     - 'lib/mastodon/*_cli.rb' |  | ||||||
|  |  | ||||||
| Metrics/CyclomaticComplexity: | Metrics/CyclomaticComplexity: | ||||||
|   Max: 25 |   Max: 25 | ||||||
|   Exclude: |  | ||||||
|     - 'lib/mastodon/*_cli.rb' |  | ||||||
|  |  | ||||||
| Layout/LineLength: | Layout/LineLength: | ||||||
|   AllowURI: true |   AllowURI: true | ||||||
| @@ -95,9 +52,7 @@ Layout/LineLength: | |||||||
|  |  | ||||||
| Metrics/MethodLength: | Metrics/MethodLength: | ||||||
|   CountComments: false |   CountComments: false | ||||||
|   Max: 65 |   Max: 55 | ||||||
|   Exclude: |  | ||||||
|     - 'lib/mastodon/*_cli.rb' |  | ||||||
|  |  | ||||||
| Metrics/ModuleLength: | Metrics/ModuleLength: | ||||||
|   CountComments: false |   CountComments: false | ||||||
| @@ -108,90 +63,34 @@ Metrics/ParameterLists: | |||||||
|   CountKeywordArgs: true |   CountKeywordArgs: true | ||||||
|  |  | ||||||
| Metrics/PerceivedComplexity: | Metrics/PerceivedComplexity: | ||||||
|   Max: 25 |   Max: 20 | ||||||
|  |  | ||||||
| Naming/MemoizedInstanceVariableName: | Naming/MemoizedInstanceVariableName: | ||||||
|   Enabled: false |   Enabled: false | ||||||
|  |  | ||||||
| Naming/MethodParameterName: |  | ||||||
|   Enabled: true |  | ||||||
|  |  | ||||||
| Rails: | Rails: | ||||||
|   Enabled: true |   Enabled: true | ||||||
|  |  | ||||||
| Rails/ApplicationController: |  | ||||||
|   Enabled: false |  | ||||||
|   Exclude: |  | ||||||
|     - 'app/controllers/well_known/**/*.rb' |  | ||||||
|  |  | ||||||
| Rails/BelongsTo: |  | ||||||
|   Enabled: false |  | ||||||
|  |  | ||||||
| Rails/ContentTag: |  | ||||||
|   Enabled: false |  | ||||||
|  |  | ||||||
| Rails/EnumHash: | Rails/EnumHash: | ||||||
|   Enabled: false |   Enabled: false | ||||||
|  |  | ||||||
|  | Rails/HasAndBelongsToMany: | ||||||
|  |   Enabled: false | ||||||
|  |  | ||||||
|  | Rails/SkipsModelValidations: | ||||||
|  |   Enabled: false | ||||||
|  |  | ||||||
|  | Rails/HttpStatus: | ||||||
|  |   Enabled: false | ||||||
|  |  | ||||||
| Rails/Exit: | Rails/Exit: | ||||||
|   Exclude: |   Exclude: | ||||||
|     - 'lib/mastodon/*' |     - 'lib/mastodon/*' | ||||||
|     - 'lib/cli.rb' |     - 'lib/cli.rb' | ||||||
|  |  | ||||||
| Rails/FilePath: |  | ||||||
|   Enabled: false |  | ||||||
|  |  | ||||||
| Rails/HasAndBelongsToMany: |  | ||||||
|   Enabled: false |  | ||||||
|  |  | ||||||
| Rails/HasManyOrHasOneDependent: |  | ||||||
|   Enabled: false |  | ||||||
|  |  | ||||||
| Rails/HelperInstanceVariable: | Rails/HelperInstanceVariable: | ||||||
|   Enabled: false |   Enabled: false | ||||||
|  |  | ||||||
| Rails/HttpStatus: |  | ||||||
|   Enabled: false |  | ||||||
|  |  | ||||||
| Rails/IndexBy: |  | ||||||
|   Enabled: false |  | ||||||
|  |  | ||||||
| Rails/InverseOf: |  | ||||||
|   Enabled: false |  | ||||||
|  |  | ||||||
| Rails/LexicallyScopedActionFilter: |  | ||||||
|   Enabled: false |  | ||||||
|  |  | ||||||
| Rails/OutputSafety: |  | ||||||
|   Enabled: true |  | ||||||
|  |  | ||||||
| Rails/RakeEnvironment: |  | ||||||
|   Enabled: false |  | ||||||
|  |  | ||||||
| Rails/RedundantForeignKey: |  | ||||||
|   Enabled: false |  | ||||||
|  |  | ||||||
| Rails/SkipsModelValidations: |  | ||||||
|   Enabled: false |  | ||||||
|  |  | ||||||
| Rails/UniqueValidationWithoutIndex: |  | ||||||
|   Enabled: false |  | ||||||
|  |  | ||||||
| Style/AccessorGrouping: |  | ||||||
|   Enabled: true |  | ||||||
|  |  | ||||||
| Style/AccessModifierDeclarations: |  | ||||||
|   Enabled: false |  | ||||||
|  |  | ||||||
| Style/ArrayCoercion: |  | ||||||
|   Enabled: true |  | ||||||
|  |  | ||||||
| Style/BisectedAttrAccessor: |  | ||||||
|   Enabled: true |  | ||||||
|  |  | ||||||
| Style/CaseLikeIf: |  | ||||||
|   Enabled: false |  | ||||||
|  |  | ||||||
| Style/ClassAndModuleChildren: | Style/ClassAndModuleChildren: | ||||||
|   Enabled: false |   Enabled: false | ||||||
|  |  | ||||||
| @@ -206,15 +105,6 @@ Style/Documentation: | |||||||
| Style/DoubleNegation: | Style/DoubleNegation: | ||||||
|   Enabled: true |   Enabled: true | ||||||
|  |  | ||||||
| Style/ExpandPathArguments: |  | ||||||
|   Enabled: false |  | ||||||
|  |  | ||||||
| Style/ExponentialNotation: |  | ||||||
|   Enabled: true |  | ||||||
|  |  | ||||||
| Style/FormatString: |  | ||||||
|   Enabled: false |  | ||||||
|  |  | ||||||
| Style/FormatStringToken: | Style/FormatStringToken: | ||||||
|   Enabled: false |   Enabled: false | ||||||
|  |  | ||||||
| @@ -224,33 +114,9 @@ Style/FrozenStringLiteralComment: | |||||||
| Style/GuardClause: | Style/GuardClause: | ||||||
|   Enabled: false |   Enabled: false | ||||||
|  |  | ||||||
| Style/HashAsLastArrayItem: |  | ||||||
|   Enabled: false |  | ||||||
|  |  | ||||||
| Style/HashEachMethods: |  | ||||||
|   Enabled: true |  | ||||||
|  |  | ||||||
| Style/HashLikeCase: |  | ||||||
|   Enabled: true |  | ||||||
|  |  | ||||||
| Style/HashTransformKeys: |  | ||||||
|   Enabled: true |  | ||||||
|  |  | ||||||
| Style/HashTransformValues: |  | ||||||
|   Enabled: false |  | ||||||
|  |  | ||||||
| Style/IfUnlessModifier: |  | ||||||
|   Enabled: false |  | ||||||
|  |  | ||||||
| Style/InverseMethods: |  | ||||||
|   Enabled: false |  | ||||||
|  |  | ||||||
| Style/Lambda: | Style/Lambda: | ||||||
|   Enabled: false |   Enabled: false | ||||||
|  |  | ||||||
| Style/MutableConstant: |  | ||||||
|   Enabled: false |  | ||||||
|  |  | ||||||
| Style/PercentLiteralDelimiters: | Style/PercentLiteralDelimiters: | ||||||
|   PreferredDelimiters: |   PreferredDelimiters: | ||||||
|     '%i': '()' |     '%i': '()' | ||||||
| @@ -259,36 +125,9 @@ Style/PercentLiteralDelimiters: | |||||||
| Style/PerlBackrefs: | Style/PerlBackrefs: | ||||||
|   AutoCorrect: false |   AutoCorrect: false | ||||||
|  |  | ||||||
| Style/RedundantAssignment: |  | ||||||
|   Enabled: false |  | ||||||
|  |  | ||||||
| Style/RedundantFetchBlock: |  | ||||||
|   Enabled: true |  | ||||||
|  |  | ||||||
| Style/RedundantFileExtensionInRequire: |  | ||||||
|   Enabled: true |  | ||||||
|  |  | ||||||
| Style/RedundantRegexpCharacterClass: |  | ||||||
|   Enabled: false |  | ||||||
|  |  | ||||||
| Style/RedundantRegexpEscape: |  | ||||||
|   Enabled: false |  | ||||||
|  |  | ||||||
| Style/RedundantReturn: |  | ||||||
|   Enabled: true |  | ||||||
|  |  | ||||||
| Style/RegexpLiteral: | Style/RegexpLiteral: | ||||||
|   Enabled: false |   Enabled: false | ||||||
|  |  | ||||||
| Style/RescueStandardError: |  | ||||||
|   Enabled: false |  | ||||||
|  |  | ||||||
| Style/SignalException: |  | ||||||
|   Enabled: false |  | ||||||
|  |  | ||||||
| Style/SlicingWithRange: |  | ||||||
|   Enabled: true |  | ||||||
|  |  | ||||||
| Style/SymbolArray: | Style/SymbolArray: | ||||||
|   Enabled: false |   Enabled: false | ||||||
|  |  | ||||||
| @@ -297,6 +136,3 @@ Style/TrailingCommaInArrayLiteral: | |||||||
|  |  | ||||||
| Style/TrailingCommaInHashLiteral: | Style/TrailingCommaInHashLiteral: | ||||||
|   EnforcedStyleForMultiline: 'comma' |   EnforcedStyleForMultiline: 'comma' | ||||||
|  |  | ||||||
| Style/UnpackFirst: |  | ||||||
|   Enabled: false |  | ||||||
|   | |||||||
| @@ -1 +1 @@ | |||||||
| 2.7.2 | 2.6.6 | ||||||
|   | |||||||
							
								
								
									
										871
									
								
								AUTHORS.md
									
									
									
									
									
								
							
							
						
						
									
										871
									
								
								AUTHORS.md
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										3
									
								
								Aptfile
									
									
									
									
									
								
							
							
						
						
									
										3
									
								
								Aptfile
									
									
									
									
									
								
							| @@ -5,6 +5,7 @@ libidn11 | |||||||
| libidn11-dev | libidn11-dev | ||||||
| libpq-dev | libpq-dev | ||||||
| libprotobuf-dev | libprotobuf-dev | ||||||
|  | libssl-dev | ||||||
| libxdamage1 | libxdamage1 | ||||||
| libxfixes3 | libxfixes3 | ||||||
| protobuf-compiler | protobuf-compiler | ||||||
| @@ -22,7 +23,7 @@ libpixman-1-0 | |||||||
| librsvg2-2 | librsvg2-2 | ||||||
| libthai-data | libthai-data | ||||||
| libthai0 | libthai0 | ||||||
| libvpx[5-9] | libvpx5 | ||||||
| libxcb-render0 | libxcb-render0 | ||||||
| libxcb-shm0 | libxcb-shm0 | ||||||
| libxrender1 | libxrender1 | ||||||
|   | |||||||
							
								
								
									
										2595
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										2595
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -14,7 +14,7 @@ If your contributions are accepted into Mastodon, you can request to be paid thr | |||||||
|  |  | ||||||
| ## Bug reports | ## Bug reports | ||||||
|  |  | ||||||
| Bug reports and feature suggestions must use descriptive and concise titles and be submitted to [GitHub Issues](https://github.com/mastodon/mastodon/issues). Please use the search function to make sure that you are not submitting duplicates, and that a similar report or request has not already been resolved or rejected. | Bug reports and feature suggestions must use descriptive and concise titles and be submitted to [GitHub Issues](https://github.com/tootsuite/mastodon/issues). Please use the search function to make sure that you are not submitting duplicates, and that a similar report or request has not already been resolved or rejected. | ||||||
|  |  | ||||||
| ## Translations | ## Translations | ||||||
|  |  | ||||||
| @@ -24,17 +24,9 @@ You can submit translations via [Crowdin](https://crowdin.com/project/mastodon). | |||||||
|  |  | ||||||
| ## Pull requests | ## Pull requests | ||||||
|  |  | ||||||
| **Please use clean, concise titles for your pull requests.** Unless the pull request is about refactoring code, updating dependencies or other internal tasks, assume that the person reading the pull request title is not a programmer or Mastodon developer, but instead a Mastodon user or server administrator, and **try to describe your change or fix from their perspective**. We use commit squashing, so the final commit in the main branch will carry the title of the pull request, and commits from the main branch are fed into the changelog. The changelog is separated into [keepachangelog.com categories](https://keepachangelog.com/en/1.0.0/), and while that spec does not prescribe how the entries ought to be named, for easier sorting, start your pull request titles using one of the verbs "Add", "Change", "Deprecate", "Remove", or "Fix" (present tense). | Please use clean, concise titles for your pull requests. We use commit squashing, so the final commit in the master branch will carry the title of the pull request. | ||||||
|  |  | ||||||
| Example: | The smaller the set of changes in the pull request is, the quicker it can be reviewed and merged. Splitting tasks into multiple smaller pull requests is often preferable. | ||||||
|  |  | ||||||
| |Not ideal|Better| |  | ||||||
| |---|----| |  | ||||||
| |Fixed NoMethodError in RemovalWorker|Fix nil error when removing statuses caused by race condition| |  | ||||||
|  |  | ||||||
| It is not always possible to phrase every change in such a manner, but it is desired. |  | ||||||
|  |  | ||||||
| **The smaller the set of changes in the pull request is, the quicker it can be reviewed and merged.** Splitting tasks into multiple smaller pull requests is often preferable. |  | ||||||
|  |  | ||||||
| **Pull requests that do not pass automated checks may not be reviewed**. In particular, you need to keep in mind: | **Pull requests that do not pass automated checks may not be reviewed**. In particular, you need to keep in mind: | ||||||
|  |  | ||||||
| @@ -44,4 +36,4 @@ It is not always possible to phrase every change in such a manner, but it is des | |||||||
|  |  | ||||||
| ## Documentation | ## Documentation | ||||||
|  |  | ||||||
| The [Mastodon documentation](https://docs.joinmastodon.org) is a statically generated site. You can [submit merge requests to mastodon/documentation](https://github.com/mastodon/documentation). | The [Mastodon documentation](https://docs.joinmastodon.org) is a statically generated site. You can [submit merge requests to mastodon/docs](https://source.joinmastodon.org/mastodon/docs). | ||||||
|   | |||||||
							
								
								
									
										84
									
								
								Dockerfile
									
									
									
									
									
								
							
							
						
						
									
										84
									
								
								Dockerfile
									
									
									
									
									
								
							| @@ -1,10 +1,10 @@ | |||||||
| FROM ubuntu:20.04 as build-dep | FROM ubuntu:18.04 as build-dep | ||||||
|  |  | ||||||
| # Use bash for the shell | # Use bash for the shell | ||||||
| SHELL ["/bin/bash", "-c"] | SHELL ["bash", "-c"] | ||||||
|  |  | ||||||
| # Install Node v12 (LTS) | # Install Node v12 (LTS) | ||||||
| ENV NODE_VER="12.21.0" | ENV NODE_VER="12.16.1" | ||||||
| RUN	ARCH= && \ | RUN	ARCH= && \ | ||||||
|     dpkgArch="$(dpkg --print-architecture)" && \ |     dpkgArch="$(dpkg --print-architecture)" && \ | ||||||
|   case "${dpkgArch##*-}" in \ |   case "${dpkgArch##*-}" in \ | ||||||
| @@ -17,19 +17,34 @@ RUN ARCH= && \ | |||||||
|     *) echo "unsupported architecture"; exit 1 ;; \ |     *) echo "unsupported architecture"; exit 1 ;; \ | ||||||
|   esac && \ |   esac && \ | ||||||
|     echo "Etc/UTC" > /etc/localtime && \ |     echo "Etc/UTC" > /etc/localtime && \ | ||||||
| 	apt-get update && \ | 	apt update && \ | ||||||
| 	apt-get install -y --no-install-recommends ca-certificates wget python && \ | 	apt -y install wget python && \ | ||||||
| 	cd ~ && \ | 	cd ~ && \ | ||||||
| 	wget -q https://nodejs.org/download/release/v$NODE_VER/node-v$NODE_VER-linux-$ARCH.tar.gz && \ | 	wget https://nodejs.org/download/release/v$NODE_VER/node-v$NODE_VER-linux-$ARCH.tar.gz && \ | ||||||
| 	tar xf node-v$NODE_VER-linux-$ARCH.tar.gz && \ | 	tar xf node-v$NODE_VER-linux-$ARCH.tar.gz && \ | ||||||
| 	rm node-v$NODE_VER-linux-$ARCH.tar.gz && \ | 	rm node-v$NODE_VER-linux-$ARCH.tar.gz && \ | ||||||
| 	mv node-v$NODE_VER-linux-$ARCH /opt/node | 	mv node-v$NODE_VER-linux-$ARCH /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 | # Install Ruby | ||||||
| ENV RUBY_VER="2.7.2" | ENV RUBY_VER="2.6.6" | ||||||
| RUN apt-get update && \ | ENV CPPFLAGS="-I/opt/jemalloc/include" | ||||||
|   apt-get install -y --no-install-recommends build-essential \ | ENV LDFLAGS="-L/opt/jemalloc/lib/" | ||||||
|     bison libyaml-dev libgdbm-dev libreadline-dev libjemalloc-dev \ | RUN apt update && \ | ||||||
|  | 	apt -y install build-essential \ | ||||||
|  | 		bison libyaml-dev libgdbm-dev libreadline-dev \ | ||||||
| 		libncurses5-dev libffi-dev zlib1g-dev libssl-dev && \ | 		libncurses5-dev libffi-dev zlib1g-dev libssl-dev && \ | ||||||
| 	cd ~ && \ | 	cd ~ && \ | ||||||
| 	wget https://cache.ruby-lang.org/pub/ruby/${RUBY_VER%.*}/ruby-$RUBY_VER.tar.gz && \ | 	wget https://cache.ruby-lang.org/pub/ruby/${RUBY_VER%.*}/ruby-$RUBY_VER.tar.gz && \ | ||||||
| @@ -39,31 +54,32 @@ RUN apt-get update && \ | |||||||
| 	  --with-jemalloc \ | 	  --with-jemalloc \ | ||||||
| 	  --with-shared \ | 	  --with-shared \ | ||||||
| 	  --disable-install-doc && \ | 	  --disable-install-doc && \ | ||||||
| 	make -j"$(nproc)" > /dev/null && \ | 	ln -s /opt/jemalloc/lib/* /usr/lib/ && \ | ||||||
| 	make install && \ | 	make -j$(nproc) > /dev/null && \ | ||||||
| 	rm -rf ../ruby-$RUBY_VER.tar.gz ../ruby-$RUBY_VER | 	make install | ||||||
|  |  | ||||||
| ENV PATH="${PATH}:/opt/ruby/bin:/opt/node/bin" | ENV PATH="${PATH}:/opt/ruby/bin:/opt/node/bin" | ||||||
|  |  | ||||||
| RUN npm install -g yarn && \ | RUN npm install -g yarn && \ | ||||||
| 	gem install bundler && \ | 	gem install bundler && \ | ||||||
| 	apt-get update && \ | 	apt update && \ | ||||||
| 	apt-get install -y --no-install-recommends git libicu-dev libidn11-dev \ | 	apt -y install git libicu-dev libidn11-dev \ | ||||||
| 	libpq-dev libprotobuf-dev protobuf-compiler shared-mime-info | 	libpq-dev libprotobuf-dev protobuf-compiler | ||||||
|  |  | ||||||
| COPY Gemfile* package.json yarn.lock /opt/mastodon/ | COPY Gemfile* package.json yarn.lock /opt/mastodon/ | ||||||
|  |  | ||||||
| RUN cd /opt/mastodon && \ | RUN cd /opt/mastodon && \ | ||||||
|   bundle config set --local deployment 'true' && \ |   bundle config set deployment 'true' && \ | ||||||
|   bundle config set --local without 'development test' && \ |   bundle config set without 'development test' && \ | ||||||
| 	bundle install -j"$(nproc)" && \ | 	bundle install -j$(nproc) && \ | ||||||
| 	yarn install --pure-lockfile | 	yarn install --pure-lockfile | ||||||
|  |  | ||||||
| FROM ubuntu:20.04 | FROM ubuntu:18.04 | ||||||
|  |  | ||||||
| # Copy over all the langs needed for runtime | # Copy over all the langs needed for runtime | ||||||
| COPY --from=build-dep /opt/node /opt/node | COPY --from=build-dep /opt/node /opt/node | ||||||
| COPY --from=build-dep /opt/ruby /opt/ruby | COPY --from=build-dep /opt/ruby /opt/ruby | ||||||
|  | COPY --from=build-dep /opt/jemalloc /opt/jemalloc | ||||||
|  |  | ||||||
| # Add more PATHs to the PATH | # Add more PATHs to the PATH | ||||||
| ENV PATH="${PATH}:/opt/ruby/bin:/opt/node/bin:/opt/mastodon/bin" | ENV PATH="${PATH}:/opt/ruby/bin:/opt/node/bin:/opt/mastodon/bin" | ||||||
| @@ -71,26 +87,32 @@ ENV PATH="${PATH}:/opt/ruby/bin:/opt/node/bin:/opt/mastodon/bin" | |||||||
| # Create the mastodon user | # Create the mastodon user | ||||||
| ARG UID=991 | ARG UID=991 | ||||||
| ARG GID=991 | ARG GID=991 | ||||||
| SHELL ["/bin/bash", "-o", "pipefail", "-c"] | RUN apt update && \ | ||||||
| RUN apt-get update && \ |  | ||||||
| 	echo "Etc/UTC" > /etc/localtime && \ | 	echo "Etc/UTC" > /etc/localtime && \ | ||||||
| 	apt-get install -y --no-install-recommends whois wget && \ | 	ln -s /opt/jemalloc/lib/* /usr/lib/ && \ | ||||||
|  | 	apt install -y whois wget && \ | ||||||
| 	addgroup --gid $GID mastodon && \ | 	addgroup --gid $GID mastodon && \ | ||||||
| 	useradd -m -u $UID -g $GID -d /opt/mastodon 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 && \ | 	echo "mastodon:`head /dev/urandom | tr -dc A-Za-z0-9 | head -c 24 | mkpasswd -s -m sha-256`" | chpasswd | ||||||
| 	rm -rf /var/lib/apt/lists/* |  | ||||||
|  |  | ||||||
| # Install mastodon runtime deps | # Install mastodon runtime deps | ||||||
| RUN apt-get update && \ | RUN apt -y --no-install-recommends install \ | ||||||
|   apt-get -y --no-install-recommends install \ | 	  libssl1.1 libpq5 imagemagick ffmpeg \ | ||||||
| 	  libssl1.1 libpq5 imagemagick ffmpeg libjemalloc2 \ | 	  libicu60 libprotobuf10 libidn11 libyaml-0-2 \ | ||||||
| 	  libicu66 libprotobuf17 libidn11 libyaml-0-2 \ | 	  file ca-certificates tzdata libreadline7 && \ | ||||||
| 	  file ca-certificates tzdata libreadline8 gcc tini && \ | 	apt -y install gcc && \ | ||||||
| 	ln -s /opt/mastodon /mastodon && \ | 	ln -s /opt/mastodon /mastodon && \ | ||||||
| 	gem install bundler && \ | 	gem install bundler && \ | ||||||
| 	rm -rf /var/cache && \ | 	rm -rf /var/cache && \ | ||||||
| 	rm -rf /var/lib/apt/lists/* | 	rm -rf /var/lib/apt/lists/* | ||||||
|  |  | ||||||
|  | # Add tini | ||||||
|  | ENV TINI_VERSION="0.18.0" | ||||||
|  | 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 | # Copy over mastodon source, and dependencies from building, and set permissions | ||||||
| COPY --chown=mastodon:mastodon . /opt/mastodon | COPY --chown=mastodon:mastodon . /opt/mastodon | ||||||
| COPY --from=build-dep --chown=mastodon:mastodon /opt/mastodon /opt/mastodon | COPY --from=build-dep --chown=mastodon:mastodon /opt/mastodon /opt/mastodon | ||||||
| @@ -113,5 +135,5 @@ RUN cd ~ && \ | |||||||
|  |  | ||||||
| # Set the work dir and the container entry point | # Set the work dir and the container entry point | ||||||
| WORKDIR /opt/mastodon | WORKDIR /opt/mastodon | ||||||
| ENTRYPOINT ["/usr/bin/tini", "--"] | ENTRYPOINT ["/tini", "--"] | ||||||
| EXPOSE 3000 4000 | EXPOSE 3000 4000 | ||||||
|   | |||||||
							
								
								
									
										136
									
								
								Gemfile
									
									
									
									
									
								
							
							
						
						
									
										136
									
								
								Gemfile
									
									
									
									
									
								
							| @@ -1,111 +1,116 @@ | |||||||
| # frozen_string_literal: true | # frozen_string_literal: true | ||||||
|  |  | ||||||
| source 'https://rubygems.org' | source 'https://rubygems.org' | ||||||
| ruby '>= 2.5.0', '< 3.1.0' | ruby '>= 2.5.0', '< 3.0.0' | ||||||
|  |  | ||||||
| gem 'pkg-config', '~> 1.4' | gem 'pkg-config', '~> 1.4' | ||||||
|  |  | ||||||
| gem 'puma', '~> 5.3' | gem 'puma', '~> 4.3' | ||||||
| gem 'rails', '~> 6.1.3' | gem 'rails', '~> 5.2.4.2' | ||||||
| gem 'sprockets', '~> 3.7.2' | gem 'sprockets', '~> 3.7.2' | ||||||
| gem 'thor', '~> 1.1' | gem 'thor', '~> 0.20' | ||||||
| gem 'rack', '~> 2.2.3' | gem 'rack', '~> 2.2.2' | ||||||
|  |  | ||||||
|  | gem 'thwait', '~> 0.1.0' | ||||||
|  | gem 'e2mmap', '~> 0.1.0' | ||||||
|  |  | ||||||
| gem 'hamlit-rails', '~> 0.2' | gem 'hamlit-rails', '~> 0.2' | ||||||
| gem 'pg', '~> 1.2' | gem 'pg', '~> 1.2' | ||||||
| gem 'makara', '~> 0.5' | gem 'makara', '~> 0.4' | ||||||
| gem 'pghero', '~> 2.8' | gem 'pghero', '~> 2.4' | ||||||
| gem 'dotenv-rails', '~> 2.7' | gem 'dotenv-rails', '~> 2.7' | ||||||
|  |  | ||||||
| gem 'aws-sdk-s3', '~> 1.95', require: false | gem 'aws-sdk-s3', '~> 1.64', 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 'streamio-ffmpeg', '~> 3.0' | ||||||
| gem 'blurhash', '~> 0.1' | gem 'blurhash', '~> 0.1' | ||||||
|  |  | ||||||
| gem 'active_model_serializers', '~> 0.10' | gem 'active_model_serializers', '~> 0.10' | ||||||
| gem 'addressable', '~> 2.7' | gem 'addressable', '~> 2.7' | ||||||
| gem 'bootsnap', '~> 1.6.0', require: false | gem 'bootsnap', '~> 1.4', require: false | ||||||
| gem 'browser' | gem 'browser' | ||||||
| gem 'charlock_holmes', '~> 0.7.7' | gem 'charlock_holmes', '~> 0.7.7' | ||||||
| gem 'iso-639' | gem 'iso-639' | ||||||
| gem 'chewy', '~> 5.2' | gem 'chewy', '~> 5.1' | ||||||
| gem 'cld3', '~> 3.4.2' | gem 'cld3', '~> 3.3.0' | ||||||
| gem 'devise', '~> 4.8' | gem 'devise', '~> 4.7' | ||||||
| gem 'devise-two-factor', '~> 4.0' | gem 'devise-two-factor', '~> 3.1' | ||||||
|  |  | ||||||
| group :pam_authentication, optional: true do | group :pam_authentication, optional: true do | ||||||
|   gem 'devise_pam_authenticatable2', '~> 9.2' |   gem 'devise_pam_authenticatable2', '~> 9.2' | ||||||
| end | end | ||||||
|  |  | ||||||
| gem 'net-ldap', '~> 0.17' | gem 'net-ldap', '~> 0.16' | ||||||
| gem 'omniauth-cas', '~> 2.0' | gem 'omniauth-cas', '~> 1.1' | ||||||
| gem 'omniauth-saml', '~> 1.10' | gem 'omniauth-saml', '~> 1.10' | ||||||
| gem 'omniauth', '~> 1.9' | gem 'omniauth', '~> 1.9' | ||||||
| gem 'omniauth-rails_csrf_protection', '~> 0.1' |  | ||||||
|  |  | ||||||
| gem 'color_diff', '~> 0.1' |  | ||||||
| gem 'discard', '~> 1.2' | gem 'discard', '~> 1.2' | ||||||
| gem 'doorkeeper', '~> 5.5' | gem 'doorkeeper', '~> 5.4' | ||||||
| gem 'ed25519', '~> 1.2' |  | ||||||
| gem 'fast_blank', '~> 1.0' | gem 'fast_blank', '~> 1.0' | ||||||
| gem 'fastimage' | gem 'fastimage' | ||||||
|  | gem 'goldfinger', '~> 2.1' | ||||||
| gem 'hiredis', '~> 0.6' | gem 'hiredis', '~> 0.6' | ||||||
| gem 'redis-namespace', '~> 1.8' | gem 'redis-namespace', '~> 1.7' | ||||||
|  | gem 'health_check', git: 'https://github.com/ianheggie/health_check', ref: '0b799ead604f900ed50685e9b2d469cd2befba5b' | ||||||
| gem 'htmlentities', '~> 4.3' | gem 'htmlentities', '~> 4.3' | ||||||
| gem 'http', '~> 4.4' | gem 'http', '~> 4.4' | ||||||
| gem 'http_accept_language', '~> 2.1' | gem 'http_accept_language', '~> 2.1' | ||||||
| gem 'httplog', '~> 1.5.0' | gem 'http_parser.rb', '~> 0.6', git: 'https://github.com/tmm1/http_parser.rb', ref: '54b17ba8c7d8d20a16dfc65d1775241833219cf2', submodules: true | ||||||
|  | gem 'httplog', '~> 1.4.2' | ||||||
| gem 'idn-ruby', require: 'idn' | gem 'idn-ruby', require: 'idn' | ||||||
| gem 'kaminari', '~> 1.2' | gem 'kaminari', '~> 1.2' | ||||||
| gem 'link_header', '~> 0.0' | gem 'link_header', '~> 0.0' | ||||||
| gem 'mime-types', '~> 3.3.1', require: 'mime/types/columnar' | gem 'mime-types', '~> 3.3.1', require: 'mime/types/columnar' | ||||||
| gem 'nokogiri', '~> 1.11' | gem 'nilsimsa', git: 'https://github.com/witgo/nilsimsa', ref: 'fd184883048b922b176939f851338d0a4971a532' | ||||||
|  | gem 'nokogiri', '~> 1.10' | ||||||
| gem 'nsa', '~> 0.2' | gem 'nsa', '~> 0.2' | ||||||
| gem 'oj', '~> 3.11' | gem 'oj', '~> 3.10' | ||||||
| gem 'ox', '~> 2.14' | gem 'ox', '~> 2.13' | ||||||
| gem 'parslet' | gem 'parslet' | ||||||
| gem 'parallel', '~> 1.20' | gem 'parallel', '~> 1.19' | ||||||
| gem 'posix-spawn' | gem 'posix-spawn', git: 'https://github.com/rtomayko/posix-spawn', ref: '58465d2e213991f8afb13b984854a49fcdcc980c' | ||||||
| gem 'pundit', '~> 2.1' | gem 'pundit', '~> 2.1' | ||||||
| gem 'premailer-rails' | gem 'premailer-rails' | ||||||
| gem 'rack-attack', '~> 6.5' | gem 'rack-attack', '~> 6.3' | ||||||
| gem 'rack-cors', '~> 1.1', require: 'rack/cors' | gem 'rack-cors', '~> 1.1', require: 'rack/cors' | ||||||
| gem 'rails-i18n', '~> 6.0' | gem 'rails-i18n', '~> 5.1' | ||||||
| gem 'rails-settings-cached', '~> 0.6' | gem 'rails-settings-cached', '~> 0.6' | ||||||
| gem 'redis', '~> 4.2', require: ['redis', 'redis/connection/hiredis'] | gem 'redis', '~> 4.1', require: ['redis', 'redis/connection/hiredis'] | ||||||
| gem 'mario-redis-lock', '~> 1.2', require: 'redis_lock' | gem 'mario-redis-lock', '~> 1.2', require: 'redis_lock' | ||||||
| gem 'rqrcode', '~> 2.0' | gem 'rqrcode', '~> 1.1' | ||||||
| gem 'ruby-progressbar', '~> 1.11' | gem 'ruby-progressbar', '~> 1.10' | ||||||
| gem 'sanitize', '~> 5.2' | gem 'sanitize', '~> 5.1' | ||||||
| gem 'scenic', '~> 1.5' | gem 'sidekiq', '~> 6.0' | ||||||
| gem 'sidekiq', '~> 6.2' |  | ||||||
| gem 'sidekiq-scheduler', '~> 3.0' | gem 'sidekiq-scheduler', '~> 3.0' | ||||||
| gem 'sidekiq-unique-jobs', '~> 7.0' | gem 'sidekiq-unique-jobs', '~> 6.0' | ||||||
| gem 'sidekiq-bulk', '~>0.2.0' | gem 'sidekiq-bulk', '~>0.2.0' | ||||||
| gem 'simple-navigation', '~> 4.3' | gem 'simple-navigation', '~> 4.1' | ||||||
| gem 'simple_form', '~> 5.1' | gem 'simple_form', '~> 5.0' | ||||||
| gem 'sprockets-rails', '~> 3.2', require: 'sprockets/railtie' | gem 'sprockets-rails', '~> 3.2', require: 'sprockets/railtie' | ||||||
| gem 'stoplight', '~> 2.2.1' | gem 'stoplight', '~> 2.2.0' | ||||||
| gem 'strong_migrations', '~> 0.7' | gem 'strong_migrations', '~> 0.6' | ||||||
| gem 'tty-prompt', '~> 0.23', require: false | gem 'tty-command', '~> 0.9', require: false | ||||||
| gem 'twitter-text', '~> 3.1.0' | gem 'tty-prompt', '~> 0.21', require: false | ||||||
| gem 'tzinfo-data', '~> 1.2021' | gem 'twitter-text', '~> 1.14' | ||||||
| gem 'webpacker', '~> 5.4' | gem 'tzinfo-data', '~> 1.2020' | ||||||
| gem 'webpush', '~> 0.3' | gem 'webpacker', '~> 5.1' | ||||||
| gem 'webauthn', '~> 3.0.0.alpha1' | gem 'webpush' | ||||||
|  |  | ||||||
| gem 'json-ld' | gem 'json-ld' | ||||||
| gem 'json-ld-preloaded', '~> 3.1' | gem 'json-ld-preloaded', '~> 3.1' | ||||||
| gem 'rdf-normalize', '~> 0.4' | gem 'rdf-normalize', '~> 0.4' | ||||||
|  |  | ||||||
| group :development, :test do | group :development, :test do | ||||||
|   gem 'fabrication', '~> 2.22' |   gem 'fabrication', '~> 2.21' | ||||||
|   gem 'fuubar', '~> 2.5' |   gem 'fuubar', '~> 2.5' | ||||||
|   gem 'i18n-tasks', '~> 0.9', require: false |   gem 'i18n-tasks', '~> 0.9', require: false | ||||||
|   gem 'pry-byebug', '~> 3.9' |   gem 'pry-byebug', '~> 3.9' | ||||||
|   gem 'pry-rails', '~> 0.3' |   gem 'pry-rails', '~> 0.3' | ||||||
|   gem 'rspec-rails', '~> 5.0' |   gem 'rspec-rails', '~> 4.0' | ||||||
| end | end | ||||||
|  |  | ||||||
| group :production, :test do | group :production, :test do | ||||||
| @@ -113,35 +118,35 @@ group :production, :test do | |||||||
| end | end | ||||||
|  |  | ||||||
| group :test do | group :test do | ||||||
|   gem 'capybara', '~> 3.35' |   gem 'capybara', '~> 3.32' | ||||||
|   gem 'climate_control', '~> 0.2' |   gem 'climate_control', '~> 0.2' | ||||||
|   gem 'faker', '~> 2.18' |   gem 'faker', '~> 2.11' | ||||||
|   gem 'microformats', '~> 4.2' |   gem 'microformats', '~> 4.2' | ||||||
|   gem 'rails-controller-testing', '~> 1.0' |   gem 'rails-controller-testing', '~> 1.0' | ||||||
|   gem 'rspec-sidekiq', '~> 3.1' |   gem 'rspec-sidekiq', '~> 3.0' | ||||||
|   gem 'simplecov', '~> 0.21', require: false |   gem 'simplecov', '~> 0.18', require: false | ||||||
|   gem 'webmock', '~> 3.13' |   gem 'webmock', '~> 3.8' | ||||||
|   gem 'parallel_tests', '~> 3.7' |   gem 'parallel_tests', '~> 2.32' | ||||||
|   gem 'rspec_junit_formatter', '~> 0.4' |   gem 'rspec_junit_formatter', '~> 0.4' | ||||||
| end | end | ||||||
|  |  | ||||||
| group :development do | group :development do | ||||||
|   gem 'active_record_query_trace', '~> 1.8' |   gem 'active_record_query_trace', '~> 1.7' | ||||||
|   gem 'annotate', '~> 3.1' |   gem 'annotate', '~> 3.1' | ||||||
|   gem 'better_errors', '~> 2.9' |   gem 'better_errors', '~> 2.7' | ||||||
|   gem 'binding_of_caller', '~> 1.0' |   gem 'binding_of_caller', '~> 0.7' | ||||||
|   gem 'bullet', '~> 6.1' |   gem 'bullet', '~> 6.1' | ||||||
|   gem 'letter_opener', '~> 1.7' |   gem 'letter_opener', '~> 1.7' | ||||||
|   gem 'letter_opener_web', '~> 1.4' |   gem 'letter_opener_web', '~> 1.4' | ||||||
|   gem 'memory_profiler' |   gem 'memory_profiler' | ||||||
|   gem 'rubocop', '~> 1.15', require: false |   gem 'rubocop', '~> 0.82', require: false | ||||||
|   gem 'rubocop-rails', '~> 2.10', require: false |   gem 'rubocop-rails', '~> 2.5', require: false | ||||||
|   gem 'brakeman', '~> 5.0', require: false |   gem 'brakeman', '~> 4.8', require: false | ||||||
|   gem 'bundler-audit', '~> 0.8', require: false |   gem 'bundler-audit', '~> 0.6', require: false | ||||||
|  |  | ||||||
|   gem 'capistrano', '~> 3.16' |   gem 'capistrano', '~> 3.14' | ||||||
|   gem 'capistrano-rails', '~> 1.6' |   gem 'capistrano-rails', '~> 1.4' | ||||||
|   gem 'capistrano-rbenv', '~> 2.2' |   gem 'capistrano-rbenv', '~> 2.1' | ||||||
|   gem 'capistrano-yarn', '~> 2.0' |   gem 'capistrano-yarn', '~> 2.0' | ||||||
|  |  | ||||||
|   gem 'stackprof' |   gem 'stackprof' | ||||||
| @@ -149,11 +154,8 @@ end | |||||||
|  |  | ||||||
| group :production do | group :production do | ||||||
|   gem 'lograge', '~> 0.11' |   gem 'lograge', '~> 0.11' | ||||||
|  |   gem 'redis-rails', '~> 5.0' | ||||||
| end | end | ||||||
|  |  | ||||||
| gem 'concurrent-ruby', require: false | gem 'concurrent-ruby', require: false | ||||||
| gem 'connection_pool', require: false | gem 'connection_pool', require: false | ||||||
|  |  | ||||||
| gem 'xorcist', '~> 1.1' |  | ||||||
|  |  | ||||||
| gem 'resolv', '~> 0.1.0' |  | ||||||
|   | |||||||
							
								
								
									
										802
									
								
								Gemfile.lock
									
									
									
									
									
								
							
							
						
						
									
										802
									
								
								Gemfile.lock
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										2
									
								
								Procfile
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								Procfile
									
									
									
									
									
								
							| @@ -1,4 +1,4 @@ | |||||||
| web: bin/heroku-web | web: if [ "$RUN_STREAMING" != "true" ]; then BIND=0.0.0.0 bundle exec puma -C config/puma.rb; else BIND=0.0.0.0 node ./streaming; fi | ||||||
| worker: bundle exec sidekiq | worker: bundle exec sidekiq | ||||||
|  |  | ||||||
| # For the streaming API, you need a separate app that shares Postgres and Redis: | # For the streaming API, you need a separate app that shares Postgres and Redis: | ||||||
|   | |||||||
| @@ -1,4 +1,4 @@ | |||||||
| web: env PORT=3000 RAILS_ENV=development bundle exec puma -C config/puma.rb | web: env PORT=3000 bundle exec puma -C config/puma.rb | ||||||
| sidekiq: env PORT=3000 RAILS_ENV=development bundle exec sidekiq | sidekiq: env PORT=3000 bundle exec sidekiq | ||||||
| stream: env PORT=4000 yarn run start | stream: env PORT=4000 yarn run start | ||||||
| webpack: ./bin/webpack-dev-server --listen-host 0.0.0.0 | webpack: ./bin/webpack-dev-server --listen-host 0.0.0.0 | ||||||
|   | |||||||
							
								
								
									
										14
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								README.md
									
									
									
									
									
								
							| @@ -1,14 +1,14 @@ | |||||||
|  |  | ||||||
| ======== | ======== | ||||||
|  |  | ||||||
| [][releases] | [][releases] | ||||||
| [][circleci] | [][circleci] | ||||||
| [][code_climate] | [][code_climate] | ||||||
| [][crowdin] | [][crowdin] | ||||||
| [][docker] | [][docker] | ||||||
|  |  | ||||||
| [releases]: https://github.com/mastodon/mastodon/releases | [releases]: https://github.com/tootsuite/mastodon/releases | ||||||
| [circleci]: https://circleci.com/gh/mastodon/mastodon | [circleci]: https://circleci.com/gh/tootsuite/mastodon | ||||||
| [code_climate]: https://codeclimate.com/github/tootsuite/mastodon | [code_climate]: https://codeclimate.com/github/tootsuite/mastodon | ||||||
| [crowdin]: https://crowdin.com/project/mastodon | [crowdin]: https://crowdin.com/project/mastodon | ||||||
| [docker]: https://hub.docker.com/r/tootsuite/mastodon/ | [docker]: https://hub.docker.com/r/tootsuite/mastodon/ | ||||||
| @@ -70,7 +70,7 @@ Mastodon acts as an OAuth2 provider so 3rd party apps can use the REST and Strea | |||||||
| - **PostgreSQL** 9.5+ | - **PostgreSQL** 9.5+ | ||||||
| - **Redis** 4+ | - **Redis** 4+ | ||||||
| - **Ruby** 2.5+ | - **Ruby** 2.5+ | ||||||
| - **Node.js** 12+ | - **Node.js** 10.13+ | ||||||
|  |  | ||||||
| The repository includes deployment configurations for **Docker and docker-compose**, but also a few specific platforms like **Heroku**, **Scalingo**, and **Nanobox**. The [**stand-alone** installation guide](https://docs.joinmastodon.org/admin/install/) is available in the documentation. | The repository includes deployment configurations for **Docker and docker-compose**, but also a few specific platforms like **Heroku**, **Scalingo**, and **Nanobox**. The [**stand-alone** installation guide](https://docs.joinmastodon.org/admin/install/) is available in the documentation. | ||||||
|  |  | ||||||
| @@ -82,11 +82,11 @@ Mastodon is **free, open-source software** licensed under **AGPLv3**. | |||||||
|  |  | ||||||
| You can open issues for bugs you've found or features you think are missing. You can also submit pull requests to this repository, or submit translations using Crowdin. To get started, take a look at [CONTRIBUTING.md](CONTRIBUTING.md). If your contributions are accepted into Mastodon, you can request to be paid through [our OpenCollective](https://opencollective.com/mastodon). | You can open issues for bugs you've found or features you think are missing. You can also submit pull requests to this repository, or submit translations using Crowdin. To get started, take a look at [CONTRIBUTING.md](CONTRIBUTING.md). If your contributions are accepted into Mastodon, you can request to be paid through [our OpenCollective](https://opencollective.com/mastodon). | ||||||
|  |  | ||||||
| **IRC channel**: #mastodon on irc.libera.chat | **IRC channel**: #mastodon on irc.freenode.net | ||||||
|  |  | ||||||
| ## License | ## License | ||||||
|  |  | ||||||
| Copyright (C) 2016-2021 Eugen Rochko & other Mastodon contributors (see [AUTHORS.md](AUTHORS.md)) | Copyright (C) 2016-2020 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. | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										13
									
								
								SECURITY.md
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								SECURITY.md
									
									
									
									
									
								
							| @@ -1,13 +0,0 @@ | |||||||
| # Security Policy |  | ||||||
|  |  | ||||||
| ## Supported Versions |  | ||||||
|  |  | ||||||
| | Version | Supported          | |  | ||||||
| | ------- | ------------------ | |  | ||||||
| | 3.4.x   | :white_check_mark: | |  | ||||||
| | 3.3.x   | :white_check_mark: | |  | ||||||
| | < 3.3   | :x:                | |  | ||||||
|  |  | ||||||
| ## Reporting a Vulnerability |  | ||||||
|  |  | ||||||
| hello@joinmastodon.org |  | ||||||
							
								
								
									
										4
									
								
								Vagrantfile
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								Vagrantfile
									
									
									
									
										vendored
									
									
								
							| @@ -12,7 +12,7 @@ curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add - | |||||||
| sudo apt-add-repository 'deb https://dl.yarnpkg.com/debian/ stable main' | sudo apt-add-repository 'deb https://dl.yarnpkg.com/debian/ stable main' | ||||||
|  |  | ||||||
| # Add repo for NodeJS | # Add repo for NodeJS | ||||||
| curl -sL https://deb.nodesource.com/setup_12.x | sudo bash - | curl -sL https://deb.nodesource.com/setup_10.x | sudo bash - | ||||||
|  |  | ||||||
| # Add firewall rule to redirect 80 to PORT and save | # Add firewall rule to redirect 80 to PORT and save | ||||||
| sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port #{ENV["PORT"]} | sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port #{ENV["PORT"]} | ||||||
| @@ -72,12 +72,10 @@ bundle install | |||||||
| yarn install | yarn install | ||||||
|  |  | ||||||
| # Build Mastodon | # Build Mastodon | ||||||
| export RAILS_ENV=development  |  | ||||||
| export $(cat ".env.vagrant" | xargs) | export $(cat ".env.vagrant" | xargs) | ||||||
| bundle exec rails db:setup | bundle exec rails db:setup | ||||||
|  |  | ||||||
| # Configure automatic loading of environment variable | # Configure automatic loading of environment variable | ||||||
| echo 'export RAILS_ENV=development' >> ~/.bash_profile |  | ||||||
| echo 'export $(cat "/vagrant/.env.vagrant" | xargs)' >> ~/.bash_profile | echo 'export $(cat "/vagrant/.env.vagrant" | xargs)' >> ~/.bash_profile | ||||||
|  |  | ||||||
| SCRIPT | SCRIPT | ||||||
|   | |||||||
							
								
								
									
										7
									
								
								app.json
									
									
									
									
									
								
							
							
						
						
									
										7
									
								
								app.json
									
									
									
									
									
								
							| @@ -1,8 +1,8 @@ | |||||||
| { | { | ||||||
|   "name": "Mastodon", |   "name": "Mastodon", | ||||||
|   "description": "A GNU Social-compatible microblogging server", |   "description": "A GNU Social-compatible microblogging server", | ||||||
|   "repository": "https://github.com/mastodon/mastodon", |   "repository": "https://github.com/tootsuite/mastodon", | ||||||
|   "logo": "https://github.com/mastodon.png", |   "logo": "https://github.com/tootsuite.png", | ||||||
|   "env": { |   "env": { | ||||||
|     "HEROKU": { |     "HEROKU": { | ||||||
|       "description": "Leave this as true", |       "description": "Leave this as true", | ||||||
| @@ -88,6 +88,9 @@ | |||||||
|     { |     { | ||||||
|       "url": "https://github.com/heroku/heroku-buildpack-apt" |       "url": "https://github.com/heroku/heroku-buildpack-apt" | ||||||
|     }, |     }, | ||||||
|  |     { | ||||||
|  |       "url": "heroku/nodejs" | ||||||
|  |     }, | ||||||
|     { |     { | ||||||
|       "url": "heroku/ruby" |       "url": "heroku/ruby" | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -31,9 +31,9 @@ class StatusesIndex < Chewy::Index | |||||||
|     }, |     }, | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   define_type ::Status.unscoped.kept.without_reblogs.includes(:media_attachments, :preloadable_poll) do |   define_type ::Status.unscoped.kept.without_reblogs.includes(:media_attachments), delete_if: ->(status) { status.searchable_by.empty? } do | ||||||
|     crutch :mentions do |collection| |     crutch :mentions do |collection| | ||||||
|       data = ::Mention.where(status_id: collection.map(&:id)).where(account: Account.local, silent: false).pluck(:status_id, :account_id) |       data = ::Mention.where(status_id: collection.map(&:id)).where(account: Account.local).pluck(:status_id, :account_id) | ||||||
|       data.each.with_object({}) { |(id, name), result| (result[id] ||= []).push(name) } |       data.each.with_object({}) { |(id, name), result| (result[id] ||= []).push(name) } | ||||||
|     end |     end | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,15 +1,12 @@ | |||||||
| # frozen_string_literal: true | # frozen_string_literal: true | ||||||
|  |  | ||||||
| class AboutController < ApplicationController | class AboutController < ApplicationController | ||||||
|   include RegistrationSpamConcern |  | ||||||
|  |  | ||||||
|   layout 'public' |   layout 'public' | ||||||
|  |  | ||||||
|   before_action :require_open_federation!, only: [:show, :more] |   before_action :require_open_federation!, only: [:show, :more] | ||||||
|   before_action :set_body_classes, only: :show |   before_action :set_body_classes, only: :show | ||||||
|   before_action :set_instance_presenter |   before_action :set_instance_presenter | ||||||
|   before_action :set_expires_in, only: [:more, :terms] |   before_action :set_expires_in, only: [:show, :more, :terms] | ||||||
|   before_action :set_registration_form_time, only: :show |  | ||||||
|  |  | ||||||
|   skip_before_action :require_functional!, only: [:more, :terms] |   skip_before_action :require_functional!, only: [:more, :terms] | ||||||
|  |  | ||||||
| @@ -20,7 +17,6 @@ class AboutController < ApplicationController | |||||||
|  |  | ||||||
|     toc_generator = TOCGenerator.new(@instance_presenter.site_extended_description) |     toc_generator = TOCGenerator.new(@instance_presenter.site_extended_description) | ||||||
|  |  | ||||||
|     @rules             = Rule.ordered |  | ||||||
|     @contents          = toc_generator.html |     @contents          = toc_generator.html | ||||||
|     @table_of_contents = toc_generator.toc |     @table_of_contents = toc_generator.toc | ||||||
|     @blocks            = DomainBlock.with_user_facing_limitations.by_severity if display_blocks? |     @blocks            = DomainBlock.with_user_facing_limitations.by_severity if display_blocks? | ||||||
|   | |||||||
| @@ -2,17 +2,15 @@ | |||||||
|  |  | ||||||
| class AccountsController < ApplicationController | class AccountsController < ApplicationController | ||||||
|   PAGE_SIZE = 20 |   PAGE_SIZE = 20 | ||||||
|   PAGE_SIZE_MAX = 200 |  | ||||||
|  |  | ||||||
|   include AccountControllerConcern |   include AccountControllerConcern | ||||||
|   include SignatureAuthentication |   include SignatureAuthentication | ||||||
|  |  | ||||||
|   before_action :require_signature!, if: -> { request.format == :json && authorized_fetch_mode? } |  | ||||||
|   before_action :set_cache_headers |   before_action :set_cache_headers | ||||||
|   before_action :set_body_classes |   before_action :set_body_classes | ||||||
|  |  | ||||||
|   skip_around_action :set_locale, if: -> { [:json, :rss].include?(request.format&.to_sym) } |   skip_around_action :set_locale, if: -> { [:json, :rss].include?(request.format&.to_sym) } | ||||||
|   skip_before_action :require_functional!, unless: :whitelist_mode? |   skip_before_action :require_functional! | ||||||
|  |  | ||||||
|   def show |   def show | ||||||
|     respond_to do |format| |     respond_to do |format| | ||||||
| @@ -29,7 +27,8 @@ class AccountsController < ApplicationController | |||||||
|         end |         end | ||||||
|  |  | ||||||
|         @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        = cached_filtered_status_page |         @statuses        = filtered_status_page | ||||||
|  |         @statuses        = cache_collection(@statuses, Status) | ||||||
|         @rss_url         = rss_url |         @rss_url         = rss_url | ||||||
|  |  | ||||||
|         unless @statuses.empty? |         unless @statuses.empty? | ||||||
| @@ -41,15 +40,14 @@ class AccountsController < ApplicationController | |||||||
|       format.rss do |       format.rss do | ||||||
|         expires_in 1.minute, public: true |         expires_in 1.minute, public: true | ||||||
|  |  | ||||||
|         limit     = params[:limit].present? ? [params[:limit].to_i, PAGE_SIZE_MAX].min : PAGE_SIZE |         @statuses = filtered_statuses.without_reblogs.limit(PAGE_SIZE) | ||||||
|         @statuses = filtered_statuses.without_reblogs.limit(limit) |  | ||||||
|         @statuses = cache_collection(@statuses, Status) |         @statuses = cache_collection(@statuses, Status) | ||||||
|         render xml: RSS::AccountSerializer.render(@account, @statuses, params[:tag]) |         render xml: RSS::AccountSerializer.render(@account, @statuses, params[:tag]) | ||||||
|       end |       end | ||||||
|  |  | ||||||
|       format.json do |       format.json do | ||||||
|         expires_in 3.minutes, public: !(authorized_fetch_mode? && signed_request_account.present?) |         expires_in 3.minutes, public: !(authorized_fetch_mode? && signed_request_account.present?) | ||||||
|         render_with_cache json: @account, content_type: 'application/activity+json', serializer: ActivityPub::ActorSerializer, adapter: ActivityPub::Adapter |         render_with_cache json: @account, content_type: 'application/activity+json', serializer: ActivityPub::ActorSerializer, adapter: ActivityPub::Adapter, fields: restrict_fields_to | ||||||
|       end |       end | ||||||
|     end |     end | ||||||
|   end |   end | ||||||
| @@ -77,7 +75,11 @@ class AccountsController < ApplicationController | |||||||
|   end |   end | ||||||
|  |  | ||||||
|   def only_media_scope |   def only_media_scope | ||||||
|     Status.joins(:media_attachments).merge(@account.media_attachments.reorder(nil)).group(:id) |     Status.where(id: account_media_status_ids) | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   def account_media_status_ids | ||||||
|  |     @account.media_attachments.attached.reorder(nil).select(:status_id).distinct | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   def no_replies_scope |   def no_replies_scope | ||||||
| @@ -98,10 +100,6 @@ class AccountsController < ApplicationController | |||||||
|     params[:username] |     params[:username] | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   def skip_temporary_suspension_response? |  | ||||||
|     request.format == :json |  | ||||||
|   end |  | ||||||
|  |  | ||||||
|   def rss_url |   def rss_url | ||||||
|     if tag_requested? |     if tag_requested? | ||||||
|       short_account_tag_url(@account, params[:tag], format: 'rss') |       short_account_tag_url(@account, params[:tag], format: 'rss') | ||||||
| @@ -131,27 +129,30 @@ class AccountsController < ApplicationController | |||||||
|   end |   end | ||||||
|  |  | ||||||
|   def media_requested? |   def media_requested? | ||||||
|     request.path.split('.').first.end_with?('/media') && !tag_requested? |     request.path.split('.').first.ends_with?('/media') && !tag_requested? | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   def replies_requested? |   def replies_requested? | ||||||
|     request.path.split('.').first.end_with?('/with_replies') && !tag_requested? |     request.path.split('.').first.ends_with?('/with_replies') && !tag_requested? | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   def tag_requested? |   def tag_requested? | ||||||
|     request.path.split('.').first.end_with?(Addressable::URI.parse("/tagged/#{params[:tag]}").normalize) |     request.path.split('.').first.ends_with?(Addressable::URI.parse("/tagged/#{params[:tag]}").normalize) | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   def cached_filtered_status_page |   def filtered_status_page | ||||||
|     cache_collection_paginated_by_id( |     filtered_statuses.paginate_by_id(PAGE_SIZE, params_slice(:max_id, :min_id, :since_id)) | ||||||
|       filtered_statuses, |  | ||||||
|       Status, |  | ||||||
|       PAGE_SIZE, |  | ||||||
|       params_slice(:max_id, :min_id, :since_id) |  | ||||||
|     ) |  | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   def params_slice(*keys) |   def params_slice(*keys) | ||||||
|     params.slice(*keys).permit(*keys) |     params.slice(*keys).permit(*keys) | ||||||
|   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 | ||||||
|   | |||||||
| @@ -8,8 +8,4 @@ class ActivityPub::BaseController < Api::BaseController | |||||||
|   def set_cache_headers |   def set_cache_headers | ||||||
|     response.headers['Vary'] = 'Signature' if authorized_fetch_mode? |     response.headers['Vary'] = 'Signature' if authorized_fetch_mode? | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   def skip_temporary_suspension_response? |  | ||||||
|     false |  | ||||||
|   end |  | ||||||
| end | end | ||||||
|   | |||||||
| @@ -1,21 +0,0 @@ | |||||||
| # frozen_string_literal: true |  | ||||||
|  |  | ||||||
| class ActivityPub::ClaimsController < ActivityPub::BaseController |  | ||||||
|   include SignatureVerification |  | ||||||
|   include AccountOwnedConcern |  | ||||||
|  |  | ||||||
|   skip_before_action :authenticate_user! |  | ||||||
|  |  | ||||||
|   before_action :require_signature! |  | ||||||
|   before_action :set_claim_result |  | ||||||
|  |  | ||||||
|   def create |  | ||||||
|     render json: @claim_result, serializer: ActivityPub::OneTimeKeySerializer |  | ||||||
|   end |  | ||||||
|  |  | ||||||
|   private |  | ||||||
|  |  | ||||||
|   def set_claim_result |  | ||||||
|     @claim_result = ::Keys::ClaimService.new.call(@account.id, params[:id]) |  | ||||||
|   end |  | ||||||
| end |  | ||||||
| @@ -5,69 +5,51 @@ class ActivityPub::CollectionsController < ActivityPub::BaseController | |||||||
|   include AccountOwnedConcern |   include AccountOwnedConcern | ||||||
|  |  | ||||||
|   before_action :require_signature!, if: :authorized_fetch_mode? |   before_action :require_signature!, if: :authorized_fetch_mode? | ||||||
|   before_action :set_items |  | ||||||
|   before_action :set_size |   before_action :set_size | ||||||
|   before_action :set_type |   before_action :set_statuses | ||||||
|   before_action :set_cache_headers |   before_action :set_cache_headers | ||||||
|  |  | ||||||
|   def show |   def show | ||||||
|     expires_in 3.minutes, public: public_fetch_mode? |     expires_in 3.minutes, public: public_fetch_mode? | ||||||
|     render_with_cache json: collection_presenter, content_type: 'application/activity+json', serializer: ActivityPub::CollectionSerializer, adapter: ActivityPub::Adapter |     render_with_cache json: collection_presenter, content_type: 'application/activity+json', serializer: ActivityPub::CollectionSerializer, adapter: ActivityPub::Adapter, skip_activities: true | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   private |   private | ||||||
|  |  | ||||||
|   def set_items |   def set_statuses | ||||||
|     case params[:id] |     @statuses = scope_for_collection | ||||||
|     when 'featured' |     @statuses = cache_collection(@statuses, Status) | ||||||
|       @items = for_signed_account { cache_collection(@account.pinned_statuses, Status) } |  | ||||||
|     when 'tags' |  | ||||||
|       @items = for_signed_account { @account.featured_tags } |  | ||||||
|     when 'devices' |  | ||||||
|       @items = @account.devices |  | ||||||
|     else |  | ||||||
|       not_found |  | ||||||
|     end |  | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   def set_size |   def set_size | ||||||
|     case params[:id] |     case params[:id] | ||||||
|     when 'featured', 'devices', 'tags' |     when 'featured' | ||||||
|       @size = @items.size |       @size = @account.pinned_statuses.count | ||||||
|     else |     else | ||||||
|       not_found |       not_found | ||||||
|     end |     end | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   def set_type |   def scope_for_collection | ||||||
|     case params[:id] |     case params[:id] | ||||||
|     when 'featured' |     when 'featured' | ||||||
|       @type = :ordered |       # Because in public fetch mode we cache the response, there would be no | ||||||
|     when 'devices', 'tags' |       # benefit from performing the check below, since a blocked account or domain | ||||||
|       @type = :unordered |       # would likely be served the cache from the reverse proxy anyway | ||||||
|  |       if authorized_fetch_mode? && !signed_request_account.nil? && (@account.blocking?(signed_request_account) || (!signed_request_account.domain.nil? && @account.domain_blocking?(signed_request_account.domain))) | ||||||
|  |         Status.none | ||||||
|       else |       else | ||||||
|       not_found |         @account.pinned_statuses | ||||||
|  |       end | ||||||
|     end |     end | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   def collection_presenter |   def collection_presenter | ||||||
|     ActivityPub::CollectionPresenter.new( |     ActivityPub::CollectionPresenter.new( | ||||||
|       id: account_collection_url(@account, params[:id]), |       id: account_collection_url(@account, params[:id]), | ||||||
|       type: @type, |       type: :ordered, | ||||||
|       size: @size, |       size: @size, | ||||||
|       items: @items |       items: @statuses | ||||||
|     ) |     ) | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   def for_signed_account |  | ||||||
|     # Because in public fetch mode we cache the response, there would be no |  | ||||||
|     # benefit from performing the check below, since a blocked account or domain |  | ||||||
|     # would likely be served the cache from the reverse proxy anyway |  | ||||||
|  |  | ||||||
|     if authorized_fetch_mode? && !signed_request_account.nil? && (@account.blocking?(signed_request_account) || (!signed_request_account.domain.nil? && @account.domain_blocking?(signed_request_account.domain))) |  | ||||||
|       [] |  | ||||||
|     else |  | ||||||
|       yield |  | ||||||
|     end |  | ||||||
|   end |  | ||||||
| end | end | ||||||
|   | |||||||
| @@ -1,36 +0,0 @@ | |||||||
| # frozen_string_literal: true |  | ||||||
|  |  | ||||||
| class ActivityPub::FollowersSynchronizationsController < ActivityPub::BaseController |  | ||||||
|   include SignatureVerification |  | ||||||
|   include AccountOwnedConcern |  | ||||||
|  |  | ||||||
|   before_action :require_signature! |  | ||||||
|   before_action :set_items |  | ||||||
|   before_action :set_cache_headers |  | ||||||
|  |  | ||||||
|   def show |  | ||||||
|     expires_in 0, public: false |  | ||||||
|     render json: collection_presenter, |  | ||||||
|            serializer: ActivityPub::CollectionSerializer, |  | ||||||
|            adapter: ActivityPub::Adapter, |  | ||||||
|            content_type: 'application/activity+json' |  | ||||||
|   end |  | ||||||
|  |  | ||||||
|   private |  | ||||||
|  |  | ||||||
|   def uri_prefix |  | ||||||
|     signed_request_account.uri[Account::URL_PREFIX_RE] |  | ||||||
|   end |  | ||||||
|  |  | ||||||
|   def set_items |  | ||||||
|     @items = @account.followers.where(Account.arel_table[:uri].matches("#{Account.sanitize_sql_like(uri_prefix)}/%", false, true)).or(@account.followers.where(uri: uri_prefix)).pluck(:uri) |  | ||||||
|   end |  | ||||||
|  |  | ||||||
|   def collection_presenter |  | ||||||
|     ActivityPub::CollectionPresenter.new( |  | ||||||
|       id: account_followers_synchronization_url(@account), |  | ||||||
|       type: :ordered, |  | ||||||
|       items: @items |  | ||||||
|     ) |  | ||||||
|   end |  | ||||||
| end |  | ||||||
| @@ -5,26 +5,25 @@ class ActivityPub::InboxesController < ActivityPub::BaseController | |||||||
|   include JsonLdHelper |   include JsonLdHelper | ||||||
|   include AccountOwnedConcern |   include AccountOwnedConcern | ||||||
|  |  | ||||||
|   before_action :skip_unknown_actor_activity |   before_action :skip_unknown_actor_delete | ||||||
|   before_action :require_signature! |   before_action :require_signature! | ||||||
|   skip_before_action :authenticate_user! |   skip_before_action :authenticate_user! | ||||||
|  |  | ||||||
|   def create |   def create | ||||||
|     upgrade_account |     upgrade_account | ||||||
|     process_collection_synchronization |  | ||||||
|     process_payload |     process_payload | ||||||
|     head 202 |     head 202 | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   private |   private | ||||||
|  |  | ||||||
|   def skip_unknown_actor_activity |   def skip_unknown_actor_delete | ||||||
|     head 202 if unknown_affected_account? |     head 202 if unknown_deleted_account? | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   def unknown_affected_account? |   def unknown_deleted_account? | ||||||
|     json = Oj.load(body, mode: :strict) |     json = Oj.load(body, mode: :strict) | ||||||
|     json.is_a?(Hash) && %w(Delete Update).include?(json['type']) && json['actor'].present? && json['actor'] == value_or_id(json['object']) && !Account.where(uri: json['actor']).exists? |     json.is_a?(Hash) && json['type'] == 'Delete' && json['actor'].present? && json['actor'] == value_or_id(json['object']) && !Account.where(uri: json['actor']).exists? | ||||||
|   rescue Oj::ParseError |   rescue Oj::ParseError | ||||||
|     false |     false | ||||||
|   end |   end | ||||||
| @@ -33,10 +32,6 @@ class ActivityPub::InboxesController < ActivityPub::BaseController | |||||||
|     params[:account_username].present? |     params[:account_username].present? | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   def skip_temporary_suspension_response? |  | ||||||
|     true |  | ||||||
|   end |  | ||||||
|  |  | ||||||
|   def body |   def body | ||||||
|     return @body if defined?(@body) |     return @body if defined?(@body) | ||||||
|  |  | ||||||
| @@ -57,19 +52,6 @@ class ActivityPub::InboxesController < ActivityPub::BaseController | |||||||
|     DeliveryFailureTracker.reset!(signed_request_account.inbox_url) |     DeliveryFailureTracker.reset!(signed_request_account.inbox_url) | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   def process_collection_synchronization |  | ||||||
|     raw_params = request.headers['Collection-Synchronization'] |  | ||||||
|     return if raw_params.blank? || ENV['DISABLE_FOLLOWERS_SYNCHRONIZATION'] == 'true' |  | ||||||
|  |  | ||||||
|     # Re-using the syntax for signature parameters |  | ||||||
|     tree   = SignatureParamsParser.new.parse(raw_params) |  | ||||||
|     params = SignatureParamsTransformer.new.apply(tree) |  | ||||||
|  |  | ||||||
|     ActivityPub::PrepareFollowersSynchronizationService.new.call(signed_request_account, params) |  | ||||||
|   rescue Parslet::ParseFailed |  | ||||||
|     Rails.logger.warn 'Error parsing Collection-Synchronization header' |  | ||||||
|   end |  | ||||||
|  |  | ||||||
|   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, @account&.id) | ||||||
|   end |   end | ||||||
|   | |||||||
| @@ -11,11 +11,7 @@ class ActivityPub::OutboxesController < ActivityPub::BaseController | |||||||
|   before_action :set_cache_headers |   before_action :set_cache_headers | ||||||
|  |  | ||||||
|   def show |   def show | ||||||
|     if page_requested? |     expires_in(page_requested? ? 0 : 3.minutes, public: public_fetch_mode? && !(signed_request_account.present? && page_requested?)) | ||||||
|       expires_in(1.minute, public: public_fetch_mode? && signed_request_account.nil?) |  | ||||||
|     else |  | ||||||
|       expires_in(3.minutes, public: public_fetch_mode?) |  | ||||||
|     end |  | ||||||
|     render json: outbox_presenter, serializer: ActivityPub::OutboxSerializer, adapter: ActivityPub::Adapter, content_type: 'application/activity+json' |     render json: outbox_presenter, serializer: ActivityPub::OutboxSerializer, adapter: ActivityPub::Adapter, content_type: 'application/activity+json' | ||||||
|   end |   end | ||||||
|  |  | ||||||
| @@ -24,49 +20,38 @@ class ActivityPub::OutboxesController < ActivityPub::BaseController | |||||||
|   def outbox_presenter |   def outbox_presenter | ||||||
|     if page_requested? |     if page_requested? | ||||||
|       ActivityPub::CollectionPresenter.new( |       ActivityPub::CollectionPresenter.new( | ||||||
|         id: outbox_url(**page_params), |         id: account_outbox_url(@account, page_params), | ||||||
|         type: :ordered, |         type: :ordered, | ||||||
|         part_of: outbox_url, |         part_of: account_outbox_url(@account), | ||||||
|         prev: prev_page, |         prev: prev_page, | ||||||
|         next: next_page, |         next: next_page, | ||||||
|         items: @statuses |         items: @statuses | ||||||
|       ) |       ) | ||||||
|     else |     else | ||||||
|       ActivityPub::CollectionPresenter.new( |       ActivityPub::CollectionPresenter.new( | ||||||
|         id: outbox_url, |         id: account_outbox_url(@account), | ||||||
|         type: :ordered, |         type: :ordered, | ||||||
|         size: @account.statuses_count, |         size: @account.statuses_count, | ||||||
|         first: outbox_url(page: true), |         first: account_outbox_url(@account, page: true), | ||||||
|         last: outbox_url(page: true, min_id: 0) |         last: account_outbox_url(@account, page: true, min_id: 0) | ||||||
|       ) |       ) | ||||||
|     end |     end | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   def outbox_url(**kwargs) |  | ||||||
|     if params[:account_username].present? |  | ||||||
|       account_outbox_url(@account, **kwargs) |  | ||||||
|     else |  | ||||||
|       instance_actor_outbox_url(**kwargs) |  | ||||||
|     end |  | ||||||
|   end |  | ||||||
|  |  | ||||||
|   def next_page |   def next_page | ||||||
|     outbox_url(page: true, max_id: @statuses.last.id) if @statuses.size == LIMIT |     account_outbox_url(@account, page: true, max_id: @statuses.last.id) if @statuses.size == LIMIT | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   def prev_page |   def prev_page | ||||||
|     outbox_url(page: true, min_id: @statuses.first.id) unless @statuses.empty? |     account_outbox_url(@account, page: true, min_id: @statuses.first.id) unless @statuses.empty? | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   def set_statuses |   def set_statuses | ||||||
|     return unless page_requested? |     return unless page_requested? | ||||||
|  |  | ||||||
|     @statuses = cache_collection_paginated_by_id( |     @statuses = @account.statuses.permitted_for(@account, signed_request_account) | ||||||
|       @account.statuses.permitted_for(@account, signed_request_account), |     @statuses = @statuses.paginate_by_id(LIMIT, params_slice(:max_id, :min_id, :since_id)) | ||||||
|       Status, |     @statuses = cache_collection(@statuses, Status) | ||||||
|       LIMIT, |  | ||||||
|       params_slice(:max_id, :min_id, :since_id) |  | ||||||
|     ) |  | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   def page_requested? |   def page_requested? | ||||||
| @@ -76,12 +61,4 @@ class ActivityPub::OutboxesController < ActivityPub::BaseController | |||||||
|   def page_params |   def page_params | ||||||
|     { page: true, max_id: params[:max_id], min_id: params[:min_id] }.compact |     { page: true, max_id: params[:max_id], min_id: params[:min_id] }.compact | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   def set_account |  | ||||||
|     @account = params[:account_username].present? ? Account.find_local!(username_param) : Account.representative |  | ||||||
|   end |  | ||||||
|  |  | ||||||
|   def set_cache_headers |  | ||||||
|     response.headers['Vary'] = 'Signature' if authorized_fetch_mode? || page_requested? |  | ||||||
|   end |  | ||||||
| end | end | ||||||
|   | |||||||
| @@ -31,7 +31,7 @@ class ActivityPub::RepliesController < ActivityPub::BaseController | |||||||
|   end |   end | ||||||
|  |  | ||||||
|   def set_replies |   def set_replies | ||||||
|     @replies = only_other_accounts? ? Status.where.not(account_id: @account.id).joins(:account).merge(Account.without_suspended) : @account.statuses |     @replies = 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.where(in_reply_to_id: @status.id, visibility: [:public, :unlisted]) | ||||||
|     @replies = @replies.paginate_by_min_id(DESCENDANTS_LIMIT, params[:min_id]) |     @replies = @replies.paginate_by_min_id(DESCENDANTS_LIMIT, params[:min_id]) | ||||||
|   end |   end | ||||||
|   | |||||||
| @@ -2,7 +2,7 @@ | |||||||
|  |  | ||||||
| module Admin | module Admin | ||||||
|   class AccountsController < BaseController |   class AccountsController < BaseController | ||||||
|     before_action :set_account, except: [:index] |     before_action :set_account, only: [:show, :redownload, :remove_avatar, :remove_header, :enable, :unsilence, :unsuspend, :memorialize, :approve, :reject] | ||||||
|     before_action :require_remote_account!, only: [:redownload] |     before_action :require_remote_account!, only: [:redownload] | ||||||
|     before_action :require_local_account!, only: [:enable, :memorialize, :approve, :reject] |     before_action :require_local_account!, only: [:enable, :memorialize, :approve, :reject] | ||||||
|  |  | ||||||
| @@ -14,65 +14,49 @@ module Admin | |||||||
|     def show |     def show | ||||||
|       authorize @account, :show? |       authorize @account, :show? | ||||||
|  |  | ||||||
|       @deletion_request        = @account.deletion_request |  | ||||||
|       @account_moderation_note = current_account.account_moderation_notes.new(target_account: @account) |       @account_moderation_note = current_account.account_moderation_notes.new(target_account: @account) | ||||||
|       @moderation_notes        = @account.targeted_moderation_notes.latest |       @moderation_notes        = @account.targeted_moderation_notes.latest | ||||||
|       @warnings                = @account.targeted_account_warnings.latest.custom |       @warnings                = @account.targeted_account_warnings.latest.custom | ||||||
|       @domain_block            = DomainBlock.rule_for(@account.domain) |  | ||||||
|     end |     end | ||||||
|  |  | ||||||
|     def memorialize |     def memorialize | ||||||
|       authorize @account, :memorialize? |       authorize @account, :memorialize? | ||||||
|       @account.memorialize! |       @account.memorialize! | ||||||
|       log_action :memorialize, @account |       log_action :memorialize, @account | ||||||
|       redirect_to admin_account_path(@account.id), notice: I18n.t('admin.accounts.memorialized_msg', username: @account.acct) |       redirect_to admin_account_path(@account.id) | ||||||
|     end |     end | ||||||
|  |  | ||||||
|     def enable |     def enable | ||||||
|       authorize @account.user, :enable? |       authorize @account.user, :enable? | ||||||
|       @account.user.enable! |       @account.user.enable! | ||||||
|       log_action :enable, @account.user |       log_action :enable, @account.user | ||||||
|       redirect_to admin_account_path(@account.id), notice: I18n.t('admin.accounts.enabled_msg', username: @account.acct) |       redirect_to admin_account_path(@account.id) | ||||||
|     end |     end | ||||||
|  |  | ||||||
|     def approve |     def approve | ||||||
|       authorize @account.user, :approve? |       authorize @account.user, :approve? | ||||||
|       @account.user.approve! |       @account.user.approve! | ||||||
|       redirect_to admin_pending_accounts_path, notice: I18n.t('admin.accounts.approved_msg', username: @account.acct) |       redirect_to admin_pending_accounts_path | ||||||
|     end |     end | ||||||
|  |  | ||||||
|     def reject |     def reject | ||||||
|       authorize @account.user, :reject? |       authorize @account.user, :reject? | ||||||
|       DeleteAccountService.new.call(@account, reserve_email: false, reserve_username: false) |       SuspendAccountService.new.call(@account, reserve_email: false, reserve_username: false) | ||||||
|       redirect_to admin_pending_accounts_path, notice: I18n.t('admin.accounts.rejected_msg', username: @account.acct) |       redirect_to admin_pending_accounts_path | ||||||
|     end |  | ||||||
|  |  | ||||||
|     def destroy |  | ||||||
|       authorize @account, :destroy? |  | ||||||
|       Admin::AccountDeletionWorker.perform_async(@account.id) |  | ||||||
|       redirect_to admin_account_path(@account.id), notice: I18n.t('admin.accounts.destroyed_msg', username: @account.acct) |  | ||||||
|     end |  | ||||||
|  |  | ||||||
|     def unsensitive |  | ||||||
|       authorize @account, :unsensitive? |  | ||||||
|       @account.unsensitize! |  | ||||||
|       log_action :unsensitive, @account |  | ||||||
|       redirect_to admin_account_path(@account.id) |  | ||||||
|     end |     end | ||||||
|  |  | ||||||
|     def unsilence |     def unsilence | ||||||
|       authorize @account, :unsilence? |       authorize @account, :unsilence? | ||||||
|       @account.unsilence! |       @account.unsilence! | ||||||
|       log_action :unsilence, @account |       log_action :unsilence, @account | ||||||
|       redirect_to admin_account_path(@account.id), notice: I18n.t('admin.accounts.unsilenced_msg', username: @account.acct) |       redirect_to admin_account_path(@account.id) | ||||||
|     end |     end | ||||||
|  |  | ||||||
|     def unsuspend |     def unsuspend | ||||||
|       authorize @account, :unsuspend? |       authorize @account, :unsuspend? | ||||||
|       @account.unsuspend! |       @account.unsuspend! | ||||||
|       Admin::UnsuspensionWorker.perform_async(@account.id) |  | ||||||
|       log_action :unsuspend, @account |       log_action :unsuspend, @account | ||||||
|       redirect_to admin_account_path(@account.id), notice: I18n.t('admin.accounts.unsuspended_msg', username: @account.acct) |       redirect_to admin_account_path(@account.id) | ||||||
|     end |     end | ||||||
|  |  | ||||||
|     def redownload |     def redownload | ||||||
| @@ -81,7 +65,7 @@ module Admin | |||||||
|       @account.update!(last_webfingered_at: nil) |       @account.update!(last_webfingered_at: nil) | ||||||
|       ResolveAccountService.new.call(@account) |       ResolveAccountService.new.call(@account) | ||||||
|  |  | ||||||
|       redirect_to admin_account_path(@account.id), notice: I18n.t('admin.accounts.redownloaded_msg', username: @account.acct) |       redirect_to admin_account_path(@account.id) | ||||||
|     end |     end | ||||||
|  |  | ||||||
|     def remove_avatar |     def remove_avatar | ||||||
| @@ -92,7 +76,7 @@ module Admin | |||||||
|  |  | ||||||
|       log_action :remove_avatar, @account.user |       log_action :remove_avatar, @account.user | ||||||
|  |  | ||||||
|       redirect_to admin_account_path(@account.id), notice: I18n.t('admin.accounts.removed_avatar_msg', username: @account.acct) |       redirect_to admin_account_path(@account.id) | ||||||
|     end |     end | ||||||
|  |  | ||||||
|     def remove_header |     def remove_header | ||||||
| @@ -103,7 +87,7 @@ module Admin | |||||||
|  |  | ||||||
|       log_action :remove_header, @account.user |       log_action :remove_header, @account.user | ||||||
|  |  | ||||||
|       redirect_to admin_account_path(@account.id), notice: I18n.t('admin.accounts.removed_header_msg', username: @account.acct) |       redirect_to admin_account_path(@account.id) | ||||||
|     end |     end | ||||||
|  |  | ||||||
|     private |     private | ||||||
|   | |||||||
| @@ -71,7 +71,7 @@ class Admin::AnnouncementsController < Admin::BaseController | |||||||
|   private |   private | ||||||
|  |  | ||||||
|   def set_announcements |   def set_announcements | ||||||
|     @announcements = AnnouncementFilter.new(filter_params).results.reverse_chronological.page(params[:page]) |     @announcements = AnnouncementFilter.new(filter_params).results.page(params[:page]) | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   def set_announcement |   def set_announcement | ||||||
|   | |||||||
| @@ -33,8 +33,6 @@ module Admin | |||||||
|       @form.save |       @form.save | ||||||
|     rescue ActionController::ParameterMissing |     rescue ActionController::ParameterMissing | ||||||
|       flash[:alert] = I18n.t('admin.accounts.no_account_selected') |       flash[:alert] = I18n.t('admin.accounts.no_account_selected') | ||||||
|     rescue Mastodon::NotPermittedError |  | ||||||
|       flash[:alert] = I18n.t('admin.custom_emojis.not_permitted') |  | ||||||
|     ensure |     ensure | ||||||
|       redirect_to admin_custom_emojis_path(filter_params) |       redirect_to admin_custom_emojis_path(filter_params) | ||||||
|     end |     end | ||||||
|   | |||||||
| @@ -4,7 +4,6 @@ require 'sidekiq/api' | |||||||
| module Admin | module Admin | ||||||
|   class DashboardController < BaseController |   class DashboardController < BaseController | ||||||
|     def index |     def index | ||||||
|       @system_checks         = Admin::SystemCheck.perform |  | ||||||
|       @users_count           = User.count |       @users_count           = User.count | ||||||
|       @pending_users_count   = User.pending.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 | ||||||
| @@ -35,6 +34,7 @@ module Admin | |||||||
|       @whitelist_enabled     = whitelist_mode? |       @whitelist_enabled     = whitelist_mode? | ||||||
|       @profile_directory     = Setting.profile_directory |       @profile_directory     = Setting.profile_directory | ||||||
|       @timeline_preview      = Setting.timeline_preview |       @timeline_preview      = Setting.timeline_preview | ||||||
|  |       @spam_check_enabled    = Setting.spam_check_enabled | ||||||
|       @trends_enabled        = Setting.trends |       @trends_enabled        = Setting.trends | ||||||
|     end |     end | ||||||
|  |  | ||||||
|   | |||||||
| @@ -22,14 +22,13 @@ module Admin | |||||||
|       if existing_domain_block.present? && !@domain_block.stricter_than?(existing_domain_block) |       if existing_domain_block.present? && !@domain_block.stricter_than?(existing_domain_block) | ||||||
|         @domain_block.save |         @domain_block.save | ||||||
|         flash.now[:alert] = I18n.t('admin.domain_blocks.existing_domain_block_html', name: existing_domain_block.domain, unblock_url: admin_domain_block_path(existing_domain_block)).html_safe # rubocop:disable Rails/OutputSafety |         flash.now[:alert] = I18n.t('admin.domain_blocks.existing_domain_block_html', name: existing_domain_block.domain, unblock_url: admin_domain_block_path(existing_domain_block)).html_safe # rubocop:disable Rails/OutputSafety | ||||||
|         @domain_block.errors.delete(:domain) |         @domain_block.errors[:domain].clear | ||||||
|         render :new |         render :new | ||||||
|       else |       else | ||||||
|         if existing_domain_block.present? |         if existing_domain_block.present? | ||||||
|           @domain_block = existing_domain_block |           @domain_block = existing_domain_block | ||||||
|           @domain_block.update(resource_params) |           @domain_block.update(resource_params) | ||||||
|         end |         end | ||||||
|  |  | ||||||
|         if @domain_block.save |         if @domain_block.save | ||||||
|           DomainBlockWorker.perform_async(@domain_block.id) |           DomainBlockWorker.perform_async(@domain_block.id) | ||||||
|           log_action :create, @domain_block |           log_action :create, @domain_block | ||||||
| @@ -41,7 +40,7 @@ module Admin | |||||||
|     end |     end | ||||||
|  |  | ||||||
|     def update |     def update | ||||||
|       authorize :domain_block, :update? |       authorize :domain_block, :create? | ||||||
|  |  | ||||||
|       @domain_block.update(update_params) |       @domain_block.update(update_params) | ||||||
|  |  | ||||||
| @@ -49,7 +48,7 @@ module Admin | |||||||
|  |  | ||||||
|       if @domain_block.save |       if @domain_block.save | ||||||
|         DomainBlockWorker.perform_async(@domain_block.id, severity_changed) |         DomainBlockWorker.perform_async(@domain_block.id, severity_changed) | ||||||
|         log_action :update, @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_instances_path(limited: '1'), notice: I18n.t('admin.domain_blocks.created_msg') | ||||||
|       else |       else | ||||||
|         render :edit |         render :edit | ||||||
| @@ -74,11 +73,11 @@ module Admin | |||||||
|     end |     end | ||||||
|  |  | ||||||
|     def update_params |     def update_params | ||||||
|       params.require(:domain_block).permit(:severity, :reject_media, :reject_reports, :private_comment, :public_comment, :obfuscate) |       params.require(:domain_block).permit(:severity, :reject_media, :reject_reports, :private_comment, :public_comment) | ||||||
|     end |     end | ||||||
|  |  | ||||||
|     def resource_params |     def resource_params | ||||||
|       params.require(:domain_block).permit(:domain, :severity, :reject_media, :reject_reports, :private_comment, :public_comment, :obfuscate) |       params.require(:domain_block).permit(:domain, :severity, :reject_media, :reject_reports, :private_comment, :public_comment) | ||||||
|     end |     end | ||||||
|   end |   end | ||||||
| end | end | ||||||
|   | |||||||
| @@ -27,7 +27,7 @@ module Admin | |||||||
|           ips       = [] |           ips       = [] | ||||||
|  |  | ||||||
|           Resolv::DNS.open do |dns| |           Resolv::DNS.open do |dns| | ||||||
|             dns.timeouts = 5 |             dns.timeouts = 1 | ||||||
|  |  | ||||||
|             hostnames = dns.getresources(@email_domain_block.domain, Resolv::DNS::Resource::IN::MX).to_a.map { |e| e.exchange.to_s } |             hostnames = dns.getresources(@email_domain_block.domain, Resolv::DNS::Resource::IN::MX).to_a.map { |e| e.exchange.to_s } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,53 +0,0 @@ | |||||||
| # frozen_string_literal: true |  | ||||||
|  |  | ||||||
| module Admin |  | ||||||
|   class FollowRecommendationsController < BaseController |  | ||||||
|     before_action :set_language |  | ||||||
|  |  | ||||||
|     def show |  | ||||||
|       authorize :follow_recommendation, :show? |  | ||||||
|  |  | ||||||
|       @form     = Form::AccountBatch.new |  | ||||||
|       @accounts = filtered_follow_recommendations |  | ||||||
|     end |  | ||||||
|  |  | ||||||
|     def update |  | ||||||
|       @form = Form::AccountBatch.new(form_account_batch_params.merge(current_account: current_account, action: action_from_button)) |  | ||||||
|       @form.save |  | ||||||
|     rescue ActionController::ParameterMissing |  | ||||||
|       # Do nothing |  | ||||||
|     ensure |  | ||||||
|       redirect_to admin_follow_recommendations_path(filter_params) |  | ||||||
|     end |  | ||||||
|  |  | ||||||
|     private |  | ||||||
|  |  | ||||||
|     def set_language |  | ||||||
|       @language = follow_recommendation_filter.language |  | ||||||
|     end |  | ||||||
|  |  | ||||||
|     def filtered_follow_recommendations |  | ||||||
|       follow_recommendation_filter.results |  | ||||||
|     end |  | ||||||
|  |  | ||||||
|     def follow_recommendation_filter |  | ||||||
|       @follow_recommendation_filter ||= FollowRecommendationFilter.new(filter_params) |  | ||||||
|     end |  | ||||||
|  |  | ||||||
|     def form_account_batch_params |  | ||||||
|       params.require(:form_account_batch).permit(:action, account_ids: []) |  | ||||||
|     end |  | ||||||
|  |  | ||||||
|     def filter_params |  | ||||||
|       params.slice(*FollowRecommendationFilter::KEYS).permit(*FollowRecommendationFilter::KEYS) |  | ||||||
|     end |  | ||||||
|  |  | ||||||
|     def action_from_button |  | ||||||
|       if params[:suppress] |  | ||||||
|         'suppress_follow_recommendation' |  | ||||||
|       elsif params[:unsuppress] |  | ||||||
|         'unsuppress_follow_recommendation' |  | ||||||
|       end |  | ||||||
|     end |  | ||||||
|   end |  | ||||||
| end |  | ||||||
| @@ -2,73 +2,65 @@ | |||||||
|  |  | ||||||
| module Admin | module Admin | ||||||
|   class InstancesController < BaseController |   class InstancesController < BaseController | ||||||
|     before_action :set_instances, only: :index |     before_action :set_domain_block, only: :show | ||||||
|     before_action :set_instance, except: :index |     before_action :set_domain_allow, only: :show | ||||||
|     before_action :set_exhausted_deliveries_days, only: :show |     before_action :set_instance, only: :show | ||||||
|  |  | ||||||
|     def index |     def index | ||||||
|       authorize :instance, :index? |       authorize :instance, :index? | ||||||
|  |  | ||||||
|  |       @instances = ordered_instances | ||||||
|     end |     end | ||||||
|  |  | ||||||
|     def show |     def show | ||||||
|       authorize :instance, :show? |       authorize :instance, :show? | ||||||
|     end |  | ||||||
|  |  | ||||||
|     def clear_delivery_errors |       @following_count = Follow.where(account: Account.where(domain: params[:id])).count | ||||||
|       authorize :delivery, :clear_delivery_errors? |       @followers_count = Follow.where(target_account: Account.where(domain: params[:id])).count | ||||||
|  |       @reports_count   = Report.where(target_account: Account.where(domain: params[:id])).count | ||||||
|       @instance.delivery_failure_tracker.clear_failures! |       @blocks_count    = Block.where(target_account: Account.where(domain: params[:id])).count | ||||||
|       redirect_to admin_instance_path(@instance.domain) |       @available       = DeliveryFailureTracker.available?(params[:id]) | ||||||
|     end |       @media_storage   = MediaAttachment.where(account: Account.where(domain: params[:id])).sum(:file_file_size) | ||||||
|  |       @private_comment = @domain_block&.private_comment | ||||||
|     def restart_delivery |       @public_comment  = @domain_block&.public_comment | ||||||
|       authorize :delivery, :restart_delivery? |  | ||||||
|  |  | ||||||
|       last_unavailable_domain = unavailable_domain |  | ||||||
|  |  | ||||||
|       if last_unavailable_domain.present? |  | ||||||
|         @instance.delivery_failure_tracker.track_success! |  | ||||||
|         log_action :destroy, last_unavailable_domain |  | ||||||
|       end |  | ||||||
|  |  | ||||||
|       redirect_to admin_instance_path(@instance.domain) |  | ||||||
|     end |  | ||||||
|  |  | ||||||
|     def stop_delivery |  | ||||||
|       authorize :delivery, :stop_delivery? |  | ||||||
|  |  | ||||||
|       UnavailableDomain.create(domain: @instance.domain) |  | ||||||
|       log_action :create, unavailable_domain |  | ||||||
|       redirect_to admin_instance_path(@instance.domain) |  | ||||||
|     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 |     def set_instance | ||||||
|       @instance = Instance.find(params[:id]) |       resource   = Account.by_domain_accounts.find_by(domain: params[:id]) | ||||||
|     end |       resource ||= @domain_block | ||||||
|  |       resource ||= @domain_allow | ||||||
|  |  | ||||||
|     def set_exhausted_deliveries_days |       if resource | ||||||
|       @exhausted_deliveries_days = @instance.delivery_failure_tracker.exhausted_deliveries_days |         @instance = Instance.new(resource) | ||||||
|  |       else | ||||||
|  |         not_found | ||||||
|       end |       end | ||||||
|  |  | ||||||
|     def set_instances |  | ||||||
|       @instances = filtered_instances.page(params[:page]) |  | ||||||
|       warning_domains_map = DeliveryFailureTracker.warning_domains_map |  | ||||||
|  |  | ||||||
|       @instances.each do |instance| |  | ||||||
|         instance.failure_days = warning_domains_map[instance.domain] |  | ||||||
|       end |  | ||||||
|     end |  | ||||||
|  |  | ||||||
|     def unavailable_domain |  | ||||||
|       UnavailableDomain.find_by(domain: @instance.domain) |  | ||||||
|     end |     end | ||||||
|  |  | ||||||
|     def filtered_instances |     def filtered_instances | ||||||
|       InstanceFilter.new(whitelist_mode? ? { allowed: true } : filter_params).results |       InstanceFilter.new(whitelist_mode? ? { allowed: true } : filter_params).results | ||||||
|     end |     end | ||||||
|  |  | ||||||
|  |     def paginated_instances | ||||||
|  |       filtered_instances.page(params[:page]) | ||||||
|  |     end | ||||||
|  |  | ||||||
|  |     helper_method :paginated_instances | ||||||
|  |  | ||||||
|  |     def ordered_instances | ||||||
|  |       paginated_instances.map { |resource| Instance.new(resource) } | ||||||
|  |     end | ||||||
|  |  | ||||||
|     def filter_params |     def filter_params | ||||||
|       params.slice(*InstanceFilter::KEYS).permit(*InstanceFilter::KEYS) |       params.slice(*InstanceFilter::KEYS).permit(*InstanceFilter::KEYS) | ||||||
|     end |     end | ||||||
|   | |||||||
| @@ -1,56 +0,0 @@ | |||||||
| # frozen_string_literal: true |  | ||||||
|  |  | ||||||
| module Admin |  | ||||||
|   class IpBlocksController < BaseController |  | ||||||
|     def index |  | ||||||
|       authorize :ip_block, :index? |  | ||||||
|  |  | ||||||
|       @ip_blocks = IpBlock.page(params[:page]) |  | ||||||
|       @form      = Form::IpBlockBatch.new |  | ||||||
|     end |  | ||||||
|  |  | ||||||
|     def new |  | ||||||
|       authorize :ip_block, :create? |  | ||||||
|  |  | ||||||
|       @ip_block = IpBlock.new(ip: '', severity: :no_access, expires_in: 1.year) |  | ||||||
|     end |  | ||||||
|  |  | ||||||
|     def create |  | ||||||
|       authorize :ip_block, :create? |  | ||||||
|  |  | ||||||
|       @ip_block = IpBlock.new(resource_params) |  | ||||||
|  |  | ||||||
|       if @ip_block.save |  | ||||||
|         log_action :create, @ip_block |  | ||||||
|         redirect_to admin_ip_blocks_path, notice: I18n.t('admin.ip_blocks.created_msg') |  | ||||||
|       else |  | ||||||
|         render :new |  | ||||||
|       end |  | ||||||
|     end |  | ||||||
|  |  | ||||||
|     def batch |  | ||||||
|       @form = Form::IpBlockBatch.new(form_ip_block_batch_params.merge(current_account: current_account, action: action_from_button)) |  | ||||||
|       @form.save |  | ||||||
|     rescue ActionController::ParameterMissing |  | ||||||
|       flash[:alert] = I18n.t('admin.ip_blocks.no_ip_block_selected') |  | ||||||
|     rescue Mastodon::NotPermittedError |  | ||||||
|       flash[:alert] = I18n.t('admin.custom_emojis.not_permitted') |  | ||||||
|     ensure |  | ||||||
|       redirect_to admin_ip_blocks_path |  | ||||||
|     end |  | ||||||
|  |  | ||||||
|     private |  | ||||||
|  |  | ||||||
|     def resource_params |  | ||||||
|       params.require(:ip_block).permit(:ip, :severity, :comment, :expires_in) |  | ||||||
|     end |  | ||||||
|  |  | ||||||
|     def action_from_button |  | ||||||
|       'delete' if params[:delete] |  | ||||||
|     end |  | ||||||
|  |  | ||||||
|     def form_ip_block_batch_params |  | ||||||
|       params.require(:form_ip_block_batch).permit(ip_block_ids: []) |  | ||||||
|     end |  | ||||||
|   end |  | ||||||
| end |  | ||||||
| @@ -1,59 +0,0 @@ | |||||||
| # frozen_string_literal: true |  | ||||||
|  |  | ||||||
| module Admin |  | ||||||
|   class RulesController < BaseController |  | ||||||
|     before_action :set_rule, except: [:index, :create] |  | ||||||
|  |  | ||||||
|     def index |  | ||||||
|       authorize :rule, :index? |  | ||||||
|  |  | ||||||
|       @rules = Rule.ordered |  | ||||||
|       @rule  = Rule.new |  | ||||||
|     end |  | ||||||
|  |  | ||||||
|     def create |  | ||||||
|       authorize :rule, :create? |  | ||||||
|  |  | ||||||
|       @rule = Rule.new(resource_params) |  | ||||||
|  |  | ||||||
|       if @rule.save |  | ||||||
|         redirect_to admin_rules_path |  | ||||||
|       else |  | ||||||
|         @rules = Rule.ordered |  | ||||||
|         render :index |  | ||||||
|       end |  | ||||||
|     end |  | ||||||
|  |  | ||||||
|     def edit |  | ||||||
|       authorize @rule, :update? |  | ||||||
|     end |  | ||||||
|  |  | ||||||
|     def update |  | ||||||
|       authorize @rule, :update? |  | ||||||
|  |  | ||||||
|       if @rule.update(resource_params) |  | ||||||
|         redirect_to admin_rules_path |  | ||||||
|       else |  | ||||||
|         render :edit |  | ||||||
|       end |  | ||||||
|     end |  | ||||||
|  |  | ||||||
|     def destroy |  | ||||||
|       authorize @rule, :destroy? |  | ||||||
|  |  | ||||||
|       @rule.discard |  | ||||||
|  |  | ||||||
|       redirect_to admin_rules_path |  | ||||||
|     end |  | ||||||
|  |  | ||||||
|     private |  | ||||||
|  |  | ||||||
|     def set_rule |  | ||||||
|       @rule = Rule.find(params[:id]) |  | ||||||
|     end |  | ||||||
|  |  | ||||||
|     def resource_params |  | ||||||
|       params.require(:rule).permit(:text, :priority) |  | ||||||
|     end |  | ||||||
|   end |  | ||||||
| end |  | ||||||
| @@ -14,7 +14,8 @@ module Admin | |||||||
|       @statuses = @account.statuses.where(visibility: [:public, :unlisted]) |       @statuses = @account.statuses.where(visibility: [:public, :unlisted]) | ||||||
|  |  | ||||||
|       if params[:media] |       if params[:media] | ||||||
|         @statuses.merge!(Status.joins(:media_attachments).merge(@account.media_attachments.reorder(nil)).group(:id)).reorder('statuses.id desc') |         account_media_status_ids = @account.media_attachments.attached.reorder(nil).select(:status_id).distinct | ||||||
|  |         @statuses.merge!(Status.where(id: account_media_status_ids)) | ||||||
|       end |       end | ||||||
|  |  | ||||||
|       @statuses = @statuses.preload(:media_attachments, :mentions).page(params[:page]).per(PER_PAGE) |       @statuses = @statuses.preload(:media_attachments, :mentions).page(params[:page]).per(PER_PAGE) | ||||||
|   | |||||||
| @@ -59,8 +59,8 @@ module Admin | |||||||
|                              .where(Status.arel_table[:id].gteq(Mastodon::Snowflake.id_at(Time.now.utc.beginning_of_day))) |                              .where(Status.arel_table[:id].gteq(Mastodon::Snowflake.id_at(Time.now.utc.beginning_of_day))) | ||||||
|                              .joins(:account) |                              .joins(:account) | ||||||
|                              .group('accounts.domain') |                              .group('accounts.domain') | ||||||
|                              .reorder(statuses_count: :desc) |                              .reorder('statuses_count desc') | ||||||
|                              .pluck(Arel.sql('accounts.domain, count(*) AS statuses_count')) |                              .pluck('accounts.domain, count(*) AS statuses_count') | ||||||
|     end |     end | ||||||
|  |  | ||||||
|     def set_counters |     def set_counters | ||||||
|   | |||||||
| @@ -7,7 +7,7 @@ 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!, unless: :whitelist_mode? |   skip_before_action :require_functional! | ||||||
|  |  | ||||||
|   before_action :require_authenticated_user!, if: :disallow_unauthenticated_api_access? |   before_action :require_authenticated_user!, if: :disallow_unauthenticated_api_access? | ||||||
|   before_action :set_cache_headers |   before_action :set_cache_headers | ||||||
| @@ -40,7 +40,7 @@ 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, Seahorse::Client::NetworkingError, Stoplight::Error::RedLight do |   rescue_from Mastodon::RaceConditionError do | ||||||
|     render json: { error: 'There was a temporary problem serving your request, please try again' }, status: 503 |     render json: { error: 'There was a temporary problem serving your request, please try again' }, status: 503 | ||||||
|   end |   end | ||||||
|  |  | ||||||
| @@ -71,7 +71,6 @@ class Api::BaseController < ApplicationController | |||||||
|  |  | ||||||
|   def limit_param(default_limit) |   def limit_param(default_limit) | ||||||
|     return default_limit unless params[:limit] |     return default_limit unless params[:limit] | ||||||
|  |  | ||||||
|     [params[:limit].to_i.abs, default_limit * 2].min |     [params[:limit].to_i.abs, default_limit * 2].min | ||||||
|   end |   end | ||||||
|  |  | ||||||
| @@ -96,14 +95,14 @@ class Api::BaseController < ApplicationController | |||||||
|   def require_user! |   def require_user! | ||||||
|     if !current_user |     if !current_user | ||||||
|       render json: { error: 'This method requires an authenticated user' }, status: 422 |       render json: { error: 'This method requires an authenticated user' }, status: 422 | ||||||
|  |     elsif current_user.disabled? | ||||||
|  |       render json: { error: 'Your login is currently disabled' }, status: 403 | ||||||
|     elsif !current_user.confirmed? |     elsif !current_user.confirmed? | ||||||
|       render json: { error: 'Your login is missing a confirmed e-mail address' }, status: 403 |       render json: { error: 'Your login is missing a confirmed e-mail address' }, status: 403 | ||||||
|     elsif !current_user.approved? |     elsif !current_user.approved? | ||||||
|       render json: { error: 'Your login is currently pending approval' }, status: 403 |       render json: { error: 'Your login is currently pending approval' }, status: 403 | ||||||
|     elsif !current_user.functional? |  | ||||||
|       render json: { error: 'Your login is currently disabled' }, status: 403 |  | ||||||
|     else |     else | ||||||
|       update_user_sign_in |       set_user_activity | ||||||
|     end |     end | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,22 +0,0 @@ | |||||||
| # frozen_string_literal: true |  | ||||||
|  |  | ||||||
| class Api::V1::Accounts::FeaturedTagsController < Api::BaseController |  | ||||||
|   before_action :set_account |  | ||||||
|   before_action :set_featured_tags |  | ||||||
|  |  | ||||||
|   respond_to :json |  | ||||||
|  |  | ||||||
|   def index |  | ||||||
|     render json: @featured_tags, each_serializer: REST::FeaturedTagSerializer |  | ||||||
|   end |  | ||||||
|  |  | ||||||
|   private |  | ||||||
|  |  | ||||||
|   def set_account |  | ||||||
|     @account = Account.find(params[:account_id]) |  | ||||||
|   end |  | ||||||
|  |  | ||||||
|   def set_featured_tags |  | ||||||
|     @featured_tags = @account.suspended? ? [] : @account.featured_tags |  | ||||||
|   end |  | ||||||
| end |  | ||||||
| @@ -25,7 +25,7 @@ class Api::V1::Accounts::FollowerAccountsController < Api::BaseController | |||||||
|   end |   end | ||||||
|  |  | ||||||
|   def hide_results? |   def hide_results? | ||||||
|     @account.suspended? || (@account.hides_followers? && current_account&.id != @account.id) || (current_account && @account.blocking?(current_account)) |     (@account.hides_followers? && current_account&.id != @account.id) || (current_account && @account.blocking?(current_account)) | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   def default_accounts |   def default_accounts | ||||||
|   | |||||||
| @@ -25,7 +25,7 @@ class Api::V1::Accounts::FollowingAccountsController < Api::BaseController | |||||||
|   end |   end | ||||||
|  |  | ||||||
|   def hide_results? |   def hide_results? | ||||||
|     @account.suspended? || (@account.hides_following? && current_account&.id != @account.id) || (current_account && @account.blocking?(current_account)) |     (@account.hides_following? && current_account&.id != @account.id) || (current_account && @account.blocking?(current_account)) | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   def default_accounts |   def default_accounts | ||||||
|   | |||||||
| @@ -5,7 +5,7 @@ class Api::V1::Accounts::IdentityProofsController < Api::BaseController | |||||||
|   before_action :set_account |   before_action :set_account | ||||||
|  |  | ||||||
|   def index |   def index | ||||||
|     @proofs = @account.suspended? ? [] : @account.identity_proofs.active |     @proofs = @account.identity_proofs.active | ||||||
|     render json: @proofs, each_serializer: REST::IdentityProofSerializer |     render json: @proofs, each_serializer: REST::IdentityProofSerializer | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   | |||||||
| @@ -6,7 +6,7 @@ class Api::V1::Accounts::ListsController < Api::BaseController | |||||||
|   before_action :set_account |   before_action :set_account | ||||||
|  |  | ||||||
|   def index |   def index | ||||||
|     @lists = @account.suspended? ? [] : @account.lists.where(account: current_account) |     @lists = @account.lists.where(account: current_account) | ||||||
|     render json: @lists, each_serializer: REST::ListSerializer |     render json: @lists, each_serializer: REST::ListSerializer | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,16 +0,0 @@ | |||||||
| # frozen_string_literal: true |  | ||||||
|  |  | ||||||
| class Api::V1::Accounts::LookupController < Api::BaseController |  | ||||||
|   before_action -> { authorize_if_got_token! :read, :'read:accounts' } |  | ||||||
|   before_action :set_account |  | ||||||
|  |  | ||||||
|   def show |  | ||||||
|     render json: @account, serializer: REST::AccountSerializer |  | ||||||
|   end |  | ||||||
|  |  | ||||||
|   private |  | ||||||
|  |  | ||||||
|   def set_account |  | ||||||
|     @account = ResolveAccountService.new.call(params[:acct], skip_webfinger: true) || raise(ActiveRecord::RecordNotFound) |  | ||||||
|   end |  | ||||||
| end |  | ||||||
| @@ -1,30 +0,0 @@ | |||||||
| # frozen_string_literal: true |  | ||||||
|  |  | ||||||
| class Api::V1::Accounts::NotesController < Api::BaseController |  | ||||||
|   include Authorization |  | ||||||
|  |  | ||||||
|   before_action -> { doorkeeper_authorize! :write, :'write:accounts' } |  | ||||||
|   before_action :require_user! |  | ||||||
|   before_action :set_account |  | ||||||
|  |  | ||||||
|   def create |  | ||||||
|     if params[:comment].blank? |  | ||||||
|       AccountNote.find_by(account: current_account, target_account: @account)&.destroy |  | ||||||
|     else |  | ||||||
|       @note = AccountNote.find_or_initialize_by(account: current_account, target_account: @account) |  | ||||||
|       @note.comment = params[:comment] |  | ||||||
|       @note.save! if @note.changed? |  | ||||||
|     end |  | ||||||
|     render json: @account, serializer: REST::RelationshipSerializer, relationships: relationships_presenter |  | ||||||
|   end |  | ||||||
|  |  | ||||||
|   private |  | ||||||
|  |  | ||||||
|   def set_account |  | ||||||
|     @account = Account.find(params[:account_id]) |  | ||||||
|   end |  | ||||||
|  |  | ||||||
|   def relationships_presenter |  | ||||||
|     AccountRelationshipsPresenter.new([@account.id], current_user.account_id) |  | ||||||
|   end |  | ||||||
| end |  | ||||||
| @@ -5,7 +5,7 @@ class Api::V1::Accounts::RelationshipsController < Api::BaseController | |||||||
|   before_action :require_user! |   before_action :require_user! | ||||||
|  |  | ||||||
|   def index |   def index | ||||||
|     accounts = Account.without_suspended.where(id: account_ids).select('id') |     accounts = Account.where(id: account_ids).select('id') | ||||||
|     # .where doesn't guarantee that our results are in the same order |     # .where doesn't guarantee that our results are in the same order | ||||||
|     # we requested them, so return the "right" order to the requestor. |     # we requested them, so return the "right" order to the requestor. | ||||||
|     @accounts = accounts.index_by(&:id).values_at(*account_ids).compact |     @accounts = accounts.index_by(&:id).values_at(*account_ids).compact | ||||||
|   | |||||||
| @@ -18,10 +18,14 @@ class Api::V1::Accounts::StatusesController < Api::BaseController | |||||||
|   end |   end | ||||||
|  |  | ||||||
|   def load_statuses |   def load_statuses | ||||||
|     @account.suspended? ? [] : cached_account_statuses |     cached_account_statuses | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   def cached_account_statuses |   def cached_account_statuses | ||||||
|  |     cache_collection account_statuses, Status | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   def account_statuses | ||||||
|     statuses = truthy_param?(:pinned) ? pinned_scope : permitted_account_statuses |     statuses = truthy_param?(:pinned) ? pinned_scope : permitted_account_statuses | ||||||
|  |  | ||||||
|     statuses.merge!(only_media_scope) if truthy_param?(:only_media) |     statuses.merge!(only_media_scope) if truthy_param?(:only_media) | ||||||
| @@ -29,12 +33,7 @@ class Api::V1::Accounts::StatusesController < Api::BaseController | |||||||
|     statuses.merge!(no_reblogs_scope) if truthy_param?(:exclude_reblogs) |     statuses.merge!(no_reblogs_scope) if truthy_param?(:exclude_reblogs) | ||||||
|     statuses.merge!(hashtag_scope)    if params[:tagged].present? |     statuses.merge!(hashtag_scope)    if params[:tagged].present? | ||||||
|  |  | ||||||
|     cache_collection_paginated_by_id( |     statuses.paginate_by_id(limit_param(DEFAULT_STATUSES_LIMIT), params_slice(:max_id, :since_id, :min_id)) | ||||||
|       statuses, |  | ||||||
|       Status, |  | ||||||
|       limit_param(DEFAULT_STATUSES_LIMIT), |  | ||||||
|       params_slice(:max_id, :since_id, :min_id) |  | ||||||
|     ) |  | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   def permitted_account_statuses |   def permitted_account_statuses | ||||||
| @@ -42,7 +41,17 @@ class Api::V1::Accounts::StatusesController < Api::BaseController | |||||||
|   end |   end | ||||||
|  |  | ||||||
|   def only_media_scope |   def only_media_scope | ||||||
|     Status.joins(:media_attachments).merge(@account.media_attachments.reorder(nil)).group(:id) |     Status.where(id: account_media_status_ids) | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   def account_media_status_ids | ||||||
|  |     # `SELECT DISTINCT id, updated_at` is too slow, so pluck ids at first, and then select id, updated_at with ids. | ||||||
|  |     # 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 | ||||||
|  |     # 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) | ||||||
|  |             .paginate_by_max_id(limit_param(DEFAULT_STATUSES_LIMIT), params[:max_id], params[:since_id]) | ||||||
|  |             .reorder(id: :desc).distinct(:id).pluck(:id) | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   def pinned_scope |   def pinned_scope | ||||||
|   | |||||||
| @@ -9,6 +9,7 @@ class Api::V1::AccountsController < Api::BaseController | |||||||
|  |  | ||||||
|   before_action :require_user!, except: [:show, :create] |   before_action :require_user!, except: [:show, :create] | ||||||
|   before_action :set_account, except: [:create] |   before_action :set_account, except: [:create] | ||||||
|  |   before_action :check_account_suspension, only: [:show] | ||||||
|   before_action :check_enabled_registrations, only: [:create] |   before_action :check_enabled_registrations, only: [:create] | ||||||
|  |  | ||||||
|   skip_before_action :require_authenticated_user!, only: :create |   skip_before_action :require_authenticated_user!, only: :create | ||||||
| @@ -20,22 +21,21 @@ class Api::V1::AccountsController < Api::BaseController | |||||||
|   end |   end | ||||||
|  |  | ||||||
|   def create |   def create | ||||||
|     token    = AppSignUpService.new.call(doorkeeper_token.application, request.remote_ip, account_params) |     token    = AppSignUpService.new.call(doorkeeper_token.application, account_params) | ||||||
|     response = Doorkeeper::OAuth::TokenResponse.new(token) |     response = Doorkeeper::OAuth::TokenResponse.new(token) | ||||||
|  |  | ||||||
|     headers.merge!(response.headers) |     headers.merge!(response.headers) | ||||||
|  |  | ||||||
|     self.response_body = Oj.dump(response.body) |     self.response_body = Oj.dump(response.body) | ||||||
|     self.status        = response.status |     self.status        = response.status | ||||||
|   rescue ActiveRecord::RecordInvalid => e |  | ||||||
|     render json: ValidationErrorFormatter.new(e, :'account.username' => :username, :'invite_request.text' => :reason).as_json, status: :unprocessable_entity |  | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   def follow |   def follow | ||||||
|     follow  = FollowService.new.call(current_user.account, @account, reblogs: params.key?(:reblogs) ? truthy_param?(:reblogs) : nil, notify: params.key?(:notify) ? truthy_param?(:notify) : nil, with_rate_limit: true) |     FollowService.new.call(current_user.account, @account, reblogs: truthy_param?(:reblogs), with_rate_limit: true) | ||||||
|     options = @account.locked? || current_user.account.silenced? ? {} : { following_map: { @account.id => { reblogs: follow.show_reblogs?, notify: follow.notify? } }, requested_map: { @account.id => false } } |  | ||||||
|  |  | ||||||
|     render json: @account, serializer: REST::RelationshipSerializer, relationships: relationships(**options) |     options = @account.locked? || current_user.account.silenced? ? {} : { following_map: { @account.id => { reblogs: truthy_param?(:reblogs) } }, requested_map: { @account.id => false } } | ||||||
|  |  | ||||||
|  |     render json: @account, serializer: REST::RelationshipSerializer, relationships: relationships(options) | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   def block |   def block | ||||||
| @@ -44,7 +44,7 @@ class Api::V1::AccountsController < Api::BaseController | |||||||
|   end |   end | ||||||
|  |  | ||||||
|   def mute |   def mute | ||||||
|     MuteService.new.call(current_user.account, @account, notifications: truthy_param?(:notifications), duration: (params[:duration]&.to_i || 0)) |     MuteService.new.call(current_user.account, @account, notifications: truthy_param?(:notifications)) | ||||||
|     render json: @account, serializer: REST::RelationshipSerializer, relationships: relationships |     render json: @account, serializer: REST::RelationshipSerializer, relationships: relationships | ||||||
|   end |   end | ||||||
|  |  | ||||||
| @@ -70,7 +70,11 @@ class Api::V1::AccountsController < Api::BaseController | |||||||
|   end |   end | ||||||
|  |  | ||||||
|   def relationships(**options) |   def relationships(**options) | ||||||
|     AccountRelationshipsPresenter.new([@account.id], current_user.account_id, **options) |     AccountRelationshipsPresenter.new([@account.id], current_user.account_id, options) | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   def check_account_suspension | ||||||
|  |     gone if @account.suspended? | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   def account_params |   def account_params | ||||||
|   | |||||||
| @@ -22,7 +22,6 @@ class Api::V1::Admin::AccountsController < Api::BaseController | |||||||
|     active |     active | ||||||
|     pending |     pending | ||||||
|     disabled |     disabled | ||||||
|     sensitized |  | ||||||
|     silenced |     silenced | ||||||
|     suspended |     suspended | ||||||
|     username |     username | ||||||
| @@ -59,20 +58,7 @@ class Api::V1::Admin::AccountsController < Api::BaseController | |||||||
|  |  | ||||||
|   def reject |   def reject | ||||||
|     authorize @account.user, :reject? |     authorize @account.user, :reject? | ||||||
|     DeleteAccountService.new.call(@account, reserve_email: false, reserve_username: false) |     SuspendAccountService.new.call(@account, reserve_email: false, reserve_username: false) | ||||||
|     render json: @account, serializer: REST::Admin::AccountSerializer |  | ||||||
|   end |  | ||||||
|  |  | ||||||
|   def destroy |  | ||||||
|     authorize @account, :destroy? |  | ||||||
|     Admin::AccountDeletionWorker.perform_async(@account.id) |  | ||||||
|     render json: @account, serializer: REST::Admin::AccountSerializer |  | ||||||
|   end |  | ||||||
|  |  | ||||||
|   def unsensitive |  | ||||||
|     authorize @account, :unsensitive? |  | ||||||
|     @account.unsensitize! |  | ||||||
|     log_action :unsensitive, @account |  | ||||||
|     render json: @account, serializer: REST::Admin::AccountSerializer |     render json: @account, serializer: REST::Admin::AccountSerializer | ||||||
|   end |   end | ||||||
|  |  | ||||||
| @@ -86,7 +72,6 @@ class Api::V1::Admin::AccountsController < Api::BaseController | |||||||
|   def unsuspend |   def unsuspend | ||||||
|     authorize @account, :unsuspend? |     authorize @account, :unsuspend? | ||||||
|     @account.unsuspend! |     @account.unsuspend! | ||||||
|     Admin::UnsuspensionWorker.perform_async(@account.id) |  | ||||||
|     log_action :unsuspend, @account |     log_action :unsuspend, @account | ||||||
|     render json: @account, serializer: REST::Admin::AccountSerializer |     render json: @account, serializer: REST::Admin::AccountSerializer | ||||||
|   end |   end | ||||||
| @@ -94,7 +79,7 @@ class Api::V1::Admin::AccountsController < Api::BaseController | |||||||
|   private |   private | ||||||
|  |  | ||||||
|   def set_accounts |   def set_accounts | ||||||
|     @accounts = filtered_accounts.order(id: :desc).includes(user: [:invite_request, :invite]).to_a_paginated_by_id(limit_param(LIMIT), params_slice(:max_id, :since_id, :min_id)) |     @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 |   end | ||||||
|  |  | ||||||
|   def set_account |   def set_account | ||||||
|   | |||||||
| @@ -63,7 +63,7 @@ class Api::V1::Admin::ReportsController < Api::BaseController | |||||||
|   private |   private | ||||||
|  |  | ||||||
|   def set_reports |   def set_reports | ||||||
|     @reports = filtered_reports.order(id: :desc).with_accounts.to_a_paginated_by_id(limit_param(LIMIT), params_slice(:max_id, :since_id, :min_id)) |     @reports = filtered_reports.order(id: :desc).with_accounts.paginate_by_id(limit_param(LIMIT), params_slice(:max_id, :since_id, :min_id)) | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   def set_report |   def set_report | ||||||
|   | |||||||
| @@ -18,8 +18,6 @@ class Api::V1::BlocksController < Api::BaseController | |||||||
|  |  | ||||||
|   def paginated_blocks |   def paginated_blocks | ||||||
|     @paginated_blocks ||= Block.eager_load(target_account: :account_stat) |     @paginated_blocks ||= Block.eager_load(target_account: :account_stat) | ||||||
|                                .joins(:target_account) |  | ||||||
|                                .merge(Account.without_suspended) |  | ||||||
|                                .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), | ||||||
|   | |||||||
| @@ -17,11 +17,14 @@ class Api::V1::BookmarksController < Api::BaseController | |||||||
|   end |   end | ||||||
|  |  | ||||||
|   def cached_bookmarks |   def cached_bookmarks | ||||||
|     cache_collection(results.map(&:status), Status) |     cache_collection( | ||||||
|  |       Status.reorder(nil).joins(:bookmarks).merge(results), | ||||||
|  |       Status | ||||||
|  |     ) | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   def results |   def results | ||||||
|     @_results ||= account_bookmarks.eager_load(:status).to_a_paginated_by_id( |     @_results ||= account_bookmarks.paginate_by_id( | ||||||
|       limit_param(DEFAULT_STATUSES_LIMIT), |       limit_param(DEFAULT_STATUSES_LIMIT), | ||||||
|       params_slice(:max_id, :since_id, :min_id) |       params_slice(:max_id, :since_id, :min_id) | ||||||
|     ) |     ) | ||||||
|   | |||||||
| @@ -32,7 +32,7 @@ class Api::V1::ConversationsController < Api::BaseController | |||||||
|  |  | ||||||
|   def paginated_conversations |   def paginated_conversations | ||||||
|     AccountConversation.where(account: current_account) |     AccountConversation.where(account: current_account) | ||||||
|                        .to_a_paginated_by_id(limit_param(LIMIT), params_slice(:max_id, :since_id, :min_id)) |                        .paginate_by_id(limit_param(LIMIT), params_slice(:max_id, :since_id, :min_id)) | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   def insert_pagination_headers |   def insert_pagination_headers | ||||||
|   | |||||||
| @@ -1,30 +0,0 @@ | |||||||
| # frozen_string_literal: true |  | ||||||
|  |  | ||||||
| class Api::V1::Crypto::DeliveriesController < Api::BaseController |  | ||||||
|   before_action -> { doorkeeper_authorize! :crypto } |  | ||||||
|   before_action :require_user! |  | ||||||
|   before_action :set_current_device |  | ||||||
|  |  | ||||||
|   def create |  | ||||||
|     devices.each do |device_params| |  | ||||||
|       DeliverToDeviceService.new.call(current_account, @current_device, device_params) |  | ||||||
|     end |  | ||||||
|  |  | ||||||
|     render_empty |  | ||||||
|   end |  | ||||||
|  |  | ||||||
|   private |  | ||||||
|  |  | ||||||
|   def set_current_device |  | ||||||
|     @current_device = Device.find_by!(access_token: doorkeeper_token) |  | ||||||
|   end |  | ||||||
|  |  | ||||||
|   def resource_params |  | ||||||
|     params.require(:device) |  | ||||||
|     params.permit(device: [:account_id, :device_id, :type, :body, :hmac]) |  | ||||||
|   end |  | ||||||
|  |  | ||||||
|   def devices |  | ||||||
|     Array(resource_params[:device]) |  | ||||||
|   end |  | ||||||
| end |  | ||||||
| @@ -1,59 +0,0 @@ | |||||||
| # frozen_string_literal: true |  | ||||||
|  |  | ||||||
| class Api::V1::Crypto::EncryptedMessagesController < Api::BaseController |  | ||||||
|   LIMIT = 80 |  | ||||||
|  |  | ||||||
|   before_action -> { doorkeeper_authorize! :crypto } |  | ||||||
|   before_action :require_user! |  | ||||||
|   before_action :set_current_device |  | ||||||
|  |  | ||||||
|   before_action :set_encrypted_messages,    only: :index |  | ||||||
|   after_action  :insert_pagination_headers, only: :index |  | ||||||
|  |  | ||||||
|   def index |  | ||||||
|     render json: @encrypted_messages, each_serializer: REST::EncryptedMessageSerializer |  | ||||||
|   end |  | ||||||
|  |  | ||||||
|   def clear |  | ||||||
|     @current_device.encrypted_messages.up_to(params[:up_to_id]).delete_all |  | ||||||
|     render_empty |  | ||||||
|   end |  | ||||||
|  |  | ||||||
|   private |  | ||||||
|  |  | ||||||
|   def set_current_device |  | ||||||
|     @current_device = Device.find_by!(access_token: doorkeeper_token) |  | ||||||
|   end |  | ||||||
|  |  | ||||||
|   def set_encrypted_messages |  | ||||||
|     @encrypted_messages = @current_device.encrypted_messages.to_a_paginated_by_id(limit_param(LIMIT), params_slice(:max_id, :since_id, :min_id)) |  | ||||||
|   end |  | ||||||
|  |  | ||||||
|   def insert_pagination_headers |  | ||||||
|     set_pagination_headers(next_path, prev_path) |  | ||||||
|   end |  | ||||||
|  |  | ||||||
|   def next_path |  | ||||||
|     api_v1_crypto_encrypted_messages_url pagination_params(max_id: pagination_max_id) if records_continue? |  | ||||||
|   end |  | ||||||
|  |  | ||||||
|   def prev_path |  | ||||||
|     api_v1_crypto_encrypted_messages_url pagination_params(min_id: pagination_since_id) unless @encrypted_messages.empty? |  | ||||||
|   end |  | ||||||
|  |  | ||||||
|   def pagination_max_id |  | ||||||
|     @encrypted_messages.last.id |  | ||||||
|   end |  | ||||||
|  |  | ||||||
|   def pagination_since_id |  | ||||||
|     @encrypted_messages.first.id |  | ||||||
|   end |  | ||||||
|  |  | ||||||
|   def records_continue? |  | ||||||
|     @encrypted_messages.size == limit_param(LIMIT) |  | ||||||
|   end |  | ||||||
|  |  | ||||||
|   def pagination_params(core_params) |  | ||||||
|     params.slice(:limit).permit(:limit).merge(core_params) |  | ||||||
|   end |  | ||||||
| end |  | ||||||
| @@ -1,25 +0,0 @@ | |||||||
| # frozen_string_literal: true |  | ||||||
|  |  | ||||||
| class Api::V1::Crypto::Keys::ClaimsController < Api::BaseController |  | ||||||
|   before_action -> { doorkeeper_authorize! :crypto } |  | ||||||
|   before_action :require_user! |  | ||||||
|   before_action :set_claim_results |  | ||||||
|  |  | ||||||
|   def create |  | ||||||
|     render json: @claim_results, each_serializer: REST::Keys::ClaimResultSerializer |  | ||||||
|   end |  | ||||||
|  |  | ||||||
|   private |  | ||||||
|  |  | ||||||
|   def set_claim_results |  | ||||||
|     @claim_results = devices.filter_map { |device_params| ::Keys::ClaimService.new.call(current_account, device_params[:account_id], device_params[:device_id]) } |  | ||||||
|   end |  | ||||||
|  |  | ||||||
|   def resource_params |  | ||||||
|     params.permit(device: [:account_id, :device_id]) |  | ||||||
|   end |  | ||||||
|  |  | ||||||
|   def devices |  | ||||||
|     Array(resource_params[:device]) |  | ||||||
|   end |  | ||||||
| end |  | ||||||
| @@ -1,17 +0,0 @@ | |||||||
| # frozen_string_literal: true |  | ||||||
|  |  | ||||||
| class Api::V1::Crypto::Keys::CountsController < Api::BaseController |  | ||||||
|   before_action -> { doorkeeper_authorize! :crypto } |  | ||||||
|   before_action :require_user! |  | ||||||
|   before_action :set_current_device |  | ||||||
|  |  | ||||||
|   def show |  | ||||||
|     render json: { one_time_keys: @current_device.one_time_keys.count } |  | ||||||
|   end |  | ||||||
|  |  | ||||||
|   private |  | ||||||
|  |  | ||||||
|   def set_current_device |  | ||||||
|     @current_device = Device.find_by!(access_token: doorkeeper_token) |  | ||||||
|   end |  | ||||||
| end |  | ||||||
| @@ -1,26 +0,0 @@ | |||||||
| # frozen_string_literal: true |  | ||||||
|  |  | ||||||
| class Api::V1::Crypto::Keys::QueriesController < Api::BaseController |  | ||||||
|   before_action -> { doorkeeper_authorize! :crypto } |  | ||||||
|   before_action :require_user! |  | ||||||
|   before_action :set_accounts |  | ||||||
|   before_action :set_query_results |  | ||||||
|  |  | ||||||
|   def create |  | ||||||
|     render json: @query_results, each_serializer: REST::Keys::QueryResultSerializer |  | ||||||
|   end |  | ||||||
|  |  | ||||||
|   private |  | ||||||
|  |  | ||||||
|   def set_accounts |  | ||||||
|     @accounts = Account.where(id: account_ids).includes(:devices) |  | ||||||
|   end |  | ||||||
|  |  | ||||||
|   def set_query_results |  | ||||||
|     @query_results = @accounts.filter_map { |account| ::Keys::QueryService.new.call(account) } |  | ||||||
|   end |  | ||||||
|  |  | ||||||
|   def account_ids |  | ||||||
|     Array(params[:id]).map(&:to_i) |  | ||||||
|   end |  | ||||||
| end |  | ||||||
| @@ -1,29 +0,0 @@ | |||||||
| # frozen_string_literal: true |  | ||||||
|  |  | ||||||
| class Api::V1::Crypto::Keys::UploadsController < Api::BaseController |  | ||||||
|   before_action -> { doorkeeper_authorize! :crypto } |  | ||||||
|   before_action :require_user! |  | ||||||
|  |  | ||||||
|   def create |  | ||||||
|     device = Device.find_or_initialize_by(access_token: doorkeeper_token) |  | ||||||
|  |  | ||||||
|     device.transaction do |  | ||||||
|       device.account = current_account |  | ||||||
|       device.update!(resource_params[:device]) |  | ||||||
|  |  | ||||||
|       if resource_params[:one_time_keys].present? && resource_params[:one_time_keys].is_a?(Enumerable) |  | ||||||
|         resource_params[:one_time_keys].each do |one_time_key_params| |  | ||||||
|           device.one_time_keys.create!(one_time_key_params) |  | ||||||
|         end |  | ||||||
|       end |  | ||||||
|     end |  | ||||||
|  |  | ||||||
|     render json: device, serializer: REST::Keys::DeviceSerializer |  | ||||||
|   end |  | ||||||
|  |  | ||||||
|   private |  | ||||||
|  |  | ||||||
|   def resource_params |  | ||||||
|     params.permit(device: [:device_id, :name, :fingerprint_key, :identity_key], one_time_keys: [:key_id, :key, :signature]) |  | ||||||
|   end |  | ||||||
| end |  | ||||||
| @@ -1,24 +0,0 @@ | |||||||
| # frozen_string_literal: true |  | ||||||
|  |  | ||||||
| class Api::V1::Emails::ConfirmationsController < Api::BaseController |  | ||||||
|   before_action :doorkeeper_authorize! |  | ||||||
|   before_action :require_user_owned_by_application! |  | ||||||
|   before_action :require_user_not_confirmed! |  | ||||||
|  |  | ||||||
|   def create |  | ||||||
|     current_user.update!(email: params[:email]) if params.key?(:email) |  | ||||||
|     current_user.resend_confirmation_instructions |  | ||||||
|  |  | ||||||
|     render_empty |  | ||||||
|   end |  | ||||||
|  |  | ||||||
|   private |  | ||||||
|  |  | ||||||
|   def require_user_owned_by_application! |  | ||||||
|     render json: { error: 'This method is only available to the application the user originally signed-up with' }, status: :forbidden unless current_user && current_user.created_by_application_id == doorkeeper_token.application_id |  | ||||||
|   end |  | ||||||
|  |  | ||||||
|   def require_user_not_confirmed! |  | ||||||
|     render json: { error: 'This method is only available while the e-mail is awaiting confirmation' }, status: :forbidden if current_user.confirmed? || current_user.unconfirmed_email.blank? |  | ||||||
|   end |  | ||||||
| end |  | ||||||
| @@ -25,7 +25,7 @@ class Api::V1::EndorsementsController < Api::BaseController | |||||||
|   end |   end | ||||||
|  |  | ||||||
|   def endorsed_accounts |   def endorsed_accounts | ||||||
|     current_account.endorsed_accounts.includes(:account_stat).without_suspended |     current_account.endorsed_accounts.includes(:account_stat) | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   def insert_pagination_headers |   def insert_pagination_headers | ||||||
|   | |||||||
| @@ -17,11 +17,14 @@ class Api::V1::FavouritesController < Api::BaseController | |||||||
|   end |   end | ||||||
|  |  | ||||||
|   def cached_favourites |   def cached_favourites | ||||||
|     cache_collection(results.map(&:status), Status) |     cache_collection( | ||||||
|  |       Status.reorder(nil).joins(:favourites).merge(results), | ||||||
|  |       Status | ||||||
|  |     ) | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   def results |   def results | ||||||
|     @_results ||= account_favourites.eager_load(:status).to_a_paginated_by_id( |     @_results ||= account_favourites.paginate_by_id( | ||||||
|       limit_param(DEFAULT_STATUSES_LIMIT), |       limit_param(DEFAULT_STATUSES_LIMIT), | ||||||
|       params_slice(:max_id, :since_id, :min_id) |       params_slice(:max_id, :since_id, :min_id) | ||||||
|     ) |     ) | ||||||
|   | |||||||
| @@ -3,15 +3,15 @@ | |||||||
| class Api::V1::FeaturedTags::SuggestionsController < Api::BaseController | class Api::V1::FeaturedTags::SuggestionsController < Api::BaseController | ||||||
|   before_action -> { doorkeeper_authorize! :read, :'read:accounts' }, only: :index |   before_action -> { doorkeeper_authorize! :read, :'read:accounts' }, only: :index | ||||||
|   before_action :require_user! |   before_action :require_user! | ||||||
|   before_action :set_recently_used_tags, only: :index |   before_action :set_most_used_tags, only: :index | ||||||
|  |  | ||||||
|   def index |   def index | ||||||
|     render json: @recently_used_tags, each_serializer: REST::TagSerializer |     render json: @most_used_tags, each_serializer: REST::TagSerializer | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   private |   private | ||||||
|  |  | ||||||
|   def set_recently_used_tags |   def set_most_used_tags | ||||||
|     @recently_used_tags = Tag.recently_used(current_account).where.not(id: current_account.featured_tags).limit(10) |     @most_used_tags = Tag.most_used(current_account).where.not(id: current_account.featured_tags).limit(10) | ||||||
|   end |   end | ||||||
| end | end | ||||||
|   | |||||||
| @@ -13,7 +13,7 @@ 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, 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 json: account, serializer: REST::RelationshipSerializer, relationships: relationships | ||||||
|   end |   end | ||||||
|  |  | ||||||
| @@ -29,7 +29,7 @@ class Api::V1::FollowRequestsController < Api::BaseController | |||||||
|   end |   end | ||||||
|  |  | ||||||
|   def relationships(**options) |   def relationships(**options) | ||||||
|     AccountRelationshipsPresenter.new([params[:id]], current_user.account_id, **options) |     AccountRelationshipsPresenter.new([params[:id]], current_user.account_id, options) | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   def load_accounts |   def load_accounts | ||||||
| @@ -37,7 +37,7 @@ class Api::V1::FollowRequestsController < Api::BaseController | |||||||
|   end |   end | ||||||
|  |  | ||||||
|   def default_accounts |   def default_accounts | ||||||
|     Account.without_suspended.includes(:follow_requests, :account_stat).references(:follow_requests) |     Account.includes(:follow_requests, :account_stat).references(:follow_requests) | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   def paginated_follow_requests |   def paginated_follow_requests | ||||||
|   | |||||||
| @@ -8,7 +8,7 @@ class Api::V1::Instances::PeersController < Api::BaseController | |||||||
|  |  | ||||||
|   def index |   def index | ||||||
|     expires_in 1.day, public: true |     expires_in 1.day, public: true | ||||||
|     render_with_cache(expires_in: 1.day) { Instance.where.not(domain: DomainBlock.select(:domain)).pluck(:domain) } |     render_with_cache(expires_in: 1.day) { Account.remote.domains } | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   private |   private | ||||||
|   | |||||||
| @@ -1,17 +0,0 @@ | |||||||
| # frozen_string_literal: true |  | ||||||
|  |  | ||||||
| class Api::V1::Instances::RulesController < Api::BaseController |  | ||||||
|   skip_before_action :require_authenticated_user!, unless: :whitelist_mode? |  | ||||||
|  |  | ||||||
|   before_action :set_rules |  | ||||||
|  |  | ||||||
|   def index |  | ||||||
|     render json: @rules, each_serializer: REST::RuleSerializer |  | ||||||
|   end |  | ||||||
|  |  | ||||||
|   private |  | ||||||
|  |  | ||||||
|   def set_rules |  | ||||||
|     @rules = Rule.ordered |  | ||||||
|   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.without_suspended.includes(:account_stat).all |       @list.accounts.includes(:account_stat).all | ||||||
|     else |     else | ||||||
|       @list.accounts.without_suspended.includes(:account_stat).paginate_by_max_id(limit_param(DEFAULT_ACCOUNTS_LIMIT), params[:max_id], params[:since_id]) |       @list.accounts.includes(:account_stat).paginate_by_max_id(limit_param(DEFAULT_ACCOUNTS_LIMIT), params[:max_id], params[:since_id]) | ||||||
|     end |     end | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   | |||||||
| @@ -38,6 +38,6 @@ class Api::V1::ListsController < Api::BaseController | |||||||
|   end |   end | ||||||
|  |  | ||||||
|   def list_params |   def list_params | ||||||
|     params.permit(:title, :replies_policy) |     params.permit(:title) | ||||||
|   end |   end | ||||||
| end | end | ||||||
|   | |||||||
| @@ -7,7 +7,7 @@ class Api::V1::MarkersController < Api::BaseController | |||||||
|   before_action :require_user! |   before_action :require_user! | ||||||
|  |  | ||||||
|   def index |   def index | ||||||
|     @markers = current_user.markers.where(timeline: Array(params[:timeline])).index_by(&:timeline) |     @markers = current_user.markers.where(timeline: Array(params[:timeline])).each_with_object({}) { |marker, h| h[marker.timeline] = marker } | ||||||
|     render json: serialize_map(@markers) |     render json: serialize_map(@markers) | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   | |||||||
| @@ -39,7 +39,7 @@ class Api::V1::MediaController < Api::BaseController | |||||||
|   end |   end | ||||||
|  |  | ||||||
|   def media_attachment_params |   def media_attachment_params | ||||||
|     params.permit(:file, :thumbnail, :description, :focus) |     params.permit(:file, :description, :focus) | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   def file_type_error |   def file_type_error | ||||||
|   | |||||||
| @@ -7,7 +7,7 @@ class Api::V1::MutesController < Api::BaseController | |||||||
|  |  | ||||||
|   def index |   def index | ||||||
|     @accounts = load_accounts |     @accounts = load_accounts | ||||||
|     render json: @accounts, each_serializer: REST::MutedAccountSerializer |     render json: @accounts, each_serializer: REST::AccountSerializer | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   private |   private | ||||||
| @@ -18,8 +18,6 @@ class Api::V1::MutesController < Api::BaseController | |||||||
|  |  | ||||||
|   def paginated_mutes |   def paginated_mutes | ||||||
|     @paginated_mutes ||= Mute.eager_load(:target_account) |     @paginated_mutes ||= Mute.eager_load(:target_account) | ||||||
|                              .joins(:target_account) |  | ||||||
|                              .merge(Account.without_suspended) |  | ||||||
|                              .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), | ||||||
|   | |||||||
| @@ -14,7 +14,7 @@ class Api::V1::NotificationsController < Api::BaseController | |||||||
|   end |   end | ||||||
|  |  | ||||||
|   def show |   def show | ||||||
|     @notification = current_account.notifications.without_suspended.find(params[:id]) |     @notification = current_account.notifications.find(params[:id]) | ||||||
|     render json: @notification, serializer: REST::NotificationSerializer |     render json: @notification, serializer: REST::NotificationSerializer | ||||||
|   end |   end | ||||||
|  |  | ||||||
| @@ -31,17 +31,18 @@ class Api::V1::NotificationsController < Api::BaseController | |||||||
|   private |   private | ||||||
|  |  | ||||||
|   def load_notifications |   def load_notifications | ||||||
|     notifications = browserable_account_notifications.includes(from_account: :account_stat).to_a_paginated_by_id( |     cache_collection paginated_notifications, Notification | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   def paginated_notifications | ||||||
|  |     browserable_account_notifications.paginate_by_id( | ||||||
|       limit_param(DEFAULT_NOTIFICATIONS_LIMIT), |       limit_param(DEFAULT_NOTIFICATIONS_LIMIT), | ||||||
|       params_slice(:max_id, :since_id, :min_id) |       params_slice(:max_id, :since_id, :min_id) | ||||||
|     ) |     ) | ||||||
|     Notification.preload_cache_collection_target_statuses(notifications) do |target_statuses| |  | ||||||
|       cache_collection(target_statuses, Status) |  | ||||||
|     end |  | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   def browserable_account_notifications |   def browserable_account_notifications | ||||||
|     current_account.notifications.without_suspended.browserable(exclude_types, from_account) |     current_account.notifications.browserable(exclude_types, from_account) | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   def target_statuses_from_notifications |   def target_statuses_from_notifications | ||||||
|   | |||||||
| @@ -3,13 +3,13 @@ | |||||||
| class Api::V1::Push::SubscriptionsController < Api::BaseController | class Api::V1::Push::SubscriptionsController < Api::BaseController | ||||||
|   before_action -> { doorkeeper_authorize! :push } |   before_action -> { doorkeeper_authorize! :push } | ||||||
|   before_action :require_user! |   before_action :require_user! | ||||||
|   before_action :set_push_subscription |   before_action :set_web_push_subscription | ||||||
|   before_action :check_push_subscription, only: [:show, :update] |   before_action :check_web_push_subscription, only: [:show, :update] | ||||||
|  |  | ||||||
|   def create |   def create | ||||||
|     @push_subscription&.destroy! |     @web_subscription&.destroy! | ||||||
|  |  | ||||||
|     @push_subscription = Web::PushSubscription.create!( |     @web_subscription = ::Web::PushSubscription.create!( | ||||||
|       endpoint: subscription_params[:endpoint], |       endpoint: subscription_params[:endpoint], | ||||||
|       key_p256dh: subscription_params[:keys][:p256dh], |       key_p256dh: subscription_params[:keys][:p256dh], | ||||||
|       key_auth: subscription_params[:keys][:auth], |       key_auth: subscription_params[:keys][:auth], | ||||||
| @@ -18,31 +18,31 @@ class Api::V1::Push::SubscriptionsController < Api::BaseController | |||||||
|       access_token_id: doorkeeper_token.id |       access_token_id: doorkeeper_token.id | ||||||
|     ) |     ) | ||||||
|  |  | ||||||
|     render json: @push_subscription, serializer: REST::WebPushSubscriptionSerializer |     render json: @web_subscription, serializer: REST::WebPushSubscriptionSerializer | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   def show |   def show | ||||||
|     render json: @push_subscription, serializer: REST::WebPushSubscriptionSerializer |     render json: @web_subscription, serializer: REST::WebPushSubscriptionSerializer | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   def update |   def update | ||||||
|     @push_subscription.update!(data: data_params) |     @web_subscription.update!(data: data_params) | ||||||
|     render json: @push_subscription, serializer: REST::WebPushSubscriptionSerializer |     render json: @web_subscription, serializer: REST::WebPushSubscriptionSerializer | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   def destroy |   def destroy | ||||||
|     @push_subscription&.destroy! |     @web_subscription&.destroy! | ||||||
|     render_empty |     render_empty | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   private |   private | ||||||
|  |  | ||||||
|   def set_push_subscription |   def set_web_push_subscription | ||||||
|     @push_subscription = Web::PushSubscription.find_by(access_token_id: doorkeeper_token.id) |     @web_subscription = ::Web::PushSubscription.find_by(access_token_id: doorkeeper_token.id) | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   def check_push_subscription |   def check_web_push_subscription | ||||||
|     not_found if @push_subscription.nil? |     not_found if @web_subscription.nil? | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   def subscription_params |   def subscription_params | ||||||
| @@ -52,6 +52,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(:policy, alerts: [:follow, :follow_request, :favourite, :reblog, :mention, :poll, :status]) |     params.require(:data).permit(alerts: [:follow, :follow_request, :favourite, :reblog, :mention, :poll]) | ||||||
|   end |   end | ||||||
| end | end | ||||||
|   | |||||||
| @@ -32,7 +32,7 @@ class Api::V1::ScheduledStatusesController < Api::BaseController | |||||||
|   private |   private | ||||||
|  |  | ||||||
|   def set_statuses |   def set_statuses | ||||||
|     @statuses = current_account.scheduled_statuses.to_a_paginated_by_id(limit_param(DEFAULT_STATUSES_LIMIT), params_slice(:max_id, :since_id, :min_id)) |     @statuses = current_account.scheduled_statuses.paginate_by_id(limit_param(DEFAULT_STATUSES_LIMIT), params_slice(:max_id, :since_id, :min_id)) | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   def set_status |   def set_status | ||||||
|   | |||||||
| @@ -5,7 +5,7 @@ class Api::V1::Statuses::BookmarksController < Api::BaseController | |||||||
|  |  | ||||||
|   before_action -> { doorkeeper_authorize! :write, :'write:bookmarks' } |   before_action -> { doorkeeper_authorize! :write, :'write:bookmarks' } | ||||||
|   before_action :require_user! |   before_action :require_user! | ||||||
|   before_action :set_status, only: [:create] |   before_action :set_status | ||||||
|  |  | ||||||
|   def create |   def create | ||||||
|     current_account.bookmarks.find_or_create_by!(account: current_account, status: @status) |     current_account.bookmarks.find_or_create_by!(account: current_account, status: @status) | ||||||
| @@ -13,20 +13,10 @@ class Api::V1::Statuses::BookmarksController < Api::BaseController | |||||||
|   end |   end | ||||||
|  |  | ||||||
|   def destroy |   def destroy | ||||||
|     bookmark = current_account.bookmarks.find_by(status_id: params[:status_id]) |     bookmark = current_account.bookmarks.find_by(status: @status) | ||||||
|  |  | ||||||
|     if bookmark |  | ||||||
|       @status = bookmark.status |  | ||||||
|     else |  | ||||||
|       @status = Status.find(params[:status_id]) |  | ||||||
|       authorize @status, :show? |  | ||||||
|     end |  | ||||||
|  |  | ||||||
|     bookmark&.destroy! |     bookmark&.destroy! | ||||||
|  |  | ||||||
|     render json: @status, serializer: REST::StatusSerializer, relationships: StatusRelationshipsPresenter.new([@status], current_account.id, bookmarks_map: { @status.id => false }) |     render json: @status, serializer: REST::StatusSerializer, relationships: StatusRelationshipsPresenter.new([@status], current_account.id, bookmarks_map: { @status.id => false }) | ||||||
|   rescue Mastodon::NotPermittedError |  | ||||||
|     not_found |  | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   private |   private | ||||||
|   | |||||||
| @@ -22,7 +22,6 @@ class Api::V1::Statuses::FavouritedByAccountsController < Api::BaseController | |||||||
|  |  | ||||||
|   def default_accounts |   def default_accounts | ||||||
|     Account |     Account | ||||||
|       .without_suspended |  | ||||||
|       .includes(:favourites, :account_stat) |       .includes(:favourites, :account_stat) | ||||||
|       .references(:favourites) |       .references(:favourites) | ||||||
|       .where(favourites: { status_id: @status.id }) |       .where(favourites: { status_id: @status.id }) | ||||||
|   | |||||||
| @@ -5,7 +5,7 @@ class Api::V1::Statuses::FavouritesController < Api::BaseController | |||||||
|  |  | ||||||
|   before_action -> { doorkeeper_authorize! :write, :'write:favourites' } |   before_action -> { doorkeeper_authorize! :write, :'write:favourites' } | ||||||
|   before_action :require_user! |   before_action :require_user! | ||||||
|   before_action :set_status, only: [:create] |   before_action :set_status | ||||||
|  |  | ||||||
|   def create |   def create | ||||||
|     FavouriteService.new.call(current_account, @status) |     FavouriteService.new.call(current_account, @status) | ||||||
| @@ -13,19 +13,8 @@ class Api::V1::Statuses::FavouritesController < Api::BaseController | |||||||
|   end |   end | ||||||
|  |  | ||||||
|   def destroy |   def destroy | ||||||
|     fav = current_account.favourites.find_by(status_id: params[:status_id]) |  | ||||||
|  |  | ||||||
|     if fav |  | ||||||
|       @status = fav.status |  | ||||||
|     UnfavouriteWorker.perform_async(current_account.id, @status.id) |     UnfavouriteWorker.perform_async(current_account.id, @status.id) | ||||||
|     else |  | ||||||
|       @status = Status.find(params[:status_id]) |  | ||||||
|       authorize @status, :show? |  | ||||||
|     end |  | ||||||
|  |  | ||||||
|     render json: @status, serializer: REST::StatusSerializer, relationships: StatusRelationshipsPresenter.new([@status], current_account.id, favourites_map: { @status.id => false }) |     render json: @status, serializer: REST::StatusSerializer, relationships: StatusRelationshipsPresenter.new([@status], current_account.id, favourites_map: { @status.id => false }) | ||||||
|   rescue Mastodon::NotPermittedError |  | ||||||
|     not_found |  | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   private |   private | ||||||
|   | |||||||
| @@ -21,7 +21,7 @@ class Api::V1::Statuses::RebloggedByAccountsController < Api::BaseController | |||||||
|   end |   end | ||||||
|  |  | ||||||
|   def default_accounts |   def default_accounts | ||||||
|     Account.without_suspended.includes(:statuses, :account_stat).references(:statuses) |     Account.includes(:statuses, :account_stat).references(:statuses) | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   def paginated_statuses |   def paginated_statuses | ||||||
|   | |||||||
| @@ -5,7 +5,7 @@ class Api::V1::Statuses::ReblogsController < Api::BaseController | |||||||
|  |  | ||||||
|   before_action -> { doorkeeper_authorize! :write, :'write:statuses' } |   before_action -> { doorkeeper_authorize! :write, :'write:statuses' } | ||||||
|   before_action :require_user! |   before_action :require_user! | ||||||
|   before_action :set_reblog, only: [:create] |   before_action :set_reblog | ||||||
|  |  | ||||||
|   override_rate_limit_headers :create, family: :statuses |   override_rate_limit_headers :create, family: :statuses | ||||||
|  |  | ||||||
| @@ -16,21 +16,15 @@ class Api::V1::Statuses::ReblogsController < Api::BaseController | |||||||
|   end |   end | ||||||
|  |  | ||||||
|   def destroy |   def destroy | ||||||
|     @status = current_account.statuses.find_by(reblog_of_id: params[:status_id]) |     @status = current_account.statuses.find_by(reblog_of_id: @reblog.id) | ||||||
|  |  | ||||||
|     if @status |     if @status | ||||||
|       authorize @status, :unreblog? |       authorize @status, :unreblog? | ||||||
|       @status.discard |       @status.discard | ||||||
|       RemovalWorker.perform_async(@status.id) |       RemovalWorker.perform_async(@status.id) | ||||||
|       @reblog = @status.reblog |  | ||||||
|     else |  | ||||||
|       @reblog = Status.find(params[:status_id]) |  | ||||||
|       authorize @reblog, :show? |  | ||||||
|     end |     end | ||||||
|  |  | ||||||
|     render json: @reblog, serializer: REST::StatusSerializer, relationships: StatusRelationshipsPresenter.new([@status], current_account.id, reblogs_map: { @reblog.id => false }) |     render json: @reblog, serializer: REST::StatusSerializer, relationships: StatusRelationshipsPresenter.new([@status], current_account.id, reblogs_map: { @reblog.id => false }) | ||||||
|   rescue Mastodon::NotPermittedError |  | ||||||
|     not_found |  | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   private |   private | ||||||
|   | |||||||
| @@ -57,7 +57,6 @@ class Api::V1::StatusesController < Api::BaseController | |||||||
|  |  | ||||||
|     @status.discard |     @status.discard | ||||||
|     RemovalWorker.perform_async(@status.id, redraft: true) |     RemovalWorker.perform_async(@status.id, redraft: true) | ||||||
|     @status.account.statuses_count = @status.account.statuses_count - 1 |  | ||||||
|  |  | ||||||
|     render json: @status, serializer: REST::StatusSerializer, source_requested: true |     render json: @status, serializer: REST::StatusSerializer, source_requested: true | ||||||
|   end |   end | ||||||
|   | |||||||
| @@ -5,20 +5,20 @@ class Api::V1::SuggestionsController < Api::BaseController | |||||||
|  |  | ||||||
|   before_action -> { doorkeeper_authorize! :read } |   before_action -> { doorkeeper_authorize! :read } | ||||||
|   before_action :require_user! |   before_action :require_user! | ||||||
|  |   before_action :set_accounts | ||||||
|  |  | ||||||
|   def index |   def index | ||||||
|     suggestions = suggestions_source.get(current_account, limit: limit_param(DEFAULT_ACCOUNTS_LIMIT)) |     render json: @accounts, each_serializer: REST::AccountSerializer | ||||||
|     render json: suggestions.map(&:account), each_serializer: REST::AccountSerializer |  | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   def destroy |   def destroy | ||||||
|     suggestions_source.remove(current_account, params[:id]) |     PotentialFriendshipTracker.remove(current_account.id, params[:id]) | ||||||
|     render_empty |     render_empty | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   private |   private | ||||||
|  |  | ||||||
|   def suggestions_source |   def set_accounts | ||||||
|     AccountSuggestions::PastInteractionsSource.new |     @accounts = PotentialFriendshipTracker.get(current_account.id, limit: limit_param(DEFAULT_ACCOUNTS_LIMIT)) | ||||||
|   end |   end | ||||||
| end | end | ||||||
|   | |||||||
| @@ -16,29 +16,30 @@ class Api::V1::Timelines::PublicController < Api::BaseController | |||||||
|   end |   end | ||||||
|  |  | ||||||
|   def load_statuses |   def load_statuses | ||||||
|     cached_public_statuses_page |     cached_public_statuses | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   def cached_public_statuses_page |   def cached_public_statuses | ||||||
|     cache_collection(public_statuses, Status) |     cache_collection public_statuses, Status | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   def public_statuses |   def public_statuses | ||||||
|     public_feed.get( |     statuses = public_timeline_statuses.paginate_by_id( | ||||||
|       limit_param(DEFAULT_STATUSES_LIMIT), |       limit_param(DEFAULT_STATUSES_LIMIT), | ||||||
|       params[:max_id], |       params_slice(:max_id, :since_id, :min_id) | ||||||
|       params[:since_id], |  | ||||||
|       params[:min_id] |  | ||||||
|     ) |     ) | ||||||
|  |  | ||||||
|  |     if truthy_param?(:only_media) | ||||||
|  |       # `SELECT DISTINCT id, updated_at` is too slow, so pluck ids at first, and then select id, updated_at with ids. | ||||||
|  |       status_ids = statuses.joins(:media_attachments).distinct(:id).pluck(:id) | ||||||
|  |       statuses.where(id: status_ids) | ||||||
|  |     else | ||||||
|  |       statuses | ||||||
|  |     end | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   def public_feed |   def public_timeline_statuses | ||||||
|     PublicFeed.new( |     Status.as_public_timeline(current_account, truthy_param?(:remote) ? :remote : truthy_param?(:local)) | ||||||
|       current_account, |  | ||||||
|       local: truthy_param?(:local), |  | ||||||
|       remote: truthy_param?(:remote), |  | ||||||
|       only_media: truthy_param?(:only_media) |  | ||||||
|     ) |  | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   def insert_pagination_headers |   def insert_pagination_headers | ||||||
|   | |||||||
| @@ -20,29 +20,30 @@ class Api::V1::Timelines::TagController < Api::BaseController | |||||||
|   end |   end | ||||||
|  |  | ||||||
|   def cached_tagged_statuses |   def cached_tagged_statuses | ||||||
|     @tag.nil? ? [] : cache_collection(tag_timeline_statuses, Status) |     cache_collection tagged_statuses, Status | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   def tagged_statuses | ||||||
|  |     if @tag.nil? | ||||||
|  |       [] | ||||||
|  |     else | ||||||
|  |       statuses = tag_timeline_statuses.paginate_by_id( | ||||||
|  |         limit_param(DEFAULT_STATUSES_LIMIT), | ||||||
|  |         params_slice(:max_id, :since_id, :min_id) | ||||||
|  |       ) | ||||||
|  |  | ||||||
|  |       if truthy_param?(:only_media) | ||||||
|  |         # `SELECT DISTINCT id, updated_at` is too slow, so pluck ids at first, and then select id, updated_at with ids. | ||||||
|  |         status_ids = statuses.joins(:media_attachments).distinct(:id).pluck(:id) | ||||||
|  |         statuses.where(id: status_ids) | ||||||
|  |       else | ||||||
|  |         statuses | ||||||
|  |       end | ||||||
|  |     end | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   def tag_timeline_statuses |   def tag_timeline_statuses | ||||||
|     tag_feed.get( |     HashtagQueryService.new.call(@tag, params.slice(:any, :all, :none), current_account, truthy_param?(:local)) | ||||||
|       limit_param(DEFAULT_STATUSES_LIMIT), |  | ||||||
|       params[:max_id], |  | ||||||
|       params[:since_id], |  | ||||||
|       params[:min_id] |  | ||||||
|     ) |  | ||||||
|   end |  | ||||||
|  |  | ||||||
|   def tag_feed |  | ||||||
|     TagFeed.new( |  | ||||||
|       @tag, |  | ||||||
|       current_account, |  | ||||||
|       any: params[:any], |  | ||||||
|       all: params[:all], |  | ||||||
|       none: params[:none], |  | ||||||
|       local: truthy_param?(:local), |  | ||||||
|       remote: truthy_param?(:remote), |  | ||||||
|       only_media: truthy_param?(:only_media) |  | ||||||
|     ) |  | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   def insert_pagination_headers |   def insert_pagination_headers | ||||||
|   | |||||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user