diff --git a/app/controllers/discourse_ai/admin/ai_spam_controller.rb b/app/controllers/discourse_ai/admin/ai_spam_controller.rb index 138908eb4..6676ab5c6 100644 --- a/app/controllers/discourse_ai/admin/ai_spam_controller.rb +++ b/app/controllers/discourse_ai/admin/ai_spam_controller.rb @@ -86,6 +86,31 @@ def test render json: result end + def fix_errors + case params[:error] + when "spam_scanner_not_admin" + begin + DiscourseAi::AiModeration::SpamScanner.fix_spam_scanner_not_admin + render json: success_json + rescue ActiveRecord::RecordInvalid + render_json_error( + I18n.t("discourse_ai.spam_detection.bot_user_update_failed"), + status: :unprocessable_entity, + ) + rescue StandardError + render_json_error( + I18n.t("discourse_ai.spam_detection.unexpected"), + status: :internal_server_error, + ) + end + else + render_json_error( + I18n.t("discourse_ai.spam_detection.invalid_error_type"), + status: :bad_request, + ) + end + end + private def allowed_params diff --git a/app/serializers/ai_spam_serializer.rb b/app/serializers/ai_spam_serializer.rb index 14f1c4748..022d1dac9 100644 --- a/app/serializers/ai_spam_serializer.rb +++ b/app/serializers/ai_spam_serializer.rb @@ -7,7 +7,8 @@ class AiSpamSerializer < ApplicationSerializer :available_llms, :stats, :flagging_username, - :spam_score_type + :spam_score_type, + :spam_scanning_user def is_enabled object[:enabled] @@ -47,4 +48,10 @@ def stats def settings object[:settings] end + + def spam_scanning_user + user = DiscourseAi::AiModeration::SpamScanner.flagging_user + + user.serializable_hash(only: %i[id username name admin]) if user.present? + end end diff --git a/assets/javascripts/discourse/components/ai-spam.gjs b/assets/javascripts/discourse/components/ai-spam.gjs index d7f7a9af1..b5425d2e3 100644 --- a/assets/javascripts/discourse/components/ai-spam.gjs +++ b/assets/javascripts/discourse/components/ai-spam.gjs @@ -13,6 +13,7 @@ import DTooltip from "discourse/components/d-tooltip"; import withEventValue from "discourse/helpers/with-event-value"; import { ajax } from "discourse/lib/ajax"; import { popupAjaxError } from "discourse/lib/ajax-error"; +import dIcon from "discourse-common/helpers/d-icon"; import i18n from "discourse-common/helpers/i18n"; import getURL from "discourse-common/lib/get-url"; import AdminConfigAreaCard from "admin/components/admin-config-area-card"; @@ -35,10 +36,51 @@ export default class AiSpam extends Component { @tracked isEnabled = false; @tracked selectedLLM = null; @tracked customInstructions = ""; + @tracked errors = []; constructor() { super(...arguments); this.initializeFromModel(); + + if (this.args.model?.spam_scanning_user?.admin === false) { + this.errors.push({ + message: i18n("discourse_ai.spam.errors.scan_not_admin.message"), + button: { + label: i18n("discourse_ai.spam.errors.scan_not_admin.action"), + action: this.fixScanUserNotAdmin, + }, + }); + } + } + + @action + async fixScanUserNotAdmin() { + const spamScanningUser = this.args.model.spam_scanning_user; + if (!spamScanningUser || spamScanningUser.admin) { + return; + } + try { + const response = await ajax( + `/admin/plugins/discourse-ai/ai-spam/fix-errors`, + { + type: "POST", + data: { + error: "spam_scanner_not_admin", + }, + } + ); + + if (response.success) { + this.toasts.success({ + data: { message: i18n("discourse_ai.spam.errors.resolved") }, + duration: 2000, + }); + } + } catch (error) { + popupAjaxError(error); + } finally { + window.location.reload(); + } } @action @@ -165,11 +207,22 @@ export default class AiSpam extends Component {