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