Merge remote-tracking branch 'parent/main' into upstream-20250403

This commit is contained in:
KMY 2025-04-03 08:36:36 +09:00
commit 32f5604499
265 changed files with 6227 additions and 3383 deletions

View file

@ -0,0 +1,10 @@
# frozen_string_literal: true
module Fasp::Provider::DebugConcern
extend ActiveSupport::Concern
def perform_debug_call
Fasp::Request.new(self)
.post('/debug/v0/callback/logs', body: { hello: 'world' })
end
end

View file

@ -4,7 +4,7 @@ module Notification::Groups
extend ActiveSupport::Concern
# `set_group_key!` needs to be updated if this list changes
GROUPABLE_NOTIFICATION_TYPES = %i(favourite reblog follow emoji_reaction).freeze
GROUPABLE_NOTIFICATION_TYPES = %i(favourite reblog follow emoji_reaction admin.sign_up).freeze
MAXIMUM_GROUP_SPAN_HOURS = 12
included do
@ -17,7 +17,7 @@ module Notification::Groups
type_prefix = case type
when :favourite, :reblog, :emoji_reaction
[type, target_status&.id].join('-')
when :follow
when :follow, :'admin.sign_up'
type
else
raise NotImplementedError

7
app/models/fasp.rb Normal file
View file

@ -0,0 +1,7 @@
# frozen_string_literal: true
module Fasp
def self.table_name_prefix
'fasp_'
end
end

View file

@ -0,0 +1,10 @@
# frozen_string_literal: true
class Fasp::Capability
include ActiveModel::Model
include ActiveModel::Attributes
attribute :id, :string
attribute :version, :string
attribute :enabled, :boolean, default: false
end

View file

@ -0,0 +1,16 @@
# frozen_string_literal: true
# == Schema Information
#
# Table name: fasp_debug_callbacks
#
# id :bigint(8) not null, primary key
# ip :string not null
# request_body :text not null
# created_at :datetime not null
# updated_at :datetime not null
# fasp_provider_id :bigint(8) not null
#
class Fasp::DebugCallback < ApplicationRecord
belongs_to :fasp_provider, class_name: 'Fasp::Provider'
end

141
app/models/fasp/provider.rb Normal file
View file

@ -0,0 +1,141 @@
# frozen_string_literal: true
# == Schema Information
#
# Table name: fasp_providers
#
# id :bigint(8) not null, primary key
# base_url :string not null
# capabilities :jsonb not null
# confirmed :boolean default(FALSE), not null
# contact_email :string
# fediverse_account :string
# name :string not null
# privacy_policy :jsonb
# provider_public_key_pem :string not null
# remote_identifier :string not null
# server_private_key_pem :string not null
# sign_in_url :string
# created_at :datetime not null
# updated_at :datetime not null
#
class Fasp::Provider < ApplicationRecord
include DebugConcern
has_many :fasp_debug_callbacks, inverse_of: :fasp_provider, class_name: 'Fasp::DebugCallback', dependent: :delete_all
validates :name, presence: true
validates :base_url, presence: true, url: true
validates :provider_public_key_pem, presence: true
validates :remote_identifier, presence: true
before_create :create_keypair
after_commit :update_remote_capabilities
def capabilities
read_attribute(:capabilities).map do |attributes|
Fasp::Capability.new(attributes)
end
end
def capabilities_attributes=(attributes)
capability_objects = attributes.values.map { |a| Fasp::Capability.new(a) }
self[:capabilities] = capability_objects.map(&:attributes)
end
def enabled_capabilities
capabilities.select(&:enabled).map(&:id)
end
def capability?(capability_name)
return false unless confirmed?
capabilities.present? && capabilities.any? do |capability|
capability.id == capability_name
end
end
def capability_enabled?(capability_name)
return false unless confirmed?
capabilities.present? && capabilities.any? do |capability|
capability.id == capability_name && capability.enabled
end
end
def server_private_key
@server_private_key ||= OpenSSL::PKey.read(server_private_key_pem)
end
def server_public_key_base64
Base64.strict_encode64(server_private_key.raw_public_key)
end
def provider_public_key_base64=(string)
return if string.blank?
self.provider_public_key_pem =
OpenSSL::PKey.new_raw_public_key(
'ed25519',
Base64.strict_decode64(string)
).public_to_pem
end
def provider_public_key
@provider_public_key ||= OpenSSL::PKey.read(provider_public_key_pem)
end
def provider_public_key_raw
provider_public_key.raw_public_key
end
def provider_public_key_fingerprint
OpenSSL::Digest.base64digest('sha256', provider_public_key_raw)
end
def url(path)
base = base_url
base = base.chomp('/') if path.start_with?('/')
"#{base}#{path}"
end
def update_info!(confirm: false)
self.confirmed = true if confirm
provider_info = Fasp::Request.new(self).get('/provider_info')
assign_attributes(
privacy_policy: provider_info['privacyPolicy'],
capabilities: provider_info['capabilities'],
sign_in_url: provider_info['signInUrl'],
contact_email: provider_info['contactEmail'],
fediverse_account: provider_info['fediverseAccount']
)
save!
end
private
def create_keypair
self.server_private_key_pem ||=
OpenSSL::PKey.generate_key('ed25519').private_to_pem
end
def update_remote_capabilities
return unless saved_change_to_attribute?(:capabilities)
old, current = saved_change_to_attribute(:capabilities)
old ||= []
current.each do |capability|
update_remote_capability(capability) if capability.key?('enabled') && !old.include?(capability)
end
end
def update_remote_capability(capability)
version, = capability['version'].split('.')
path = "/capabilities/#{capability['id']}/#{version}/activation"
if capability['enabled']
Fasp::Request.new(self).post(path)
else
Fasp::Request.new(self).delete(path)
end
end
end

View file

@ -23,6 +23,8 @@
class Poll < ApplicationRecord
include Expireable
MAKE_FETCH_HAPPEN = 1.minute
belongs_to :account
belongs_to :status
@ -113,7 +115,7 @@ class Poll < ApplicationRecord
end
def time_passed_since_last_fetch?
last_fetched_at.nil? || last_fetched_at < 1.minute.ago
last_fetched_at.nil? || last_fetched_at < MAKE_FETCH_HAPPEN.ago
end
def show_totals_now?

View file

@ -507,7 +507,7 @@ class Status < ApplicationRecord
end
def bookmarks_map(status_ids, account_id)
Bookmark.select(:status_id).where(status_id: status_ids).where(account_id: account_id).map { |f| [f.status_id, true] }.to_h
Bookmark.select(:status_id).where(status_id: status_ids).where(account_id: account_id).to_h { |f| [f.status_id, true] }
end
def reblogs_map(status_ids, account_id)