diff --git a/app/controllers/cms/translate/access_logs_controller.rb b/app/controllers/cms/translate/access_logs_controller.rb new file mode 100644 index 00000000000..85cb515633b --- /dev/null +++ b/app/controllers/cms/translate/access_logs_controller.rb @@ -0,0 +1,34 @@ +class Cms::Translate::AccessLogsController < ApplicationController + include Cms::BaseFilter + include Cms::CrudFilter + + model Translate::AccessLog + navi_view "cms/translate/main/navi" + + public + + def download + @item = Translate::DownloadParam.new + @item.save_term = '1.day' + return if request.get? || request.head? + + @item.attributes = params.require(:item).permit(:encoding, :save_term) + if @item.invalid? + render + return + end + + cond = { site_id: @cur_site.id } + items = @model.where(cond) + @item.save_term_in_time.try do |from| + items = items.gte(created: from) + end + items = items.reorder(created: 1) + + enumerable = items.enum_csv(cur_site: @cur_site, encoding: @item.encoding) + filename = "translate_access_logs_#{Time.zone.now.to_i}.csv" + + response.status = 200 + send_enum enumerable, type: enumerable.content_type, filename: filename + end +end diff --git a/app/controllers/concerns/translate/public_filter.rb b/app/controllers/concerns/translate/public_filter.rb index a6621b2c687..159d31fad64 100644 --- a/app/controllers/concerns/translate/public_filter.rb +++ b/app/controllers/concerns/translate/public_filter.rb @@ -11,8 +11,21 @@ def set_request_path_with_translate return if !@cur_site.translate_enabled? return if @cur_main_path !~ /^#{@cur_site.translate_location}\/.+?\// + deny_message = nil if browser.bot? - Rails.logger.info("translate denied due to a bot access: #{request.user_agent}") + deny_message = "bot access: #{request.user_agent}" + end + if @cur_site.translate_deny_no_refererr? && request.referer.blank? + deny_message = "no referer access" + end + Translate::AccessLog.create_log!(@cur_site, request) do |item| + item.path = request_path + item.remote_addr = remote_addr + item.deny_message = deny_message + end + + if deny_message.present? + Rails.logger.info("translate denied due to a #{deny_message}") return end diff --git a/app/jobs/translate/access_log/purge_job.rb b/app/jobs/translate/access_log/purge_job.rb new file mode 100644 index 00000000000..9088dce0620 --- /dev/null +++ b/app/jobs/translate/access_log/purge_job.rb @@ -0,0 +1,21 @@ +class Translate::AccessLog::PurgeJob < Cms::ApplicationJob + + DEFAULT_THRESHOLD_DAYS = 60 + + before_perform :set_items + + def model + Translate::AccessLog + end + + def perform + count = @items.destroy_all + Rails.logger.info "#{I18n.l(@threshold.to_date)}以前の#{model.model_name.human}を#{count}件削除しました。" + end + + def set_items + @threshold = Time.zone.now - (site.translate_access_log_threshold || DEFAULT_THRESHOLD_DAYS).days + @items = model.site(site).lt(created: @threshold) + @items + end +end diff --git a/app/models/concerns/ss/addon/translate/site_setting.rb b/app/models/concerns/ss/addon/translate/site_setting.rb index 62b87d299a3..4818a4dbf6a 100644 --- a/app/models/concerns/ss/addon/translate/site_setting.rb +++ b/app/models/concerns/ss/addon/translate/site_setting.rb @@ -33,6 +33,10 @@ module Translate::SiteSetting field :translate_google_api_request_count, type: Integer, default: 0 field :translate_google_api_request_word_count, type: Integer, default: 0 + # access log + field :translate_referer_restriction, type: String, default: "disabled" + field :translate_access_log_threshold, type: Integer, default: 60 + permit_params :translate_state permit_params :translate_source_id permit_params translate_target_ids: [] @@ -53,6 +57,9 @@ module Translate::SiteSetting permit_params :translate_google_api_request_count permit_params :translate_google_api_request_word_count + permit_params :translate_referer_restriction + permit_params :translate_access_log_threshold + validates :translate_api, presence: true, if: -> { translate_enabled? } validate :validate_translate_source, if: -> { translate_api.present? } validate :validate_translate_targets, if: -> { translate_api.present? } @@ -122,6 +129,17 @@ def translate_url ::File.join(url, translate_location, "/") end + def translate_referer_restriction_options + [ + [I18n.t("translate.options.referer_restriction.disabled"), "disabled"], + [I18n.t("translate.options.referer_restriction.enabled"), "enabled"], + ] + end + + def translate_deny_no_refererr? + translate_referer_restriction == "enabled" + end + def find_translate_target(code) translate_targets.select { |item| item.code == code }.first end diff --git a/app/models/translate/access_log.rb b/app/models/translate/access_log.rb new file mode 100644 index 00000000000..b5fe352af9a --- /dev/null +++ b/app/models/translate/access_log.rb @@ -0,0 +1,62 @@ +class Translate::AccessLog + include SS::Document + include SS::Reference::Site + include Cms::SitePermission + + set_permission_name "cms_tools", :use + + store_in_repl_master + index({ created: -1 }) + + field :path, type: String + field :remote_addr, type: String + field :user_agent, type: String + field :referer, type: String + field :deny_message, type: String + + validates :path, presence: true + + default_scope ->{ order_by(created: -1) } + + def bot? + return false if user_agent.blank? + Browser.new(user_agent).bot? + end + + class << self + def create_log!(site, request) + item = self.new + item.cur_site = site + item.path = request.path + item.user_agent = request.user_agent + item.remote_addr = request.remote_addr + item.referer = request.referer + yield item if block_given? + item.save! + item + end + + def search(params = {}) + criteria = self.where({}) + return criteria if params.blank? + + if params[:keyword].present? + criteria = criteria.keyword_in params[:keyword], :path + end + criteria + end + + def enum_csv(options) + exporter = SS::Csv.draw(:export, context: self) do |drawer| + drawer.column :created + drawer.column :path + drawer.column :user_agent + drawer.column :remote_addr + drawer.column :referer + drawer.column :deny_message + end + + exporter.enum(all, options) + end + end +end diff --git a/app/models/translate/download_param.rb b/app/models/translate/download_param.rb new file mode 100644 index 00000000000..4c8e80f2fba --- /dev/null +++ b/app/models/translate/download_param.rb @@ -0,0 +1,25 @@ +class Translate::DownloadParam + include ActiveModel::Model + include ActiveModel::Attributes + + attr_accessor :cur_site, :cur_user + + attribute :encoding, :string + attribute :save_term, :string + + validates :encoding, presence: true, inclusion: { in: %w(Shift_JIS UTF-8), allow_blank: true } + validates :save_term, inclusion: { in: %w(1.day 1.month 1.year), allow_blank: true } + + def save_term_options + %w(1.day 1.month 1.year).map do |v| + [ I18n.t("ss.options.duration.#{v.sub(".", "_")}"), v ] + end + end + + def save_term_in_time(now = nil) + return if save_term.blank? + + now ||= Time.zone.now + now - SS::Duration.parse(save_term) + end +end diff --git a/app/views/cms/translate/access_logs/_menu.html.erb b/app/views/cms/translate/access_logs/_menu.html.erb new file mode 100644 index 00000000000..86685207bff --- /dev/null +++ b/app/views/cms/translate/access_logs/_menu.html.erb @@ -0,0 +1,12 @@ + diff --git a/app/views/cms/translate/access_logs/_show.html.erb b/app/views/cms/translate/access_logs/_show.html.erb new file mode 100644 index 00000000000..dc5c9e03e16 --- /dev/null +++ b/app/views/cms/translate/access_logs/_show.html.erb @@ -0,0 +1,16 @@ +
+
<%= @model.t :path %>
+
<%= @item.path %>
+ +
<%= @model.t :remote_addr %>
+
<%= @item.remote_addr %>
+ +
<%= @model.t :user_agent %>
+
<%= @item.user_agent %>
+ +
<%= @model.t :referer %>
+
<%= @item.referer %>
+ +
<%= @model.t :deny_message %>
+
<%= @item.deny_message %>
+
diff --git a/app/views/cms/translate/access_logs/download.html.erb b/app/views/cms/translate/access_logs/download.html.erb new file mode 100644 index 00000000000..f176f1a8c22 --- /dev/null +++ b/app/views/cms/translate/access_logs/download.html.erb @@ -0,0 +1,27 @@ +<%= form_for :item, url: { action: :download }, html: { method: :post } do |f| %> + <%= error_messages_for :item %> + +
+

