2016-11-15 10:56:29 -05:00
# frozen_string_literal: true
2016-03-05 06:50:59 -05:00
class ApiController < ApplicationController
2016-11-09 11:48:44 -05:00
DEFAULT_STATUSES_LIMIT = 20
DEFAULT_ACCOUNTS_LIMIT = 40
2016-03-05 06:50:59 -05:00
protect_from_forgery with : :null_session
2016-10-22 13:38:47 -04:00
2016-08-17 11:56:23 -04:00
skip_before_action :verify_authenticity_token
2016-03-07 06:42:33 -05:00
2016-10-22 13:38:47 -04:00
before_action :set_rate_limit_headers
2017-02-26 17:23:06 -05:00
rescue_from ActiveRecord :: RecordInvalid , Mastodon :: ValidationError do | e |
2016-09-30 16:31:16 -04:00
render json : { error : e . to_s } , status : 422
2016-08-26 13:12:19 -04:00
end
rescue_from ActiveRecord :: RecordNotFound do
render json : { error : 'Record not found' } , status : 404
end
2016-09-17 11:03:36 -04: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 07:26:44 -04:00
rescue_from OpenSSL :: SSL :: SSLError do
render json : { error : 'Remote SSL certificate could not be verified' } , status : 503
end
2017-02-26 17:23:06 -05:00
rescue_from Mastodon :: NotPermittedError do
2016-12-22 15:34:19 -05:00
render json : { error : 'This action is not allowed' } , status : 403
end
2016-11-21 10:19:35 -05:00
def doorkeeper_unauthorized_render_options ( error : nil )
{ json : { error : ( error . try ( :description ) || 'Not authorized' ) } }
2016-10-22 13:38:47 -04:00
end
def doorkeeper_forbidden_render_options ( * )
{ json : { error : 'This action is outside the authorized scopes' } }
end
2016-03-07 06:42:33 -05:00
protected
2016-10-22 13:38:47 -04: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 09:21:22 -05:00
response . headers [ 'X-RateLimit-Reset' ] = ( now + ( match_data [ :period ] - now . to_i % match_data [ :period ] ) ) . iso8601 ( 6 )
2016-10-22 13:38:47 -04:00
end
2016-11-09 11:48:44 -05:00
def set_pagination_headers ( next_path = nil , prev_path = nil )
links = [ ]
2016-11-15 10:56:29 -05:00
links << [ next_path , [ %w( rel next ) ] ] if next_path
links << [ prev_path , [ %w( rel prev ) ] ] if prev_path
2016-11-09 11:48:44 -05:00
response . headers [ 'Link' ] = LinkHeader . new ( links )
end
2017-01-23 22:22:10 -05: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 06:42:33 -05:00
def current_resource_owner
2016-11-23 03:20:34 -05:00
@current_user || = User . find ( doorkeeper_token . resource_owner_id ) if doorkeeper_token
2016-03-07 06:42:33 -05:00
end
def current_user
2016-11-23 03:20:34 -05:00
super || current_resource_owner
2016-11-08 17:22:44 -05:00
rescue ActiveRecord :: RecordNotFound
nil
end
def require_user!
current_resource_owner
2017-03-03 17:45:48 -05:00
set_user_activity
2016-11-08 17:22:44 -05:00
rescue ActiveRecord :: RecordNotFound
render json : { error : 'This method requires an authenticated user' } , status : 422
2016-03-07 06:42:33 -05:00
end
2016-09-26 17:55:21 -04:00
def render_empty
render json : { } , status : 200
end
2016-10-16 12:57:54 -04:00
2016-11-15 10:56:29 -05:00
def set_maps ( statuses ) # rubocop:disable Style/AccessorMethodName
2016-11-08 17:22:44 -05:00
if current_account . nil?
@reblogs_map = { }
@favourites_map = { }
return
end
2017-01-23 07:43:14 -05:00
status_ids = statuses . compact . flat_map { | s | [ s . id , s . reblog_of_id ] } . uniq
2016-11-08 17:22:44 -05:00
@reblogs_map = Status . reblogs_map ( status_ids , current_account )
@favourites_map = Status . favourites_map ( status_ids , current_account )
2016-10-16 12:57:54 -04:00
end
2016-11-21 10:10:42 -05:00
def set_counters_maps ( statuses ) # rubocop:disable Style/AccessorMethodName
2017-01-23 07:43:14 -05:00
status_ids = statuses . compact . map { | s | s . reblog? ? s . reblog_of_id : s . id } . uniq
2017-03-05 20:25:41 -05: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 10:10:42 -05:00
end
def set_account_counters_maps ( accounts ) # rubocop:disable Style/AccessorMethodName
2017-01-23 07:43:14 -05:00
account_ids = accounts . compact . map ( & :id ) . uniq
2017-03-05 20:25:41 -05: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 10:10:42 -05:00
end
2016-03-05 06:50:59 -05:00
end