diff --git a/app/assets/javascripts/admin/controllers/admin_dashboard_controller.js b/app/assets/javascripts/admin/controllers/admin_dashboard_controller.js index 3bfacd35c68..5dc03a709ba 100644 --- a/app/assets/javascripts/admin/controllers/admin_dashboard_controller.js +++ b/app/assets/javascripts/admin/controllers/admin_dashboard_controller.js @@ -42,5 +42,9 @@ Discourse.AdminDashboardController = Ember.Controller.extend({ problemsTimestamp: function() { return moment(this.get('problemsFetchedAt')).format('LLL'); - }.property('problemsFetchedAt') + }.property('problemsFetchedAt'), + + updatedTimestamp: function() { + return moment(this.get('updated_at')).format('LLL'); + }.property('updated_at') }); diff --git a/app/assets/javascripts/admin/routes/admin_dashboard_route.js b/app/assets/javascripts/admin/routes/admin_dashboard_route.js index fb3562bc5e5..8af0dab4bee 100644 --- a/app/assets/javascripts/admin/routes/admin_dashboard_route.js +++ b/app/assets/javascripts/admin/routes/admin_dashboard_route.js @@ -14,7 +14,7 @@ Discourse.AdminDashboardRoute = Discourse.Route.extend({ }, fetchDashboardData: function(c) { - if( !c.get('dashboardFetchedAt') || moment().subtract('hour', 1).toDate() > c.get('dashboardFetchedAt') ) { + if( !c.get('dashboardFetchedAt') || moment().subtract('minutes', 30).toDate() > c.get('dashboardFetchedAt') ) { c.set('dashboardFetchedAt', new Date()); Discourse.AdminDashboard.find().then(function(d) { if( Discourse.SiteSettings.version_checks ){ @@ -32,7 +32,7 @@ Discourse.AdminDashboardRoute = Discourse.Route.extend({ c.set('top_referrers', topReferrers); } - ['admins', 'moderators', 'blocked', 'banned', 'top_traffic_sources', 'top_referred_topics'].forEach(function(x) { + ['admins', 'moderators', 'blocked', 'banned', 'top_traffic_sources', 'top_referred_topics', 'updated_at'].forEach(function(x) { c.set(x, d[x]); }); diff --git a/app/assets/javascripts/admin/templates/dashboard.js.handlebars b/app/assets/javascripts/admin/templates/dashboard.js.handlebars index 7c90503fdc0..e3e57f9e7fd 100644 --- a/app/assets/javascripts/admin/templates/dashboard.js.handlebars +++ b/app/assets/javascripts/admin/templates/dashboard.js.handlebars @@ -276,3 +276,9 @@
+ +
+
{{i18n admin.dashboard.last_updated}} {{updatedTimestamp}}
+
+
+
\ No newline at end of file diff --git a/app/controllers/admin/dashboard_controller.rb b/app/controllers/admin/dashboard_controller.rb index 1b46c082e2d..b27a1d04a20 100644 --- a/app/controllers/admin/dashboard_controller.rb +++ b/app/controllers/admin/dashboard_controller.rb @@ -1,9 +1,6 @@ class Admin::DashboardController < Admin::AdminController - def index - dashboard_data = Rails.cache.fetch("admin-dashboard-data-#{Discourse::VERSION::STRING}", expires_in: 1.hour) do - AdminDashboardData.fetch_all.as_json - end + dashboard_data = AdminDashboardData.fetch_cached_stats || Jobs::DashboardStats.new.execute({}) dashboard_data.merge!({version_check: DiscourseUpdates.check_version.as_json}) if SiteSetting.version_checks? render json: dashboard_data end diff --git a/app/models/admin_dashboard_data.rb b/app/models/admin_dashboard_data.rb index 02581e9bf0a..5c2c47fc64f 100644 --- a/app/models/admin_dashboard_data.rb +++ b/app/models/admin_dashboard_data.rb @@ -41,9 +41,17 @@ class AdminDashboardData notification_email_check ].compact end - def self.fetch_all + def self.fetch_stats AdminDashboardData.new end + def self.fetch_cached_stats + # The DashboardStats job is responsible for generating and caching this. + stats = $redis.get(stats_cache_key) + stats ? JSON.parse(stats) : nil + end + def self.stats_cache_key + 'dash-stats' + end def self.fetch_problems AdminDashboardData.new.problems @@ -58,10 +66,16 @@ class AdminDashboardData blocked: User.blocked.count, top_referrers: IncomingLinksReport.find('top_referrers').as_json, top_traffic_sources: IncomingLinksReport.find('top_traffic_sources').as_json, - top_referred_topics: IncomingLinksReport.find('top_referred_topics').as_json + top_referred_topics: IncomingLinksReport.find('top_referred_topics').as_json, + updated_at: Time.zone.now.as_json } end + def self.recalculate_interval + # Could be configurable, but clockwork + multisite need to support it. + 30 # minutes + end + def rails_env_check I18n.t("dashboard.rails_env_warning", env: Rails.env) unless Rails.env == 'production' end diff --git a/app/models/site_setting.rb b/app/models/site_setting.rb index 6f027adb77a..2dcf0ad07dc 100644 --- a/app/models/site_setting.rb +++ b/app/models/site_setting.rb @@ -239,6 +239,7 @@ class SiteSetting < ActiveRecord::Base setting(:delete_user_max_age, 7) setting(:delete_all_posts_max, 10) + def self.generate_api_key! self.api_key = SecureRandom.hex(32) end diff --git a/config/clock.rb b/config/clock.rb index db7907fa80e..0272fd8ff40 100644 --- a/config/clock.rb +++ b/config/clock.rb @@ -35,5 +35,5 @@ module Clockwork every(1.minute, 'clockwork_heartbeat') every(1.minute, 'poll_mailbox') every(30.minutes, 'destroy_old_deletion_stubs') - + every(AdminDashboardData.recalculate_interval.minutes, 'dashboard_stats') end diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index bff82b261c5..81aa002adf2 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -1027,6 +1027,7 @@ en: dashboard: title: "Dashboard" + last_updated: "Dashboard last updated:" version: "Version" up_to_date: "You're up to date!" critical_available: "A critical update is available." diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index 4bcd24329ac..3fae19782d1 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -663,7 +663,6 @@ en: delete_user_max_age: "The maximum age of a user, in days, which can be deleted by an admin." delete_all_posts_max: "The maximum number of posts that can be deleted at once with the Delete All Posts button. If a user has more than this many posts, the posts cannot all be deleted at once and the user can't be deleted." - notification_types: mentioned: "%{display_username} mentioned you in %{link}" liked: "%{display_username} liked your post in %{link}" diff --git a/lib/jobs/dashboard_stats.rb b/lib/jobs/dashboard_stats.rb new file mode 100644 index 00000000000..0e4fb46b8a7 --- /dev/null +++ b/lib/jobs/dashboard_stats.rb @@ -0,0 +1,15 @@ +module Jobs + class DashboardStats < Jobs::Base + + def execute(args) + stats_json = AdminDashboardData.fetch_stats.as_json + + # Add some extra time to the expiry so that the next job run has plenty of time to + # finish before previous cached value expires. + $redis.setex AdminDashboardData.stats_cache_key, (AdminDashboardData.recalculate_interval + 5).minutes, stats_json.to_json + + stats_json + end + + end +end \ No newline at end of file diff --git a/spec/controllers/admin/dashboard_controller_spec.rb b/spec/controllers/admin/dashboard_controller_spec.rb index 6cf637c3d4d..17d0ee253a0 100644 --- a/spec/controllers/admin/dashboard_controller_spec.rb +++ b/spec/controllers/admin/dashboard_controller_spec.rb @@ -3,9 +3,7 @@ require_dependency 'discourse_version_check' describe Admin::DashboardController do before do - #NOTE: Rails.cache should be blanked between tests, at the moment we can share state with it - # that is seriously bust on quite a few levels - Rails.cache.delete("admin-dashboard-data-#{Discourse::VERSION::STRING}") + AdminDashboardData.stubs(:fetch_cached_stats).returns({reports:[]}) Jobs::VersionCheck.any_instance.stubs(:execute).returns(true) end @@ -45,13 +43,6 @@ describe Admin::DashboardController do json['version_check'].should_not be_present end end - - it 'returns report data' do - xhr :get, :index - json = JSON.parse(response.body) - json.should have_key('reports') - json['reports'].should be_a(Array) - end end context '.problems' do