beetle_reporter 0.1.0 → 0.1.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +70 -16
  4. data/Rakefile +5 -9
  5. data/app/assets/config/beetle_reporter_manifest.js +1 -0
  6. data/app/assets/stylesheets/beetle_reporter/application.css +15 -0
  7. data/app/assets/stylesheets/beetle_reporter/home.css +4 -0
  8. data/app/assets/stylesheets/beetle_reporter/reports.css +4 -0
  9. data/app/assets/stylesheets/beetle_reporter/tailwind.min.css +1 -0
  10. data/app/assets/stylesheets/scaffold.css +80 -0
  11. data/app/controllers/beetle_reporter/application_controller.rb +11 -0
  12. data/app/controllers/beetle_reporter/home_controller.rb +8 -0
  13. data/app/controllers/beetle_reporter/reports_controller.rb +166 -0
  14. data/app/helpers/beetle_reporter/application_helper.rb +9 -0
  15. data/app/helpers/beetle_reporter/home_helper.rb +4 -0
  16. data/app/helpers/beetle_reporter/reports_helper.rb +4 -0
  17. data/app/jobs/beetle_reporter/application_job.rb +4 -0
  18. data/app/mailers/beetle_reporter/application_mailer.rb +6 -0
  19. data/app/models/beetle_reporter/application_record.rb +5 -0
  20. data/app/models/beetle_reporter/report.rb +123 -0
  21. data/app/views/beetle_reporter/home/index.html.haml +4 -0
  22. data/app/views/beetle_reporter/reports/_form.html.haml +14 -0
  23. data/app/views/beetle_reporter/reports/_report.html.haml +55 -0
  24. data/app/views/beetle_reporter/reports/destroy.turbo_stream.haml +1 -0
  25. data/app/views/beetle_reporter/reports/edit.html.haml +6 -0
  26. data/app/views/beetle_reporter/reports/index.html.haml +38 -0
  27. data/app/views/beetle_reporter/reports/new.html.haml +5 -0
  28. data/app/views/beetle_reporter/reports/show.html.haml +1 -0
  29. data/app/views/layouts/beetle_reporter/application.html.haml +22 -0
  30. data/config/locales/beetle_reporter.yml +18 -0
  31. data/config/routes.rb +13 -0
  32. data/db/migrate/20211216073022_create_beetle_reporter_reports.rb +20 -0
  33. data/lib/beetle_reporter/configuration.rb +9 -0
  34. data/lib/beetle_reporter/engine.rb +9 -0
  35. data/lib/beetle_reporter/version.rb +1 -3
  36. data/lib/beetle_reporter.rb +46 -4
  37. data/lib/tasks/beetle_reporter_tasks.rake +4 -0
  38. metadata +137 -15
  39. data/.rspec +0 -3
  40. data/.rubocop.yml +0 -13
  41. data/CHANGELOG.md +0 -5
  42. data/Gemfile +0 -12
  43. data/LICENSE.txt +0 -21
  44. data/beetle_reporter.gemspec +0 -39
  45. data/bin/console +0 -15
  46. data/bin/setup +0 -8