<%= t "ss.download" %>

+ +
+
<%= t('ss.encoding') %>
+
+ <% %w(UTF-8 Shift_JIS).each do |encoding| %> + + <% end %> +
+ +
<%= @item.class.human_attribute_name :save_term %>
+
<%= f.select :save_term, @item.save_term_options, include_blank: t("history.options.duration.all_save") %>
+
+
+ + +<% end %> diff --git a/app/views/cms/translate/access_logs/index.html.erb b/app/views/cms/translate/access_logs/index.html.erb new file mode 100644 index 00000000000..3e8b8e1e603 --- /dev/null +++ b/app/views/cms/translate/access_logs/index.html.erb @@ -0,0 +1,37 @@ +
+
+ <%= render template: "_search" %> +
+ + +
+ +<%= paginate @items if @items.try(:current_page) %> diff --git a/app/views/cms/translate/main/_navi.html.erb b/app/views/cms/translate/main/_navi.html.erb index db583ab9ad8..5addbf9d7d2 100644 --- a/app/views/cms/translate/main/_navi.html.erb +++ b/app/views/cms/translate/main/_navi.html.erb @@ -3,6 +3,7 @@

<%= link_to t("translate.text_cache"), cms_translate_text_caches_path %>

<%= link_to t("translate.site_setting"), cms_translate_site_setting_path %>

