diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt new file mode 100644 index 00000000..ec45db72 --- /dev/null +++ b/CONTRIBUTORS.txt @@ -0,0 +1,10 @@ +List of contributors, format is "Name @github " + +Andreas N. @pixeldesu +Georg G. @nilsding +Yuki @yukimono +Adel D.-K. +Howl +Iain D. +Jona H. @schisma @ix +Robin B. diff --git a/Gemfile b/Gemfile index f3273c1a..737571bc 100644 --- a/Gemfile +++ b/Gemfile @@ -68,6 +68,8 @@ gem 'redis' gem 'fake_email_validator' +gem 'rollbar', '~> 1.5.3' + group :development do # require spring 1.3.5 since shit's on fire on my local instance with 1.3.4 (Gem::LoadError) gem 'spring', '~> 1.3.5' diff --git a/Gemfile.lock b/Gemfile.lock index 468e48f3..c9ffaa3d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -360,6 +360,8 @@ GEM remotipart (1.2.1) responders (2.1.0) railties (>= 4.2.0, < 5) + rollbar (1.5.3) + multi_json (~> 1.3) rspec-core (3.0.4) rspec-support (~> 3.0.0) rspec-expectations (3.0.4) @@ -529,6 +531,7 @@ DEPENDENCIES rake redcarpet redis + rollbar (~> 1.5.3) rspec-rails (~> 3.0.0) ruby-progressbar sanitize diff --git a/app/controllers/ajax/question_controller.rb b/app/controllers/ajax/question_controller.rb index c07305d2..17f61a53 100644 --- a/app/controllers/ajax/question_controller.rb +++ b/app/controllers/ajax/question_controller.rb @@ -55,16 +55,13 @@ class Ajax::QuestionController < ApplicationController if params[:rcpt] == 'followers' unless current_user.nil? - current_user.followers.each do |f| - Inbox.create!(user_id: f.id, question_id: question.id, new: true) - end + QuestionWorker.perform_async params[:rcpt], current_user.id, question.id end elsif params[:rcpt].start_with? 'grp:' unless current_user.nil? begin - current_user.groups.find_by_name!(params[:rcpt].sub 'grp:', '').members.each do |m| - Inbox.create!(user_id: m.user.id, question_id: question.id, new: true) - end + current_user.groups.find_by_name!(params[:rcpt].sub 'grp:', '') + QuestionWorker.perform_async params[:rcpt], current_user.id, question.id rescue ActiveRecord::RecordNotFound @status = :not_found @message = I18n.t('messages.question.create.not_found') @@ -73,6 +70,13 @@ class Ajax::QuestionController < ApplicationController end end else + if User.find(params[:rcpt]).nil? + @status = :not_found + @message = I18n.t('messages.question.create.not_found') + @success = false + return + end + Inbox.create!(user_id: params[:rcpt], question_id: question.id, new: true) end diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index 7580d04f..68376f4a 100644 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml @@ -5,9 +5,6 @@ %meta{'http-equiv' => 'X-UA-Compatible' ,content: 'IE=edge'} %meta{name: 'viewport', content: 'width=device-width, initial-scale=1, user-scalable=no'} %meta{name: 'theme-color', content: '#5e35b1'} - %meta{name: "mobile-web-app-capable", content: "yes"} - %meta{name: "apple-mobile-web-app-capable", content: "yes"} - %meta{name: 'apple-mobile-web-app-status-bar-style', content: 'black-translucent'} %link{rel: 'apple-touch-icon', href: '/apple-touch-icon-precomposed.png'} %link{rel: 'icon', href: '/images/favicon/favicon-16.png', sizes: '16x16'} %link{rel: 'icon', href: '/icon-152.png', sizes: '152x152'} diff --git a/app/views/shared/_question.html.haml b/app/views/shared/_question.html.haml index 393fad72..4e7717c3 100644 --- a/app/views/shared/_question.html.haml +++ b/app/views/shared/_question.html.haml @@ -1,3 +1,4 @@ +- type ||= nil .panel.panel-default.questionbox{data: { id: q.id }} .panel-body .media diff --git a/app/views/user/questions.js.erb b/app/views/user/questions.js.erb index 01344836..a27fddb8 100644 --- a/app/views/user/questions.js.erb +++ b/app/views/user/questions.js.erb @@ -1,8 +1,8 @@ $('#questions').append('<% @questions.each do |q| - %><%= j render 'shared/question', q: q + %><%= j render 'shared/question', q: q, type: nil %><% end %>'); <% if @questions.next_page %> $('#pagination').html('<%= j will_paginate @questions, renderer: BootstrapPagination::Rails, page_links: false %>'); <% else %> $('#pagination, #load-more-btn').remove(); -<% end %> \ No newline at end of file +<% end %> diff --git a/app/workers/deletion_worker.rb b/app/workers/deletion_worker.rb index 5ddaf186..bd98371f 100644 --- a/app/workers/deletion_worker.rb +++ b/app/workers/deletion_worker.rb @@ -1,14 +1,14 @@ class DeletionWorker include Sidekiq::Worker - sidekiq_options queue: :deletion + sidekiq_options queue: :deletion, retry: false # @param resource_id [Integer] user id passed from Devise def perform(resource_id) begin User.find(resource_id).destroy! rescue => e - Rails.logger.error "failed to delete user: #{e.message}" + logger.info "failed to delete user: #{e.message}" end end end diff --git a/app/workers/question_worker.rb b/app/workers/question_worker.rb new file mode 100644 index 00000000..98a614c8 --- /dev/null +++ b/app/workers/question_worker.rb @@ -0,0 +1,27 @@ +class QuestionWorker + include Sidekiq::Worker + + sidekiq_options queue: :question, retry: false + + # @param rcpt [String] recipient + # @param user_id [Integer] user id passed from Devise + # @param question_id [Integer] newly created question id + def perform(rcpt, user_id, question_id) + begin + user = User.find(user_id) + if rcpt == 'followers' + user.followers.each do |f| + Inbox.create(user_id: f.id, question_id: question_id, new: true) + end + elsif rcpt.start_with? 'grp:' + user.groups.find_by_name!(rcpt.sub 'grp:', '').members.each do |m| + Inbox.create(user_id: m.user.id, question_id: question_id, new: true) + end + else + logger.info "unknown rcpt #{rcpt}" + end + rescue => e + logger.info "failed to ask question: #{e.message}" + end + end +end diff --git a/app/workers/share_worker.rb b/app/workers/share_worker.rb index e607919b..0bc57799 100644 --- a/app/workers/share_worker.rb +++ b/app/workers/share_worker.rb @@ -1,7 +1,7 @@ class ShareWorker include Sidekiq::Worker - sidekiq_options queue: :share + sidekiq_options queue: :share, retry: false # @param user_id [Integer] the user id # @param answer_id [Integer] the user id @@ -11,8 +11,8 @@ class ShareWorker begin service.post(Answer.find(answer_id)) if services.include? service.provider rescue => e - Rails.logger.error "failed to post answer #{answer_id} to #{service.provider} for user #{user_id}: #{e.message}" + logger.info "failed to post answer #{answer_id} to #{service.provider} for user #{user_id}: #{e.message}" end end end -end \ No newline at end of file +end diff --git a/config/initializers/rollbar.rb b/config/initializers/rollbar.rb new file mode 100644 index 00000000..a148e79e --- /dev/null +++ b/config/initializers/rollbar.rb @@ -0,0 +1,54 @@ +require 'rollbar/rails' +Rollbar.configure do |config| + # Without configuration, Rollbar is enabled in all environments. + # To disable in specific environments, set config.enabled=false. + + config.access_token = ENV['ROLLBAR_ACCESS_TOKEN'] + + # Here we'll disable in 'test' and 'development': + if Rails.env.test? or Rails.env.development? + config.enabled = false + end + + # scrub out the "confirmation_token" field + config.scrub_fields |= [:confirmation_token] + + # By default, Rollbar will try to call the `current_user` controller method + # to fetch the logged-in user object, and then call that object's `id`, + # `username`, and `email` methods to fetch those properties. To customize: + # config.person_method = "my_current_user" + # config.person_id_method = "my_id" + # config.person_username_method = "my_username" + # config.person_email_method = "my_email" + + # If you want to attach custom data to all exception and message reports, + # provide a lambda like the following. It should return a hash. + # config.custom_data_method = lambda { {:some_key => "some_value" } } + + # Add exception class names to the exception_level_filters hash to + # change the level that exception is reported at. Note that if an exception + # has already been reported and logged the level will need to be changed + # via the rollbar interface. + # Valid levels: 'critical', 'error', 'warning', 'info', 'debug', 'ignore' + # 'ignore' will cause the exception to not be reported at all. + # config.exception_level_filters.merge!('MyCriticalException' => 'critical') + # + # You can also specify a callable, which will be called with the exception instance. + # config.exception_level_filters.merge!('MyCriticalException' => lambda { |e| 'critical' }) + + # Enable asynchronous reporting (uses girl_friday or Threading if girl_friday + # is not installed) + # config.use_async = true + # Supply your own async handler: + # config.async_handler = Proc.new { |payload| + # Thread.new { Rollbar.process_payload_safely(payload) } + # } + + # Enable asynchronous reporting (using sucker_punch) + # config.use_sucker_punch + + # Enable delayed reporting (using Sidekiq) + # config.use_sidekiq + # You can supply custom Sidekiq options: + config.use_sidekiq 'queue' => 'rollbar' +end diff --git a/config/sidekiq.yml b/config/sidekiq.yml index 87312f72..50db074a 100644 --- a/config/sidekiq.yml +++ b/config/sidekiq.yml @@ -10,3 +10,5 @@ production: - paperclip - mailer - deletion + - rollbar + - question