Gab Social. All are welcome.
This commit is contained in:
97
app/controllers/api/base_controller.rb
Normal file
97
app/controllers/api/base_controller.rb
Normal file
@@ -0,0 +1,97 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::BaseController < ApplicationController
|
||||
DEFAULT_STATUSES_LIMIT = 20
|
||||
DEFAULT_ACCOUNTS_LIMIT = 40
|
||||
|
||||
include RateLimitHeaders
|
||||
|
||||
skip_before_action :store_current_location
|
||||
skip_before_action :check_user_permissions
|
||||
|
||||
before_action :set_cache_headers
|
||||
|
||||
protect_from_forgery with: :null_session
|
||||
|
||||
rescue_from ActiveRecord::RecordInvalid, GabSocial::ValidationError do |e|
|
||||
render json: { error: e.to_s }, status: 422
|
||||
end
|
||||
|
||||
rescue_from ActiveRecord::RecordNotFound do
|
||||
render json: { error: 'Record not found' }, status: 404
|
||||
end
|
||||
|
||||
rescue_from HTTP::Error, GabSocial::UnexpectedResponseError do
|
||||
render json: { error: 'Remote data could not be fetched' }, status: 503
|
||||
end
|
||||
|
||||
rescue_from OpenSSL::SSL::SSLError do
|
||||
render json: { error: 'Remote SSL certificate could not be verified' }, status: 503
|
||||
end
|
||||
|
||||
rescue_from GabSocial::NotPermittedError do
|
||||
render json: { error: 'This action is not allowed' }, status: 403
|
||||
end
|
||||
|
||||
def doorkeeper_unauthorized_render_options(error: nil)
|
||||
{ json: { error: (error.try(:description) || 'Not authorized') } }
|
||||
end
|
||||
|
||||
def doorkeeper_forbidden_render_options(*)
|
||||
{ json: { error: 'This action is outside the authorized scopes' } }
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def set_pagination_headers(next_path = nil, prev_path = nil)
|
||||
links = []
|
||||
links << [next_path, [%w(rel next)]] if next_path
|
||||
links << [prev_path, [%w(rel prev)]] if prev_path
|
||||
response.headers['Link'] = LinkHeader.new(links) unless links.empty?
|
||||
end
|
||||
|
||||
def limit_param(default_limit)
|
||||
return default_limit unless params[:limit]
|
||||
[params[:limit].to_i.abs, default_limit * 2].min
|
||||
end
|
||||
|
||||
def params_slice(*keys)
|
||||
params.slice(*keys).permit(*keys)
|
||||
end
|
||||
|
||||
def current_resource_owner
|
||||
@current_user ||= User.find(doorkeeper_token.resource_owner_id) if doorkeeper_token
|
||||
end
|
||||
|
||||
def current_user
|
||||
current_resource_owner || super
|
||||
rescue ActiveRecord::RecordNotFound
|
||||
nil
|
||||
end
|
||||
|
||||
def require_user!
|
||||
if !current_user
|
||||
render json: { error: 'This method requires an authenticated user' }, status: 422
|
||||
elsif current_user.disabled?
|
||||
render json: { error: 'Your login is currently disabled' }, status: 403
|
||||
elsif !current_user.confirmed?
|
||||
render json: { error: 'Your login is missing a confirmed e-mail address' }, status: 403
|
||||
elsif !current_user.approved?
|
||||
render json: { error: 'Your login is currently pending approval' }, status: 403
|
||||
else
|
||||
set_user_activity
|
||||
end
|
||||
end
|
||||
|
||||
def render_empty
|
||||
render json: {}, status: 200
|
||||
end
|
||||
|
||||
def authorize_if_got_token!(*scopes)
|
||||
doorkeeper_authorize!(*scopes) if doorkeeper_token
|
||||
end
|
||||
|
||||
def set_cache_headers
|
||||
response.headers['Cache-Control'] = 'no-cache, no-store, max-age=0, must-revalidate'
|
||||
end
|
||||
end
|
||||
24
app/controllers/api/oembed_controller.rb
Normal file
24
app/controllers/api/oembed_controller.rb
Normal file
@@ -0,0 +1,24 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::OEmbedController < Api::BaseController
|
||||
respond_to :json
|
||||
|
||||
def show
|
||||
@status = status_finder.status
|
||||
render json: @status, serializer: OEmbedSerializer, width: maxwidth_or_default, height: maxheight_or_default
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def status_finder
|
||||
StatusFinder.new(params[:url])
|
||||
end
|
||||
|
||||
def maxwidth_or_default
|
||||
(params[:maxwidth].presence || 400).to_i
|
||||
end
|
||||
|
||||
def maxheight_or_default
|
||||
params[:maxheight].present? ? params[:maxheight].to_i : nil
|
||||
end
|
||||
end
|
||||
30
app/controllers/api/proofs_controller.rb
Normal file
30
app/controllers/api/proofs_controller.rb
Normal file
@@ -0,0 +1,30 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::ProofsController < Api::BaseController
|
||||
before_action :set_account
|
||||
before_action :set_provider
|
||||
before_action :check_account_approval
|
||||
before_action :check_account_suspension
|
||||
|
||||
def index
|
||||
render json: @account, serializer: @provider.serializer_class
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_provider
|
||||
@provider = ProofProvider.find(params[:provider]) || raise(ActiveRecord::RecordNotFound)
|
||||
end
|
||||
|
||||
def set_account
|
||||
@account = Account.find_local!(params[:username])
|
||||
end
|
||||
|
||||
def check_account_approval
|
||||
not_found if @account.user_pending?
|
||||
end
|
||||
|
||||
def check_account_suspension
|
||||
gone if @account.suspended?
|
||||
end
|
||||
end
|
||||
73
app/controllers/api/push_controller.rb
Normal file
73
app/controllers/api/push_controller.rb
Normal file
@@ -0,0 +1,73 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::PushController < Api::BaseController
|
||||
include SignatureVerification
|
||||
|
||||
def update
|
||||
response, status = process_push_request
|
||||
render plain: response, status: status
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def process_push_request
|
||||
case hub_mode
|
||||
when 'subscribe'
|
||||
Pubsubhubbub::SubscribeService.new.call(account_from_topic, hub_callback, hub_secret, hub_lease_seconds, verified_domain)
|
||||
when 'unsubscribe'
|
||||
Pubsubhubbub::UnsubscribeService.new.call(account_from_topic, hub_callback)
|
||||
else
|
||||
["Unknown mode: #{hub_mode}", 422]
|
||||
end
|
||||
end
|
||||
|
||||
def hub_mode
|
||||
params['hub.mode']
|
||||
end
|
||||
|
||||
def hub_topic
|
||||
params['hub.topic']
|
||||
end
|
||||
|
||||
def hub_callback
|
||||
params['hub.callback']
|
||||
end
|
||||
|
||||
def hub_lease_seconds
|
||||
params['hub.lease_seconds']
|
||||
end
|
||||
|
||||
def hub_secret
|
||||
params['hub.secret']
|
||||
end
|
||||
|
||||
def account_from_topic
|
||||
if hub_topic.present? && local_domain? && account_feed_path?
|
||||
Account.find_local(hub_topic_params[:username])
|
||||
end
|
||||
end
|
||||
|
||||
def hub_topic_params
|
||||
@_hub_topic_params ||= Rails.application.routes.recognize_path(hub_topic_uri.path)
|
||||
end
|
||||
|
||||
def hub_topic_uri
|
||||
@_hub_topic_uri ||= Addressable::URI.parse(hub_topic).normalize
|
||||
end
|
||||
|
||||
def local_domain?
|
||||
TagManager.instance.web_domain?(hub_topic_domain)
|
||||
end
|
||||
|
||||
def verified_domain
|
||||
return signed_request_account.domain if signed_request_account
|
||||
end
|
||||
|
||||
def hub_topic_domain
|
||||
hub_topic_uri.host + (hub_topic_uri.port ? ":#{hub_topic_uri.port}" : '')
|
||||
end
|
||||
|
||||
def account_feed_path?
|
||||
hub_topic_params[:controller] == 'accounts' && hub_topic_params[:action] == 'show' && hub_topic_params[:format] == 'atom'
|
||||
end
|
||||
end
|
||||
37
app/controllers/api/salmon_controller.rb
Normal file
37
app/controllers/api/salmon_controller.rb
Normal file
@@ -0,0 +1,37 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::SalmonController < Api::BaseController
|
||||
include SignatureVerification
|
||||
|
||||
before_action :set_account
|
||||
respond_to :txt
|
||||
|
||||
def update
|
||||
if verify_payload?
|
||||
process_salmon
|
||||
head 202
|
||||
elsif payload.present?
|
||||
render plain: signature_verification_failure_reason, status: 401
|
||||
else
|
||||
head 400
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_account
|
||||
@account = Account.find(params[:id])
|
||||
end
|
||||
|
||||
def payload
|
||||
@_payload ||= request.body.read
|
||||
end
|
||||
|
||||
def verify_payload?
|
||||
payload.present? && VerifySalmonService.new.call(payload)
|
||||
end
|
||||
|
||||
def process_salmon
|
||||
SalmonWorker.perform_async(@account.id, payload.force_encoding('UTF-8'))
|
||||
end
|
||||
end
|
||||
51
app/controllers/api/subscriptions_controller.rb
Normal file
51
app/controllers/api/subscriptions_controller.rb
Normal file
@@ -0,0 +1,51 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::SubscriptionsController < Api::BaseController
|
||||
before_action :set_account
|
||||
respond_to :txt
|
||||
|
||||
def show
|
||||
if subscription.valid?(params['hub.topic'])
|
||||
@account.update(subscription_expires_at: future_expires)
|
||||
render plain: encoded_challenge, status: 200
|
||||
else
|
||||
head 404
|
||||
end
|
||||
end
|
||||
|
||||
def update
|
||||
if subscription.verify(body, request.headers['HTTP_X_HUB_SIGNATURE'])
|
||||
ProcessingWorker.perform_async(@account.id, body.force_encoding('UTF-8'))
|
||||
end
|
||||
|
||||
head 200
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def subscription
|
||||
@_subscription ||= @account.subscription(
|
||||
api_subscription_url(@account.id)
|
||||
)
|
||||
end
|
||||
|
||||
def body
|
||||
@_body ||= request.body.read
|
||||
end
|
||||
|
||||
def encoded_challenge
|
||||
HTMLEntities.new.encode(params['hub.challenge'])
|
||||
end
|
||||
|
||||
def future_expires
|
||||
Time.now.utc + lease_seconds_or_default
|
||||
end
|
||||
|
||||
def lease_seconds_or_default
|
||||
(params['hub.lease_seconds'] || 1.day).to_i.seconds
|
||||
end
|
||||
|
||||
def set_account
|
||||
@account = Account.find(params[:id])
|
||||
end
|
||||
end
|
||||
20
app/controllers/api/v1/account_by_username_controller.rb
Normal file
20
app/controllers/api/v1/account_by_username_controller.rb
Normal file
@@ -0,0 +1,20 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::AccountByUsernameController < Api::BaseController
|
||||
before_action :set_account
|
||||
before_action :check_account_suspension
|
||||
|
||||
respond_to :json
|
||||
|
||||
def show
|
||||
render json: @account, serializer: REST::AccountSerializer
|
||||
end
|
||||
|
||||
def set_account
|
||||
@account = Account.find_local!(params[:username])
|
||||
end
|
||||
|
||||
def check_account_suspension
|
||||
gone if @account.suspended?
|
||||
end
|
||||
end
|
||||
38
app/controllers/api/v1/accounts/credentials_controller.rb
Normal file
38
app/controllers/api/v1/accounts/credentials_controller.rb
Normal file
@@ -0,0 +1,38 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::Accounts::CredentialsController < Api::BaseController
|
||||
before_action -> { doorkeeper_authorize! :read, :'read:accounts' }, except: [:update]
|
||||
before_action -> { doorkeeper_authorize! :write, :'write:accounts' }, only: [:update]
|
||||
before_action :require_user!
|
||||
|
||||
def show
|
||||
@account = current_account
|
||||
render json: @account, serializer: REST::CredentialAccountSerializer
|
||||
end
|
||||
|
||||
def update
|
||||
@account = current_account
|
||||
UpdateAccountService.new.call(@account, account_params, raise_error: true)
|
||||
UserSettingsDecorator.new(current_user).update(user_settings_params) if user_settings_params
|
||||
ActivityPub::UpdateDistributionWorker.perform_async(@account.id)
|
||||
render json: @account, serializer: REST::CredentialAccountSerializer
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def account_params
|
||||
params.permit(:display_name, :note, :avatar, :header, :locked, :bot, :discoverable, fields_attributes: [:name, :value])
|
||||
end
|
||||
|
||||
def user_settings_params
|
||||
return nil unless params.key?(:source)
|
||||
|
||||
source_params = params.require(:source)
|
||||
|
||||
{
|
||||
'setting_default_privacy' => source_params.fetch(:privacy, @account.user.setting_default_privacy),
|
||||
'setting_default_sensitive' => source_params.fetch(:sensitive, @account.user.setting_default_sensitive),
|
||||
'setting_default_language' => source_params.fetch(:language, @account.user.setting_default_language),
|
||||
}
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,74 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::Accounts::FollowerAccountsController < Api::BaseController
|
||||
before_action -> { doorkeeper_authorize! :read, :'read:accounts' }
|
||||
before_action :set_account
|
||||
after_action :insert_pagination_headers
|
||||
|
||||
respond_to :json
|
||||
|
||||
def index
|
||||
@accounts = load_accounts
|
||||
render json: @accounts, each_serializer: REST::AccountSerializer
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_account
|
||||
@account = Account.find(params[:account_id])
|
||||
end
|
||||
|
||||
def load_accounts
|
||||
return [] if hide_results?
|
||||
|
||||
default_accounts.merge(paginated_follows).to_a
|
||||
end
|
||||
|
||||
def hide_results?
|
||||
(@account.user_hides_network? && current_account.id != @account.id) || (current_account && @account.blocking?(current_account))
|
||||
end
|
||||
|
||||
def default_accounts
|
||||
Account.includes(:active_relationships, :account_stat).references(:active_relationships)
|
||||
end
|
||||
|
||||
def paginated_follows
|
||||
Follow.where(target_account: @account).paginate_by_max_id(
|
||||
limit_param(DEFAULT_ACCOUNTS_LIMIT),
|
||||
params[:max_id],
|
||||
params[:since_id]
|
||||
)
|
||||
end
|
||||
|
||||
def insert_pagination_headers
|
||||
set_pagination_headers(next_path, prev_path)
|
||||
end
|
||||
|
||||
def next_path
|
||||
if records_continue?
|
||||
api_v1_account_followers_url pagination_params(max_id: pagination_max_id)
|
||||
end
|
||||
end
|
||||
|
||||
def prev_path
|
||||
unless @accounts.empty?
|
||||
api_v1_account_followers_url pagination_params(since_id: pagination_since_id)
|
||||
end
|
||||
end
|
||||
|
||||
def pagination_max_id
|
||||
@accounts.last.active_relationships.first.id
|
||||
end
|
||||
|
||||
def pagination_since_id
|
||||
@accounts.first.active_relationships.first.id
|
||||
end
|
||||
|
||||
def records_continue?
|
||||
@accounts.size == limit_param(DEFAULT_ACCOUNTS_LIMIT)
|
||||
end
|
||||
|
||||
def pagination_params(core_params)
|
||||
params.slice(:limit).permit(:limit).merge(core_params)
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,74 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::Accounts::FollowingAccountsController < Api::BaseController
|
||||
before_action -> { doorkeeper_authorize! :read, :'read:accounts' }
|
||||
before_action :set_account
|
||||
after_action :insert_pagination_headers
|
||||
|
||||
respond_to :json
|
||||
|
||||
def index
|
||||
@accounts = load_accounts
|
||||
render json: @accounts, each_serializer: REST::AccountSerializer
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_account
|
||||
@account = Account.find(params[:account_id])
|
||||
end
|
||||
|
||||
def load_accounts
|
||||
return [] if hide_results?
|
||||
|
||||
default_accounts.merge(paginated_follows).to_a
|
||||
end
|
||||
|
||||
def hide_results?
|
||||
(@account.user_hides_network? && current_account.id != @account.id) || (current_account && @account.blocking?(current_account))
|
||||
end
|
||||
|
||||
def default_accounts
|
||||
Account.includes(:passive_relationships, :account_stat).references(:passive_relationships)
|
||||
end
|
||||
|
||||
def paginated_follows
|
||||
Follow.where(account: @account).paginate_by_max_id(
|
||||
limit_param(DEFAULT_ACCOUNTS_LIMIT),
|
||||
params[:max_id],
|
||||
params[:since_id]
|
||||
)
|
||||
end
|
||||
|
||||
def insert_pagination_headers
|
||||
set_pagination_headers(next_path, prev_path)
|
||||
end
|
||||
|
||||
def next_path
|
||||
if records_continue?
|
||||
api_v1_account_following_index_url pagination_params(max_id: pagination_max_id)
|
||||
end
|
||||
end
|
||||
|
||||
def prev_path
|
||||
unless @accounts.empty?
|
||||
api_v1_account_following_index_url pagination_params(since_id: pagination_since_id)
|
||||
end
|
||||
end
|
||||
|
||||
def pagination_max_id
|
||||
@accounts.last.passive_relationships.first.id
|
||||
end
|
||||
|
||||
def pagination_since_id
|
||||
@accounts.first.passive_relationships.first.id
|
||||
end
|
||||
|
||||
def records_continue?
|
||||
@accounts.size == limit_param(DEFAULT_ACCOUNTS_LIMIT)
|
||||
end
|
||||
|
||||
def pagination_params(core_params)
|
||||
params.slice(:limit).permit(:limit).merge(core_params)
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,19 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::Accounts::IdentityProofsController < Api::BaseController
|
||||
before_action :require_user!
|
||||
before_action :set_account
|
||||
|
||||
respond_to :json
|
||||
|
||||
def index
|
||||
@proofs = @account.identity_proofs.active
|
||||
render json: @proofs, each_serializer: REST::IdentityProofSerializer
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_account
|
||||
@account = Account.find(params[:account_id])
|
||||
end
|
||||
end
|
||||
20
app/controllers/api/v1/accounts/lists_controller.rb
Normal file
20
app/controllers/api/v1/accounts/lists_controller.rb
Normal file
@@ -0,0 +1,20 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::Accounts::ListsController < Api::BaseController
|
||||
before_action -> { doorkeeper_authorize! :read, :'read:lists' }
|
||||
before_action :require_user!
|
||||
before_action :set_account
|
||||
|
||||
respond_to :json
|
||||
|
||||
def index
|
||||
@lists = @account.lists.where(account: current_account)
|
||||
render json: @lists, each_serializer: REST::ListSerializer
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_account
|
||||
@account = Account.find(params[:account_id])
|
||||
end
|
||||
end
|
||||
32
app/controllers/api/v1/accounts/pins_controller.rb
Normal file
32
app/controllers/api/v1/accounts/pins_controller.rb
Normal file
@@ -0,0 +1,32 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::Accounts::PinsController < Api::BaseController
|
||||
include Authorization
|
||||
|
||||
before_action -> { doorkeeper_authorize! :write, :'write:accounts' }
|
||||
before_action :require_user!
|
||||
before_action :set_account
|
||||
|
||||
respond_to :json
|
||||
|
||||
def create
|
||||
AccountPin.create!(account: current_account, target_account: @account)
|
||||
render json: @account, serializer: REST::RelationshipSerializer, relationships: relationships_presenter
|
||||
end
|
||||
|
||||
def destroy
|
||||
pin = AccountPin.find_by(account: current_account, target_account: @account)
|
||||
pin&.destroy!
|
||||
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
|
||||
26
app/controllers/api/v1/accounts/relationships_controller.rb
Normal file
26
app/controllers/api/v1/accounts/relationships_controller.rb
Normal file
@@ -0,0 +1,26 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::Accounts::RelationshipsController < Api::BaseController
|
||||
before_action -> { doorkeeper_authorize! :read, :'read:follows' }
|
||||
before_action :require_user!
|
||||
|
||||
respond_to :json
|
||||
|
||||
def index
|
||||
accounts = Account.where(id: account_ids).select('id')
|
||||
# .where doesn't guarantee that our results are in the same order
|
||||
# we requested them, so return the "right" order to the requestor.
|
||||
@accounts = accounts.index_by(&:id).values_at(*account_ids).compact
|
||||
render json: @accounts, each_serializer: REST::RelationshipSerializer, relationships: relationships
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def relationships
|
||||
AccountRelationshipsPresenter.new(@accounts, current_user.account_id)
|
||||
end
|
||||
|
||||
def account_ids
|
||||
Array(params[:id]).map(&:to_i)
|
||||
end
|
||||
end
|
||||
26
app/controllers/api/v1/accounts/search_controller.rb
Normal file
26
app/controllers/api/v1/accounts/search_controller.rb
Normal file
@@ -0,0 +1,26 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::Accounts::SearchController < Api::BaseController
|
||||
before_action -> { doorkeeper_authorize! :read, :'read:accounts' }
|
||||
before_action :require_user!
|
||||
|
||||
respond_to :json
|
||||
|
||||
def show
|
||||
@accounts = account_search
|
||||
render json: @accounts, each_serializer: REST::AccountSerializer
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def account_search
|
||||
AccountSearchService.new.call(
|
||||
params[:q],
|
||||
current_account,
|
||||
limit: limit_param(DEFAULT_ACCOUNTS_LIMIT),
|
||||
resolve: truthy_param?(:resolve),
|
||||
following: truthy_param?(:following),
|
||||
offset: params[:offset]
|
||||
)
|
||||
end
|
||||
end
|
||||
112
app/controllers/api/v1/accounts/statuses_controller.rb
Normal file
112
app/controllers/api/v1/accounts/statuses_controller.rb
Normal file
@@ -0,0 +1,112 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::Accounts::StatusesController < Api::BaseController
|
||||
before_action -> { authorize_if_got_token! :read, :'read:statuses' }
|
||||
before_action :set_account
|
||||
after_action :insert_pagination_headers
|
||||
|
||||
respond_to :json
|
||||
|
||||
def index
|
||||
@statuses = load_statuses
|
||||
render json: @statuses, each_serializer: REST::StatusSerializer, relationships: StatusRelationshipsPresenter.new(@statuses, current_user&.account_id)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_account
|
||||
@account = Account.find(params[:account_id])
|
||||
end
|
||||
|
||||
def load_statuses
|
||||
cached_account_statuses
|
||||
end
|
||||
|
||||
def cached_account_statuses
|
||||
cache_collection account_statuses, Status
|
||||
end
|
||||
|
||||
def account_statuses
|
||||
statuses = truthy_param?(:pinned) ? pinned_scope : permitted_account_statuses
|
||||
statuses = statuses.paginate_by_id(limit_param(DEFAULT_STATUSES_LIMIT), params_slice(:max_id, :since_id, :min_id))
|
||||
|
||||
statuses.merge!(only_media_scope) if truthy_param?(:only_media)
|
||||
statuses.merge!(no_replies_scope) if truthy_param?(:exclude_replies)
|
||||
statuses.merge!(no_reblogs_scope) if truthy_param?(:exclude_reblogs)
|
||||
statuses.merge!(hashtag_scope) if params[:tagged].present?
|
||||
|
||||
statuses
|
||||
end
|
||||
|
||||
def permitted_account_statuses
|
||||
@account.statuses.permitted_for(@account, current_account)
|
||||
end
|
||||
|
||||
def only_media_scope
|
||||
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
|
||||
|
||||
def pinned_scope
|
||||
@account.pinned_statuses
|
||||
end
|
||||
|
||||
def no_replies_scope
|
||||
Status.without_replies
|
||||
end
|
||||
|
||||
def no_reblogs_scope
|
||||
Status.without_reblogs
|
||||
end
|
||||
|
||||
def hashtag_scope
|
||||
tag = Tag.find_normalized(params[:tagged])
|
||||
|
||||
if tag
|
||||
Status.tagged_with(tag.id)
|
||||
else
|
||||
Status.none
|
||||
end
|
||||
end
|
||||
|
||||
def pagination_params(core_params)
|
||||
params.slice(:limit, :only_media, :exclude_replies).permit(:limit, :only_media, :exclude_replies).merge(core_params)
|
||||
end
|
||||
|
||||
def insert_pagination_headers
|
||||
set_pagination_headers(next_path, prev_path)
|
||||
end
|
||||
|
||||
def next_path
|
||||
if records_continue?
|
||||
api_v1_account_statuses_url pagination_params(max_id: pagination_max_id)
|
||||
end
|
||||
end
|
||||
|
||||
def prev_path
|
||||
unless @statuses.empty?
|
||||
api_v1_account_statuses_url pagination_params(min_id: pagination_since_id)
|
||||
end
|
||||
end
|
||||
|
||||
def records_continue?
|
||||
@statuses.size == limit_param(DEFAULT_STATUSES_LIMIT)
|
||||
end
|
||||
|
||||
def pagination_max_id
|
||||
@statuses.last.id
|
||||
end
|
||||
|
||||
def pagination_since_id
|
||||
@statuses.first.id
|
||||
end
|
||||
end
|
||||
89
app/controllers/api/v1/accounts_controller.rb
Normal file
89
app/controllers/api/v1/accounts_controller.rb
Normal file
@@ -0,0 +1,89 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::AccountsController < Api::BaseController
|
||||
before_action -> { authorize_if_got_token! :read, :'read:accounts' }, except: [:create, :follow, :unfollow, :block, :unblock, :mute, :unmute]
|
||||
before_action -> { doorkeeper_authorize! :follow, :'write:follows' }, only: [:follow, :unfollow]
|
||||
before_action -> { doorkeeper_authorize! :follow, :'write:mutes' }, only: [:mute, :unmute]
|
||||
before_action -> { doorkeeper_authorize! :follow, :'write:blocks' }, only: [:block, :unblock]
|
||||
before_action -> { doorkeeper_authorize! :write, :'write:accounts' }, only: [:create]
|
||||
|
||||
before_action :require_user!, except: [:show, :create]
|
||||
before_action :set_account, except: [:create]
|
||||
before_action :check_account_suspension, only: [:show]
|
||||
before_action :check_enabled_registrations, only: [:create]
|
||||
|
||||
respond_to :json
|
||||
|
||||
def show
|
||||
render json: @account, serializer: REST::AccountSerializer
|
||||
end
|
||||
|
||||
def create
|
||||
token = AppSignUpService.new.call(doorkeeper_token.application, account_params)
|
||||
response = Doorkeeper::OAuth::TokenResponse.new(token)
|
||||
|
||||
headers.merge!(response.headers)
|
||||
|
||||
self.response_body = Oj.dump(response.body)
|
||||
self.status = response.status
|
||||
end
|
||||
|
||||
def follow
|
||||
FollowService.new.call(current_user.account, @account, reblogs: truthy_param?(:reblogs))
|
||||
|
||||
options = @account.locked? ? {} : { following_map: { @account.id => { reblogs: truthy_param?(:reblogs) } }, requested_map: { @account.id => false } }
|
||||
|
||||
render json: @account, serializer: REST::RelationshipSerializer, relationships: relationships(options)
|
||||
end
|
||||
|
||||
def block
|
||||
BlockService.new.call(current_user.account, @account)
|
||||
render json: @account, serializer: REST::RelationshipSerializer, relationships: relationships
|
||||
end
|
||||
|
||||
def mute
|
||||
MuteService.new.call(current_user.account, @account, notifications: truthy_param?(:notifications))
|
||||
render json: @account, serializer: REST::RelationshipSerializer, relationships: relationships
|
||||
end
|
||||
|
||||
def unfollow
|
||||
UnfollowService.new.call(current_user.account, @account)
|
||||
render json: @account, serializer: REST::RelationshipSerializer, relationships: relationships
|
||||
end
|
||||
|
||||
def unblock
|
||||
UnblockService.new.call(current_user.account, @account)
|
||||
render json: @account, serializer: REST::RelationshipSerializer, relationships: relationships
|
||||
end
|
||||
|
||||
def unmute
|
||||
UnmuteService.new.call(current_user.account, @account)
|
||||
render json: @account, serializer: REST::RelationshipSerializer, relationships: relationships
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_account
|
||||
@account = Account.find(params[:id])
|
||||
end
|
||||
|
||||
def relationships(**options)
|
||||
AccountRelationshipsPresenter.new([@account.id], current_user.account_id, options)
|
||||
end
|
||||
|
||||
def check_account_suspension
|
||||
gone if @account.suspended?
|
||||
end
|
||||
|
||||
def account_params
|
||||
params.permit(:username, :email, :password, :agreement, :locale)
|
||||
end
|
||||
|
||||
def check_enabled_registrations
|
||||
forbidden if single_user_mode? || !allowed_registrations?
|
||||
end
|
||||
|
||||
def allowed_registrations?
|
||||
Setting.registrations_mode != 'none'
|
||||
end
|
||||
end
|
||||
11
app/controllers/api/v1/apps/credentials_controller.rb
Normal file
11
app/controllers/api/v1/apps/credentials_controller.rb
Normal file
@@ -0,0 +1,11 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::Apps::CredentialsController < Api::BaseController
|
||||
before_action -> { doorkeeper_authorize! :read }
|
||||
|
||||
respond_to :json
|
||||
|
||||
def show
|
||||
render json: doorkeeper_token.application, serializer: REST::ApplicationSerializer, fields: %i(name website vapid_key)
|
||||
end
|
||||
end
|
||||
27
app/controllers/api/v1/apps_controller.rb
Normal file
27
app/controllers/api/v1/apps_controller.rb
Normal file
@@ -0,0 +1,27 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::AppsController < Api::BaseController
|
||||
def create
|
||||
@app = Doorkeeper::Application.create!(application_options)
|
||||
render json: @app, serializer: REST::ApplicationSerializer
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def application_options
|
||||
{
|
||||
name: app_params[:client_name],
|
||||
redirect_uri: app_params[:redirect_uris],
|
||||
scopes: app_scopes_or_default,
|
||||
website: app_params[:website],
|
||||
}
|
||||
end
|
||||
|
||||
def app_scopes_or_default
|
||||
app_params[:scopes] || Doorkeeper.configuration.default_scopes
|
||||
end
|
||||
|
||||
def app_params
|
||||
params.permit(:client_name, :redirect_uris, :scopes, :website)
|
||||
end
|
||||
end
|
||||
62
app/controllers/api/v1/blocks_controller.rb
Normal file
62
app/controllers/api/v1/blocks_controller.rb
Normal file
@@ -0,0 +1,62 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::BlocksController < Api::BaseController
|
||||
before_action -> { doorkeeper_authorize! :follow, :'read:blocks' }
|
||||
before_action :require_user!
|
||||
after_action :insert_pagination_headers
|
||||
|
||||
respond_to :json
|
||||
|
||||
def index
|
||||
@accounts = load_accounts
|
||||
render json: @accounts, each_serializer: REST::AccountSerializer
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def load_accounts
|
||||
paginated_blocks.map(&:target_account)
|
||||
end
|
||||
|
||||
def paginated_blocks
|
||||
@paginated_blocks ||= Block.eager_load(target_account: :account_stat)
|
||||
.where(account: current_account)
|
||||
.paginate_by_max_id(
|
||||
limit_param(DEFAULT_ACCOUNTS_LIMIT),
|
||||
params[:max_id],
|
||||
params[:since_id]
|
||||
)
|
||||
end
|
||||
|
||||
def insert_pagination_headers
|
||||
set_pagination_headers(next_path, prev_path)
|
||||
end
|
||||
|
||||
def next_path
|
||||
if records_continue?
|
||||
api_v1_blocks_url pagination_params(max_id: pagination_max_id)
|
||||
end
|
||||
end
|
||||
|
||||
def prev_path
|
||||
unless paginated_blocks.empty?
|
||||
api_v1_blocks_url pagination_params(since_id: pagination_since_id)
|
||||
end
|
||||
end
|
||||
|
||||
def pagination_max_id
|
||||
paginated_blocks.last.id
|
||||
end
|
||||
|
||||
def pagination_since_id
|
||||
paginated_blocks.first.id
|
||||
end
|
||||
|
||||
def records_continue?
|
||||
paginated_blocks.size == limit_param(DEFAULT_ACCOUNTS_LIMIT)
|
||||
end
|
||||
|
||||
def pagination_params(core_params)
|
||||
params.slice(:limit).permit(:limit).merge(core_params)
|
||||
end
|
||||
end
|
||||
71
app/controllers/api/v1/conversations_controller.rb
Normal file
71
app/controllers/api/v1/conversations_controller.rb
Normal file
@@ -0,0 +1,71 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::ConversationsController < Api::BaseController
|
||||
LIMIT = 20
|
||||
|
||||
before_action -> { doorkeeper_authorize! :read, :'read:statuses' }, only: :index
|
||||
before_action -> { doorkeeper_authorize! :write, :'write:conversations' }, except: :index
|
||||
before_action :require_user!
|
||||
before_action :set_conversation, except: :index
|
||||
after_action :insert_pagination_headers, only: :index
|
||||
|
||||
respond_to :json
|
||||
|
||||
def index
|
||||
@conversations = paginated_conversations
|
||||
render json: @conversations, each_serializer: REST::ConversationSerializer
|
||||
end
|
||||
|
||||
def read
|
||||
@conversation.update!(unread: false)
|
||||
render json: @conversation, serializer: REST::ConversationSerializer
|
||||
end
|
||||
|
||||
def destroy
|
||||
@conversation.destroy!
|
||||
render_empty
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_conversation
|
||||
@conversation = AccountConversation.where(account: current_account).find(params[:id])
|
||||
end
|
||||
|
||||
def paginated_conversations
|
||||
AccountConversation.where(account: current_account)
|
||||
.paginate_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
|
||||
if records_continue?
|
||||
api_v1_conversations_url pagination_params(max_id: pagination_max_id)
|
||||
end
|
||||
end
|
||||
|
||||
def prev_path
|
||||
unless @conversations.empty?
|
||||
api_v1_conversations_url pagination_params(min_id: pagination_since_id)
|
||||
end
|
||||
end
|
||||
|
||||
def pagination_max_id
|
||||
@conversations.last.last_status_id
|
||||
end
|
||||
|
||||
def pagination_since_id
|
||||
@conversations.first.last_status_id
|
||||
end
|
||||
|
||||
def records_continue?
|
||||
@conversations.size == limit_param(LIMIT)
|
||||
end
|
||||
|
||||
def pagination_params(core_params)
|
||||
params.slice(:limit).permit(:limit).merge(core_params)
|
||||
end
|
||||
end
|
||||
13
app/controllers/api/v1/custom_emojis_controller.rb
Normal file
13
app/controllers/api/v1/custom_emojis_controller.rb
Normal file
@@ -0,0 +1,13 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::CustomEmojisController < Api::BaseController
|
||||
respond_to :json
|
||||
|
||||
skip_before_action :set_cache_headers
|
||||
|
||||
def index
|
||||
render_cached_json('api:v1:custom_emojis', expires_in: 1.minute) do
|
||||
ActiveModelSerializers::SerializableResource.new(CustomEmoji.local.where(disabled: false), each_serializer: REST::CustomEmojiSerializer)
|
||||
end
|
||||
end
|
||||
end
|
||||
78
app/controllers/api/v1/domain_blocks_controller.rb
Normal file
78
app/controllers/api/v1/domain_blocks_controller.rb
Normal file
@@ -0,0 +1,78 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::DomainBlocksController < Api::BaseController
|
||||
BLOCK_LIMIT = 100
|
||||
|
||||
before_action -> { doorkeeper_authorize! :follow, :'read:blocks' }, only: :show
|
||||
before_action -> { doorkeeper_authorize! :follow, :'write:blocks' }, except: :show
|
||||
before_action :require_user!
|
||||
after_action :insert_pagination_headers, only: :show
|
||||
|
||||
respond_to :json
|
||||
|
||||
def show
|
||||
@blocks = load_domain_blocks
|
||||
render json: @blocks.map(&:domain)
|
||||
end
|
||||
|
||||
def create
|
||||
current_account.block_domain!(domain_block_params[:domain])
|
||||
AfterAccountDomainBlockWorker.perform_async(current_account.id, domain_block_params[:domain])
|
||||
render_empty
|
||||
end
|
||||
|
||||
def destroy
|
||||
current_account.unblock_domain!(domain_block_params[:domain])
|
||||
render_empty
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def load_domain_blocks
|
||||
account_domain_blocks.paginate_by_max_id(
|
||||
limit_param(BLOCK_LIMIT),
|
||||
params[:max_id],
|
||||
params[:since_id]
|
||||
)
|
||||
end
|
||||
|
||||
def account_domain_blocks
|
||||
current_account.domain_blocks
|
||||
end
|
||||
|
||||
def insert_pagination_headers
|
||||
set_pagination_headers(next_path, prev_path)
|
||||
end
|
||||
|
||||
def next_path
|
||||
if records_continue?
|
||||
api_v1_domain_blocks_url pagination_params(max_id: pagination_max_id)
|
||||
end
|
||||
end
|
||||
|
||||
def prev_path
|
||||
unless @blocks.empty?
|
||||
api_v1_domain_blocks_url pagination_params(since_id: pagination_since_id)
|
||||
end
|
||||
end
|
||||
|
||||
def pagination_max_id
|
||||
@blocks.last.id
|
||||
end
|
||||
|
||||
def pagination_since_id
|
||||
@blocks.first.id
|
||||
end
|
||||
|
||||
def records_continue?
|
||||
@blocks.size == limit_param(BLOCK_LIMIT)
|
||||
end
|
||||
|
||||
def pagination_params(core_params)
|
||||
params.slice(:limit).permit(:limit).merge(core_params)
|
||||
end
|
||||
|
||||
def domain_block_params
|
||||
params.permit(:domain)
|
||||
end
|
||||
end
|
||||
72
app/controllers/api/v1/endorsements_controller.rb
Normal file
72
app/controllers/api/v1/endorsements_controller.rb
Normal file
@@ -0,0 +1,72 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::EndorsementsController < Api::BaseController
|
||||
before_action -> { doorkeeper_authorize! :read, :'read:accounts' }
|
||||
before_action :require_user!
|
||||
after_action :insert_pagination_headers
|
||||
|
||||
respond_to :json
|
||||
|
||||
def index
|
||||
@accounts = load_accounts
|
||||
render json: @accounts, each_serializer: REST::AccountSerializer
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def load_accounts
|
||||
if unlimited?
|
||||
endorsed_accounts.all
|
||||
else
|
||||
endorsed_accounts.paginate_by_max_id(
|
||||
limit_param(DEFAULT_ACCOUNTS_LIMIT),
|
||||
params[:max_id],
|
||||
params[:since_id]
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
def endorsed_accounts
|
||||
current_account.endorsed_accounts.includes(:account_stat)
|
||||
end
|
||||
|
||||
def insert_pagination_headers
|
||||
set_pagination_headers(next_path, prev_path)
|
||||
end
|
||||
|
||||
def next_path
|
||||
return if unlimited?
|
||||
|
||||
if records_continue?
|
||||
api_v1_endorsements_url pagination_params(max_id: pagination_max_id)
|
||||
end
|
||||
end
|
||||
|
||||
def prev_path
|
||||
return if unlimited?
|
||||
|
||||
unless @accounts.empty?
|
||||
api_v1_endorsements_url pagination_params(since_id: pagination_since_id)
|
||||
end
|
||||
end
|
||||
|
||||
def pagination_max_id
|
||||
@accounts.last.id
|
||||
end
|
||||
|
||||
def pagination_since_id
|
||||
@accounts.first.id
|
||||
end
|
||||
|
||||
def records_continue?
|
||||
@accounts.size == limit_param(DEFAULT_ACCOUNTS_LIMIT)
|
||||
end
|
||||
|
||||
def pagination_params(core_params)
|
||||
params.slice(:limit).permit(:limit).merge(core_params)
|
||||
end
|
||||
|
||||
def unlimited?
|
||||
params[:limit] == '0'
|
||||
end
|
||||
end
|
||||
70
app/controllers/api/v1/favourites_controller.rb
Normal file
70
app/controllers/api/v1/favourites_controller.rb
Normal file
@@ -0,0 +1,70 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::FavouritesController < Api::BaseController
|
||||
before_action -> { doorkeeper_authorize! :read, :'read:favourites' }
|
||||
before_action :require_user!
|
||||
after_action :insert_pagination_headers
|
||||
|
||||
respond_to :json
|
||||
|
||||
def index
|
||||
@statuses = load_statuses
|
||||
render json: @statuses, each_serializer: REST::StatusSerializer, relationships: StatusRelationshipsPresenter.new(@statuses, current_user&.account_id)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def load_statuses
|
||||
cached_favourites
|
||||
end
|
||||
|
||||
def cached_favourites
|
||||
cache_collection(
|
||||
Status.reorder(nil).joins(:favourites).merge(results),
|
||||
Status
|
||||
)
|
||||
end
|
||||
|
||||
def results
|
||||
@_results ||= account_favourites.paginate_by_id(
|
||||
limit_param(DEFAULT_STATUSES_LIMIT),
|
||||
params_slice(:max_id, :since_id, :min_id)
|
||||
)
|
||||
end
|
||||
|
||||
def account_favourites
|
||||
current_account.favourites
|
||||
end
|
||||
|
||||
def insert_pagination_headers
|
||||
set_pagination_headers(next_path, prev_path)
|
||||
end
|
||||
|
||||
def next_path
|
||||
if records_continue?
|
||||
api_v1_favourites_url pagination_params(max_id: pagination_max_id)
|
||||
end
|
||||
end
|
||||
|
||||
def prev_path
|
||||
unless results.empty?
|
||||
api_v1_favourites_url pagination_params(min_id: pagination_since_id)
|
||||
end
|
||||
end
|
||||
|
||||
def pagination_max_id
|
||||
results.last.id
|
||||
end
|
||||
|
||||
def pagination_since_id
|
||||
results.first.id
|
||||
end
|
||||
|
||||
def records_continue?
|
||||
results.size == limit_param(DEFAULT_STATUSES_LIMIT)
|
||||
end
|
||||
|
||||
def pagination_params(core_params)
|
||||
params.slice(:limit).permit(:limit).merge(core_params)
|
||||
end
|
||||
end
|
||||
48
app/controllers/api/v1/filters_controller.rb
Normal file
48
app/controllers/api/v1/filters_controller.rb
Normal file
@@ -0,0 +1,48 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::FiltersController < Api::BaseController
|
||||
before_action -> { doorkeeper_authorize! :read, :'read:filters' }, only: [:index, :show]
|
||||
before_action -> { doorkeeper_authorize! :write, :'write:filters' }, except: [:index, :show]
|
||||
before_action :require_user!
|
||||
before_action :set_filters, only: :index
|
||||
before_action :set_filter, only: [:show, :update, :destroy]
|
||||
|
||||
respond_to :json
|
||||
|
||||
def index
|
||||
render json: @filters, each_serializer: REST::FilterSerializer
|
||||
end
|
||||
|
||||
def create
|
||||
@filter = current_account.custom_filters.create!(resource_params)
|
||||
render json: @filter, serializer: REST::FilterSerializer
|
||||
end
|
||||
|
||||
def show
|
||||
render json: @filter, serializer: REST::FilterSerializer
|
||||
end
|
||||
|
||||
def update
|
||||
@filter.update!(resource_params)
|
||||
render json: @filter, serializer: REST::FilterSerializer
|
||||
end
|
||||
|
||||
def destroy
|
||||
@filter.destroy!
|
||||
render_empty
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_filters
|
||||
@filters = current_account.custom_filters
|
||||
end
|
||||
|
||||
def set_filter
|
||||
@filter = current_account.custom_filters.find(params[:id])
|
||||
end
|
||||
|
||||
def resource_params
|
||||
params.permit(:phrase, :expires_in, :irreversible, :whole_word, context: [])
|
||||
end
|
||||
end
|
||||
78
app/controllers/api/v1/follow_requests_controller.rb
Normal file
78
app/controllers/api/v1/follow_requests_controller.rb
Normal file
@@ -0,0 +1,78 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::FollowRequestsController < Api::BaseController
|
||||
before_action -> { doorkeeper_authorize! :follow, :'read:follows' }, only: :index
|
||||
before_action -> { doorkeeper_authorize! :follow, :'write:follows' }, except: :index
|
||||
before_action :require_user!
|
||||
after_action :insert_pagination_headers, only: :index
|
||||
|
||||
def index
|
||||
@accounts = load_accounts
|
||||
render json: @accounts, each_serializer: REST::AccountSerializer
|
||||
end
|
||||
|
||||
def authorize
|
||||
AuthorizeFollowService.new.call(account, current_account)
|
||||
NotifyService.new.call(current_account, Follow.find_by(account: account, target_account: current_account))
|
||||
render_empty
|
||||
end
|
||||
|
||||
def reject
|
||||
RejectFollowService.new.call(account, current_account)
|
||||
render_empty
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def account
|
||||
Account.find(params[:id])
|
||||
end
|
||||
|
||||
def load_accounts
|
||||
default_accounts.merge(paginated_follow_requests).to_a
|
||||
end
|
||||
|
||||
def default_accounts
|
||||
Account.includes(:follow_requests, :account_stat).references(:follow_requests)
|
||||
end
|
||||
|
||||
def paginated_follow_requests
|
||||
FollowRequest.where(target_account: current_account).paginate_by_max_id(
|
||||
limit_param(DEFAULT_ACCOUNTS_LIMIT),
|
||||
params[:max_id],
|
||||
params[:since_id]
|
||||
)
|
||||
end
|
||||
|
||||
def insert_pagination_headers
|
||||
set_pagination_headers(next_path, prev_path)
|
||||
end
|
||||
|
||||
def next_path
|
||||
if records_continue?
|
||||
api_v1_follow_requests_url pagination_params(max_id: pagination_max_id)
|
||||
end
|
||||
end
|
||||
|
||||
def prev_path
|
||||
unless @accounts.empty?
|
||||
api_v1_follow_requests_url pagination_params(since_id: pagination_since_id)
|
||||
end
|
||||
end
|
||||
|
||||
def pagination_max_id
|
||||
@accounts.last.follow_requests.last.id
|
||||
end
|
||||
|
||||
def pagination_since_id
|
||||
@accounts.first.follow_requests.first.id
|
||||
end
|
||||
|
||||
def records_continue?
|
||||
@accounts.size == limit_param(DEFAULT_ACCOUNTS_LIMIT)
|
||||
end
|
||||
|
||||
def pagination_params(core_params)
|
||||
params.slice(:limit).permit(:limit).merge(core_params)
|
||||
end
|
||||
end
|
||||
31
app/controllers/api/v1/follows_controller.rb
Normal file
31
app/controllers/api/v1/follows_controller.rb
Normal file
@@ -0,0 +1,31 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::FollowsController < Api::BaseController
|
||||
before_action -> { doorkeeper_authorize! :follow, :'write:follows' }
|
||||
before_action :require_user!
|
||||
|
||||
respond_to :json
|
||||
|
||||
def create
|
||||
raise ActiveRecord::RecordNotFound if follow_params[:uri].blank?
|
||||
|
||||
@account = FollowService.new.call(current_user.account, target_uri).try(:target_account)
|
||||
|
||||
if @account.nil?
|
||||
username, domain = target_uri.split('@')
|
||||
@account = Account.find_remote!(username, domain)
|
||||
end
|
||||
|
||||
render json: @account, serializer: REST::AccountSerializer
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def target_uri
|
||||
follow_params[:uri].strip.gsub(/\A@/, '')
|
||||
end
|
||||
|
||||
def follow_params
|
||||
params.permit(:uri)
|
||||
end
|
||||
end
|
||||
101
app/controllers/api/v1/groups/accounts_controller.rb
Normal file
101
app/controllers/api/v1/groups/accounts_controller.rb
Normal file
@@ -0,0 +1,101 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::Groups::AccountsController < Api::BaseController
|
||||
include Authorization
|
||||
|
||||
before_action -> { doorkeeper_authorize! :read, :'read:groups' }, only: [:show]
|
||||
before_action -> { doorkeeper_authorize! :write, :'write:groups' }, except: [:show]
|
||||
|
||||
before_action :require_user!
|
||||
before_action :set_group
|
||||
|
||||
after_action :insert_pagination_headers, only: :index
|
||||
|
||||
def show
|
||||
@accounts = load_accounts
|
||||
render json: @accounts, each_serializer: REST::AccountSerializer
|
||||
end
|
||||
|
||||
def create
|
||||
authorize @group, :join?
|
||||
|
||||
@group.accounts << current_account
|
||||
render json: @group, serializer: REST::GroupRelationshipSerializer, relationships: relationships
|
||||
end
|
||||
|
||||
def update
|
||||
authorize @group, :update_account?
|
||||
|
||||
GroupAccount.where(group: @group, account_id: current_account.id).update(group_account_params)
|
||||
render_empty
|
||||
end
|
||||
|
||||
def destroy
|
||||
authorize @group, :leave?
|
||||
|
||||
GroupAccount.where(group: @group, account_id: current_account.id).destroy_all
|
||||
render json: @group, serializer: REST::GroupRelationshipSerializer, relationships: relationships
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def relationships
|
||||
GroupRelationshipsPresenter.new([@group.id], current_user.account_id)
|
||||
end
|
||||
|
||||
def set_group
|
||||
@group = Group.find(params[:group_id])
|
||||
end
|
||||
|
||||
def load_accounts
|
||||
if unlimited?
|
||||
@group.accounts.includes(:account_stat).all
|
||||
else
|
||||
@group.accounts.includes(:account_stat).paginate_by_max_id(limit_param(DEFAULT_ACCOUNTS_LIMIT), params[:max_id], params[:since_id])
|
||||
end
|
||||
end
|
||||
|
||||
def insert_pagination_headers
|
||||
set_pagination_headers(next_path, prev_path)
|
||||
end
|
||||
|
||||
def next_path
|
||||
return if unlimited?
|
||||
|
||||
if records_continue?
|
||||
api_v1_group_accounts_url pagination_params(max_id: pagination_max_id)
|
||||
end
|
||||
end
|
||||
|
||||
def prev_path
|
||||
return if unlimited?
|
||||
|
||||
unless @accounts.empty?
|
||||
api_v1_group_accounts_url pagination_params(since_id: pagination_since_id)
|
||||
end
|
||||
end
|
||||
|
||||
def pagination_max_id
|
||||
@accounts.last.id
|
||||
end
|
||||
|
||||
def pagination_since_id
|
||||
@accounts.first.id
|
||||
end
|
||||
|
||||
def records_continue?
|
||||
@accounts.size == limit_param(DEFAULT_ACCOUNTS_LIMIT)
|
||||
end
|
||||
|
||||
def pagination_params(core_params)
|
||||
params.slice(:limit).permit(:limit).merge(core_params)
|
||||
end
|
||||
|
||||
def unlimited?
|
||||
params[:limit] == '0'
|
||||
end
|
||||
|
||||
def group_account_params
|
||||
params.permit(:role, :write_permissions)
|
||||
end
|
||||
end
|
||||
26
app/controllers/api/v1/groups/relationships_controller.rb
Normal file
26
app/controllers/api/v1/groups/relationships_controller.rb
Normal file
@@ -0,0 +1,26 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::Groups::RelationshipsController < Api::BaseController
|
||||
before_action -> { doorkeeper_authorize! :read, :'read:groups' }
|
||||
before_action :require_user!
|
||||
|
||||
respond_to :json
|
||||
|
||||
def index
|
||||
groups = Group.where(id: group_ids).select('id')
|
||||
# .where doesn't guarantee that our results are in the same order
|
||||
# we requested them, so return the "right" order to the requestor.
|
||||
@groups = groups.index_by(&:id).values_at(*group_ids).compact
|
||||
render json: @groups, each_serializer: REST::GroupRelationshipSerializer, relationships: relationships
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def relationships
|
||||
GroupRelationshipsPresenter.new(@groups, current_user.account_id)
|
||||
end
|
||||
|
||||
def group_ids
|
||||
Array(params[:id]).map(&:to_i)
|
||||
end
|
||||
end
|
||||
66
app/controllers/api/v1/groups_controller.rb
Normal file
66
app/controllers/api/v1/groups_controller.rb
Normal file
@@ -0,0 +1,66 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::GroupsController < Api::BaseController
|
||||
include Authorization
|
||||
|
||||
before_action -> { doorkeeper_authorize! :read, :'read:groups' }, only: [:index, :show]
|
||||
before_action -> { doorkeeper_authorize! :write, :'write:groups' }, except: [:index, :show]
|
||||
|
||||
before_action :require_user!
|
||||
before_action :set_group, except: [:index, :create]
|
||||
|
||||
def index
|
||||
@groups = Group.joins(:group_accounts).where(is_archived: false, group_accounts: { account: current_account }).all
|
||||
render json: @groups, each_serializer: REST::GroupSerializer
|
||||
end
|
||||
|
||||
def show
|
||||
render json: @group, serializer: REST::GroupSerializer
|
||||
end
|
||||
|
||||
def create
|
||||
@group = Group.create!(group_params.merge(account: current_account))
|
||||
render json: @group, serializer: REST::GroupSerializer
|
||||
end
|
||||
|
||||
def update
|
||||
authorize @group, :update?
|
||||
|
||||
@group.update!(group_params)
|
||||
render json: @group, serializer: REST::GroupSerializer
|
||||
end
|
||||
|
||||
def destroy
|
||||
authorize @group, :destroy?
|
||||
|
||||
@group.is_archived = true
|
||||
@group.save!
|
||||
render_empty
|
||||
end
|
||||
|
||||
def destroy_status
|
||||
authorize @group, :destroy_status?
|
||||
|
||||
status = Status.find(params[:status_id])
|
||||
GroupUnlinkStatusService.new.call(current_account, @group, status)
|
||||
render_empty
|
||||
end
|
||||
|
||||
def approve_status
|
||||
authorize @group, :approve_status?
|
||||
|
||||
status = Status.find(params[:status_id])
|
||||
GroupApproveStatusService.new.call(current_account, @group, status)
|
||||
render_empty
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_group
|
||||
@group = Group.find(params[:id])
|
||||
end
|
||||
|
||||
def group_params
|
||||
params.permit(:title, :cover_image, :description)
|
||||
end
|
||||
end
|
||||
37
app/controllers/api/v1/instances/activity_controller.rb
Normal file
37
app/controllers/api/v1/instances/activity_controller.rb
Normal file
@@ -0,0 +1,37 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::Instances::ActivityController < Api::BaseController
|
||||
before_action :require_enabled_api!
|
||||
skip_before_action :set_cache_headers
|
||||
|
||||
respond_to :json
|
||||
|
||||
def show
|
||||
render_cached_json('api:v1:instances:activity:show', expires_in: 1.day) { activity }
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def activity
|
||||
weeks = []
|
||||
|
||||
12.times do |i|
|
||||
day = i.weeks.ago.to_date
|
||||
week_id = day.cweek
|
||||
week = Date.commercial(day.cwyear, week_id)
|
||||
|
||||
weeks << {
|
||||
week: week.to_time.to_i.to_s,
|
||||
statuses: Redis.current.get("activity:statuses:local:#{week_id}") || '0',
|
||||
logins: Redis.current.pfcount("activity:logins:#{week_id}").to_s,
|
||||
registrations: Redis.current.get("activity:accounts:local:#{week_id}") || '0',
|
||||
}
|
||||
end
|
||||
|
||||
weeks
|
||||
end
|
||||
|
||||
def require_enabled_api!
|
||||
head 404 unless Setting.activity_api_enabled
|
||||
end
|
||||
end
|
||||
18
app/controllers/api/v1/instances/peers_controller.rb
Normal file
18
app/controllers/api/v1/instances/peers_controller.rb
Normal file
@@ -0,0 +1,18 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::Instances::PeersController < Api::BaseController
|
||||
before_action :require_enabled_api!
|
||||
skip_before_action :set_cache_headers
|
||||
|
||||
respond_to :json
|
||||
|
||||
def index
|
||||
render_cached_json('api:v1:instances:peers:index', expires_in: 1.day) { Account.remote.domains }
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def require_enabled_api!
|
||||
head 404 unless Setting.peers_api_enabled
|
||||
end
|
||||
end
|
||||
12
app/controllers/api/v1/instances_controller.rb
Normal file
12
app/controllers/api/v1/instances_controller.rb
Normal file
@@ -0,0 +1,12 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::InstancesController < Api::BaseController
|
||||
respond_to :json
|
||||
skip_before_action :set_cache_headers
|
||||
|
||||
def show
|
||||
render_cached_json('api:v1:instances', expires_in: 5.minutes) do
|
||||
ActiveModelSerializers::SerializableResource.new({}, serializer: REST::InstanceSerializer)
|
||||
end
|
||||
end
|
||||
end
|
||||
97
app/controllers/api/v1/lists/accounts_controller.rb
Normal file
97
app/controllers/api/v1/lists/accounts_controller.rb
Normal file
@@ -0,0 +1,97 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::Lists::AccountsController < Api::BaseController
|
||||
before_action -> { doorkeeper_authorize! :read, :'read:lists' }, only: [:show]
|
||||
before_action -> { doorkeeper_authorize! :write, :'write:lists' }, except: [:show]
|
||||
|
||||
before_action :require_user!
|
||||
before_action :set_list
|
||||
|
||||
after_action :insert_pagination_headers, only: :show
|
||||
|
||||
def show
|
||||
@accounts = load_accounts
|
||||
render json: @accounts, each_serializer: REST::AccountSerializer
|
||||
end
|
||||
|
||||
def create
|
||||
ApplicationRecord.transaction do
|
||||
list_accounts.each do |account|
|
||||
@list.accounts << account
|
||||
end
|
||||
end
|
||||
|
||||
render_empty
|
||||
end
|
||||
|
||||
def destroy
|
||||
ListAccount.where(list: @list, account_id: account_ids).destroy_all
|
||||
render_empty
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_list
|
||||
@list = List.where(account: current_account).find(params[:list_id])
|
||||
end
|
||||
|
||||
def load_accounts
|
||||
if unlimited?
|
||||
@list.accounts.includes(:account_stat).all
|
||||
else
|
||||
@list.accounts.includes(:account_stat).paginate_by_max_id(limit_param(DEFAULT_ACCOUNTS_LIMIT), params[:max_id], params[:since_id])
|
||||
end
|
||||
end
|
||||
|
||||
def list_accounts
|
||||
Account.find(account_ids)
|
||||
end
|
||||
|
||||
def account_ids
|
||||
Array(resource_params[:account_ids])
|
||||
end
|
||||
|
||||
def resource_params
|
||||
params.permit(account_ids: [])
|
||||
end
|
||||
|
||||
def insert_pagination_headers
|
||||
set_pagination_headers(next_path, prev_path)
|
||||
end
|
||||
|
||||
def next_path
|
||||
return if unlimited?
|
||||
|
||||
if records_continue?
|
||||
api_v1_list_accounts_url pagination_params(max_id: pagination_max_id)
|
||||
end
|
||||
end
|
||||
|
||||
def prev_path
|
||||
return if unlimited?
|
||||
|
||||
unless @accounts.empty?
|
||||
api_v1_list_accounts_url pagination_params(since_id: pagination_since_id)
|
||||
end
|
||||
end
|
||||
|
||||
def pagination_max_id
|
||||
@accounts.last.id
|
||||
end
|
||||
|
||||
def pagination_since_id
|
||||
@accounts.first.id
|
||||
end
|
||||
|
||||
def records_continue?
|
||||
@accounts.size == limit_param(DEFAULT_ACCOUNTS_LIMIT)
|
||||
end
|
||||
|
||||
def pagination_params(core_params)
|
||||
params.slice(:limit).permit(:limit).merge(core_params)
|
||||
end
|
||||
|
||||
def unlimited?
|
||||
params[:limit] == '0'
|
||||
end
|
||||
end
|
||||
43
app/controllers/api/v1/lists_controller.rb
Normal file
43
app/controllers/api/v1/lists_controller.rb
Normal file
@@ -0,0 +1,43 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::ListsController < Api::BaseController
|
||||
before_action -> { doorkeeper_authorize! :read, :'read:lists' }, only: [:index, :show]
|
||||
before_action -> { doorkeeper_authorize! :write, :'write:lists' }, except: [:index, :show]
|
||||
|
||||
before_action :require_user!
|
||||
before_action :set_list, except: [:index, :create]
|
||||
|
||||
def index
|
||||
@lists = List.where(account: current_account).all
|
||||
render json: @lists, each_serializer: REST::ListSerializer
|
||||
end
|
||||
|
||||
def show
|
||||
render json: @list, serializer: REST::ListSerializer
|
||||
end
|
||||
|
||||
def create
|
||||
@list = List.create!(list_params.merge(account: current_account))
|
||||
render json: @list, serializer: REST::ListSerializer
|
||||
end
|
||||
|
||||
def update
|
||||
@list.update!(list_params)
|
||||
render json: @list, serializer: REST::ListSerializer
|
||||
end
|
||||
|
||||
def destroy
|
||||
@list.destroy!
|
||||
render_empty
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_list
|
||||
@list = List.where(account: current_account).find(params[:id])
|
||||
end
|
||||
|
||||
def list_params
|
||||
params.permit(:title)
|
||||
end
|
||||
end
|
||||
40
app/controllers/api/v1/media_controller.rb
Normal file
40
app/controllers/api/v1/media_controller.rb
Normal file
@@ -0,0 +1,40 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::MediaController < Api::BaseController
|
||||
before_action -> { doorkeeper_authorize! :write, :'write:media' }
|
||||
before_action :require_user!
|
||||
|
||||
include ObfuscateFilename
|
||||
obfuscate_filename :file
|
||||
|
||||
respond_to :json
|
||||
|
||||
def create
|
||||
@media = current_account.media_attachments.create!(media_params)
|
||||
render json: @media, serializer: REST::MediaAttachmentSerializer
|
||||
rescue Paperclip::Errors::NotIdentifiedByImageMagickError
|
||||
render json: file_type_error, status: 422
|
||||
rescue Paperclip::Error
|
||||
render json: processing_error, status: 500
|
||||
end
|
||||
|
||||
def update
|
||||
@media = current_account.media_attachments.where(status_id: nil).find(params[:id])
|
||||
@media.update!(media_params)
|
||||
render json: @media, serializer: REST::MediaAttachmentSerializer
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def media_params
|
||||
params.permit(:file, :description, :focus)
|
||||
end
|
||||
|
||||
def file_type_error
|
||||
{ error: 'File type of uploaded media could not be verified' }
|
||||
end
|
||||
|
||||
def processing_error
|
||||
{ error: 'Error processing thumbnail for uploaded media' }
|
||||
end
|
||||
end
|
||||
62
app/controllers/api/v1/mutes_controller.rb
Normal file
62
app/controllers/api/v1/mutes_controller.rb
Normal file
@@ -0,0 +1,62 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::MutesController < Api::BaseController
|
||||
before_action -> { doorkeeper_authorize! :follow, :'read:mutes' }
|
||||
before_action :require_user!
|
||||
after_action :insert_pagination_headers
|
||||
|
||||
respond_to :json
|
||||
|
||||
def index
|
||||
@accounts = load_accounts
|
||||
render json: @accounts, each_serializer: REST::AccountSerializer
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def load_accounts
|
||||
paginated_mutes.map(&:target_account)
|
||||
end
|
||||
|
||||
def paginated_mutes
|
||||
@paginated_mutes ||= Mute.eager_load(:target_account)
|
||||
.where(account: current_account)
|
||||
.paginate_by_max_id(
|
||||
limit_param(DEFAULT_ACCOUNTS_LIMIT),
|
||||
params[:max_id],
|
||||
params[:since_id]
|
||||
)
|
||||
end
|
||||
|
||||
def insert_pagination_headers
|
||||
set_pagination_headers(next_path, prev_path)
|
||||
end
|
||||
|
||||
def next_path
|
||||
if records_continue?
|
||||
api_v1_mutes_url pagination_params(max_id: pagination_max_id)
|
||||
end
|
||||
end
|
||||
|
||||
def prev_path
|
||||
unless paginated_mutes.empty?
|
||||
api_v1_mutes_url pagination_params(since_id: pagination_since_id)
|
||||
end
|
||||
end
|
||||
|
||||
def pagination_max_id
|
||||
paginated_mutes.last.id
|
||||
end
|
||||
|
||||
def pagination_since_id
|
||||
paginated_mutes.first.id
|
||||
end
|
||||
|
||||
def records_continue?
|
||||
paginated_mutes.size == limit_param(DEFAULT_ACCOUNTS_LIMIT)
|
||||
end
|
||||
|
||||
def pagination_params(core_params)
|
||||
params.slice(:limit).permit(:limit).merge(core_params)
|
||||
end
|
||||
end
|
||||
91
app/controllers/api/v1/notifications_controller.rb
Normal file
91
app/controllers/api/v1/notifications_controller.rb
Normal file
@@ -0,0 +1,91 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::NotificationsController < Api::BaseController
|
||||
before_action -> { doorkeeper_authorize! :read, :'read:notifications' }, except: [:clear, :dismiss]
|
||||
before_action -> { doorkeeper_authorize! :write, :'write:notifications' }, only: [:clear, :dismiss]
|
||||
before_action :require_user!
|
||||
after_action :insert_pagination_headers, only: :index
|
||||
|
||||
respond_to :json
|
||||
|
||||
DEFAULT_NOTIFICATIONS_LIMIT = 15
|
||||
|
||||
def index
|
||||
@notifications = load_notifications
|
||||
render json: @notifications, each_serializer: REST::NotificationSerializer, relationships: StatusRelationshipsPresenter.new(target_statuses_from_notifications, current_user&.account_id)
|
||||
end
|
||||
|
||||
def show
|
||||
@notification = current_account.notifications.find(params[:id])
|
||||
render json: @notification, serializer: REST::NotificationSerializer
|
||||
end
|
||||
|
||||
def clear
|
||||
current_account.notifications.delete_all
|
||||
render_empty
|
||||
end
|
||||
|
||||
def dismiss
|
||||
current_account.notifications.find_by!(id: params[:id]).destroy!
|
||||
render_empty
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def load_notifications
|
||||
cache_collection paginated_notifications, Notification
|
||||
end
|
||||
|
||||
def paginated_notifications
|
||||
browserable_account_notifications.paginate_by_id(
|
||||
limit_param(DEFAULT_NOTIFICATIONS_LIMIT),
|
||||
params_slice(:max_id, :since_id, :min_id)
|
||||
)
|
||||
end
|
||||
|
||||
def browserable_account_notifications
|
||||
current_account.notifications.browserable(exclude_types, from_account)
|
||||
end
|
||||
|
||||
def target_statuses_from_notifications
|
||||
@notifications.reject { |notification| notification.target_status.nil? }.map(&:target_status)
|
||||
end
|
||||
|
||||
def insert_pagination_headers
|
||||
set_pagination_headers(next_path, prev_path)
|
||||
end
|
||||
|
||||
def next_path
|
||||
unless @notifications.empty?
|
||||
api_v1_notifications_url pagination_params(max_id: pagination_max_id)
|
||||
end
|
||||
end
|
||||
|
||||
def prev_path
|
||||
unless @notifications.empty?
|
||||
api_v1_notifications_url pagination_params(min_id: pagination_since_id)
|
||||
end
|
||||
end
|
||||
|
||||
def pagination_max_id
|
||||
@notifications.last.id
|
||||
end
|
||||
|
||||
def pagination_since_id
|
||||
@notifications.first.id
|
||||
end
|
||||
|
||||
def exclude_types
|
||||
val = params.permit(exclude_types: [])[:exclude_types] || []
|
||||
val = [val] unless val.is_a?(Enumerable)
|
||||
val
|
||||
end
|
||||
|
||||
def from_account
|
||||
params[:account_id]
|
||||
end
|
||||
|
||||
def pagination_params(core_params)
|
||||
params.slice(:limit, :exclude_types).permit(:limit, exclude_types: []).merge(core_params)
|
||||
end
|
||||
end
|
||||
29
app/controllers/api/v1/polls/votes_controller.rb
Normal file
29
app/controllers/api/v1/polls/votes_controller.rb
Normal file
@@ -0,0 +1,29 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::Polls::VotesController < Api::BaseController
|
||||
include Authorization
|
||||
|
||||
before_action -> { doorkeeper_authorize! :write, :'write:statuses' }
|
||||
before_action :require_user!
|
||||
before_action :set_poll
|
||||
|
||||
respond_to :json
|
||||
|
||||
def create
|
||||
VoteService.new.call(current_account, @poll, vote_params[:choices])
|
||||
render json: @poll, serializer: REST::PollSerializer
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_poll
|
||||
@poll = Poll.attached.find(params[:poll_id])
|
||||
authorize @poll.status, :show?
|
||||
rescue GabSocial::NotPermittedError
|
||||
raise ActiveRecord::RecordNotFound
|
||||
end
|
||||
|
||||
def vote_params
|
||||
params.permit(choices: [])
|
||||
end
|
||||
end
|
||||
13
app/controllers/api/v1/polls_controller.rb
Normal file
13
app/controllers/api/v1/polls_controller.rb
Normal file
@@ -0,0 +1,13 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::PollsController < Api::BaseController
|
||||
before_action -> { authorize_if_got_token! :read, :'read:statuses' }, only: :show
|
||||
|
||||
respond_to :json
|
||||
|
||||
def show
|
||||
@poll = Poll.attached.find(params[:id])
|
||||
ActivityPub::FetchRemotePollService.new.call(@poll, current_account) if user_signed_in? && @poll.possibly_stale?
|
||||
render json: @poll, serializer: REST::PollSerializer, include_results: true
|
||||
end
|
||||
end
|
||||
12
app/controllers/api/v1/preferences_controller.rb
Normal file
12
app/controllers/api/v1/preferences_controller.rb
Normal file
@@ -0,0 +1,12 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::PreferencesController < Api::BaseController
|
||||
before_action -> { doorkeeper_authorize! :read, :'read:accounts' }
|
||||
before_action :require_user!
|
||||
|
||||
respond_to :json
|
||||
|
||||
def index
|
||||
render json: current_account, serializer: REST::PreferencesSerializer
|
||||
end
|
||||
end
|
||||
56
app/controllers/api/v1/push/subscriptions_controller.rb
Normal file
56
app/controllers/api/v1/push/subscriptions_controller.rb
Normal file
@@ -0,0 +1,56 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::Push::SubscriptionsController < Api::BaseController
|
||||
before_action -> { doorkeeper_authorize! :push }
|
||||
before_action :require_user!
|
||||
before_action :set_web_push_subscription
|
||||
|
||||
def create
|
||||
@web_subscription&.destroy!
|
||||
|
||||
@web_subscription = ::Web::PushSubscription.create!(
|
||||
endpoint: subscription_params[:endpoint],
|
||||
key_p256dh: subscription_params[:keys][:p256dh],
|
||||
key_auth: subscription_params[:keys][:auth],
|
||||
data: data_params,
|
||||
user_id: current_user.id,
|
||||
access_token_id: doorkeeper_token.id
|
||||
)
|
||||
|
||||
render json: @web_subscription, serializer: REST::WebPushSubscriptionSerializer
|
||||
end
|
||||
|
||||
def show
|
||||
raise ActiveRecord::RecordNotFound if @web_subscription.nil?
|
||||
|
||||
render json: @web_subscription, serializer: REST::WebPushSubscriptionSerializer
|
||||
end
|
||||
|
||||
def update
|
||||
raise ActiveRecord::RecordNotFound if @web_subscription.nil?
|
||||
|
||||
@web_subscription.update!(data: data_params)
|
||||
|
||||
render json: @web_subscription, serializer: REST::WebPushSubscriptionSerializer
|
||||
end
|
||||
|
||||
def destroy
|
||||
@web_subscription&.destroy!
|
||||
render_empty
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_web_push_subscription
|
||||
@web_subscription = ::Web::PushSubscription.find_by(access_token_id: doorkeeper_token.id)
|
||||
end
|
||||
|
||||
def subscription_params
|
||||
params.require(:subscription).permit(:endpoint, keys: [:auth, :p256dh])
|
||||
end
|
||||
|
||||
def data_params
|
||||
return {} if params[:data].blank?
|
||||
params.require(:data).permit(alerts: [:follow, :favourite, :reblog, :mention, :poll])
|
||||
end
|
||||
end
|
||||
38
app/controllers/api/v1/reports_controller.rb
Normal file
38
app/controllers/api/v1/reports_controller.rb
Normal file
@@ -0,0 +1,38 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::ReportsController < Api::BaseController
|
||||
before_action -> { doorkeeper_authorize! :write, :'write:reports' }, only: [:create]
|
||||
before_action :require_user!
|
||||
|
||||
respond_to :json
|
||||
|
||||
def create
|
||||
@report = ReportService.new.call(
|
||||
current_account,
|
||||
reported_account,
|
||||
status_ids: reported_status_ids,
|
||||
comment: report_params[:comment],
|
||||
forward: report_params[:forward]
|
||||
)
|
||||
|
||||
render json: @report, serializer: REST::ReportSerializer
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def reported_status_ids
|
||||
reported_account.statuses.find(status_ids).pluck(:id)
|
||||
end
|
||||
|
||||
def status_ids
|
||||
Array(report_params[:status_ids])
|
||||
end
|
||||
|
||||
def reported_account
|
||||
Account.find(report_params[:account_id])
|
||||
end
|
||||
|
||||
def report_params
|
||||
params.permit(:account_id, :comment, :forward, status_ids: [])
|
||||
end
|
||||
end
|
||||
77
app/controllers/api/v1/scheduled_statuses_controller.rb
Normal file
77
app/controllers/api/v1/scheduled_statuses_controller.rb
Normal file
@@ -0,0 +1,77 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::ScheduledStatusesController < Api::BaseController
|
||||
include Authorization
|
||||
|
||||
before_action -> { doorkeeper_authorize! :read, :'read:statuses' }, except: [:update, :destroy]
|
||||
before_action -> { doorkeeper_authorize! :write, :'write:statuses' }, only: [:update, :destroy]
|
||||
|
||||
before_action :set_statuses, only: :index
|
||||
before_action :set_status, except: :index
|
||||
|
||||
after_action :insert_pagination_headers, only: :index
|
||||
|
||||
def index
|
||||
render json: @statuses, each_serializer: REST::ScheduledStatusSerializer
|
||||
end
|
||||
|
||||
def show
|
||||
render json: @status, serializer: REST::ScheduledStatusSerializer
|
||||
end
|
||||
|
||||
def update
|
||||
@status.update!(scheduled_status_params)
|
||||
render json: @status, serializer: REST::ScheduledStatusSerializer
|
||||
end
|
||||
|
||||
def destroy
|
||||
@status.destroy!
|
||||
render_empty
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_statuses
|
||||
@statuses = current_account.scheduled_statuses.paginate_by_id(limit_param(DEFAULT_STATUSES_LIMIT), params_slice(:max_id, :since_id, :min_id))
|
||||
end
|
||||
|
||||
def set_status
|
||||
@status = current_account.scheduled_statuses.find(params[:id])
|
||||
end
|
||||
|
||||
def scheduled_status_params
|
||||
params.permit(:scheduled_at)
|
||||
end
|
||||
|
||||
def pagination_params(core_params)
|
||||
params.slice(:limit).permit(:limit).merge(core_params)
|
||||
end
|
||||
|
||||
def insert_pagination_headers
|
||||
set_pagination_headers(next_path, prev_path)
|
||||
end
|
||||
|
||||
def next_path
|
||||
if records_continue?
|
||||
api_v1_scheduled_statuses_url pagination_params(max_id: pagination_max_id)
|
||||
end
|
||||
end
|
||||
|
||||
def prev_path
|
||||
unless @statuses.empty?
|
||||
api_v1_scheduled_statuses_url pagination_params(min_id: pagination_since_id)
|
||||
end
|
||||
end
|
||||
|
||||
def records_continue?
|
||||
@statuses.size == limit_param(DEFAULT_STATUSES_LIMIT)
|
||||
end
|
||||
|
||||
def pagination_max_id
|
||||
@statuses.last.id
|
||||
end
|
||||
|
||||
def pagination_since_id
|
||||
@statuses.first.id
|
||||
end
|
||||
end
|
||||
27
app/controllers/api/v1/search_controller.rb
Normal file
27
app/controllers/api/v1/search_controller.rb
Normal file
@@ -0,0 +1,27 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::SearchController < Api::BaseController
|
||||
RESULTS_LIMIT = 20
|
||||
|
||||
respond_to :json
|
||||
|
||||
def index
|
||||
@search = Search.new(search_results)
|
||||
render json: @search, serializer: REST::SearchSerializer
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def search_results
|
||||
SearchService.new.call(
|
||||
params[:q],
|
||||
current_account,
|
||||
limit_param(RESULTS_LIMIT),
|
||||
search_params.merge(resolve: truthy_param?(:resolve))
|
||||
)
|
||||
end
|
||||
|
||||
def search_params
|
||||
params.permit(:type, :offset, :min_id, :max_id, :account_id)
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,77 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::Statuses::FavouritedByAccountsController < Api::BaseController
|
||||
include Authorization
|
||||
|
||||
before_action -> { authorize_if_got_token! :read, :'read:accounts' }
|
||||
before_action :set_status
|
||||
after_action :insert_pagination_headers
|
||||
|
||||
respond_to :json
|
||||
|
||||
def index
|
||||
@accounts = load_accounts
|
||||
render json: @accounts, each_serializer: REST::AccountSerializer
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def load_accounts
|
||||
default_accounts.merge(paginated_favourites).to_a
|
||||
end
|
||||
|
||||
def default_accounts
|
||||
Account
|
||||
.includes(:favourites, :account_stat)
|
||||
.references(:favourites)
|
||||
.where(favourites: { status_id: @status.id })
|
||||
end
|
||||
|
||||
def paginated_favourites
|
||||
Favourite.paginate_by_max_id(
|
||||
limit_param(DEFAULT_ACCOUNTS_LIMIT),
|
||||
params[:max_id],
|
||||
params[:since_id]
|
||||
)
|
||||
end
|
||||
|
||||
def insert_pagination_headers
|
||||
set_pagination_headers(next_path, prev_path)
|
||||
end
|
||||
|
||||
def next_path
|
||||
if records_continue?
|
||||
api_v1_status_favourited_by_index_url pagination_params(max_id: pagination_max_id)
|
||||
end
|
||||
end
|
||||
|
||||
def prev_path
|
||||
unless @accounts.empty?
|
||||
api_v1_status_favourited_by_index_url pagination_params(since_id: pagination_since_id)
|
||||
end
|
||||
end
|
||||
|
||||
def pagination_max_id
|
||||
@accounts.last.favourites.last.id
|
||||
end
|
||||
|
||||
def pagination_since_id
|
||||
@accounts.first.favourites.first.id
|
||||
end
|
||||
|
||||
def records_continue?
|
||||
@accounts.size == limit_param(DEFAULT_ACCOUNTS_LIMIT)
|
||||
end
|
||||
|
||||
def set_status
|
||||
@status = Status.find(params[:status_id])
|
||||
authorize @status, :show?
|
||||
rescue GabSocial::NotPermittedError
|
||||
# Reraise in order to get a 404 instead of a 403 error code
|
||||
raise ActiveRecord::RecordNotFound
|
||||
end
|
||||
|
||||
def pagination_params(core_params)
|
||||
params.slice(:limit).permit(:limit).merge(core_params)
|
||||
end
|
||||
end
|
||||
38
app/controllers/api/v1/statuses/favourites_controller.rb
Normal file
38
app/controllers/api/v1/statuses/favourites_controller.rb
Normal file
@@ -0,0 +1,38 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::Statuses::FavouritesController < Api::BaseController
|
||||
include Authorization
|
||||
|
||||
before_action -> { doorkeeper_authorize! :write, :'write:favourites' }
|
||||
before_action :require_user!
|
||||
|
||||
respond_to :json
|
||||
|
||||
def create
|
||||
@status = favourited_status
|
||||
render json: @status, serializer: REST::StatusSerializer
|
||||
end
|
||||
|
||||
def destroy
|
||||
@status = requested_status
|
||||
@favourites_map = { @status.id => false }
|
||||
|
||||
UnfavouriteWorker.perform_async(current_user.account_id, @status.id)
|
||||
|
||||
render json: @status, serializer: REST::StatusSerializer, relationships: StatusRelationshipsPresenter.new([@status], current_user&.account_id, favourites_map: @favourites_map)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def favourited_status
|
||||
service_result.status.reload
|
||||
end
|
||||
|
||||
def service_result
|
||||
FavouriteService.new.call(current_user.account, requested_status)
|
||||
end
|
||||
|
||||
def requested_status
|
||||
Status.find(params[:status_id])
|
||||
end
|
||||
end
|
||||
41
app/controllers/api/v1/statuses/mutes_controller.rb
Normal file
41
app/controllers/api/v1/statuses/mutes_controller.rb
Normal file
@@ -0,0 +1,41 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::Statuses::MutesController < Api::BaseController
|
||||
include Authorization
|
||||
|
||||
before_action -> { doorkeeper_authorize! :write, :'write:mutes' }
|
||||
before_action :require_user!
|
||||
before_action :set_status
|
||||
before_action :set_conversation
|
||||
|
||||
respond_to :json
|
||||
|
||||
def create
|
||||
current_account.mute_conversation!(@conversation)
|
||||
@mutes_map = { @conversation.id => true }
|
||||
|
||||
render json: @status, serializer: REST::StatusSerializer
|
||||
end
|
||||
|
||||
def destroy
|
||||
current_account.unmute_conversation!(@conversation)
|
||||
@mutes_map = { @conversation.id => false }
|
||||
|
||||
render json: @status, serializer: REST::StatusSerializer
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_status
|
||||
@status = Status.find(params[:status_id])
|
||||
authorize @status, :show?
|
||||
rescue GabSocial::NotPermittedError
|
||||
# Reraise in order to get a 404 instead of a 403 error code
|
||||
raise ActiveRecord::RecordNotFound
|
||||
end
|
||||
|
||||
def set_conversation
|
||||
@conversation = @status.conversation
|
||||
raise GabSocial::ValidationError if @conversation.nil?
|
||||
end
|
||||
end
|
||||
54
app/controllers/api/v1/statuses/pins_controller.rb
Normal file
54
app/controllers/api/v1/statuses/pins_controller.rb
Normal file
@@ -0,0 +1,54 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::Statuses::PinsController < Api::BaseController
|
||||
include Authorization
|
||||
|
||||
before_action -> { doorkeeper_authorize! :write, :'write:accounts' }
|
||||
before_action :require_user!
|
||||
before_action :set_status
|
||||
|
||||
respond_to :json
|
||||
|
||||
def create
|
||||
StatusPin.create!(account: current_account, status: @status)
|
||||
distribute_add_activity!
|
||||
render json: @status, serializer: REST::StatusSerializer
|
||||
end
|
||||
|
||||
def destroy
|
||||
pin = StatusPin.find_by(account: current_account, status: @status)
|
||||
|
||||
if pin
|
||||
pin.destroy!
|
||||
distribute_remove_activity!
|
||||
end
|
||||
|
||||
render json: @status, serializer: REST::StatusSerializer
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_status
|
||||
@status = Status.find(params[:status_id])
|
||||
end
|
||||
|
||||
def distribute_add_activity!
|
||||
json = ActiveModelSerializers::SerializableResource.new(
|
||||
@status,
|
||||
serializer: ActivityPub::AddSerializer,
|
||||
adapter: ActivityPub::Adapter
|
||||
).as_json
|
||||
|
||||
ActivityPub::RawDistributionWorker.perform_async(Oj.dump(json), current_account.id)
|
||||
end
|
||||
|
||||
def distribute_remove_activity!
|
||||
json = ActiveModelSerializers::SerializableResource.new(
|
||||
@status,
|
||||
serializer: ActivityPub::RemoveSerializer,
|
||||
adapter: ActivityPub::Adapter
|
||||
).as_json
|
||||
|
||||
ActivityPub::RawDistributionWorker.perform_async(Oj.dump(json), current_account.id)
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,74 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::Statuses::RebloggedByAccountsController < Api::BaseController
|
||||
include Authorization
|
||||
|
||||
before_action -> { authorize_if_got_token! :read, :'read:accounts' }
|
||||
before_action :set_status
|
||||
after_action :insert_pagination_headers
|
||||
|
||||
respond_to :json
|
||||
|
||||
def index
|
||||
@accounts = load_accounts
|
||||
render json: @accounts, each_serializer: REST::AccountSerializer
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def load_accounts
|
||||
default_accounts.merge(paginated_statuses).to_a
|
||||
end
|
||||
|
||||
def default_accounts
|
||||
Account.includes(:statuses, :account_stat).references(:statuses)
|
||||
end
|
||||
|
||||
def paginated_statuses
|
||||
Status.where(reblog_of_id: @status.id).where(visibility: [:public, :unlisted]).paginate_by_max_id(
|
||||
limit_param(DEFAULT_ACCOUNTS_LIMIT),
|
||||
params[:max_id],
|
||||
params[:since_id]
|
||||
)
|
||||
end
|
||||
|
||||
def insert_pagination_headers
|
||||
set_pagination_headers(next_path, prev_path)
|
||||
end
|
||||
|
||||
def next_path
|
||||
if records_continue?
|
||||
api_v1_status_reblogged_by_index_url pagination_params(max_id: pagination_max_id)
|
||||
end
|
||||
end
|
||||
|
||||
def prev_path
|
||||
unless @accounts.empty?
|
||||
api_v1_status_reblogged_by_index_url pagination_params(since_id: pagination_since_id)
|
||||
end
|
||||
end
|
||||
|
||||
def pagination_max_id
|
||||
@accounts.last.statuses.last.id
|
||||
end
|
||||
|
||||
def pagination_since_id
|
||||
@accounts.first.statuses.first.id
|
||||
end
|
||||
|
||||
def records_continue?
|
||||
@accounts.size == limit_param(DEFAULT_ACCOUNTS_LIMIT)
|
||||
end
|
||||
|
||||
def set_status
|
||||
@status = Status.find(params[:status_id])
|
||||
authorize @status, :show?
|
||||
rescue GabSocial::NotPermittedError
|
||||
# Reraise in order to get a 404 instead of a 403 error code
|
||||
raise ActiveRecord::RecordNotFound
|
||||
end
|
||||
|
||||
def pagination_params(core_params)
|
||||
params.slice(:limit).permit(:limit).merge(core_params)
|
||||
end
|
||||
end
|
||||
39
app/controllers/api/v1/statuses/reblogs_controller.rb
Normal file
39
app/controllers/api/v1/statuses/reblogs_controller.rb
Normal file
@@ -0,0 +1,39 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::Statuses::ReblogsController < Api::BaseController
|
||||
include Authorization
|
||||
|
||||
before_action -> { doorkeeper_authorize! :write, :'write:statuses' }
|
||||
before_action :require_user!
|
||||
|
||||
respond_to :json
|
||||
|
||||
def create
|
||||
@status = ReblogService.new.call(current_user.account, status_for_reblog, reblog_params)
|
||||
render json: @status, serializer: REST::StatusSerializer
|
||||
end
|
||||
|
||||
def destroy
|
||||
@status = status_for_destroy.reblog
|
||||
@reblogs_map = { @status.id => false }
|
||||
|
||||
authorize status_for_destroy, :unreblog?
|
||||
RemovalWorker.perform_async(status_for_destroy.id)
|
||||
|
||||
render json: @status, serializer: REST::StatusSerializer, relationships: StatusRelationshipsPresenter.new([@status], current_user&.account_id, reblogs_map: @reblogs_map)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def status_for_reblog
|
||||
Status.find params[:status_id]
|
||||
end
|
||||
|
||||
def status_for_destroy
|
||||
current_user.account.statuses.where(reblog_of_id: params[:status_id]).first!
|
||||
end
|
||||
|
||||
def reblog_params
|
||||
params.permit(:visibility, :status)
|
||||
end
|
||||
end
|
||||
103
app/controllers/api/v1/statuses_controller.rb
Normal file
103
app/controllers/api/v1/statuses_controller.rb
Normal file
@@ -0,0 +1,103 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::StatusesController < Api::BaseController
|
||||
include Authorization
|
||||
|
||||
before_action -> { authorize_if_got_token! :read, :'read:statuses' }, except: [:create, :destroy]
|
||||
before_action -> { doorkeeper_authorize! :write, :'write:statuses' }, only: [:create, :destroy]
|
||||
before_action :require_user!, except: [:show, :context, :card]
|
||||
before_action :set_status, only: [:show, :context, :card]
|
||||
|
||||
respond_to :json
|
||||
|
||||
# This API was originally unlimited, pagination cannot be introduced without
|
||||
# breaking backwards-compatibility. Arbitrarily high number to cover most
|
||||
# conversations as quasi-unlimited, it would be too much work to render more
|
||||
# than this anyway
|
||||
CONTEXT_LIMIT = 4_096
|
||||
|
||||
def show
|
||||
@status = cache_collection([@status], Status).first
|
||||
render json: @status, serializer: REST::StatusSerializer
|
||||
end
|
||||
|
||||
def context
|
||||
ancestors_results = @status.in_reply_to_id.nil? ? [] : @status.ancestors(CONTEXT_LIMIT, current_account)
|
||||
descendants_results = @status.descendants(CONTEXT_LIMIT, current_account)
|
||||
loaded_ancestors = cache_collection(ancestors_results, Status)
|
||||
loaded_descendants = cache_collection(descendants_results, Status)
|
||||
|
||||
@context = Context.new(ancestors: loaded_ancestors, descendants: loaded_descendants)
|
||||
statuses = [@status] + @context.ancestors + @context.descendants
|
||||
|
||||
render json: @context, serializer: REST::ContextSerializer, relationships: StatusRelationshipsPresenter.new(statuses, current_user&.account_id)
|
||||
end
|
||||
|
||||
def card
|
||||
@card = @status.preview_cards.first
|
||||
|
||||
if @card.nil?
|
||||
render_empty
|
||||
else
|
||||
render json: @card, serializer: REST::PreviewCardSerializer
|
||||
end
|
||||
end
|
||||
|
||||
def create
|
||||
@status = PostStatusService.new.call(current_user.account,
|
||||
text: status_params[:status],
|
||||
thread: status_params[:in_reply_to_id].blank? ? nil : Status.find(status_params[:in_reply_to_id]),
|
||||
media_ids: status_params[:media_ids],
|
||||
sensitive: status_params[:sensitive],
|
||||
spoiler_text: status_params[:spoiler_text],
|
||||
visibility: status_params[:visibility],
|
||||
scheduled_at: status_params[:scheduled_at],
|
||||
application: doorkeeper_token.application,
|
||||
poll: status_params[:poll],
|
||||
idempotency: request.headers['Idempotency-Key'],
|
||||
group_id: status_params[:group_id])
|
||||
|
||||
render json: @status, serializer: @status.is_a?(ScheduledStatus) ? REST::ScheduledStatusSerializer : REST::StatusSerializer
|
||||
end
|
||||
|
||||
def destroy
|
||||
@status = Status.where(account_id: current_user.account).find(params[:id])
|
||||
authorize @status, :destroy?
|
||||
|
||||
RemovalWorker.perform_async(@status.id)
|
||||
|
||||
render json: @status, serializer: REST::StatusSerializer, source_requested: true
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_status
|
||||
@status = Status.find(params[:id])
|
||||
authorize @status, :show?
|
||||
rescue GabSocial::NotPermittedError
|
||||
raise ActiveRecord::RecordNotFound
|
||||
end
|
||||
|
||||
def status_params
|
||||
params.permit(
|
||||
:status,
|
||||
:in_reply_to_id,
|
||||
:sensitive,
|
||||
:spoiler_text,
|
||||
:visibility,
|
||||
:scheduled_at,
|
||||
:group_id,
|
||||
media_ids: [],
|
||||
poll: [
|
||||
:multiple,
|
||||
:hide_totals,
|
||||
:expires_in,
|
||||
options: [],
|
||||
],
|
||||
)
|
||||
end
|
||||
|
||||
def pagination_params(core_params)
|
||||
params.slice(:limit).permit(:limit).merge(core_params)
|
||||
end
|
||||
end
|
||||
15
app/controllers/api/v1/streaming_controller.rb
Normal file
15
app/controllers/api/v1/streaming_controller.rb
Normal file
@@ -0,0 +1,15 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::StreamingController < Api::BaseController
|
||||
respond_to :json
|
||||
|
||||
def index
|
||||
if Rails.configuration.x.streaming_api_base_url != request.host
|
||||
uri = URI.parse(request.url)
|
||||
uri.host = URI.parse(Rails.configuration.x.streaming_api_base_url).host
|
||||
redirect_to uri.to_s, status: 301
|
||||
else
|
||||
raise ActiveRecord::RecordNotFound
|
||||
end
|
||||
end
|
||||
end
|
||||
26
app/controllers/api/v1/suggestions_controller.rb
Normal file
26
app/controllers/api/v1/suggestions_controller.rb
Normal file
@@ -0,0 +1,26 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::SuggestionsController < Api::BaseController
|
||||
include Authorization
|
||||
|
||||
before_action -> { doorkeeper_authorize! :read }
|
||||
before_action :require_user!
|
||||
before_action :set_accounts
|
||||
|
||||
respond_to :json
|
||||
|
||||
def index
|
||||
render json: @accounts, each_serializer: REST::AccountSerializer
|
||||
end
|
||||
|
||||
def destroy
|
||||
PotentialFriendshipTracker.remove(current_account.id, params[:id])
|
||||
render_empty
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_accounts
|
||||
@accounts = PotentialFriendshipTracker.get(current_account.id, limit: limit_param(DEFAULT_ACCOUNTS_LIMIT))
|
||||
end
|
||||
end
|
||||
63
app/controllers/api/v1/timelines/direct_controller.rb
Normal file
63
app/controllers/api/v1/timelines/direct_controller.rb
Normal file
@@ -0,0 +1,63 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::Timelines::DirectController < Api::BaseController
|
||||
before_action -> { doorkeeper_authorize! :read, :'read:statuses' }, only: [:show]
|
||||
before_action :require_user!, only: [:show]
|
||||
after_action :insert_pagination_headers, unless: -> { @statuses.empty? }
|
||||
|
||||
respond_to :json
|
||||
|
||||
def show
|
||||
@statuses = load_statuses
|
||||
render json: @statuses, each_serializer: REST::StatusSerializer, relationships: StatusRelationshipsPresenter.new(@statuses, current_user&.account_id)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def load_statuses
|
||||
cached_direct_statuses
|
||||
end
|
||||
|
||||
def cached_direct_statuses
|
||||
cache_collection direct_statuses, Status
|
||||
end
|
||||
|
||||
def direct_statuses
|
||||
direct_timeline_statuses
|
||||
end
|
||||
|
||||
def direct_timeline_statuses
|
||||
# this query requires built in pagination.
|
||||
Status.as_direct_timeline(
|
||||
current_account,
|
||||
limit_param(DEFAULT_STATUSES_LIMIT),
|
||||
params[:max_id],
|
||||
params[:since_id],
|
||||
true # returns array of cache_ids object
|
||||
)
|
||||
end
|
||||
|
||||
def insert_pagination_headers
|
||||
set_pagination_headers(next_path, prev_path)
|
||||
end
|
||||
|
||||
def pagination_params(core_params)
|
||||
params.permit(:local, :limit).merge(core_params)
|
||||
end
|
||||
|
||||
def next_path
|
||||
api_v1_timelines_direct_url pagination_params(max_id: pagination_max_id)
|
||||
end
|
||||
|
||||
def prev_path
|
||||
api_v1_timelines_direct_url pagination_params(since_id: pagination_since_id)
|
||||
end
|
||||
|
||||
def pagination_max_id
|
||||
@statuses.last.id
|
||||
end
|
||||
|
||||
def pagination_since_id
|
||||
@statuses.first.id
|
||||
end
|
||||
end
|
||||
73
app/controllers/api/v1/timelines/group_controller.rb
Normal file
73
app/controllers/api/v1/timelines/group_controller.rb
Normal file
@@ -0,0 +1,73 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::Timelines::GroupController < Api::BaseController
|
||||
before_action -> { doorkeeper_authorize! :read, :'read:groups' }
|
||||
before_action :require_user!
|
||||
before_action :set_group
|
||||
before_action :set_statuses
|
||||
|
||||
after_action :insert_pagination_headers, unless: -> { @statuses.empty? }
|
||||
|
||||
def show
|
||||
render json: @statuses,
|
||||
each_serializer: REST::StatusSerializer,
|
||||
relationships: StatusRelationshipsPresenter.new(@statuses, current_user.account_id)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_group
|
||||
@group = Group.find(params[:id])
|
||||
end
|
||||
|
||||
def set_statuses
|
||||
@statuses = cached_group_statuses
|
||||
end
|
||||
|
||||
def cached_group_statuses
|
||||
cache_collection group_statuses, Status
|
||||
end
|
||||
|
||||
def group_statuses
|
||||
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
|
||||
|
||||
def group_statuses
|
||||
GroupQueryService.new.call(@group)
|
||||
end
|
||||
|
||||
def insert_pagination_headers
|
||||
set_pagination_headers(next_path, prev_path)
|
||||
end
|
||||
|
||||
def pagination_params(core_params)
|
||||
params.slice(:limit).permit(:limit).merge(core_params)
|
||||
end
|
||||
|
||||
def next_path
|
||||
api_v1_timelines_group_url params[:id], pagination_params(max_id: pagination_max_id)
|
||||
end
|
||||
|
||||
def prev_path
|
||||
api_v1_timelines_group_url params[:id], pagination_params(min_id: pagination_since_id)
|
||||
end
|
||||
|
||||
def pagination_max_id
|
||||
@statuses.last.id
|
||||
end
|
||||
|
||||
def pagination_since_id
|
||||
@statuses.first.id
|
||||
end
|
||||
end
|
||||
69
app/controllers/api/v1/timelines/home_controller.rb
Normal file
69
app/controllers/api/v1/timelines/home_controller.rb
Normal file
@@ -0,0 +1,69 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::Timelines::HomeController < Api::BaseController
|
||||
before_action -> { doorkeeper_authorize! :read, :'read:statuses' }, only: [:show]
|
||||
before_action :require_user!, only: [:show]
|
||||
after_action :insert_pagination_headers, unless: -> { @statuses.empty? }
|
||||
|
||||
respond_to :json
|
||||
|
||||
def show
|
||||
@statuses = load_statuses
|
||||
|
||||
render json: @statuses,
|
||||
each_serializer: REST::StatusSerializer,
|
||||
relationships: StatusRelationshipsPresenter.new(@statuses, current_user&.account_id),
|
||||
status: regeneration_in_progress? ? 206 : 200
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def load_statuses
|
||||
cached_home_statuses
|
||||
end
|
||||
|
||||
def cached_home_statuses
|
||||
cache_collection home_statuses, Status
|
||||
end
|
||||
|
||||
def home_statuses
|
||||
account_home_feed.get(
|
||||
limit_param(DEFAULT_STATUSES_LIMIT),
|
||||
params[:max_id],
|
||||
params[:since_id],
|
||||
params[:min_id]
|
||||
)
|
||||
end
|
||||
|
||||
def account_home_feed
|
||||
HomeFeed.new(current_account)
|
||||
end
|
||||
|
||||
def insert_pagination_headers
|
||||
set_pagination_headers(next_path, prev_path)
|
||||
end
|
||||
|
||||
def pagination_params(core_params)
|
||||
params.slice(:local, :limit).permit(:local, :limit).merge(core_params)
|
||||
end
|
||||
|
||||
def next_path
|
||||
api_v1_timelines_home_url pagination_params(max_id: pagination_max_id)
|
||||
end
|
||||
|
||||
def prev_path
|
||||
api_v1_timelines_home_url pagination_params(min_id: pagination_since_id)
|
||||
end
|
||||
|
||||
def pagination_max_id
|
||||
@statuses.last.id
|
||||
end
|
||||
|
||||
def pagination_since_id
|
||||
@statuses.first.id
|
||||
end
|
||||
|
||||
def regeneration_in_progress?
|
||||
Redis.current.exists("account:#{current_account.id}:regeneration")
|
||||
end
|
||||
end
|
||||
67
app/controllers/api/v1/timelines/list_controller.rb
Normal file
67
app/controllers/api/v1/timelines/list_controller.rb
Normal file
@@ -0,0 +1,67 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::Timelines::ListController < Api::BaseController
|
||||
before_action -> { doorkeeper_authorize! :read, :'read:lists' }
|
||||
before_action :require_user!
|
||||
before_action :set_list
|
||||
before_action :set_statuses
|
||||
|
||||
after_action :insert_pagination_headers, unless: -> { @statuses.empty? }
|
||||
|
||||
def show
|
||||
render json: @statuses,
|
||||
each_serializer: REST::StatusSerializer,
|
||||
relationships: StatusRelationshipsPresenter.new(@statuses, current_user.account_id)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_list
|
||||
@list = List.where(account: current_account).find(params[:id])
|
||||
end
|
||||
|
||||
def set_statuses
|
||||
@statuses = cached_list_statuses
|
||||
end
|
||||
|
||||
def cached_list_statuses
|
||||
cache_collection list_statuses, Status
|
||||
end
|
||||
|
||||
def list_statuses
|
||||
list_feed.get(
|
||||
limit_param(DEFAULT_STATUSES_LIMIT),
|
||||
params[:max_id],
|
||||
params[:since_id],
|
||||
params[:min_id]
|
||||
)
|
||||
end
|
||||
|
||||
def list_feed
|
||||
ListFeed.new(@list)
|
||||
end
|
||||
|
||||
def insert_pagination_headers
|
||||
set_pagination_headers(next_path, prev_path)
|
||||
end
|
||||
|
||||
def pagination_params(core_params)
|
||||
params.slice(:limit).permit(:limit).merge(core_params)
|
||||
end
|
||||
|
||||
def next_path
|
||||
api_v1_timelines_list_url params[:id], pagination_params(max_id: pagination_max_id)
|
||||
end
|
||||
|
||||
def prev_path
|
||||
api_v1_timelines_list_url params[:id], pagination_params(min_id: pagination_since_id)
|
||||
end
|
||||
|
||||
def pagination_max_id
|
||||
@statuses.last.id
|
||||
end
|
||||
|
||||
def pagination_since_id
|
||||
@statuses.first.id
|
||||
end
|
||||
end
|
||||
65
app/controllers/api/v1/timelines/public_controller.rb
Normal file
65
app/controllers/api/v1/timelines/public_controller.rb
Normal file
@@ -0,0 +1,65 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::Timelines::PublicController < Api::BaseController
|
||||
after_action :insert_pagination_headers, unless: -> { @statuses.empty? }
|
||||
|
||||
respond_to :json
|
||||
|
||||
def show
|
||||
@statuses = load_statuses
|
||||
render json: @statuses, each_serializer: REST::StatusSerializer, relationships: StatusRelationshipsPresenter.new(@statuses, current_user&.account_id)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def load_statuses
|
||||
cached_public_statuses
|
||||
end
|
||||
|
||||
def cached_public_statuses
|
||||
cache_collection public_statuses, Status
|
||||
end
|
||||
|
||||
def public_statuses
|
||||
statuses = public_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
|
||||
|
||||
def public_timeline_statuses
|
||||
Status.as_public_timeline(current_account, truthy_param?(:local))
|
||||
end
|
||||
|
||||
def insert_pagination_headers
|
||||
set_pagination_headers(next_path, prev_path)
|
||||
end
|
||||
|
||||
def pagination_params(core_params)
|
||||
params.slice(:local, :limit, :only_media).permit(:local, :limit, :only_media).merge(core_params)
|
||||
end
|
||||
|
||||
def next_path
|
||||
api_v1_timelines_public_url pagination_params(max_id: pagination_max_id)
|
||||
end
|
||||
|
||||
def prev_path
|
||||
api_v1_timelines_public_url pagination_params(min_id: pagination_since_id)
|
||||
end
|
||||
|
||||
def pagination_max_id
|
||||
@statuses.last.id
|
||||
end
|
||||
|
||||
def pagination_since_id
|
||||
@statuses.first.id
|
||||
end
|
||||
end
|
||||
74
app/controllers/api/v1/timelines/tag_controller.rb
Normal file
74
app/controllers/api/v1/timelines/tag_controller.rb
Normal file
@@ -0,0 +1,74 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::Timelines::TagController < Api::BaseController
|
||||
before_action :load_tag
|
||||
after_action :insert_pagination_headers, unless: -> { @statuses.empty? }
|
||||
|
||||
respond_to :json
|
||||
|
||||
def show
|
||||
@statuses = load_statuses
|
||||
render json: @statuses, each_serializer: REST::StatusSerializer, relationships: StatusRelationshipsPresenter.new(@statuses, current_user&.account_id)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def load_tag
|
||||
@tag = Tag.find_normalized(params[:id])
|
||||
end
|
||||
|
||||
def load_statuses
|
||||
cached_tagged_statuses
|
||||
end
|
||||
|
||||
def cached_tagged_statuses
|
||||
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
|
||||
|
||||
def tag_timeline_statuses
|
||||
HashtagQueryService.new.call(@tag, params.slice(:any, :all, :none), current_account, truthy_param?(:local))
|
||||
end
|
||||
|
||||
def insert_pagination_headers
|
||||
set_pagination_headers(next_path, prev_path)
|
||||
end
|
||||
|
||||
def pagination_params(core_params)
|
||||
params.slice(:local, :limit, :only_media).permit(:local, :limit, :only_media).merge(core_params)
|
||||
end
|
||||
|
||||
def next_path
|
||||
api_v1_timelines_tag_url params[:id], pagination_params(max_id: pagination_max_id)
|
||||
end
|
||||
|
||||
def prev_path
|
||||
api_v1_timelines_tag_url params[:id], pagination_params(min_id: pagination_since_id)
|
||||
end
|
||||
|
||||
def pagination_max_id
|
||||
@statuses.last.id
|
||||
end
|
||||
|
||||
def pagination_since_id
|
||||
@statuses.first.id
|
||||
end
|
||||
end
|
||||
21
app/controllers/api/v1/trends_controller.rb
Normal file
21
app/controllers/api/v1/trends_controller.rb
Normal file
@@ -0,0 +1,21 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::TrendsController < Api::BaseController
|
||||
include Authorization
|
||||
|
||||
before_action -> { doorkeeper_authorize! :read }
|
||||
before_action :require_user!
|
||||
before_action :set_tags
|
||||
|
||||
respond_to :json
|
||||
|
||||
def index
|
||||
render json: @tags, each_serializer: REST::TagSerializer
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_tags
|
||||
@tags = TrendingTags.get(7)
|
||||
end
|
||||
end
|
||||
8
app/controllers/api/v2/search_controller.rb
Normal file
8
app/controllers/api/v2/search_controller.rb
Normal file
@@ -0,0 +1,8 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V2::SearchController < Api::V1::SearchController
|
||||
def index
|
||||
@search = Search.new(search_results)
|
||||
render json: @search, serializer: REST::V2::SearchSerializer
|
||||
end
|
||||
end
|
||||
9
app/controllers/api/web/base_controller.rb
Normal file
9
app/controllers/api/web/base_controller.rb
Normal file
@@ -0,0 +1,9 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::Web::BaseController < Api::BaseController
|
||||
protect_from_forgery with: :exception
|
||||
|
||||
rescue_from ActionController::InvalidAuthenticityToken do
|
||||
render json: { error: "Can't verify CSRF token authenticity." }, status: 422
|
||||
end
|
||||
end
|
||||
21
app/controllers/api/web/embeds_controller.rb
Normal file
21
app/controllers/api/web/embeds_controller.rb
Normal file
@@ -0,0 +1,21 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::Web::EmbedsController < Api::Web::BaseController
|
||||
respond_to :json
|
||||
|
||||
before_action :require_user!
|
||||
|
||||
def create
|
||||
status = StatusFinder.new(params[:url]).status
|
||||
render json: status, serializer: OEmbedSerializer, width: 400
|
||||
rescue ActiveRecord::RecordNotFound
|
||||
oembed = FetchOEmbedService.new.call(params[:url])
|
||||
oembed[:html] = Formatter.instance.sanitize(oembed[:html], Sanitize::Config::GABSOCIAL_OEMBED) if oembed[:html].present?
|
||||
|
||||
if oembed
|
||||
render json: oembed
|
||||
else
|
||||
render json: {}, status: :not_found
|
||||
end
|
||||
end
|
||||
end
|
||||
63
app/controllers/api/web/push_subscriptions_controller.rb
Normal file
63
app/controllers/api/web/push_subscriptions_controller.rb
Normal file
@@ -0,0 +1,63 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::Web::PushSubscriptionsController < Api::Web::BaseController
|
||||
respond_to :json
|
||||
|
||||
before_action :require_user!
|
||||
|
||||
def create
|
||||
active_session = current_session
|
||||
|
||||
unless active_session.web_push_subscription.nil?
|
||||
active_session.web_push_subscription.destroy!
|
||||
active_session.update!(web_push_subscription: nil)
|
||||
end
|
||||
|
||||
# Mobile devices do not support regular notifications, so we enable push notifications by default
|
||||
alerts_enabled = active_session.detection.device.mobile? || active_session.detection.device.tablet?
|
||||
|
||||
data = {
|
||||
alerts: {
|
||||
follow: alerts_enabled,
|
||||
favourite: alerts_enabled,
|
||||
reblog: alerts_enabled,
|
||||
mention: alerts_enabled,
|
||||
poll: alerts_enabled,
|
||||
},
|
||||
}
|
||||
|
||||
data.deep_merge!(data_params) if params[:data]
|
||||
|
||||
web_subscription = ::Web::PushSubscription.create!(
|
||||
endpoint: subscription_params[:endpoint],
|
||||
key_p256dh: subscription_params[:keys][:p256dh],
|
||||
key_auth: subscription_params[:keys][:auth],
|
||||
data: data,
|
||||
user_id: active_session.user_id,
|
||||
access_token_id: active_session.access_token_id
|
||||
)
|
||||
|
||||
active_session.update!(web_push_subscription: web_subscription)
|
||||
|
||||
render json: web_subscription, serializer: REST::WebPushSubscriptionSerializer
|
||||
end
|
||||
|
||||
def update
|
||||
params.require([:id])
|
||||
|
||||
web_subscription = ::Web::PushSubscription.find(params[:id])
|
||||
web_subscription.update!(data: data_params)
|
||||
|
||||
render json: web_subscription, serializer: REST::WebPushSubscriptionSerializer
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def subscription_params
|
||||
@subscription_params ||= params.require(:subscription).permit(:endpoint, keys: [:auth, :p256dh])
|
||||
end
|
||||
|
||||
def data_params
|
||||
@data_params ||= params.require(:data).permit(alerts: [:follow, :favourite, :reblog, :mention, :poll])
|
||||
end
|
||||
end
|
||||
20
app/controllers/api/web/settings_controller.rb
Normal file
20
app/controllers/api/web/settings_controller.rb
Normal file
@@ -0,0 +1,20 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::Web::SettingsController < Api::Web::BaseController
|
||||
respond_to :json
|
||||
|
||||
before_action :require_user!
|
||||
|
||||
def update
|
||||
setting.data = params[:data]
|
||||
setting.save!
|
||||
|
||||
render_empty
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def setting
|
||||
@_setting ||= ::Web::Setting.where(user: current_user).first_or_initialize(user: current_user)
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user