2025-01-19 01:13:59 +08:00
|
|
|
|
class ApplicationController < ActionController::Base
|
2025-01-23 19:02:52 +08:00
|
|
|
|
include SeoConcern
|
2025-01-19 01:13:59 +08:00
|
|
|
|
# Only allow modern browsers supporting webp images, web push, badges, import maps, CSS nesting, and CSS :has.
|
2025-01-27 09:30:01 +08:00
|
|
|
|
before_action :log_browser_info
|
2025-02-19 17:38:49 +08:00
|
|
|
|
before_action :set_content_type_for_rss, if: -> { request.format.rss? }
|
2025-01-27 06:53:01 +08:00
|
|
|
|
# allow_browser versions: :modern
|
2025-01-28 01:25:40 +08:00
|
|
|
|
# allow_browser versions: :modern,
|
|
|
|
|
# patterns: [
|
|
|
|
|
# # 鸿蒙系统相关
|
|
|
|
|
# /OpenHarmony/, # 鸿蒙系统标识
|
|
|
|
|
# /ArkWeb\/[\d.]+/, # 鸿蒙浏览器内核
|
|
|
|
|
# /Mobile HuaweiBrowser/, # 华为浏览器(新格式)
|
|
|
|
|
# /HuaweiBrowser\/[\d.]+/, # 华为浏览器(旧格式)
|
|
|
|
|
#
|
|
|
|
|
# # 夸克浏览器(更宽松的匹配)
|
|
|
|
|
# /Quark[\s\/][\d.]+/, # 匹配 "Quark/7.4.6.681" 或 "Quark 7.4.6.681"
|
|
|
|
|
#
|
|
|
|
|
# /Mobile Safari/,
|
|
|
|
|
# /Chrome\/[\d.]+/,
|
|
|
|
|
# /Quark\/[\d.]+/,
|
|
|
|
|
# /HuaweiBrowser\/[\d.]+/,
|
|
|
|
|
# /MiuiBrowser\/[\d.]+/,
|
|
|
|
|
# /VivoBrowser\/[\d.]+/,
|
|
|
|
|
# /OppoBrowser\/[\d.]+/,
|
|
|
|
|
# /UCBrowser\/[\d.]+/,
|
|
|
|
|
# /QQBrowser\/[\d.]+/,
|
|
|
|
|
# /MicroMessenger\/[\d.]+/,
|
|
|
|
|
# /Alipay/,
|
|
|
|
|
# /BaiduBoxApp/,
|
|
|
|
|
# /baiduboxapp/i,
|
|
|
|
|
# /SogouMobile/,
|
|
|
|
|
# /Weibo/,
|
|
|
|
|
# /DingTalk/,
|
|
|
|
|
# /ToutiaoMicroApp/,
|
|
|
|
|
# /BytedanceWebview/,
|
|
|
|
|
# /ArkWeb/
|
|
|
|
|
# ],
|
|
|
|
|
# on_failure: ->(browser) {
|
|
|
|
|
# Rails.logger.warn <<~BROWSER_INFO
|
|
|
|
|
# Browser Blocked:
|
|
|
|
|
# User Agent: #{browser.ua}
|
|
|
|
|
# Name: #{browser.name}
|
|
|
|
|
# Version: #{browser.version}
|
|
|
|
|
# Platform: #{browser.platform.name}
|
|
|
|
|
# Device: #{browser.device.name}
|
|
|
|
|
# Mobile: #{browser.mobile?}
|
|
|
|
|
# Modern: #{browser.modern?}
|
|
|
|
|
# Bot: #{browser.bot?}
|
|
|
|
|
# BROWSER_INFO
|
|
|
|
|
# }
|
2025-02-22 15:00:22 +08:00
|
|
|
|
around_action :set_locale
|
2025-01-27 00:43:18 +08:00
|
|
|
|
after_action :track_action
|
|
|
|
|
|
2025-01-27 09:00:54 +08:00
|
|
|
|
def log_browser_info
|
|
|
|
|
# 构建详细的浏览器信息
|
2025-01-28 01:28:07 +08:00
|
|
|
|
Rails.logger.debug "User Agent: #{request.user_agent}"
|
2025-01-28 01:24:49 +08:00
|
|
|
|
|
2025-01-27 09:00:54 +08:00
|
|
|
|
# 如果是被拦截的浏览器,记录额外信息
|
2025-01-27 09:42:08 +08:00
|
|
|
|
# unless browser_allowed?
|
2025-01-28 01:26:31 +08:00
|
|
|
|
# Rails.logger.info "User Agent: #{request.user_agent}"
|
2025-01-27 09:42:08 +08:00
|
|
|
|
# end
|
2025-01-27 09:00:54 +08:00
|
|
|
|
end
|
|
|
|
|
|
2025-01-27 00:43:18 +08:00
|
|
|
|
protected
|
|
|
|
|
|
|
|
|
|
def track_action
|
|
|
|
|
ahoy.track "Viewed Application", request.path_parameters
|
|
|
|
|
end
|
2025-01-21 18:27:26 +08:00
|
|
|
|
|
2025-02-11 17:40:13 +08:00
|
|
|
|
def authenticate_admin_user!
|
|
|
|
|
unless current_user&.admin?
|
|
|
|
|
flash[:alert] = "您没有权限访问该页面。"
|
|
|
|
|
redirect_to root_path
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2025-01-21 18:27:26 +08:00
|
|
|
|
private
|
|
|
|
|
|
2025-02-22 15:00:22 +08:00
|
|
|
|
def set_locale(&action)
|
2025-02-21 17:51:25 +08:00
|
|
|
|
I18n.locale = extract_locale || I18n.default_locale
|
|
|
|
|
I18n.fallbacks[I18n.locale] = [ I18n.locale, I18n.default_locale ].uniq
|
2025-02-22 15:00:22 +08:00
|
|
|
|
locale = params[:locale] || extract_locale_from_accept_language_header || I18n.default_locale
|
|
|
|
|
I18n.with_locale(locale, &action)
|
2025-02-22 00:13:35 +08:00
|
|
|
|
# 重定向到带有语言前缀的相同路径
|
|
|
|
|
# redirect_to "/#{locale}#{request.fullpath}"
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def extract_locale_from_accept_language_header
|
2025-02-22 15:00:22 +08:00
|
|
|
|
return I18n.default_locale.to_s unless request.env["HTTP_ACCEPT_LANGUAGE"]
|
|
|
|
|
|
|
|
|
|
available_locales = I18n.available_locales.map(&:to_s)
|
|
|
|
|
|
2025-02-22 00:13:35 +08:00
|
|
|
|
accept_language = request.env["HTTP_ACCEPT_LANGUAGE"].to_s
|
|
|
|
|
# 修改正则表达式以匹配 'zh-CN' 这样的格式
|
|
|
|
|
if (full_locale = accept_language.scan(/^[a-z]{2}-[A-Z]{2}/).first)
|
2025-02-22 15:00:22 +08:00
|
|
|
|
locale = full_locale
|
2025-02-22 00:13:35 +08:00
|
|
|
|
else
|
|
|
|
|
# 否则只匹配语言代码 (例如 'en')
|
2025-02-22 15:00:22 +08:00
|
|
|
|
locale = accept_language.scan(/^[a-z]{2}/).first || I18n.default_locale.to_s
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
return locale if available_locales.include?(locale)
|
|
|
|
|
|
|
|
|
|
# 尝试基础语言匹配(例如:当请求 'zh' 时匹配 'zh-CN')
|
2025-02-22 15:00:37 +08:00
|
|
|
|
base_language = locale.split("-").first
|
2025-02-22 15:00:22 +08:00
|
|
|
|
matching_locale = available_locales.find do |available_locale|
|
|
|
|
|
available_locale.start_with?(base_language)
|
2025-02-22 00:13:35 +08:00
|
|
|
|
end
|
2025-02-22 15:00:22 +08:00
|
|
|
|
matching_locale ? matching_locale : I18n.default_locale.to_s
|
2025-02-22 00:13:35 +08:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def sanitize_locale(locale)
|
|
|
|
|
# 直接使用 I18n.available_locales
|
|
|
|
|
locale = locale.to_sym
|
|
|
|
|
I18n.available_locales.include?(locale) ? locale : I18n.default_locale
|
2025-02-21 17:51:25 +08:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def extract_locale
|
|
|
|
|
parsed_locale = params[:locale]
|
|
|
|
|
I18n.available_locales.map(&:to_s).include?(parsed_locale) ? parsed_locale : nil
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def default_url_options
|
|
|
|
|
{ locale: I18n.locale }
|
2025-01-21 18:27:26 +08:00
|
|
|
|
end
|
2025-02-19 17:38:49 +08:00
|
|
|
|
|
|
|
|
|
def set_content_type_for_rss
|
|
|
|
|
response.headers["Content-Type"] = "application/rss+xml; charset=utf-8"
|
|
|
|
|
end
|
2025-01-19 01:13:59 +08:00
|
|
|
|
end
|