Merge commit 'ab85f59c30
' into kb_migration
This commit is contained in:
commit
c8a0050660
30 changed files with 321 additions and 63 deletions
24
.github/workflows/build-image.yml
vendored
24
.github/workflows/build-image.yml
vendored
|
@ -12,6 +12,7 @@ on:
|
||||||
- Dockerfile
|
- Dockerfile
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
|
packages: write
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-image:
|
build-image:
|
||||||
|
@ -26,15 +27,28 @@ jobs:
|
||||||
- uses: hadolint/hadolint-action@v3.1.0
|
- uses: hadolint/hadolint-action@v3.1.0
|
||||||
- uses: docker/setup-qemu-action@v2
|
- uses: docker/setup-qemu-action@v2
|
||||||
- uses: docker/setup-buildx-action@v2
|
- uses: docker/setup-buildx-action@v2
|
||||||
- uses: docker/login-action@v2
|
|
||||||
|
- name: Log in to Docker Hub
|
||||||
|
uses: docker/login-action@v2
|
||||||
with:
|
with:
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
if: github.event_name != 'pull_request'
|
if: github.repository == 'mastodon/mastodon' && github.event_name != 'pull_request'
|
||||||
|
|
||||||
|
- name: Log in to the Github Container registry
|
||||||
|
uses: docker/login-action@v2
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
if: github.repository == 'mastodon/mastodon' && github.event_name != 'pull_request'
|
||||||
|
|
||||||
- uses: docker/metadata-action@v4
|
- uses: docker/metadata-action@v4
|
||||||
id: meta
|
id: meta
|
||||||
with:
|
with:
|
||||||
images: tootsuite/mastodon
|
images: |
|
||||||
|
tootsuite/mastodon
|
||||||
|
ghcr.io/mastodon/mastodon
|
||||||
flavor: |
|
flavor: |
|
||||||
latest=auto
|
latest=auto
|
||||||
tags: |
|
tags: |
|
||||||
|
@ -42,13 +56,15 @@ jobs:
|
||||||
type=pep440,pattern={{raw}}
|
type=pep440,pattern={{raw}}
|
||||||
type=pep440,pattern=v{{major}}.{{minor}}
|
type=pep440,pattern=v{{major}}.{{minor}}
|
||||||
type=ref,event=pr
|
type=ref,event=pr
|
||||||
|
|
||||||
- uses: docker/build-push-action@v4
|
- uses: docker/build-push-action@v4
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
platforms: linux/amd64,linux/arm64
|
platforms: linux/amd64,linux/arm64
|
||||||
provenance: false
|
provenance: false
|
||||||
builder: ${{ steps.buildx.outputs.name }}
|
builder: ${{ steps.buildx.outputs.name }}
|
||||||
push: ${{ github.event_name != 'pull_request' }}
|
push: ${{ github.repository == 'mastodon/mastodon' && github.event_name != 'pull_request' }}
|
||||||
tags: ${{ steps.meta.outputs.tags }}
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
cache-from: type=gha
|
cache-from: type=gha
|
||||||
cache-to: type=gha,mode=max
|
cache-to: type=gha,mode=max
|
||||||
|
|
51
CHANGELOG.md
51
CHANGELOG.md
|
@ -2,6 +2,57 @@
|
||||||
|
|
||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
|
## [4.1.1] - 2023-03-16
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Add redirection from paths with url-encoded `@` to their decoded form ([thijskh](https://github.com/mastodon/mastodon/pull/23593))
|
||||||
|
- Add `lang` attribute to native language names in language picker in Web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23749))
|
||||||
|
- Add headers to outgoing mails to avoid auto-replies ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23597))
|
||||||
|
- Add support for refreshing many accounts at once with `tootctl accounts refresh` ([9p4](https://github.com/mastodon/mastodon/pull/23304))
|
||||||
|
- Add confirmation modal when clicking to edit a post with a non-empty compose form ([PauloVilarinho](https://github.com/mastodon/mastodon/pull/23936))
|
||||||
|
- Add support for the HAproxy PROXY protocol through the `PROXY_PROTO_V1` environment variable ([CSDUMMI](https://github.com/mastodon/mastodon/pull/24064))
|
||||||
|
- Add `SENDFILE_HEADER` environment variable ([Gargron](https://github.com/mastodon/mastodon/pull/24123))
|
||||||
|
- Add cache headers to static files served through Rails ([Gargron](https://github.com/mastodon/mastodon/pull/24120))
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Increase contrast of upload progress bar background ([toolmantim](https://github.com/mastodon/mastodon/pull/23836))
|
||||||
|
- Change post auto-deletion throttling constants to better scale with server size ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23320))
|
||||||
|
- Change order of bookmark and favourite sidebar entries in single-column UI for consistency ([TerryGarcia](https://github.com/mastodon/mastodon/pull/23701))
|
||||||
|
- Change `ActivityPub::DeliveryWorker` retries to be spread out more ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/21956))
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Fix “Remove all followers from the selected domains” also removing follows and notifications ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23805))
|
||||||
|
- Fix streaming metrics format ([emilweth](https://github.com/mastodon/mastodon/pull/23519), [emilweth](https://github.com/mastodon/mastodon/pull/23520))
|
||||||
|
- Fix case-sensitive check for previously used hashtags in hashtag autocompletion ([deanveloper](https://github.com/mastodon/mastodon/pull/23526))
|
||||||
|
- Fix focus point of already-attached media not saving after edit ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23566))
|
||||||
|
- Fix sidebar behavior in settings/admin UI on mobile ([wxt2005](https://github.com/mastodon/mastodon/pull/23764))
|
||||||
|
- Fix inefficiency when searching accounts per username in admin interface ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23801))
|
||||||
|
- Fix duplicate “Publish” button on mobile ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23804))
|
||||||
|
- Fix server error when failing to follow back followers from `/relationships` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23787))
|
||||||
|
- Fix server error when attempting to display the edit history of a trendable post in the admin interface ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23574))
|
||||||
|
- Fix `tootctl accounts migrate` crashing because of a typo ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23567))
|
||||||
|
- Fix original account being unfollowed on migration before the follow request to the new account could be sent ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/21957))
|
||||||
|
- Fix the “Back” button in column headers sometimes leaving Mastodon ([c960657](https://github.com/mastodon/mastodon/pull/23953))
|
||||||
|
- Fix pgBouncer resetting application name on every transaction ([Gargron](https://github.com/mastodon/mastodon/pull/23958))
|
||||||
|
- Fix unconfirmed accounts being counted as active users ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23803))
|
||||||
|
- Fix `/api/v1/streaming` sub-paths not being redirected ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23988))
|
||||||
|
- Fix drag'n'drop upload area text that spans multiple lines not being centered ([vintprox](https://github.com/mastodon/mastodon/pull/24029))
|
||||||
|
- Fix sidekiq jobs not triggering Elasticsearch index updates ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/24046))
|
||||||
|
- Fix tags being unnecessarily stripped from plain-text short site description ([c960657](https://github.com/mastodon/mastodon/pull/23975))
|
||||||
|
- Fix HTML entities not being un-escaped in extracted plain-text from remote posts ([c960657](https://github.com/mastodon/mastodon/pull/24019))
|
||||||
|
- Fix dashboard crash on ElasticSearch server error ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23751))
|
||||||
|
- Fix incorrect post links in strikes when the account is remote ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23611))
|
||||||
|
- Fix misleading error code when receiving invalid WebAuthn credentials ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23568))
|
||||||
|
- Fix duplicate mails being sent when the SMTP server is too slow to close the connection ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23750))
|
||||||
|
|
||||||
|
### Security
|
||||||
|
|
||||||
|
- Change user backups to use expiring URLs for download when possible ([Gargron](https://github.com/mastodon/mastodon/pull/24136))
|
||||||
|
- Add warning for object storage misconfiguration ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/24137))
|
||||||
|
|
||||||
## [4.1.0] - 2023-02-10
|
## [4.1.0] - 2023-02-10
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
|
@ -8,12 +8,10 @@
|
||||||
[](https://github.com/mastodon/mastodon/actions/workflows/test-ruby.yml)
|
[](https://github.com/mastodon/mastodon/actions/workflows/test-ruby.yml)
|
||||||
[][code_climate]
|
[][code_climate]
|
||||||
[][crowdin]
|
[][crowdin]
|
||||||
[][docker]
|
|
||||||
|
|
||||||
[releases]: https://github.com/mastodon/mastodon/releases
|
[releases]: https://github.com/mastodon/mastodon/releases
|
||||||
[code_climate]: https://codeclimate.com/github/mastodon/mastodon
|
[code_climate]: https://codeclimate.com/github/mastodon/mastodon
|
||||||
[crowdin]: https://crowdin.com/project/mastodon
|
[crowdin]: https://crowdin.com/project/mastodon
|
||||||
[docker]: https://hub.docker.com/r/tootsuite/mastodon/
|
|
||||||
|
|
||||||
Mastodon is a **free, open-source social network server** based on ActivityPub where users can follow friends and discover new ones. On Mastodon, users can publish anything they want: links, pictures, text, video. All Mastodon servers are interoperable as a federated network (users on one server can seamlessly communicate with users from another one, including non-Mastodon software that implements ActivityPub!)
|
Mastodon is a **free, open-source social network server** based on ActivityPub where users can follow friends and discover new ones. On Mastodon, users can publish anything they want: links, pictures, text, video. All Mastodon servers are interoperable as a federated network (users on one server can seamlessly communicate with users from another one, including non-Mastodon software that implements ActivityPub!)
|
||||||
|
|
||||||
|
@ -30,6 +28,7 @@ Click below to **learn more** in a video:
|
||||||
- [View sponsors](https://joinmastodon.org/sponsors)
|
- [View sponsors](https://joinmastodon.org/sponsors)
|
||||||
- [Blog](https://blog.joinmastodon.org)
|
- [Blog](https://blog.joinmastodon.org)
|
||||||
- [Documentation](https://docs.joinmastodon.org)
|
- [Documentation](https://docs.joinmastodon.org)
|
||||||
|
- [Official Docker image](https://github.com/mastodon/mastodon/pkgs/container/mastodon)
|
||||||
- [Browse Mastodon servers](https://joinmastodon.org/communities)
|
- [Browse Mastodon servers](https://joinmastodon.org/communities)
|
||||||
- [Browse Mastodon apps](https://joinmastodon.org/apps)
|
- [Browse Mastodon apps](https://joinmastodon.org/apps)
|
||||||
|
|
||||||
|
|
27
app/controllers/backups_controller.rb
Normal file
27
app/controllers/backups_controller.rb
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class BackupsController < ApplicationController
|
||||||
|
include RoutingHelper
|
||||||
|
|
||||||
|
skip_before_action :require_functional!
|
||||||
|
|
||||||
|
before_action :authenticate_user!
|
||||||
|
before_action :set_backup
|
||||||
|
|
||||||
|
def download
|
||||||
|
case Paperclip::Attachment.default_options[:storage]
|
||||||
|
when :s3
|
||||||
|
redirect_to @backup.dump.expiring_url(10)
|
||||||
|
when :fog
|
||||||
|
redirect_to @backup.dump.expiring_url(Time.now.utc + 10)
|
||||||
|
when :filesystem
|
||||||
|
redirect_to full_asset_url(@backup.dump.url)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def set_backup
|
||||||
|
@backup = current_user.backups.find(params[:id])
|
||||||
|
end
|
||||||
|
end
|
|
@ -52,7 +52,7 @@ module Settings
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
flash[:error] = I18n.t('webauthn_credentials.create.error')
|
flash[:error] = I18n.t('webauthn_credentials.create.error')
|
||||||
status = :internal_server_error
|
status = :unprocessable_entity
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
flash[:error] = t('webauthn_credentials.create.error')
|
flash[:error] = t('webauthn_credentials.create.error')
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
class Admin::SystemCheck
|
class Admin::SystemCheck
|
||||||
ACTIVE_CHECKS = [
|
ACTIVE_CHECKS = [
|
||||||
|
Admin::SystemCheck::MediaPrivacyCheck,
|
||||||
Admin::SystemCheck::DatabaseSchemaCheck,
|
Admin::SystemCheck::DatabaseSchemaCheck,
|
||||||
Admin::SystemCheck::SidekiqProcessCheck,
|
Admin::SystemCheck::SidekiqProcessCheck,
|
||||||
Admin::SystemCheck::RulesCheck,
|
Admin::SystemCheck::RulesCheck,
|
||||||
|
|
|
@ -31,7 +31,7 @@ class Admin::SystemCheck::ElasticsearchCheck < Admin::SystemCheck::BaseCheck
|
||||||
def running_version
|
def running_version
|
||||||
@running_version ||= begin
|
@running_version ||= begin
|
||||||
Chewy.client.info['version']['number']
|
Chewy.client.info['version']['number']
|
||||||
rescue Faraday::ConnectionFailed
|
rescue Faraday::ConnectionFailed, Elasticsearch::Transport::Transport::Error
|
||||||
nil
|
nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
105
app/lib/admin/system_check/media_privacy_check.rb
Normal file
105
app/lib/admin/system_check/media_privacy_check.rb
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class Admin::SystemCheck::MediaPrivacyCheck < Admin::SystemCheck::BaseCheck
|
||||||
|
include RoutingHelper
|
||||||
|
|
||||||
|
def skip?
|
||||||
|
!current_user.can?(:view_devops)
|
||||||
|
end
|
||||||
|
|
||||||
|
def pass?
|
||||||
|
check_media_uploads!
|
||||||
|
@failure_message.nil?
|
||||||
|
end
|
||||||
|
|
||||||
|
def message
|
||||||
|
Admin::SystemCheck::Message.new(@failure_message, @failure_value, @failure_action, true)
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def check_media_uploads!
|
||||||
|
if Rails.configuration.x.use_s3
|
||||||
|
check_media_listing_inaccessible_s3!
|
||||||
|
else
|
||||||
|
check_media_listing_inaccessible!
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def check_media_listing_inaccessible!
|
||||||
|
full_url = full_asset_url(media_attachment.file.url(:original, false))
|
||||||
|
|
||||||
|
# Check if we can list the uploaded file. If true, that's an error
|
||||||
|
directory_url = Addressable::URI.parse(full_url)
|
||||||
|
directory_url.query = nil
|
||||||
|
filename = directory_url.path.gsub(%r{.*/}, '')
|
||||||
|
directory_url.path = directory_url.path.gsub(%r{/[^/]+\Z}, '/')
|
||||||
|
Request.new(:get, directory_url, allow_local: true).perform do |res|
|
||||||
|
if res.truncated_body&.include?(filename)
|
||||||
|
@failure_message = use_storage? ? :upload_check_privacy_error_object_storage : :upload_check_privacy_error
|
||||||
|
@failure_action = 'https://docs.joinmastodon.org/admin/optional/object-storage/#FS'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
rescue
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def check_media_listing_inaccessible_s3!
|
||||||
|
urls_to_check = []
|
||||||
|
paperclip_options = Paperclip::Attachment.default_options
|
||||||
|
s3_protocol = paperclip_options[:s3_protocol]
|
||||||
|
s3_host_alias = paperclip_options[:s3_host_alias]
|
||||||
|
s3_host_name = paperclip_options[:s3_host_name]
|
||||||
|
bucket_name = paperclip_options.dig(:s3_credentials, :bucket)
|
||||||
|
|
||||||
|
urls_to_check << "#{s3_protocol}://#{s3_host_alias}/" if s3_host_alias.present?
|
||||||
|
urls_to_check << "#{s3_protocol}://#{s3_host_name}/#{bucket_name}/"
|
||||||
|
urls_to_check.uniq.each do |full_url|
|
||||||
|
check_s3_listing!(full_url)
|
||||||
|
break if @failure_message.present?
|
||||||
|
end
|
||||||
|
rescue
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def check_s3_listing!(full_url)
|
||||||
|
bucket_url = Addressable::URI.parse(full_url)
|
||||||
|
bucket_url.path = bucket_url.path.delete_suffix(media_attachment.file.path(:original))
|
||||||
|
bucket_url.query = "max-keys=1&x-random=#{SecureRandom.hex(10)}"
|
||||||
|
Request.new(:get, bucket_url, allow_local: true).perform do |res|
|
||||||
|
if res.truncated_body&.include?('ListBucketResult')
|
||||||
|
@failure_message = :upload_check_privacy_error_object_storage
|
||||||
|
@failure_action = 'https://docs.joinmastodon.org/admin/optional/object-storage/#S3'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def media_attachment
|
||||||
|
@media_attachment ||= begin
|
||||||
|
attachment = Account.representative.media_attachments.first
|
||||||
|
if attachment.present?
|
||||||
|
attachment.touch # rubocop:disable Rails/SkipsModelValidations
|
||||||
|
attachment
|
||||||
|
else
|
||||||
|
create_test_attachment!
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def create_test_attachment!
|
||||||
|
Tempfile.create(%w(test-upload .jpg), binmode: true) do |tmp_file|
|
||||||
|
tmp_file.write(
|
||||||
|
Base64.decode64(
|
||||||
|
'/9j/4QAiRXhpZgAATU0AKgAAAAgAAQESAAMAAAABAAYAAAA' \
|
||||||
|
'AAAD/2wCEAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBA' \
|
||||||
|
'QEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQE' \
|
||||||
|
'BAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAf/AABEIAAEAAgMBEQACEQEDEQH/x' \
|
||||||
|
'ABKAAEAAAAAAAAAAAAAAAAAAAALEAEAAAAAAAAAAAAAAAAAAAAAAQEAAAAAAAAAAAAAAAA' \
|
||||||
|
'AAAAAEQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIRAxEAPwA/8H//2Q=='
|
||||||
|
)
|
||||||
|
)
|
||||||
|
tmp_file.flush
|
||||||
|
Account.representative.media_attachments.create!(file: tmp_file)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,11 +1,12 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class Admin::SystemCheck::Message
|
class Admin::SystemCheck::Message
|
||||||
attr_reader :key, :value, :action
|
attr_reader :key, :value, :action, :critical
|
||||||
|
|
||||||
def initialize(key, value = nil, action = nil)
|
def initialize(key, value = nil, action = nil, critical = false)
|
||||||
@key = key
|
@key = key
|
||||||
@value = value
|
@value = value
|
||||||
@action = action
|
@action = action
|
||||||
|
@critical = critical
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -20,4 +20,10 @@ class ApplicationMailer < ActionMailer::Base
|
||||||
headers['X-Auto-Response-Suppress'] = 'All'
|
headers['X-Auto-Response-Suppress'] = 'All'
|
||||||
headers['Auto-Submitted'] = 'auto-generated'
|
headers['Auto-Submitted'] = 'auto-generated'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def set_autoreply_headers!
|
||||||
|
headers['Precedence'] = 'list'
|
||||||
|
headers['X-Auto-Response-Suppress'] = 'All'
|
||||||
|
headers['Auto-Submitted'] = 'auto-generated'
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -18,6 +18,6 @@
|
||||||
class Backup < ApplicationRecord
|
class Backup < ApplicationRecord
|
||||||
belongs_to :user, inverse_of: :backups
|
belongs_to :user, inverse_of: :backups
|
||||||
|
|
||||||
has_attached_file :dump
|
has_attached_file :dump, s3_permissions: 'private'
|
||||||
validates_attachment_content_type :dump, content_type: /\Aapplication/
|
do_not_validate_attachment_file_type :dump
|
||||||
end
|
end
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
- unless @system_checks.empty?
|
- unless @system_checks.empty?
|
||||||
.flash-message-stack
|
.flash-message-stack
|
||||||
- @system_checks.each do |message|
|
- @system_checks.each do |message|
|
||||||
.flash-message.warning
|
.flash-message{ class: message.critical ? 'alert' : 'warning' }
|
||||||
= t("admin.system_checks.#{message.key}.message_html", value: message.value ? content_tag(:strong, message.value) : nil)
|
= t("admin.system_checks.#{message.key}.message_html", value: message.value ? content_tag(:strong, message.value) : nil)
|
||||||
- if message.action
|
- if message.action
|
||||||
= link_to t("admin.system_checks.#{message.key}.action"), message.action
|
= link_to t("admin.system_checks.#{message.key}.action"), message.action
|
||||||
|
|
|
@ -54,15 +54,15 @@
|
||||||
.strike-card__statuses-list__item
|
.strike-card__statuses-list__item
|
||||||
- if (status = status_map[status_id.to_i])
|
- if (status = status_map[status_id.to_i])
|
||||||
.one-liner
|
.one-liner
|
||||||
= link_to short_account_status_url(@report.target_account, status_id), class: 'emojify' do
|
.emojify= one_line_preview(status)
|
||||||
= one_line_preview(status)
|
|
||||||
|
|
||||||
- status.ordered_media_attachments.each do |media_attachment|
|
- status.ordered_media_attachments.each do |media_attachment|
|
||||||
%abbr{ title: media_attachment.description }
|
%abbr{ title: media_attachment.description }
|
||||||
= fa_icon 'link'
|
= fa_icon 'link'
|
||||||
= media_attachment.file_file_name
|
= media_attachment.file_file_name
|
||||||
.strike-card__statuses-list__item__meta
|
.strike-card__statuses-list__item__meta
|
||||||
%time.formatted{ datetime: status.created_at.iso8601, title: l(status.created_at) }= l(status.created_at)
|
= link_to ActivityPub::TagManager.instance.url_for(status), target: '_blank' do
|
||||||
|
%time.formatted{ datetime: status.created_at.iso8601, title: l(status.created_at) }= l(status.created_at)
|
||||||
- unless status.application.nil?
|
- unless status.application.nil?
|
||||||
·
|
·
|
||||||
= status.application.name
|
= status.application.name
|
||||||
|
|
16
app/views/application/_sidebar.html.haml
Normal file
16
app/views/application/_sidebar.html.haml
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
.hero-widget
|
||||||
|
.hero-widget__img
|
||||||
|
= image_tag @instance_presenter.thumbnail&.file&.url(:'@1x') || asset_pack_path('media/images/preview.png'), alt: @instance_presenter.title
|
||||||
|
|
||||||
|
.hero-widget__text
|
||||||
|
%p= @instance_presenter.description.presence || t('about.about_mastodon_html')
|
||||||
|
|
||||||
|
- if Setting.trends && !(user_signed_in? && !current_user.setting_trends)
|
||||||
|
- trends = Trends.tags.query.allowed.limit(3)
|
||||||
|
|
||||||
|
- unless trends.empty?
|
||||||
|
.endorsements-widget.trends-widget
|
||||||
|
%h4.emojify= t('footer.trending_now')
|
||||||
|
|
||||||
|
- trends.each do |tag|
|
||||||
|
= react_component :hashtag, hashtag: ActiveModelSerializers::SerializableResource.new(tag, serializer: REST::TagSerializer, scope: current_user, scope_name: :current_user).as_json
|
|
@ -50,15 +50,15 @@
|
||||||
.strike-card__statuses-list__item
|
.strike-card__statuses-list__item
|
||||||
- if (status = status_map[status_id.to_i])
|
- if (status = status_map[status_id.to_i])
|
||||||
.one-liner
|
.one-liner
|
||||||
= link_to short_account_status_url(@strike.target_account, status_id), class: 'emojify' do
|
.emojify= one_line_preview(status)
|
||||||
= one_line_preview(status)
|
|
||||||
|
|
||||||
- status.ordered_media_attachments.each do |media_attachment|
|
- status.ordered_media_attachments.each do |media_attachment|
|
||||||
%abbr{ title: media_attachment.description }
|
%abbr{ title: media_attachment.description }
|
||||||
= fa_icon 'link'
|
= fa_icon 'link'
|
||||||
= media_attachment.file_file_name
|
= media_attachment.file_file_name
|
||||||
.strike-card__statuses-list__item__meta
|
.strike-card__statuses-list__item__meta
|
||||||
%time.formatted{ datetime: status.created_at.iso8601, title: l(status.created_at) }= l(status.created_at)
|
= link_to ActivityPub::TagManager.instance.url_for(status), target: '_blank' do
|
||||||
|
%time.formatted{ datetime: status.created_at.iso8601, title: l(status.created_at) }= l(status.created_at)
|
||||||
- unless status.application.nil?
|
- unless status.application.nil?
|
||||||
·
|
·
|
||||||
= status.application.name
|
= status.application.name
|
||||||
|
|
|
@ -64,6 +64,6 @@
|
||||||
%td= l backup.created_at
|
%td= l backup.created_at
|
||||||
- if backup.processed?
|
- if backup.processed?
|
||||||
%td= number_to_human_size backup.dump_file_size
|
%td= number_to_human_size backup.dump_file_size
|
||||||
%td= table_link_to 'download', t('exports.archive_takeout.download'), backup.dump.url
|
%td= table_link_to 'download', t('exports.archive_takeout.download'), download_backup_url(backup)
|
||||||
- else
|
- else
|
||||||
%td{ colspan: 2 }= t('exports.archive_takeout.in_progress')
|
%td{ colspan: 2 }= t('exports.archive_takeout.in_progress')
|
||||||
|
|
|
@ -55,5 +55,5 @@
|
||||||
%tbody
|
%tbody
|
||||||
%tr
|
%tr
|
||||||
%td.button-primary
|
%td.button-primary
|
||||||
= link_to full_asset_url(@backup.dump.url) do
|
= link_to download_backup_url(@backup) do
|
||||||
%span= t 'exports.archive_takeout.download'
|
%span= t 'exports.archive_takeout.download'
|
||||||
|
|
|
@ -4,4 +4,4 @@
|
||||||
|
|
||||||
<%= t 'user_mailer.backup_ready.explanation' %>
|
<%= t 'user_mailer.backup_ready.explanation' %>
|
||||||
|
|
||||||
=> <%= full_asset_url(@backup.dump.url) %>
|
=> <%= download_backup_url(@backup) %>
|
||||||
|
|
|
@ -11,7 +11,7 @@ class ActivityPub::MigratedFollowDeliveryWorker < ActivityPub::DeliveryWorker
|
||||||
def unfollow_old_account!(old_target_account_id)
|
def unfollow_old_account!(old_target_account_id)
|
||||||
old_target_account = Account.find(old_target_account_id)
|
old_target_account = Account.find(old_target_account_id)
|
||||||
UnfollowService.new.call(@source_account, old_target_account, skip_unmerge: true)
|
UnfollowService.new.call(@source_account, old_target_account, skip_unmerge: true)
|
||||||
rescue
|
rescue StandardError
|
||||||
true
|
true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -35,6 +35,7 @@ require_relative '../lib/terrapin/multi_pipe_extensions'
|
||||||
require_relative '../lib/mastodon/snowflake'
|
require_relative '../lib/mastodon/snowflake'
|
||||||
require_relative '../lib/mastodon/version'
|
require_relative '../lib/mastodon/version'
|
||||||
require_relative '../lib/mastodon/rack_middleware'
|
require_relative '../lib/mastodon/rack_middleware'
|
||||||
|
require_relative '../lib/public_file_server_middleware'
|
||||||
require_relative '../lib/devise/two_factor_ldap_authenticatable'
|
require_relative '../lib/devise/two_factor_ldap_authenticatable'
|
||||||
require_relative '../lib/devise/two_factor_pam_authenticatable'
|
require_relative '../lib/devise/two_factor_pam_authenticatable'
|
||||||
require_relative '../lib/chewy/strategy/mastodon'
|
require_relative '../lib/chewy/strategy/mastodon'
|
||||||
|
@ -181,6 +182,10 @@ module Mastodon
|
||||||
config.active_job.queue_adapter = :sidekiq
|
config.active_job.queue_adapter = :sidekiq
|
||||||
config.action_mailer.deliver_later_queue_name = 'mailers'
|
config.action_mailer.deliver_later_queue_name = 'mailers'
|
||||||
|
|
||||||
|
# We use our own middleware for this
|
||||||
|
config.public_file_server.enabled = false
|
||||||
|
|
||||||
|
config.middleware.use PublicFileServerMiddleware if Rails.env.development? || ENV['RAILS_SERVE_STATIC_FILES'] == 'true'
|
||||||
config.middleware.use Rack::Attack
|
config.middleware.use Rack::Attack
|
||||||
config.middleware.use Mastodon::RackMiddleware
|
config.middleware.use Mastodon::RackMiddleware
|
||||||
|
|
||||||
|
|
|
@ -16,12 +16,7 @@ Rails.application.configure do
|
||||||
# Run rails dev:cache to toggle caching.
|
# Run rails dev:cache to toggle caching.
|
||||||
if Rails.root.join('tmp/caching-dev.txt').exist?
|
if Rails.root.join('tmp/caching-dev.txt').exist?
|
||||||
config.action_controller.perform_caching = true
|
config.action_controller.perform_caching = true
|
||||||
|
|
||||||
config.cache_store = :redis_cache_store, REDIS_CACHE_PARAMS
|
config.cache_store = :redis_cache_store, REDIS_CACHE_PARAMS
|
||||||
|
|
||||||
config.public_file_server.headers = {
|
|
||||||
'Cache-Control' => "public, max-age=#{2.days.to_i}",
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
config.action_controller.perform_caching = false
|
config.action_controller.perform_caching = false
|
||||||
|
|
||||||
|
|
|
@ -19,27 +19,16 @@ Rails.application.configure do
|
||||||
# or in config/master.key. This key is used to decrypt credentials (and other encrypted files).
|
# or in config/master.key. This key is used to decrypt credentials (and other encrypted files).
|
||||||
# config.require_master_key = true
|
# config.require_master_key = true
|
||||||
|
|
||||||
# Disable serving static files from the `/public` folder by default since
|
|
||||||
# Apache or NGINX already handles this.
|
|
||||||
config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present?
|
|
||||||
|
|
||||||
ActiveSupport::Logger.new(STDOUT).tap do |logger|
|
ActiveSupport::Logger.new(STDOUT).tap do |logger|
|
||||||
logger.formatter = config.log_formatter
|
logger.formatter = config.log_formatter
|
||||||
config.logger = ActiveSupport::TaggedLogging.new(logger)
|
config.logger = ActiveSupport::TaggedLogging.new(logger)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Compress JavaScripts and CSS.
|
|
||||||
# config.assets.js_compressor = Uglifier.new(mangle: false)
|
|
||||||
# config.assets.css_compressor = :sass
|
|
||||||
|
|
||||||
# Do not fallback to assets pipeline if a precompiled asset is missed.
|
# Do not fallback to assets pipeline if a precompiled asset is missed.
|
||||||
config.assets.compile = false
|
config.assets.compile = false
|
||||||
|
|
||||||
# `config.assets.precompile` and `config.assets.version` have moved to config/initializers/assets.rb
|
|
||||||
|
|
||||||
# Specifies the header that your server uses for sending files.
|
# Specifies the header that your server uses for sending files.
|
||||||
# config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache
|
config.action_dispatch.x_sendfile_header = ENV['SENDFILE_HEADER'] if ENV['SENDFILE_HEADER'].present?
|
||||||
config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX
|
|
||||||
|
|
||||||
# Allow to specify public IP of reverse proxy if it's needed
|
# Allow to specify public IP of reverse proxy if it's needed
|
||||||
config.action_dispatch.trusted_proxies = ENV['TRUSTED_PROXY_IP'].split(/(?:\s*,\s*|\s+)/).map { |item| IPAddr.new(item) } if ENV['TRUSTED_PROXY_IP'].present?
|
config.action_dispatch.trusted_proxies = ENV['TRUSTED_PROXY_IP'].split(/(?:\s*,\s*|\s+)/).map { |item| IPAddr.new(item) } if ENV['TRUSTED_PROXY_IP'].present?
|
||||||
|
@ -67,7 +56,7 @@ Rails.application.configure do
|
||||||
|
|
||||||
# Enable locale fallbacks for I18n (makes lookups for any locale fall back to
|
# Enable locale fallbacks for I18n (makes lookups for any locale fall back to
|
||||||
# English when a translation cannot be found).
|
# English when a translation cannot be found).
|
||||||
config.i18n.fallbacks = [:en]
|
config.i18n.fallbacks = true
|
||||||
|
|
||||||
# Send deprecation notices to registered listeners.
|
# Send deprecation notices to registered listeners.
|
||||||
config.active_support.deprecation = :notify
|
config.active_support.deprecation = :notify
|
||||||
|
|
|
@ -12,11 +12,6 @@ Rails.application.configure do
|
||||||
# preloads Rails for running tests, you may have to set it to true.
|
# preloads Rails for running tests, you may have to set it to true.
|
||||||
config.eager_load = false
|
config.eager_load = false
|
||||||
|
|
||||||
# Configure public file server for tests with Cache-Control for performance.
|
|
||||||
config.public_file_server.enabled = true
|
|
||||||
config.public_file_server.headers = {
|
|
||||||
'Cache-Control' => "public, max-age=#{1.hour.to_i}"
|
|
||||||
}
|
|
||||||
config.assets.digest = false
|
config.assets.digest = false
|
||||||
|
|
||||||
# Show full error reports and disable caching.
|
# Show full error reports and disable caching.
|
||||||
|
|
|
@ -824,6 +824,12 @@ en:
|
||||||
message_html: You haven't defined any server rules.
|
message_html: You haven't defined any server rules.
|
||||||
sidekiq_process_check:
|
sidekiq_process_check:
|
||||||
message_html: No Sidekiq process running for the %{value} queue(s). Please review your Sidekiq configuration
|
message_html: No Sidekiq process running for the %{value} queue(s). Please review your Sidekiq configuration
|
||||||
|
upload_check_privacy_error:
|
||||||
|
action: Check here for more information
|
||||||
|
message_html: "<strong>Your web server is misconfigured. The privacy of your users is at risk.</strong>"
|
||||||
|
upload_check_privacy_error_object_storage:
|
||||||
|
action: Check here for more information
|
||||||
|
message_html: "<strong>Your object storage is misconfigured. The privacy of your users is at risk.</strong>"
|
||||||
tags:
|
tags:
|
||||||
review: Review status
|
review: Review status
|
||||||
updated_msg: Hashtag settings updated successfully
|
updated_msg: Hashtag settings updated successfully
|
||||||
|
|
|
@ -22,3 +22,5 @@ on_worker_boot do
|
||||||
end
|
end
|
||||||
|
|
||||||
plugin :tmp_restart
|
plugin :tmp_restart
|
||||||
|
|
||||||
|
set_remote_address(proxy_protocol: :v1) if ENV['PROXY_PROTO_V1'] == 'true'
|
||||||
|
|
|
@ -221,6 +221,7 @@ Rails.application.routes.draw do
|
||||||
resource :statuses_cleanup, controller: :statuses_cleanup, only: [:show, :update]
|
resource :statuses_cleanup, controller: :statuses_cleanup, only: [:show, :update]
|
||||||
|
|
||||||
get '/media_proxy/:id/(*any)', to: 'media_proxy#show', as: :media_proxy, format: false
|
get '/media_proxy/:id/(*any)', to: 'media_proxy#show', as: :media_proxy, format: false
|
||||||
|
get '/backups/:id/download', to: 'backups#download', as: :download_backup, format: false
|
||||||
|
|
||||||
resource :authorize_interaction, only: [:show, :create]
|
resource :authorize_interaction, only: [:show, :create]
|
||||||
resource :share, only: [:show, :create]
|
resource :share, only: [:show, :create]
|
||||||
|
|
|
@ -56,7 +56,7 @@ services:
|
||||||
|
|
||||||
web:
|
web:
|
||||||
build: .
|
build: .
|
||||||
image: tootsuite/mastodon
|
image: ghcr.io/mastodon/mastodon
|
||||||
restart: always
|
restart: always
|
||||||
env_file: .env.production
|
env_file: .env.production
|
||||||
command: bash -c "rm -f /mastodon/tmp/pids/server.pid; bundle exec rails s -p 3000"
|
command: bash -c "rm -f /mastodon/tmp/pids/server.pid; bundle exec rails s -p 3000"
|
||||||
|
@ -77,7 +77,7 @@ services:
|
||||||
|
|
||||||
streaming:
|
streaming:
|
||||||
build: .
|
build: .
|
||||||
image: tootsuite/mastodon
|
image: ghcr.io/mastodon/mastodon
|
||||||
restart: always
|
restart: always
|
||||||
env_file: .env.production
|
env_file: .env.production
|
||||||
command: node ./streaming
|
command: node ./streaming
|
||||||
|
@ -95,7 +95,7 @@ services:
|
||||||
|
|
||||||
sidekiq:
|
sidekiq:
|
||||||
build: .
|
build: .
|
||||||
image: tootsuite/mastodon
|
image: ghcr.io/mastodon/mastodon
|
||||||
restart: always
|
restart: always
|
||||||
env_file: .env.production
|
env_file: .env.production
|
||||||
command: bundle exec sidekiq
|
command: bundle exec sidekiq
|
||||||
|
|
|
@ -13,7 +13,7 @@ module Mastodon
|
||||||
end
|
end
|
||||||
|
|
||||||
def patch
|
def patch
|
||||||
0
|
1
|
||||||
end
|
end
|
||||||
|
|
||||||
def flags
|
def flags
|
||||||
|
|
43
lib/public_file_server_middleware.rb
Normal file
43
lib/public_file_server_middleware.rb
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'action_dispatch/middleware/static'
|
||||||
|
|
||||||
|
class PublicFileServerMiddleware
|
||||||
|
SERVICE_WORKER_TTL = 7.days.to_i
|
||||||
|
CACHE_TTL = 28.days.to_i
|
||||||
|
|
||||||
|
def initialize(app)
|
||||||
|
@app = app
|
||||||
|
@file_handler = ActionDispatch::FileHandler.new(Rails.application.paths['public'].first)
|
||||||
|
end
|
||||||
|
|
||||||
|
def call(env)
|
||||||
|
file = @file_handler.attempt(env)
|
||||||
|
|
||||||
|
# If the request is not a static file, move on!
|
||||||
|
return @app.call(env) if file.nil?
|
||||||
|
|
||||||
|
status, headers, response = file
|
||||||
|
|
||||||
|
# Set cache headers on static files. Some paths require different cache headers
|
||||||
|
headers['Cache-Control'] = begin
|
||||||
|
request_path = env['REQUEST_PATH']
|
||||||
|
|
||||||
|
if request_path.start_with?('/sw.js')
|
||||||
|
"public, max-age=#{SERVICE_WORKER_TTL}, must-revalidate"
|
||||||
|
elsif request_path.start_with?(paperclip_root_url)
|
||||||
|
"public, max-age=#{CACHE_TTL}, immutable"
|
||||||
|
else
|
||||||
|
"public, max-age=#{CACHE_TTL}, must-revalidate"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
[status, headers, response]
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def paperclip_root_url
|
||||||
|
ENV.fetch('PAPERCLIP_ROOT_URL', '/system')
|
||||||
|
end
|
||||||
|
end
|
|
@ -248,7 +248,7 @@ describe Settings::TwoFactorAuthentication::WebauthnCredentialsController do
|
||||||
|
|
||||||
post :create, params: { credential: new_webauthn_credential, nickname: 'USB Key' }
|
post :create, params: { credential: new_webauthn_credential, nickname: 'USB Key' }
|
||||||
|
|
||||||
expect(response).to have_http_status(500)
|
expect(response).to have_http_status(422)
|
||||||
expect(flash[:error]).to be_present
|
expect(flash[:error]).to be_present
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -268,7 +268,7 @@ describe Settings::TwoFactorAuthentication::WebauthnCredentialsController do
|
||||||
|
|
||||||
post :create, params: { credential: new_webauthn_credential, nickname: nickname }
|
post :create, params: { credential: new_webauthn_credential, nickname: nickname }
|
||||||
|
|
||||||
expect(response).to have_http_status(500)
|
expect(response).to have_http_status(422)
|
||||||
expect(flash[:error]).to be_present
|
expect(flash[:error]).to be_present
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue