프로필의 승인 버튼이 작동하지 않아요

Notification claims X users for approval, but none are found 토론을 이어서 말씀드립니다:

최근 제 포럼에서 require_approval 설정을 다시 활성화했습니다. 그러자마자 승인 대기 중인 사용자가 있다는 알림을 받았는데, 메시지 내 링크를 클릭해도 검토 대기열에 사용자가 없습니다. 이후로 7일마다 이 알림을 받고 있습니다. 그래서 데이터 탐색기(data explorer)를 사용해 승인되지 않았지만 검토 대기열에 표시되지 않는 사용자를 찾으려 했습니다.

다른 주제에서 인용된 보다 상세한 단계

사용자 관리 페이지를 보면 예상대로 승인(approve) 버튼이 있습니다. 문제는 버튼이 작동하지 않는다는 것입니다. 오류를 보여주는 모달과 같은 가시적인 피드백은 없습니다. 하지만 브라우저 콘솔에는 하나 있습니다.

브라우저 콘솔의 오류
ajax.js:233  PUT https://my-forum.discourse.group/admin/users/123/approve 500 (Internal Server Error)
send @ jquery.js:9940
ajax @ jquery.js:9521
performAjax @ ajax.js:233
(anonymous) @ ajax.js:246
approve @ admin-user.js:220
approve @ index.js:224
(anonymous) @ d-button.gjs:206
invoke @ index.js:264
flush @ index.js:180
flush @ index.js:334
_end @ index.js:762
end @ index.js:565
_runExpiredTimers @ index.js:869
setTimeout
setTimeout @ index.js:39
_installTimerTimeout @ index.js:912
_reinstallTimerTimeout @ index.js:896
_later @ index.js:829
later @ index.js:652
next @ index.js:562
_triggerAction @ d-button.gjs:203
click @ d-button.gjs:161


/admin/users/123/anon84265489:1 Uncaught (in promise) {jqXHR: {…}, textStatus: 'error', errorThrown: ''}errorThrown: ""jqXHR: abort: ƒ (e)always: ƒ ()catch: ƒ (e)done: ƒ ()fail: ƒ ()getAllResponseHeaders: ƒ ()getResponseHeader: ƒ (e)jqTextStatus: "error"overrideMimeType: ƒ (e)pipe: ƒ ()progress: ƒ ()promise: ƒ (e)readyState: 4requestedUrl: "/admin/users/123/approve"responseText: "<!DOCTYPE html>\n<html>\n<head>\n  <title>Oops - Error 500</title>\n  <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\n</head>\n<body>\n    <h1>Oops</h1>\n    <p>The software powering this discussion forum encountered an unexpected problem. We apologize for the inconvenience.</p>\n    <p>Detailed information about the error was logged, and an automatic notification generated. We'll take a look at it.</p>\n    <p>No further action is necessary. However, if the error condition persists, you can provide additional detail, including steps to reproduce the error, by posting a discussion topic in the site's feedback category.</p>\n</body>\n</html>\n"setRequestHeader: ƒ (e,t)state: ƒ ()status: 500statusCode: ƒ (e)statusText: "error"then: ƒ (e,i,n)[[Prototype]]: ObjecttextStatus: "error"[[Prototype]]: Object
Promise.then
approve @ admin-user.js:222
approve @ index.js:224
(anonymous) @ d-button.gjs:206
invoke @ index.js:264
flush @ index.js:180
flush @ index.js:334
_end @ index.js:762
end @ index.js:565
_runExpiredTimers @ index.js:869
setTimeout
setTimeout @ index.js:39
_installTimerTimeout @ index.js:912
_reinstallTimerTimeout @ index.js:896
_later @ index.js:829
later @ index.js:652
next @ index.js:562
_triggerAction @ d-button.gjs:203
click @ d-button.gjs:161

다음으로 /logs를 확인했습니다.

/logs의 오류
Message (14 copies reported)

Reviewable::InvalidAction (Can't perform `approve_user` on ReviewableUser)
app/models/reviewable.rb:860:in 'Reviewable#validate_action!'
app/models/reviewable.rb:360:in 'Reviewable#perform'
app/controllers/admin/users_controller.rb:306:in 'Admin::UsersController#approve'
app/controllers/application_controller.rb:452:in 'block in ApplicationController#with_resolved_locale'
app/controllers/application_controller.rb:452:in 'ApplicationController#with_resolved_locale'
app/controllers/application_controller.rb:1101:in 'ApplicationController#ensure_dont_cache_page'
lib/middleware/omniauth_bypass_middleware.rb:35:in 'Middleware::OmniauthBypassMiddleware#call'
lib/middleware/crawler_hooks.rb:13:in 'Middleware::CrawlerHooks#call'
lib/content_security_policy/middleware.rb:12:in 'ContentSecurityPolicy::Middleware#call'
lib/middleware/anonymous_cache.rb:417:in 'Middleware::AnonymousCache#call'
lib/middleware/csp_script_nonce_injector.rb:12:in 'Middleware::CspScriptNonceInjector#call'
lib/middleware/track_view_session_id_injector.rb:12:in 'Middleware::TrackViewSessionIdInjector#call'
config/initializers/008-rack-cors.rb:26:in 'Discourse::Cors#call'
lib/middleware/default_headers.rb:13:in 'Middleware::DefaultHeaders#call'
config/initializers/100-quiet_logger.rb:20:in 'DiscourseRackQuietAssetsLogger#call'
config/initializers/100-silence_logger.rb:29:in 'SilenceLogger#call'
lib/middleware/enforce_hostname.rb:23:in 'Middleware::EnforceHostname#call'
lib/middleware/request_tracker.rb:372:in 'Middleware::RequestTracker#call'
lib/middleware/overload_protections.rb:18:in 'Middleware::OverloadProtections#call'
lib/middleware/processing_request.rb:14:in 'Middleware::ProcessingRequest#call'


Backtrace

app/models/reviewable.rb:860:in 'Reviewable#validate_action!'
app/models/reviewable.rb:360:in 'Reviewable#perform'
app/controllers/admin/users_controller.rb:306:in 'Admin::UsersController#approve'
actionpack (8.0.5) lib/action_controller/metal/basic_implicit_render.rb:8:in 'ActionController::BasicImplicitRender#send_action'
actionpack (8.0.5) lib/abstract_controller/base.rb:215:in 'AbstractController::Base#process_action'
actionpack (8.0.5) lib/action_controller/metal/rendering.rb:193:in 'ActionController::Rendering#process_action'
actionpack (8.0.5) lib/abstract_controller/callbacks.rb:261:in 'block in AbstractController::Callbacks#process_action'
activesupport (8.0.5) lib/active_support/callbacks.rb:120:in 'block in ActiveSupport::Callbacks#run_callbacks'
app/controllers/application_controller.rb:452:in 'block in ApplicationController#with_resolved_locale'
i18n (1.14.8) lib/i18n.rb:354:in 'I18n::Base#with_locale'
app/controllers/application_controller.rb:452:in 'ApplicationController#with_resolved_locale'
activesupport (8.0.5) lib/active_support/callbacks.rb:129:in 'block in ActiveSupport::Callbacks#run_callbacks'
app/controllers/application_controller.rb:1101:in 'ApplicationController#ensure_dont_cache_page'
activesupport (8.0.5) lib/active_support/callbacks.rb:129:in 'block in ActiveSupport::Callbacks#run_callbacks'
activesupport (8.0.5) lib/active_support/callbacks.rb:140:in 'ActiveSupport::Callbacks#run_callbacks'
actionpack (8.0.5) lib/abstract_controller/callbacks.rb:260:in 'AbstractController::Callbacks#process_action'
actionpack (8.0.5) lib/action_controller/metal/rescue.rb:27:in 'ActionController::Rescue#process_action'
actionpack (8.0.5) lib/action_controller/metal/instrumentation.rb:76:in 'block in ActionController::Instrumentation#process_action'
activesupport (8.0.5) lib/active_support/notifications.rb:210:in 'block in ActiveSupport::Notifications.instrument'
activesupport (8.0.5) lib/active_support/notifications/instrumenter.rb:58:in 'ActiveSupport::Notifications::Instrumenter#instrument'
activesupport (8.0.5) lib/active_support/notifications.rb:210:in 'ActiveSupport::Notifications.instrument'
actionpack (8.0.5) lib/action_controller/metal/instrumentation.rb:75:in 'ActionController::Instrumentation#process_action'
actionpack (8.0.5) lib/action_controller/metal/params_wrapper.rb:259:in 'ActionController::ParamsWrapper#process_action'
activerecord (8.0.5) lib/active_record/railties/controller_runtime.rb:39:in 'ActiveRecord::Railties::ControllerRuntime#process_action'
actionpack (8.0.5) lib/abstract_controller/base.rb:152:in 'AbstractController::Base#process'
actionview (8.0.5) lib/action_view/rendering.rb:40:in 'ActionView::Rendering#process'
rack-mini-profiler (4.0.1) lib/mini_profiler/profiling_methods.rb:90:in 'block in ActionController::Base#profile_method'
actionpack (8.0.5) lib/action_controller/metal.rb:252:in 'ActionController::Metal#dispatch'
actionpack (8.0.5) lib/action_controller/metal.rb:335:in 'ActionController::Metal.dispatch'
actionpack (8.0.5) lib/action_dispatch/routing/route_set.rb:67:in 'ActionDispatch::Routing::RouteSet::Dispatcher#dispatch'
actionpack (8.0.5) lib/action_dispatch/routing/route_set.rb:50:in 'ActionDispatch::Routing::RouteSet::Dispatcher#serve'
actionpack (8.0.5) lib/action_dispatch/routing/mapper.rb:32:in 'block in <class:Constraints>'
actionpack (8.0.5) lib/action_dispatch/routing/mapper.rb:62:in 'ActionDispatch::Routing::Mapper::Constraints#serve'
actionpack (8.0.5) lib/action_dispatch/journey/router.rb:53:in 'block in ActionDispatch::Journey::Router#serve'
actionpack (8.0.5) lib/action_dispatch/journey/router.rb:133:in 'block in ActionDispatch::Journey::Router#find_routes'
actionpack (8.0.5) lib/action_dispatch/journey/router.rb:126:in 'Array#each'
actionpack (8.0.5) lib/action_dispatch/journey/router.rb:126:in 'ActionDispatch::Journey::Router#find_routes'
actionpack (8.0.5) lib/action_dispatch/journey/router.rb:34:in 'ActionDispatch::Journey::Router#serve'
actionpack (8.0.5) lib/action_dispatch/routing/route_set.rb:908:in 'ActionDispatch::Routing::RouteSet#call'
lib/middleware/omniauth_bypass_middleware.rb:35:in 'Middleware::OmniauthBypassMiddleware#call'
lib/middleware/crawler_hooks.rb:13:in 'Middleware::CrawlerHooks#call'
rack (2.2.23) lib/rack/tempfile_reaper.rb:15:in 'Rack::TempfileReaper#call'
rack (2.2.23) lib/rack/conditional_get.rb:40:in 'Rack::ConditionalGet#call'
rack (2.2.23) lib/rack/head.rb:12:in 'Rack::Head#call'
actionpack (8.0.5) lib/action_dispatch/http/permissions_policy.rb:38:in 'ActionDispatch::PermissionsPolicy::Middleware#call'
lib/content_security_policy/middleware.rb:12:in 'ContentSecurityPolicy::Middleware#call'
lib/middleware/anonymous_cache.rb:417:in 'Middleware::AnonymousCache#call'
lib/middleware/csp_script_nonce_injector.rb:12:in 'Middleware::CspScriptNonceInjector#call'
lib/middleware/track_view_session_id_injector.rb:12:in 'Middleware::TrackViewSessionIdInjector#call'
config/initializers/008-rack-cors.rb:26:in 'Discourse::Cors#call'
rack (2.2.23) lib/rack/session/abstract/id.rb:266:in 'Rack::Session::Abstract::Persisted#context'
rack (2.2.23) lib/rack/session/abstract/id.rb:260:in 'Rack::Session::Abstract::Persisted#call'
actionpack (8.0.5) lib/action_dispatch/middleware/cookies.rb:706:in 'ActionDispatch::Cookies#call'
actionpack (8.0.5) lib/action_dispatch/middleware/callbacks.rb:31:in 'block in ActionDispatch::Callbacks#call'
activesupport (8.0.5) lib/active_support/callbacks.rb:100:in 'ActiveSupport::Callbacks#run_callbacks'
actionpack (8.0.5) lib/action_dispatch/middleware/callbacks.rb:30:in 'ActionDispatch::Callbacks#call'
actionpack (8.0.5) lib/action_dispatch/middleware/debug_exceptions.rb:31:in 'ActionDispatch::DebugExceptions#call'
actionpack (8.0.5) lib/action_dispatch/middleware/show_exceptions.rb:32:in 'ActionDispatch::ShowExceptions#call'
logster (2.21.0) lib/logster/middleware/reporter.rb:40:in 'Logster::Middleware::Reporter#call'
lib/middleware/default_headers.rb:13:in 'Middleware::DefaultHeaders#call'
lograge (0.14.0) lib/lograge/rails_ext/rack/logger.rb:18:in 'Rails::Rack::Logger#call_app'
railties (8.0.5) lib/rails/rack/logger.rb:29:in 'Rails::Rack::Logger#call'
config/initializers/100-quiet_logger.rb:20:in 'DiscourseRackQuietAssetsLogger#call'
config/initializers/100-silence_logger.rb:29:in 'SilenceLogger#call'
actionpack (8.0.5) lib/action_dispatch/middleware/request_id.rb:34:in 'ActionDispatch::RequestId#call'
lib/middleware/enforce_hostname.rb:23:in 'Middleware::EnforceHostname#call'
rack (2.2.23) lib/rack/method_override.rb:24:in 'Rack::MethodOverride#call'
rack (2.2.23) lib/rack/sendfile.rb:127:in 'Rack::Sendfile#call'
plugins/discourse-prometheus/lib/middleware/metrics.rb:14:in 'DiscoursePrometheus::Middleware::Metrics#call'
rack-mini-profiler (4.0.1) lib/mini_profiler.rb:191:in 'Rack::MiniProfiler#call'
message_bus (4.5.2) lib/message_bus/rack/middleware.rb:60:in 'MessageBus::Rack::Middleware#call'
lib/middleware/request_tracker.rb:372:in 'Middleware::RequestTracker#call'
actionpack (8.0.5) lib/action_dispatch/middleware/remote_ip.rb:96:in 'ActionDispatch::RemoteIp#call'
lib/middleware/overload_protections.rb:18:in 'Middleware::OverloadProtections#call'
lib/middleware/processing_request.rb:14:in 'Middleware::ProcessingRequest#call'
rails_failover (2.3.0) lib/rails_failover/active_record/middleware.rb:67:in 'block in RailsFailover::ActiveRecord::Middleware#call'
activerecord (8.0.5) lib/active_record/connection_handling.rb:401:in 'ActiveRecord::ConnectionHandling#with_role_and_shard'
activerecord (8.0.5) lib/active_record/connection_handling.rb:149:in 'ActiveRecord::ConnectionHandling#connected_to'
rails_failover (2.3.0) lib/rails_failover/active_record/middleware.rb:64:in 'RailsFailover::ActiveRecord::Middleware#call'
rails_multisite (7.0.0) lib/rails_multisite/middleware.rb:26:in 'RailsMultisite::Middleware#call'
railties (8.0.5) lib/rails/engine.rb:535:in 'Rails::Engine#call'
railties (8.0.5) lib/rails/railtie.rb:226:in 'Kernel#public_send'
railties (8.0

Env

HTTP HOSTS: my-forum.discourse.group

이것이 무엇 때문에 발생하는지 확신하지 못하겠습니다. 설정을 활성화했을 때 다른 익명화(anonymous)된 사용자들은 정상적으로 승인되었기 때문입니다.

3개의 좋아요

나도 동의합니다.

(수정: 이 상태에 있는 사용자를 정확히 한 명 발견했습니다. 몇 주 전까지 활동했고, 익명 처리되지 않았습니다)

3개의 좋아요

사용자가 익명 처리되었다는 것이 이 버그의 원인임을 확인하는 데 도움이 되는 "나도 그래"라는 답변이 정말 유용합니다.

데이터 탐색기에는 해당 사용자가 moderater(중재자)에 의해 검토 가능하다고 표시됩니다.

user_id id type target_id target_type reviewable_by_moderator
anon84265489 264 ReviewableUser 123 User true
query
SELECT 
    u.id AS user_id,
    r.id,
    r.type,
    r.target_id,
    r.target_type,
    r.reviewable_by_moderator
FROM reviewables r
JOIN users u
    ON r.target_id = u.id
WHERE u.approved = false
  AND u.active = true
2개의 좋아요

이것이 어떻게 오작동하는지 이해하지 못하지만, 이 작업을 수행할 수 없다고 결론을 내리는 일부 코드 조각은 찾을 수 있습니다. 하지만 왜 그렇게 결론을 내리는지는 알 수 없습니다. 아마도 reviewable.rb의 859번째 줄에 해당 로직이 있는 것 같습니다.

approve()는 users_controller.rb의 306번째 줄에 있습니다.

perform()는 reviewable.rb의 360번째 줄에 있습니다.

validate_action!()는 reviewable.rb의 860번째 줄에 있으며 예외를 발생시킵니다.

1개의 좋아요

문제가 이전에 검토된 사용자인 것 같습니다. 같은 사용자를 다시 검토할 수 있는지는 확실하지 않습니다. 지금까지 이 문제를 재현하지 못했습니다.

아직도 재현에 실패했습니다. 하지만 검토 대기열에 표시되지 않는 사용자는 12월에 가입한 후 suspect_user로 플래그가 지정되었던 사용자라는 것을 발견했습니다. 당시 제 포럼에서는 must_approve_users가 비활성화되어 있었습니다. 그래서 당시 해당 사용자에게 조치를 취했습니다. disagreed가 사용자를 유지하도록 선택했다는 의미인지는 확실하지 않습니다. 하지만 방금 재현을 시도했을 때 다른 유일한 옵션은 사용자를 삭제하는 것이었습니다. status = 2가 어떻게 발생했는지 확실하지 않습니다.
몇 주 전 must_approve_users를 활성화했을 때, target_id가 user_id인 reviewable이 존재했기 때문에 해당 사용자는 자동으로 승인되지 않았습니다. 이는 타당합니다.

하지만 플래그가 지정되었을 때 승인도 삭제도 하지 않은 사용자가 어떻게 존재하게 되었는지 여전히 알 수 없습니다.

reviewable status status reason context created_at
264 disagreed 2 suspect_user NULL 2025-12-18T14:16:28.322Z
query
SELECT
    rs.reviewable_id,
    CASE
      WHEN rs.status = 0 THEN 'pending'
      WHEN rs.status = 1 THEN 'agreed'
      WHEN rs.status = 2 THEN 'disagreed'
      WHEN rs.status = 3 THEN 'ignored'
    END as status,
    rs.status as status_id,
    rs.reason,
    rs.context,
    rs.created_at
FROM reviewable_scores rs
JOIN reviewables r
ON rs.reviewable_id = r.id
JOIN users u
ON r.target_id = u.id
WHERE u.approved = false
AND u.active = true
2개의 좋아요

좋은 지적입니다 - 제 경우에도 동일한 상황이네요 - 먼 과거에 suspect_user 상태가 된 사용자(제 경우 몇 주 전까지 살아있고 활동적인 사용자였음)였기 때문입니다.

새 사용자 승인 기능을 켠 후로 이 사용자는 포럼을 사용할 수 없게 된 것으로 보입니다.
가입일 2021년 4월 9일 마지막 게시 4월 16일 확인일 4월 16일 조회수 1311 신뢰 수준 멤버
통계 78일 방문 4시간 읽기 시간 3분 최근 읽기 시간 83개 주제 열람
491개 게시물 읽음 6개 부여 31개 수취 4개 주제 생성 25개 게시물 생성

검토 대기열을 다시 살펴보니 해당 날짜에 항목이 없었고, 해당 사용자名的 항목도 없었습니다.

수정: 매우 이상한데, 그 suspect_user 날짜는 사용자가 가입한 날짜보다 약 9~10일 전입니다.

2개의 좋아요

이제 제 경우를 보니 스팸 사용자가 ID 332를 할당받았고, 이후 플래그가 걸려 삭제되었습니다. 며칠 뒤 새로운 사용자가 등록되면서 다시 ID 332를 할당받았습니다.

영향을 받은 유효한 사용자의 ID를 알고 있으므로, 다음 Data Explorer 쿼리를 실행했습니다:

SELECT *
FROM reviewables
WHERE target_id = 332
ORDER BY created_at DESC

수정: ask.discourse에 따르면 이런 일은 일어나서는 안 된다고 합니다. 다만, 저희는 데이터베이스에 직접 개입한 적이 없다고 말할 수 있습니다. 이전에는 마이그레이션을 수행한 적이 있으므로 데이터베이스 복원 작업과 관련이 있을 수 있겠지만, 해당 시점(2021년 3월)에는 마이그레이션을 한 적이 없었습니다.

이 문제는 이전에 폐쇄된 관찰 사항의 재현(repro)일 수 있다고 생각합니다:
삭제된 사용자 정보가 새 사용자 승인에 표시됨

2개의 좋아요

제 경우에는 그렇지 않다고 생각합니다. reviewable은 사용자보다 나중에 생성되었습니다:

reviewables 테이블에서:

id type status target target_type created_at updated_at
264 ReviewableUser 2 123 User 2025-12-18T14:16:28.315Z 2026-01-06T10:14:42.142Z

users 테이블에서:

id username created_at
123 anon84265489 2025-12-15T11:31:28.666Z
2개의 좋아요

잘못된 대상 ID를 가진 유효한 검토 가능 레코드처럼 보이나요? 제 레코드도 그렇게 보입니다.

이 경우 '승인’이 허용되지 않는 작업이기 때문에 사용자를 승인할 수 없는 것 같습니다. 문제의 특정 검토 가능 레코드들에 뭔가 문제가 있는 것일 수 있습니다.

아마도 승인되지 않은 사용자들이 같은 이유로 검토 대기열에 표시되지 않는 것 같습니다.

여기서 여러 가지 버그가 있는 것 같습니다.

  • 승인 버튼이 예외를 발생시킨다면, 사용자에게 그 원인이 표시되어야 합니다.
  • 검토 대기열에 모든 항목이 표시되지 않습니다.
  • 잘못된 검토 가능 레코드가 데이터베이스에 생성되었습니다.
  • 계정 승인을 활성화했을 때, 기존 사용자 중 일부가 자동으로 승인되지 않았습니다.
  • 그 실패가 조용하게 처리되었습니다.

이 변경 사항이 이 주제에서 여러분이 언급한 몇 가지 엣지 케이스를 기대하기는 하지만 잡아낼 수 있기를 바랍니다 :+1:

2개의 좋아요

어차피 track-setting-changesmust_approve_users 섹션을 작업하고 계신 만큼, target_id가 올바른 것뿐 아니라 target_type도 사용자인지 확인하는 검사도 추가해 주시겠습니까? 이렇게 하면 플래그된 채팅 메시지로 인해 사용자가 승인되지 못하는 문제를 방지할 수 있습니다.

1개의 좋아요

얼마 전 포럼을 2026.7.0으로 업데이트한 후, 문제가 있었던 계정 하나에서 승인 버튼이 이제 정상적으로 작동하는 것을 확인했고 해당 사용자를 승인했습니다.

이 상황에 대한 근본 원인이 정확히 파악되지 않았으며, 다른 포럼에서 얼마나 많은 계정이 여전히 승인되지 않은 채 남아 있는지조차 알 수 없습니다.