2016-11-15 16:56:29 +01:00
# frozen_string_literal: true
2016-03-05 12:50:59 +01:00
class ApiController < ApplicationController
2016-11-09 17:48:44 +01:00
DEFAULT_STATUSES_LIMIT = 20
DEFAULT_ACCOUNTS_LIMIT = 40
2016-03-05 12:50:59 +01:00
protect_from_forgery with : :null_session
2016-10-22 19:38:47 +02:00
2016-08-17 17:56:23 +02:00
skip_before_action :verify_authenticity_token
2017-04-09 22:21:52 +02:00
skip_before_action :store_current_location
2016-03-07 12:42:33 +01:00
2016-10-22 19:38:47 +02:00
before_action :set_rate_limit_headers
2017-02-26 23:23:06 +01:00
rescue_from ActiveRecord :: RecordInvalid , Mastodon :: ValidationError do | e |
2016-09-30 22:31:16 +02:00
render json : { error : e . to_s } , status : 422
2016-08-26 19:12:19 +02:00
end
rescue_from ActiveRecord :: RecordNotFound do
render json : { error : 'Record not found' } , status : 404
end
2016-09-17 17:03:36 +02:00
rescue_from Goldfinger :: Error do
render json : { error : 'Remote account could not be resolved' } , status : 422
end
rescue_from HTTP :: Error do
render json : { error : 'Remote data could not be fetched' } , status : 503
end
2016-10-05 13:26:44 +02:00
rescue_from OpenSSL :: SSL :: SSLError do
render json : { error : 'Remote SSL certificate could not be verified' } , status : 503
end
2017-02-26 23:23:06 +01:00
rescue_from Mastodon :: NotPermittedError do
2016-12-22 21:34:19 +01:00
render json : { error : 'This action is not allowed' } , status : 403
end
2016-11-21 16:19:35 +01:00
def doorkeeper_unauthorized_render_options ( error : nil )
{ json : { error : ( error . try ( :description ) || 'Not authorized' ) } }
2016-10-22 19:38:47 +02:00
end
def doorkeeper_forbidden_render_options ( * )
{ json : { error : 'This action is outside the authorized scopes' } }
end
2016-03-07 12:42:33 +01:00
protected
2016-10-22 19:38:47 +02:00
def set_rate_limit_headers
return if request . env [ 'rack.attack.throttle_data' ] . nil?
now = Time . now . utc
match_data = request . env [ 'rack.attack.throttle_data' ] [ 'api' ]
response . headers [ 'X-RateLimit-Limit' ] = match_data [ :limit ] . to_s
response . headers [ 'X-RateLimit-Remaining' ] = ( match_data [ :limit ] - match_data [ :count ] ) . to_s
2016-11-25 15:21:22 +01:00
response . headers [ 'X-RateLimit-Reset' ] = ( now + ( match_data [ :period ] - now . to_i % match_data [ :period ] ) ) . iso8601 ( 6 )
2016-10-22 19:38:47 +02:00
end
2016-11-09 17:48:44 +01:00
def set_pagination_headers ( next_path = nil , prev_path = nil )
links = [ ]
2016-11-15 16:56:29 +01:00
links << [ next_path , [ %w( rel next ) ] ] if next_path
links << [ prev_path , [ %w( rel prev ) ] ] if prev_path
2016-11-09 17:48:44 +01:00
response . headers [ 'Link' ] = LinkHeader . new ( links )
end
2017-01-24 04:22:10 +01:00
def limit_param ( default_limit )
return default_limit unless params [ :limit ]
[ params [ :limit ] . to_i . abs , default_limit * 2 ] . min
end
2016-03-07 12:42:33 +01:00
def current_resource_owner
2016-11-23 09:20:34 +01:00
@current_user || = User . find ( doorkeeper_token . resource_owner_id ) if doorkeeper_token
2016-03-07 12:42:33 +01:00
end
def current_user
2016-11-23 09:20:34 +01:00
super || current_resource_owner
2016-11-08 23:22:44 +01:00
rescue ActiveRecord :: RecordNotFound
nil
end
def require_user!
current_resource_owner
2017-03-03 23:45:48 +01:00
set_user_activity
2016-11-08 23:22:44 +01:00
rescue ActiveRecord :: RecordNotFound
render json : { error : 'This method requires an authenticated user' } , status : 422
2016-03-07 12:42:33 +01:00
end
2016-09-26 23:55:21 +02:00
def render_empty
render json : { } , status : 200
end
2016-10-16 18:57:54 +02:00
2016-11-15 16:56:29 +01:00
def set_maps ( statuses ) # rubocop:disable Style/AccessorMethodName
2016-11-08 23:22:44 +01:00
if current_account . nil?
@reblogs_map = { }
@favourites_map = { }
return
end
2017-01-23 13:43:14 +01:00
status_ids = statuses . compact . flat_map { | s | [ s . id , s . reblog_of_id ] } . uniq
2016-11-08 23:22:44 +01:00
@reblogs_map = Status . reblogs_map ( status_ids , current_account )
@favourites_map = Status . favourites_map ( status_ids , current_account )
2016-10-16 18:57:54 +02:00
end
2016-11-21 16:10:42 +01:00
def set_counters_maps ( statuses ) # rubocop:disable Style/AccessorMethodName
2017-01-23 13:43:14 +01:00
status_ids = statuses . compact . map { | s | s . reblog? ? s . reblog_of_id : s . id } . uniq
2017-03-06 02:25:41 +01:00
@favourites_counts_map = Favourite . select ( 'status_id, COUNT(id) AS favourites_count' ) . group ( 'status_id' ) . where ( status_id : status_ids ) . map { | f | [ f . status_id , f . favourites_count ] } . to_h
@reblogs_counts_map = Status . select ( 'statuses.id, COUNT(reblogs.id) AS reblogs_count' ) . joins ( 'LEFT OUTER JOIN statuses AS reblogs ON statuses.id = reblogs.reblog_of_id' ) . where ( id : status_ids ) . group ( 'statuses.id' ) . map { | r | [ r . id , r . reblogs_count ] } . to_h
2016-11-21 16:10:42 +01:00
end
def set_account_counters_maps ( accounts ) # rubocop:disable Style/AccessorMethodName
2017-01-23 13:43:14 +01:00
account_ids = accounts . compact . map ( & :id ) . uniq
2017-03-06 02:25:41 +01:00
@followers_counts_map = Follow . unscoped . select ( 'target_account_id, COUNT(account_id) AS followers_count' ) . group ( 'target_account_id' ) . where ( target_account_id : account_ids ) . map { | f | [ f . target_account_id , f . followers_count ] } . to_h
@following_counts_map = Follow . unscoped . select ( 'account_id, COUNT(target_account_id) AS following_count' ) . group ( 'account_id' ) . where ( account_id : account_ids ) . map { | f | [ f . account_id , f . following_count ] } . to_h
@statuses_counts_map = Status . unscoped . select ( 'account_id, COUNT(id) AS statuses_count' ) . group ( 'account_id' ) . where ( account_id : account_ids ) . map { | s | [ s . account_id , s . statuses_count ] } . to_h
2016-11-21 16:10:42 +01:00
end
2016-03-05 12:50:59 +01:00
end