diff --git a/app/controllers/rate_limits_controller.rb b/app/controllers/rate_limits_controller.rb
new file mode 100644
index 000000000..daaa8d899
--- /dev/null
+++ b/app/controllers/rate_limits_controller.rb
@@ -0,0 +1,8 @@
+class RateLimitsController < ApplicationController
+ respond_to :html, :json, :xml
+
+ def index
+ @rate_limits = authorize RateLimit.visible(CurrentUser.user).paginated_search(params, count_pages: true)
+ respond_with(@rate_limits)
+ end
+end
diff --git a/app/models/rate_limit.rb b/app/models/rate_limit.rb
index b3d0b8ab0..dc9daee90 100644
--- a/app/models/rate_limit.rb
+++ b/app/models/rate_limit.rb
@@ -5,6 +5,22 @@ class RateLimit < ApplicationRecord
expired.delete_all
end
+ def self.visible(user)
+ if user.is_owner?
+ all
+ elsif user.is_anonymous?
+ none
+ else
+ where(key: [user.cache_key])
+ end
+ end
+
+ def self.search(params)
+ q = search_attributes(params, :id, :created_at, :updated_at, :limited, :points, :action, :key)
+ q = q.apply_default_order(params)
+ q
+ end
+
# `action` is the action being limited. Usually a controller endpoint.
# `keys` is who is being limited. Usually a [user, ip] pair, meaning the action is limited both by the user's ID and their IP.
# `cost` is the number of points the action costs.
diff --git a/app/policies/rate_limit_policy.rb b/app/policies/rate_limit_policy.rb
new file mode 100644
index 000000000..9970ac553
--- /dev/null
+++ b/app/policies/rate_limit_policy.rb
@@ -0,0 +1,5 @@
+class RateLimitPolicy < ApplicationPolicy
+ def index?
+ true
+ end
+end
diff --git a/app/views/rate_limits/index.html.erb b/app/views/rate_limits/index.html.erb
new file mode 100644
index 000000000..4ac3ffcc0
--- /dev/null
+++ b/app/views/rate_limits/index.html.erb
@@ -0,0 +1,21 @@
+
+
+ <%= table_for @rate_limits, class: "striped autofit" do |t| %>
+ <% t.column :action %>
+
+ <% t.column :key %>
+
+ <% t.column :points do |rate_limit| %>
+ <%= rate_limit.points.round(2) %>
+ <% end %>
+
+ <% t.column :limited? %>
+
+ <% t.column :updated_at do |rate_limit| %>
+ <%= time_ago_in_words_tagged rate_limit.updated_at %>
+ <% end %>
+ <% end %>
+
+ <%= numbered_paginator(@rate_limits) %>
+
+
diff --git a/config/routes.rb b/config/routes.rb
index 61010d9ed..82df33b1c 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -220,6 +220,7 @@ Rails.application.routes.draw do
end
end
resources :artist_commentary_versions, :only => [:index, :show]
+ resources :rate_limits, only: [:index]
resource :related_tag, :only => [:show, :update]
resources :recommended_posts, only: [:index]
resources :robots, only: [:index]
diff --git a/test/functional/rate_limits_controller_test.rb b/test/functional/rate_limits_controller_test.rb
new file mode 100644
index 000000000..499a018cc
--- /dev/null
+++ b/test/functional/rate_limits_controller_test.rb
@@ -0,0 +1,33 @@
+require 'test_helper'
+
+class RateLimitsControllerTest < ActionDispatch::IntegrationTest
+ context "The rate limits controller" do
+ context "index action" do
+ setup do
+ @user = create(:user)
+ create(:rate_limit, key: @user.cache_key)
+ end
+
+ should "show all rate limits to the owner" do
+ get_auth rate_limits_path, create(:owner_user)
+
+ assert_response :success
+ assert_select "tbody tr", count: 2 # 2 because the login action creates a second rate limit.
+ end
+
+ should "show the user their own rate limits" do
+ get_auth rate_limits_path, @user
+
+ assert_response :success
+ assert_select "tbody tr", count: 1
+ end
+
+ should "not show users rate limits belonging to other users" do
+ get_auth rate_limits_path, create(:user)
+
+ assert_response :success
+ assert_select "tbody tr", count: 0
+ end
+ end
+ end
+end