<%= link_to t("translate.lang"), cms_translate_langs_path %>

+

<%= link_to t("translate.access_log"), cms_translate_access_logs_path %>

<%= render partial: "cms/main/conf_navi" %> diff --git a/app/views/cms/translate/site_settings/_form_access_restriction.html.erb b/app/views/cms/translate/site_settings/_form_access_restriction.html.erb new file mode 100644 index 00000000000..95d904895ff --- /dev/null +++ b/app/views/cms/translate/site_settings/_form_access_restriction.html.erb @@ -0,0 +1,10 @@ +
+
<%= @model.t :translate_referer_restriction %><%= @model.tt :translate_referer_restriction %>
+
<%= f.select :translate_referer_restriction, @item.translate_referer_restriction_options %>
+ +
<%= @model.t :translate_access_log_threshold %><%= @model.tt :translate_access_log_threshold %>
+
+ <%= f.number_field :translate_access_log_threshold, min: 0 %> + <%= t("datetime.prompts.day") %> +
+
diff --git a/app/views/cms/translate/site_settings/_show_access_restriction.html.erb b/app/views/cms/translate/site_settings/_show_access_restriction.html.erb new file mode 100644 index 00000000000..d2447db3c18 --- /dev/null +++ b/app/views/cms/translate/site_settings/_show_access_restriction.html.erb @@ -0,0 +1,7 @@ +
+
<%= @model.t :translate_referer_restriction %>
+
<%= @item.label :translate_referer_restriction %>
+ +
<%= @model.t :translate_access_log_threshold %>
+
<%= @item.translate_access_log_threshold %>
+
diff --git a/app/views/cms/translate/site_settings/edit.html.erb b/app/views/cms/translate/site_settings/edit.html.erb index d7cb8bb2956..9f56df8a9b3 100644 --- a/app/views/cms/translate/site_settings/edit.html.erb +++ b/app/views/cms/translate/site_settings/edit.html.erb @@ -39,6 +39,18 @@ <% end %> +
+ <% + addon_options = {} + addon_options[:id] = "addon-basic" + addon_options[:head] = I18n.t("translate.views.api_access_restriction") + %> + <% buf = render template: "_form_access_restriction", locals: { f: f, addon: addon_options } %> + <%= render "ss/crud/addon", addon: addon_options do %> + <%= buf %> + <% end %> +
+