statuses_controller.rb 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. # frozen_string_literal: true
  2. class Api::V1::StatusesController < Api::BaseController
  3. include Authorization
  4. before_action -> { authorize_if_got_token! :read, :'read:statuses' }, except: [:create, :update, :destroy]
  5. before_action -> { doorkeeper_authorize! :write, :'write:statuses' }, only: [:create, :update, :destroy]
  6. before_action :require_user!, except: [:show, :context]
  7. before_action :set_status, only: [:show, :context]
  8. before_action :set_thread, only: [:create]
  9. override_rate_limit_headers :create, family: :statuses
  10. override_rate_limit_headers :update, family: :statuses
  11. # This API was originally unlimited, pagination cannot be introduced without
  12. # breaking backwards-compatibility. Arbitrarily high number to cover most
  13. # conversations as quasi-unlimited, it would be too much work to render more
  14. # than this anyway
  15. CONTEXT_LIMIT = 4_096
  16. # This remains expensive and we don't want to show everything to logged-out users
  17. ANCESTORS_LIMIT = 40
  18. DESCENDANTS_LIMIT = 60
  19. DESCENDANTS_DEPTH_LIMIT = 20
  20. def show
  21. cache_if_unauthenticated!
  22. @status = cache_collection([@status], Status).first
  23. render json: @status, serializer: REST::StatusSerializer
  24. end
  25. def context
  26. cache_if_unauthenticated!
  27. ancestors_limit = CONTEXT_LIMIT
  28. descendants_limit = CONTEXT_LIMIT
  29. descendants_depth_limit = nil
  30. if current_account.nil?
  31. ancestors_limit = ANCESTORS_LIMIT
  32. descendants_limit = DESCENDANTS_LIMIT
  33. descendants_depth_limit = DESCENDANTS_DEPTH_LIMIT
  34. end
  35. ancestors_results = @status.in_reply_to_id.nil? ? [] : @status.ancestors(ancestors_limit, current_account)
  36. descendants_results = @status.descendants(descendants_limit, current_account, descendants_depth_limit)
  37. loaded_ancestors = cache_collection(ancestors_results, Status)
  38. loaded_descendants = cache_collection(descendants_results, Status)
  39. @context = Context.new(ancestors: loaded_ancestors, descendants: loaded_descendants)
  40. statuses = [@status] + @context.ancestors + @context.descendants
  41. render json: @context, serializer: REST::ContextSerializer, relationships: StatusRelationshipsPresenter.new(statuses, current_user&.account_id)
  42. end
  43. def create
  44. @status = PostStatusService.new.call(
  45. current_user.account,
  46. text: status_params[:status],
  47. thread: @thread,
  48. media_ids: status_params[:media_ids],
  49. sensitive: status_params[:sensitive],
  50. spoiler_text: status_params[:spoiler_text],
  51. visibility: status_params[:visibility],
  52. language: status_params[:language],
  53. scheduled_at: status_params[:scheduled_at],
  54. application: doorkeeper_token.application,
  55. poll: status_params[:poll],
  56. allowed_mentions: status_params[:allowed_mentions],
  57. idempotency: request.headers['Idempotency-Key'],
  58. with_rate_limit: true
  59. )
  60. render json: @status, serializer: serializer_for_status
  61. rescue PostStatusService::UnexpectedMentionsError => e
  62. render json: unexpected_accounts_error_json(e), status: 422
  63. end
  64. def update
  65. @status = Status.where(account: current_account).find(params[:id])
  66. authorize @status, :update?
  67. UpdateStatusService.new.call(
  68. @status,
  69. current_account.id,
  70. text: status_params[:status],
  71. media_ids: status_params[:media_ids],
  72. media_attributes: status_params[:media_attributes],
  73. sensitive: status_params[:sensitive],
  74. language: status_params[:language],
  75. spoiler_text: status_params[:spoiler_text],
  76. poll: status_params[:poll]
  77. )
  78. render json: @status, serializer: REST::StatusSerializer
  79. end
  80. def destroy
  81. @status = Status.where(account: current_account).find(params[:id])
  82. authorize @status, :destroy?
  83. @status.discard_with_reblogs
  84. StatusPin.find_by(status: @status)&.destroy
  85. @status.account.statuses_count = @status.account.statuses_count - 1
  86. json = render_to_body json: @status, serializer: REST::StatusSerializer, source_requested: true
  87. RemovalWorker.perform_async(@status.id, { 'redraft' => true })
  88. render json: json
  89. end
  90. private
  91. def set_status
  92. @status = Status.find(params[:id])
  93. authorize @status, :show?
  94. rescue Mastodon::NotPermittedError
  95. not_found
  96. end
  97. def set_thread
  98. @thread = Status.find(status_params[:in_reply_to_id]) if status_params[:in_reply_to_id].present?
  99. authorize(@thread, :show?) if @thread.present?
  100. rescue ActiveRecord::RecordNotFound, Mastodon::NotPermittedError
  101. render json: { error: I18n.t('statuses.errors.in_reply_not_found') }, status: 404
  102. end
  103. def status_params
  104. params.permit(
  105. :status,
  106. :in_reply_to_id,
  107. :sensitive,
  108. :spoiler_text,
  109. :visibility,
  110. :language,
  111. :scheduled_at,
  112. allowed_mentions: [],
  113. media_ids: [],
  114. media_attributes: [
  115. :id,
  116. :thumbnail,
  117. :description,
  118. :focus,
  119. ],
  120. poll: [
  121. :multiple,
  122. :hide_totals,
  123. :expires_in,
  124. options: [],
  125. ]
  126. )
  127. end
  128. def serializer_for_status
  129. @status.is_a?(ScheduledStatus) ? REST::ScheduledStatusSerializer : REST::StatusSerializer
  130. end
  131. def unexpected_accounts_error_json(error)
  132. {
  133. error: error.message,
  134. unexpected_accounts: serialized_accounts(error.accounts),
  135. }
  136. end
  137. def serialized_accounts(accounts)
  138. ActiveModel::Serializer::CollectionSerializer.new(accounts, serializer: REST::AccountSerializer)
  139. end
  140. def pagination_params(core_params)
  141. params.slice(:limit).permit(:limit).merge(core_params)
  142. end
  143. end