@@ -0,0 +1,80 @@
1
+ body {
2
+ background-color: #fff;
3
+ color: #333;
4
+ margin: 33px;
5
+ }
6
+
7
+ body, p, ol, ul, td {
8
+ font-family: verdana, arial, helvetica, sans-serif;
9
+ font-size: 13px;
10
+ line-height: 18px;
11
+ }
12
+
13
+ pre {
14
+ background-color: #eee;
15
+ padding: 10px;
16
+ font-size: 11px;
17
+ }
18
+
19
+ a {
20
+ color: #000;
21
+ }
22
+
23
+ a:visited {
24
+ color: #666;
25
+ }
26
+
27
+ a:hover {
28
+ color: #fff;
29
+ background-color: #000;
30
+ }
31
+
32
+ th {
33
+ padding-bottom: 5px;
34
+ }
35
+
36
+ td {
37
+ padding: 0 5px 7px;
38
+ }
39
+
40
+ div.field,
41
+ div.actions {
42
+ margin-bottom: 10px;
43
+ }
44
+
45
+ #notice {
46
+ color: green;
47
+ }
48
+
49
+ .field_with_errors {
50
+ padding: 2px;
51
+ background-color: red;
52
+ display: table;
53
+ }
54
+
55
+ #error_explanation {
56
+ width: 450px;
57
+ border: 2px solid red;
58
+ padding: 7px 7px 0;
59
+ margin-bottom: 20px;
60
+ background-color: #f0f0f0;
61
+ }
62
+
63
+ #error_explanation h2 {
64
+ text-align: left;
65
+ font-weight: bold;
66
+ padding: 5px 5px 5px 15px;
67
+ font-size: 12px;
68
+ margin: -7px -7px 0;
69
+ background-color: #c00;
70
+ color: #fff;
71
+ }
72
+
73
+ #error_explanation ul li {
74
+ font-size: 12px;
75
+ list-style: square;
76
+ }
77
+
78
+ label {
79
+ display: block;
80
+ }
@@ -0,0 +1,11 @@
1
+ module BeetleReporter
2
+ class ApplicationController < ActionController::Base
3
+ include ActionView::RecordIdentifier
4
+
5
+ include Pagy::Backend
6
+
7
+ def current_user
8
+ main_app.scope.request.env['warden']&.user
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,8 @@
1
+ require_dependency "beetle_reporter/application_controller"
2
+
3
+ module BeetleReporter
4
+ class HomeController < ApplicationController
5
+ def index
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,166 @@
1
+ module BeetleReporter
2
+ class ReportsController < BeetleReporter::ApplicationController
3
+ before_action :load_report, only: %w[show edit update destroy ignore undo suspend hide remove mute forbid]
4
+
5
+ def index
6
+ # authorize Report
7
+
8
+ load_reports
9
+
10
+ # skip_policy_scope
11
+ end
12
+
13
+ def show
14
+ # authorize @report
15
+ end
16
+
17
+ def new
18
+ # authorize Report
19
+
20
+ @report = Report.new
21
+ end
22
+
23
+ def create
24
+ # authorize Report
25
+
26
+ create_params = report_params.to_hash.deep_symbolize_keys
27
+ @report = report_scope.new(create_params)
28
+ @report.user = current_user
29
+
30
+ respond_to do |format|
31
+ if @report.save
32
+ format.turbo_stream { render turbo_stream: turbo_stream.replace(dom_id(@report), partial: "report", locals: {report: @report}) }
33
+ format.html { redirect_to reports_path, notice: "report was successfully created." }
34
+ else
35
+ format.html { render :edit, status: :unprocessable_entity }
36
+ end
37
+ end
38
+ end
39
+
40
+ def edit
41
+ # authorize @report
42
+ end
43
+
44
+ def update
45
+ # authorize @report
46
+
47
+ update_params = report_params.to_hash.deep_symbolize_keys
48
+
49
+ respond_to do |format|
50
+ if @report.update(update_params)
51
+ format.turbo_stream { render turbo_stream: turbo_stream.replace(dom_id(@report), partial: "report", locals: {report: @report}) }
52
+ format.html { redirect_to reports_path, notice: "report was successfully updated." }
53
+ else
54
+ format.html { render :edit, status: :unprocessable_entity }
55
+ end
56
+ end
57
+ end
58
+
59
+ def destroy
60
+ # authorize @report
61
+
62
+ @report.discard
63
+
64
+ respond_to do |format|
65
+ format.turbo_stream
66
+ end
67
+ end
68
+
69
+ def ignore
70
+ @report.ignore!
71
+
72
+ respond_to do |format|
73
+ format.turbo_stream { render turbo_stream: turbo_stream.remove(dom_id(@report)) }
74
+ format.html { redirect_to reports_url, notice: 'report was successfully ignored.' }
75
+ end
76
+ end
77
+
78
+ def suspend
79
+ @report.suspend!
80
+
81
+ respond_to do |format|
82
+ format.turbo_stream { render turbo_stream: turbo_stream.remove(dom_id(@report)) }
83
+ format.html { redirect_to reports_url, notice: 'report was successfully suspended.' }
84
+ end
85
+ end
86
+
87
+ def hide
88
+ @report.beetle_hide(params[:reason])
89
+
90
+ respond_to do |format|
91
+ format.turbo_stream { render turbo_stream: turbo_stream.remove(dom_id(@report)) }
92
+ format.html { redirect_to reports_url, notice: 'report was successfully hidden.' }
93
+ end
94
+ end
95
+
96
+ def remove
97
+ @report.beetle_remove(params[:reason])
98
+
99
+ respond_to do |format|
100
+ format.turbo_stream { render turbo_stream: turbo_stream.remove(dom_id(@report)) }
101
+ format.html { redirect_to reports_url, notice: 'report was successfully removed.' }
102
+ end
103
+ end
104
+
105
+ def mute
106
+ # TODO: 禁言时间
107
+ @report.beetle_mute(params[:reason])
108
+
109
+ respond_to do |format|
110
+ format.turbo_stream { render turbo_stream: turbo_stream.remove(dom_id(@report)) }
111
+ format.html { redirect_to reports_url, notice: 'report was successfully muted.' }
112
+ end
113
+ end
114
+
115
+ def forbid
116
+ @report.beetle_forbid(params[:reason])
117
+
118
+ respond_to do |format|
119
+ format.turbo_stream { render turbo_stream: turbo_stream.remove(dom_id(@report)) }
120
+ format.html { redirect_to reports_url, notice: 'report was successfully forbiden.' }
121
+ end
122
+ end
123
+
124
+ def undo
125
+ @report.undo!
126
+
127
+ respond_to do |format|
128
+ format.turbo_stream { render turbo_stream: turbo_stream.remove(dom_id(@report)) }
129
+ format.html { redirect_to reports_url, notice: 'report was successfully undo.' }
130
+ end
131
+ end
132
+
133
+ private
134
+
135
+ def load_reports
136
+ builder = report_scope.includes(:record)
137
+ builder = builder.where("reason ilike ?", "%#{params[:q]}%") if params[:q].present?
138
+
139
+ if params[:filter] != 'all'
140
+ builder = builder.pending
141
+ builder = builder.unscope(where: :aasm_state).where(aasm_state: params[:state]) if params[:state].present?
142
+ end
143
+ builder = builder.where(record_type: params[:type]&.classify) if params[:type].present?
144
+
145
+ builder = builder.order(id: :desc, reported_count: :desc)
146
+ builder = builder.reorder(updated_at: :desc) if params[:order] == 'last_updated'
147
+ builder = builder.reorder(created_at: :desc) if params[:order] == 'last_created'
148
+ builder = builder.reorder(updated_at: :asc) if params[:order] == 'oldest_updated'
149
+ builder = builder.reorder(created_at: :asc) if params[:order] == 'oldest_created'
150
+
151
+ @pagy, @reports = pagy(builder)
152
+ end
153
+
154
+ def load_report
155
+ @report = report_scope.find(params[:id] || params[:report_id])
156
+ end
157
+
158
+ def report_scope
159
+ BeetleReporter::Report.kept
160
+ end
161
+
162
+ def report_params
163
+ params.require(:report).permit(:id, :record_id, :record_type, :reason, :user_id, :device_id)
164
+ end
165
+ end
166
+ end
@@ -0,0 +1,9 @@
1
+ module BeetleReporter
2
+ module ApplicationHelper
3
+ include Pagy::Frontend
4
+
5
+ def current_user
6
+ main_app.scope.request.env['warden']&.user
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,4 @@
1
+ module BeetleReporter
2
+ module HomeHelper
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module BeetleReporter
2
+ module ReportsHelper
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module BeetleReporter
2
+ class ApplicationJob < ActiveJob::Base
3
+ end
4
+ end
@@ -0,0 +1,6 @@
1
+ module BeetleReporter
2
+ class ApplicationMailer < ActionMailer::Base
3
+ default from: 'from@example.com'
4
+ layout 'mailer'
5
+ end
6
+ end
@@ -0,0 +1,5 @@
1
+ module BeetleReporter
2
+ class ApplicationRecord < ActiveRecord::Base
3
+ self.abstract_class = true
4
+ end
5
+ end
@@ -0,0 +1,123 @@
1
+ module BeetleReporter
2
+ class Report < ApplicationRecord
3
+ include AASM
4
+ include Discard::Model
5
+
6
+ REASON_TYPES = %w(垃圾广告营销 侮辱谩骂内容 淫秽色情内容 涉政敏感信息 违法有害信息 内容令人不适 内容存在错误)
7
+
8
+ belongs_to :record, polymorphic: true, required: false
9
+ belongs_to :user, class_name: BeetleReporter.config.user_class, optional: true
10
+
11
+ aasm do
12
+ state :pending, initial: true
13
+ state :ignored, :hidden, :removed, :muted, :forbiden, :suspended
14
+
15
+ event :ignore do
16
+ transitions from: :pending, to: :ignored
17
+ end
18
+
19
+ event :hide do
20
+ transitions from: %i[pending ignored], to: :hidden, after: Proc.new {|*args| set_reason(*args) }
21
+ end
22
+
23
+ event :remove do
24
+ transitions from: %i[pending ignored], to: :removed, after: Proc.new {|*args| set_reason(*args) }
25
+ end
26
+
27
+ event :mute do
28
+ transitions from: %i[pending ignored], to: :muted, after: Proc.new {|*args| set_reason(*args) }
29
+ end
30
+
31
+ event :forbid do
32
+ transitions from: %i[pending muted], to: :forbiden, after: Proc.new {|*args| set_reason(*args) }
33
+ end
34
+
35
+ event :suspend do
36
+ transitions from: :pending, to: :suspended
37
+ end
38
+
39
+ event :undo do
40
+ transitions from: %i[ignored hidden removed muted forbiden suspended], to: :pending, after: :undo_callback
41
+ end
42
+ end
43
+
44
+ def beetle_hide(reason)
45
+ return if record.blank?
46
+ raise NoMethodError unless record.respond_to?(:beetle_hide)
47
+
48
+ ret = record.beetle_hide
49
+ hide!(reason) if ret
50
+ end
51
+
52
+ def beetle_remove(reason)
53
+ return if record.blank?
54
+ raise NoMethodError unless record.respond_to?(:beetle_remove)
55
+
56
+ ret = record.beetle_remove
57
+ remove!(reason) if ret
58
+ end
59
+
60
+ def beetle_mute(reason)
61
+ return if record.blank?
62
+ return if record&.user.blank?
63
+ raise NoMethodError unless record.respond_to?(:beetle_mute)
64
+
65
+ ret = record.beetle_mute
66
+ mute!(reason) if ret
67
+ end
68
+
69
+ def beetle_mute(reason)
70
+ return if record.blank?
71
+ return if record&.user.blank?
72
+ raise NoMethodError unless record.respond_to?(:beetle_mute)
73
+
74
+ ret = record.beetle_mute
75
+ mute!(reason) if ret
76
+ end
77
+
78
+ def beetle_forbid(reason)
79
+ return if record.blank?
80
+ return if record&.user.blank?
81
+ raise NoMethodError unless record.respond_to?(:beetle_forbid)
82
+
83
+ ret = record.beetle_forbid
84
+ forbid!(reason) if ret
85
+ end
86
+
87
+ def record_path
88
+ return '#' if record.blank?
89
+
90
+ record.beetle_record_path
91
+ end
92
+
93
+ def record_partial_path
94
+ record.beetle_record_partial_path
95
+ end
96
+
97
+ def undo_callback
98
+ if hidden?
99
+ ret = record.beetle_undo_hide
100
+ update_columns(reason: nil) if ret
101
+ elsif removed?
102
+ ret = record.beetle_undo_remove
103
+ update_columns(reason: nil) if ret
104
+ elsif muted?
105
+ ret = record.beetle_undo_mute
106
+ update_columns(reason: nil) if ret
107
+ elsif forbiden?
108
+ ret = record.beetle_undo_forbid
109
+ update_columns(reason: nil) if ret
110
+ else
111
+ update_columns(reason: nil)
112
+ end
113
+ end
114
+
115
+ def set_reason(reason)
116
+ update_columns(reason: reason)
117
+ end
118
+
119
+ def self.record_type_options
120
+ select(:record_type).where.not(record_type: nil).distinct.map{|t| [t.record_type, t.record_type.downcase]}
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,4 @@
1
+ %h1{class: "text-red-400 text-xl"} Home#index
2
+ %p Find me in app/views/beetle_reporter/home/index.html.erb2
3
+
4
+ %div=link_to "举报中心", reports_path
@@ -0,0 +1,14 @@
1
+ - url = report&.id.blank? ? reports_path : report_path(report)
2
+
3
+ = form_with(model: report, id: dom_id(report), url: url, local: true ) do |form|
4
+ - if report.errors.any?
5
+ %h2= "#{pluralize(report.errors.count, "error")} prohibited this report from being saved:"
6
+ %ul
7
+ - report.errors.each do |error|
8
+ %li= error.full_message
9
+ %div{class: "space-y-2"}
10
+ %div
11
+ %div{class: "text-gray-500 text-xs"}= form.label :reason, "举报原因"
12
+ %div= form.text_field :reason, class: "border p-1 w-full rounded-md"
13
+ %div
14
+ %div= form.submit "确认", class: "px-2 py-0.5 rounded-md bg-gray-800 text-white"
@@ -0,0 +1,55 @@
1
+ = turbo_frame_tag dom_id(report) do
2
+ %div{class: "flex flex-col space-y-2 border rounded-md p-2 mt-2"}
3
+ %div{class: "flex justify-between text-xs"}
4
+ %span{class: "space-x-2"}
5
+ - unless report.record.blank?
6
+ = link_to report.record_path, data: { turbo: false }, target: "_blank" do
7
+ %span{class: "bg-indigo-100 text-indigo-600 py-1 px-2 rounded-lg"}="#{report.record_type.upcase}##{report.record_id}"
8
+ %span{class: "bg-yellow-100 text-yellow-600 rounded-md p-1"}=t("beetle_reporter.aasm_state.#{report.aasm_state}")
9
+ - unless report.reasons.blank?
10
+ - report.reasons.each do |reason|
11
+ %span{class: "border border-red-600 text-red-600 rounded-lg text-xs p-0.5"}= reason
12
+ %span{class: "flex space-x-2 items-center text-sm"}
13
+ - if report.reason
14
+ %span{class: "bg-red-100 text-red-600 p-1 rounded-lg"}= "处置原因:#{report.reason}"
15
+ %span{class: "space-x-1"}
16
+ - report.user_ids.compact.uniq.each do |uid|
17
+ = link_to main_app.send(BeetleReporter.config.main_app_user_path_method.to_sym, uid), data: {turbo: false} do
18
+ %span{class: "bg-blue-100 rounded-lg px-1 py-0.5"}= "#{uid}"
19
+ %span{class: "bg-red-600 text-white rounded-full w-5 h-5 flex justify-center items-center"}= "#{report.reported_count}"
20
+ %span{class: "bg-gray-100 text-gray-600 py-1 px-2 rounded-lg"}
21
+ = link_to report.created_at, report_path(report), data: {turbo: false}
22
+ - unless current_user&.admin?
23
+ = button_to '删除', report, method: :delete, data: { confirm: 'Are you sure?'}, class: "bg-red-600 text-white rounded-md px-2 py-0.5 cursor-pointer"
24
+ %div{class: "flex justify-between items-center py-2 w-full"}
25
+ %div{class: "flex flex-col rounded-lg bg-gray-50 p-3 text-sm space-y-4 w-full"}
26
+ - unless report.record
27
+ %h3{class: "text-2xl text-red-600 flex items-center"}
28
+ 对应条目已被永久删除,当前审核请求已无效
29
+ =button_to "点我删除", report, method: :delete, class: "ml-2 bg-yellow-500 text-white rounded-lg px-2 py-0.5 cursor-pointer"
30
+ - else
31
+ = render report.record_partial_path, record: report.record
32
+ %div{class: "flex flex-col space-y-2"}
33
+ - if report.may_ignore?
34
+ = button_to "无效举报", report_ignore_path(report), class: "text-yellow-600 cursor-pointer px-2 py-0.5 bg-gray-100 rounded-lg"
35
+ - if report.may_suspend?
36
+ = button_to "暂缓处理", report_suspend_path(report), class: "text-indigo-600 cursor-pointer px-2 py-0.5 bg-gray-100 rounded-lg"
37
+ - if report.may_undo?
38
+ = button_to "撤销", report_undo_path(report), class: "text-red-600 cursor-pointer px-2 py-0.5 bg-gray-100 rounded-lg"
39
+ - if report.may_hide?
40
+ = form_with(url: report_hide_path(report), class: "space-x-1 flex items-center") do |f|
41
+ = f.text_field :reason, value: report.reason, placeholder: "隐藏原因是什么?", class: "border rounded-md m py-0.5 px-1"
42
+ = f.submit "隐藏内容", class: "bg-red-500 text-white cursor-pointer px-2 py-0.5 bg-gray-100 rounded-lg"
43
+ - if report.may_remove?
44
+ = form_with(url: report_remove_path(report), class: "space-x-1 flex items-center") do |f|
45
+ = f.text_field :reason, value: report.reason, placeholder: "删除原因是什么?", class: "border rounded-md m py-0.5 px-1"
46
+ = f.submit "删除内容", class: "bg-red-600 text-white cursor-pointer px-2 py-0.5 bg-gray-100 rounded-lg"
47
+ - if report.may_mute?
48
+ = form_with(url: report_mute_path(report), class: "space-x-1 flex items-center") do |f|
49
+ -# = f.text_field :mute_days, value: report.mute_days, placeholder: "禁言天数", class: "border rounded-md m py-0.5 px-1"
50
+ = f.text_field :reason, value: report.reason, placeholder: "禁言原因是什么?", class: "border rounded-md m py-0.5 px-1"
51
+ = f.submit "禁言用户", class: "bg-red-700 text-white cursor-pointer px-2 py-0.5 bg-gray-100 rounded-lg"
52
+ - if report.may_forbid?
53
+ = form_with(url: report_forbid_path(report), class: "space-x-1 flex items-center") do |f|
54
+ = f.text_field :reason, value: report.reason, placeholder: "封禁原因是什么?", class: "border rounded-md m py-0.5 px-1"
55
+ = f.submit "封禁用户", class: "bg-red-800 text-white cursor-pointer px-2 py-0.5 bg-gray-100 rounded-lg"
@@ -0,0 +1 @@
1
+ = turbo_stream.remove(@report)
@@ -0,0 +1,6 @@
1
+ = turbo_frame_tag dom_id(@report) do
2
+ %div{class: "bg-gray-100 p-2 rounded-lg"}
3
+ %div{class: "flex justify-between"}
4
+ %div{class: "text-lg font-bold"} 编辑
5
+ %div= link_to '返回', reports_path, class: "text-gray-500"
6
+ = render 'form', report: @report
@@ -0,0 +1,38 @@
1
+ %div{class: "flex flex-col space-y-2 justify-center"}
2
+ %div{class: "flex justify-between"}
3
+ %div
4
+ = form_with(url: reports_path, method: :get, class: "flex items-center") do |f|
5
+ %div{class: "space-x-0.5"}
6
+ = f.text_field :q, value: params[:q], placeholder: "关键词", class: "border rounded-md m py-0.5 px-1"
7
+ = f.select :type, options_for_select(BeetleReporter::Report.record_type_options, params[:type]), {include_blank: "-- 筛选 --"}, class: "border rounded-md m py-0.5 px-1"
8
+ = f.select :order, options_for_select([%w[按最近创建 last_created], %w[按最晚创建 oldest_created]], params[:order]), {include_blank: "-- 排序 --"}, class: "border rounded-md m py-0.5 px-1"
9
+ = f.hidden_field :state, value: params[:state]
10
+ = f.hidden_field :filter, value: params[:filter]
11
+ = f.submit "检索", class: "rounded-md px-2 py-1 text-sm bg-black text-white cursor-pointer"
12
+ = link_to "重置", reports_path, class: "text-sm"
13
+ %div{class: "flex items-center space-x-2"}
14
+ %div{class: "text-sm text-gray-600"}="共 #{@pagy.count} 条"
15
+ %div{class: "bg-gray-200 rounded-full px-3 py-1 text-xs #{params[:state].blank? && params[:filter].blank? ? 'font-bold' : ''}"}
16
+ = link_to "待处理", request.params.merge(state: "", filter: "")
17
+ %div{class: "bg-gray-200 rounded-full px-3 py-1 text-xs #{params[:state] == 'ignored' ? 'font-bold' : ''}"}
18
+ = link_to "无效举报", request.params.merge(state: "ignored", filter: "")
19
+ %div{class: "bg-gray-200 rounded-full px-3 py-1 text-xs #{params[:state] == 'hidden' ? 'font-bold' : ''}"}
20
+ = link_to "已隐藏", request.params.merge(state: "hidden", filter: "")
21
+ %div{class: "bg-gray-200 rounded-full px-3 py-1 text-xs #{params[:state] == 'removed' ? 'font-bold' : ''}"}
22
+ = link_to "已删除", request.params.merge(state: "removed", filter: "")
23
+ %div{class: "bg-gray-200 rounded-full px-3 py-1 text-xs #{params[:state] == 'muted' ? 'font-bold' : ''}"}
24
+ = link_to "已禁言用户", request.params.merge(state: "muted", filter: "")
25
+ %div{class: "bg-gray-200 rounded-full px-3 py-1 text-xs #{params[:state] == 'forbiden' ? 'font-bold' : ''}"}
26
+ = link_to "已封禁用户", request.params.merge(state: "forbiden", filter: "")
27
+ %div{class: "bg-gray-200 rounded-full px-3 py-1 text-xs #{params[:state] == 'suspended' ? 'font-bold' : ''}"}
28
+ = link_to "暂缓处理", request.params.merge(state: "suspended", filter: "")
29
+ %div{class: "bg-gray-200 rounded-full px-3 py-1 text-xs #{params[:filter] == "all" ? 'font-bold' : ''}"}
30
+ = link_to "全部", request.params.merge(filter: "all", state: "")
31
+ %div
32
+ - if @reports.blank?
33
+ %div{class: "flex justify-center text-gray-500 p-8 border rounded-md"} 空空如也
34
+ - else
35
+ - @reports.each do |report|
36
+ = render report
37
+ %div{class: "flex justify-center mt-2"}
38
+ = raw pagy_nav(@pagy)
@@ -0,0 +1,5 @@
1
+ %div{class: "bg-gray-100 p-2 rounded-lg"}
2
+ %div{class: "flex justify-between"}
3
+ %div{class: "text-lg font-bold"} 添加
4
+ %div= link_to '返回', reports_path, class: "text-gray-500"
5
+ = render 'form', report: @report
@@ -0,0 +1 @@
1
+ = render @report
@@ -0,0 +1,22 @@
1
+ !!! 5
2
+ %html
3
+ %head
4
+ %title 🐞 举报中心
5
+ = csrf_meta_tags
6
+ = csp_meta_tag
7
+ = stylesheet_link_tag "beetle_reporter/application", media: "all"
8
+ = javascript_include_tag "turbo", type: "module"
9
+ = yield :head
10
+ %body{class: "flex flex-col space-y-6 p-4"}
11
+ %div{class: "flex justify-between items-center"}
12
+ %h2{class: "text-xl bg-black text-white rounded-full py-1 px-4 cursor-pointer"}
13
+ %span{class: "shadow-lg"} 🐞
14
+ %span 举报中心
15
+
16
+ %ul{class: "flex justify-end items-center space-x-2 text-blue-600"}
17
+ %li= link_to "待处理(#{BeetleReporter::Report.pending.count})", reports_path
18
+ %li= link_to "控制台", main_app.send(BeetleReporter.config.main_app_root_path_method.to_sym)
19
+ %li= current_user.email
20
+
21
+ %div
22
+ = yield
@@ -0,0 +1,18 @@
1
+ "zh-CN":
2
+ beetle_reporter:
3
+ action:
4
+ mod: 审核
5
+ banned: 禁用
6
+ ignore: 忽略
7
+ replace: 替换
8
+ block: 禁用
9
+ pass: 忽略
10
+ review: 审核
11
+ aasm_state:
12
+ pending: 待审
13
+ suspended: 暂缓处理
14
+ ignored: 无效举报
15
+ hidden: 已隐藏
16
+ removed: 已删除
17
+ muted: 已禁言用户
18
+ forbiden: 已封禁用户
data/config/routes.rb ADDED
@@ -0,0 +1,13 @@
1
+ BeetleReporter::Engine.routes.draw do
2
+ resources :reports do
3
+ post :ignore
4
+ post :undo
5
+ post :suspend
6
+ post :hide
7
+ post :remove
8
+ post :mute
9
+ post :forbid
10
+ end
11
+
12
+ root "reports#index"
13
+ end
@@ -0,0 +1,20 @@
1
+ class CreateBeetleReporterReports < ActiveRecord::Migration[6.1]
2
+ def change
3
+ create_table :beetle_reporter_reports do |t|
4
+ t.bigint :record_id
5
+ t.string :record_type
6
+ t.string :reason
7
+ t.bigint :user_id
8
+ t.bigint :user_ids, array: true, default: []
9
+ t.string :reasons, array: true, default: []
10
+ t.integer :reported_count, default: 0
11
+ t.string :device_id
12
+ t.string :aasm_state
13
+ t.string :treatment_reason
14
+ t.datetime :discarded_at
15
+
16
+ t.timestamps
17
+ end
18
+ add_index :beetle_reporter_reports, :discarded_at
19
+ end
20
+ end
@@ -0,0 +1,9 @@
1
+ module BeetleReporter
2
+ class Configuration
3
+ attr_accessor :user_class
4
+ attr_accessor :current_user_method
5
+ attr_accessor :user_name_method
6
+ attr_accessor :main_app_root_path_method
7
+ attr_accessor :main_app_user_path_method
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module BeetleReporter
2
+ class Engine < ::Rails::Engine
3
+ isolate_namespace BeetleReporter
4
+
5
+ initializer "beetle_reporter.assets.precompile" do |app|
6
+ app.config.assets.precompile += %w( beetle_reporter/application.css )
7
+ end
8
+ end
9
+ end