From 663c0fd7ecbbabb1e3a03d0a4a7a0109f1488615 Mon Sep 17 00:00:00 2001 From: NangKhatMang Date: Thu, 21 Aug 2025 10:40:13 +0630 Subject: [PATCH 1/2] [boost_post] add boost post service --- app/controllers/api/v1/statuses_controller.rb | 48 +++++++++++++++++++ app/services/reblog_post_service.rb | 38 +++++++++++++++ app/services/search_post_service.rb | 21 ++++++++ config/routes/api_v1.rb | 9 +++- 4 files changed, 114 insertions(+), 2 deletions(-) create mode 100644 app/controllers/api/v1/statuses_controller.rb create mode 100644 app/services/reblog_post_service.rb create mode 100644 app/services/search_post_service.rb diff --git a/app/controllers/api/v1/statuses_controller.rb b/app/controllers/api/v1/statuses_controller.rb new file mode 100644 index 00000000..91510eb4 --- /dev/null +++ b/app/controllers/api/v1/statuses_controller.rb @@ -0,0 +1,48 @@ +module Api::V1 + class StatusesController < ApiController + before_action :whitelist_domains + + def boost_post + @api_base_url = ENV.fetch('MASTODON_INSTANCE_URL') + + unless params[:post_url].present? && params[:boost_post_username].present? && params[:boost_post_user_domain].present? + return render_error("Missing post_url or boost_post_username or boost_post_user_domain", :unprocessable_entity) + end + + @token = fetch_oauth_token(params[:boost_post_username], params[:boost_post_user_domain]) + return render_error("Could not generate OAuth token for #{params[:boost_post_username]}@#{params[:boost_post_user_domain]}", :unauthorized) unless @token.present? + + post_id = SearchPostService.new(@api_base_url, @token, params[:post_url]).call + return render_error("Post not found or invalid post URL", :not_found) unless post_id.present? + + result = ReblogPostService.new(@token, post_id).call + render json: result + end + + private + + def whitelist_domains + allowed_domains = ["mo-me.social"] + + unless allowed_domains.include?(request.host) + render_error("Request domain not allowed", :forbidden) + end + end + + def fetch_oauth_token(username, domain) + admin = Account.find_by(username: username, domain: domain) + return nil unless admin&.user + + GenerateAdminAccessTokenService.new(admin.user.id).call + rescue => e + nil + end + + def render_error(message, status) + render json: { + status: status, + body: message + } + end + end +end diff --git a/app/services/reblog_post_service.rb b/app/services/reblog_post_service.rb new file mode 100644 index 00000000..b5eb9683 --- /dev/null +++ b/app/services/reblog_post_service.rb @@ -0,0 +1,38 @@ +require 'httparty' + +class ReblogPostService + include HTTParty + + base_uri ENV['MASTODON_INSTANCE_URL'] + + def initialize(token, status_id) + @token = token + @status_id = status_id + end + + def call + reblog_post + end + + private + + # Reblog a status + def reblog_post + response = self.class.post( + "/api/v1/statuses/#{@status_id}/reblog", + headers: { + "Authorization" => "Bearer #{@token}", + "Content-Type" => "application/json" + }, + body: { visibility: 'public' }.to_json + ) + + { + status: response.success? ? :ok : :error, + body: response.parsed_response + } + rescue StandardError => e + Rails.logger.error("ReblogPostService Error: #{e.message}") + { status: :error, body: { error: e.message } } + end +end diff --git a/app/services/search_post_service.rb b/app/services/search_post_service.rb new file mode 100644 index 00000000..0a5e36ee --- /dev/null +++ b/app/services/search_post_service.rb @@ -0,0 +1,21 @@ +require 'httparty' + +class SearchPostService + def initialize(api_base_url, token, query) + @api_base_url = api_base_url + @token = token + @query = query + end + + def call + response = HTTParty.get("#{@api_base_url}/api/v2/search", + query: { q: @query, type: 'statuses', resolve: true, limit: 1 }, + headers: { 'Authorization' => "Bearer #{@token}" } + ) + if response.success? + return response['statuses']&.first&.dig('id') + end + + nil + end +end diff --git a/config/routes/api_v1.rb b/config/routes/api_v1.rb index 6e58b171..c32273c6 100644 --- a/config/routes/api_v1.rb +++ b/config/routes/api_v1.rb @@ -101,7 +101,7 @@ get 'general_icons', to: 'community_links#general' get 'social_icons', to: 'community_links#social' - resources :app_versions,only: [] do + resources :app_versions,only: [] do collection do get 'check_version' => 'app_versions#check_version', as: 'check_version' end @@ -112,6 +112,11 @@ post :upsert end end + + resources :statuses, only: [] do + collection do + post :boost_post + end + end end end - From f08d62c414402b8b9b0eade06e48b5236779abba Mon Sep 17 00:00:00 2001 From: NangKhatMang Date: Wed, 27 Aug 2025 12:25:12 +0630 Subject: [PATCH 2/2] [boost_post] modify account fetch --- app/controllers/api/v1/statuses_controller.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/controllers/api/v1/statuses_controller.rb b/app/controllers/api/v1/statuses_controller.rb index 91510eb4..11ababca 100644 --- a/app/controllers/api/v1/statuses_controller.rb +++ b/app/controllers/api/v1/statuses_controller.rb @@ -29,8 +29,9 @@ def whitelist_domains end end - def fetch_oauth_token(username, domain) - admin = Account.find_by(username: username, domain: domain) + def fetch_oauth_token(username, user_domain) + user_domain = nil if user_domain == 'channel.org' + admin = Account.find_by(username: username, domain: user_domain) return nil unless admin&.user GenerateAdminAccessTokenService.new(admin.user.id).call