Compare commits
No commits in common. "eb06398308408ada0b4da38d1b5624698091685e" and "155669866a050a362d3846c360584dcd97c192f5" have entirely different histories.
eb06398308
...
155669866a
48
.github/workflows/docker-dev.yml
vendored
48
.github/workflows/docker-dev.yml
vendored
@ -1,48 +0,0 @@
|
|||||||
name: Docker
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- dev
|
|
||||||
|
|
||||||
env:
|
|
||||||
# Use docker.io for Docker Hub if empty
|
|
||||||
REGISTRY: docker.io
|
|
||||||
IMAGE_NAME: ${{ github.event.repository.name }}
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
docker:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
-
|
|
||||||
name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 0 # 获取完整的 git history 以便生成正确的 tag
|
|
||||||
-
|
|
||||||
name: Get Version
|
|
||||||
id: get_version
|
|
||||||
run: |
|
|
||||||
echo "VERSION=$(git describe --dirty --always --tags --abbrev=7)" >> $GITHUB_OUTPUT
|
|
||||||
-
|
|
||||||
name: Login to ${{ env.REGISTRY }}
|
|
||||||
if: github.event_name != 'pull_request'
|
|
||||||
uses: docker/login-action@v3
|
|
||||||
with:
|
|
||||||
registry: ${{ env.REGISTRY }}
|
|
||||||
username: ${{github.actor}}
|
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
|
||||||
-
|
|
||||||
name: Set up QEMU
|
|
||||||
uses: docker/setup-qemu-action@v3
|
|
||||||
-
|
|
||||||
name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v3
|
|
||||||
-
|
|
||||||
name: Build and push
|
|
||||||
uses: docker/build-push-action@v6
|
|
||||||
with:
|
|
||||||
context: .
|
|
||||||
push: ${{ github.event_name != 'pull_request' }}
|
|
||||||
tags: |
|
|
||||||
${{ env.REGISTRY }}/${{github.actor}}/${{ github.event.repository.name }}:dev
|
|
@ -47,5 +47,5 @@ jobs:
|
|||||||
context: .
|
context: .
|
||||||
push: ${{ github.event_name != 'pull_request' }}
|
push: ${{ github.event_name != 'pull_request' }}
|
||||||
tags: |
|
tags: |
|
||||||
${{ env.REGISTRY }}/${{github.actor}}/${{ github.event.repository.name }}:main
|
${{ env.REGISTRY }}/${{github.actor}}/${{ github.event.repository.name }}:latest
|
||||||
${{ env.REGISTRY }}/${{github.actor}}/${{ github.event.repository.name }}:${{ steps.get_version.outputs.VERSION }}
|
${{ env.REGISTRY }}/${{github.actor}}/${{ github.event.repository.name }}:${{ steps.get_version.outputs.VERSION }}
|
2
Gemfile
2
Gemfile
@ -50,8 +50,6 @@ gem "kaminari", "~> 1.2"
|
|||||||
gem "meta-tags", "~> 2.22"
|
gem "meta-tags", "~> 2.22"
|
||||||
gem "sitemap_generator", "~> 6.3"
|
gem "sitemap_generator", "~> 6.3"
|
||||||
|
|
||||||
gem "ahoy_matey", "~> 5.2"
|
|
||||||
|
|
||||||
# gem "whenever", "~> 1.0"
|
# gem "whenever", "~> 1.0"
|
||||||
gem "ruby-openai", "~> 7.3"
|
gem "ruby-openai", "~> 7.3"
|
||||||
gem "httparty", "~> 0.22.0"
|
gem "httparty", "~> 0.22.0"
|
||||||
|
@ -84,10 +84,6 @@ GEM
|
|||||||
uri (>= 0.13.1)
|
uri (>= 0.13.1)
|
||||||
addressable (2.8.7)
|
addressable (2.8.7)
|
||||||
public_suffix (>= 2.0.2, < 7.0)
|
public_suffix (>= 2.0.2, < 7.0)
|
||||||
ahoy_matey (5.2.1)
|
|
||||||
activesupport (>= 6.1)
|
|
||||||
device_detector (>= 1)
|
|
||||||
safely_block (>= 0.4)
|
|
||||||
arbre (1.7.0)
|
arbre (1.7.0)
|
||||||
activesupport (>= 3.0.0)
|
activesupport (>= 3.0.0)
|
||||||
ruby2_keywords (>= 0.0.2)
|
ruby2_keywords (>= 0.0.2)
|
||||||
@ -140,7 +136,6 @@ GEM
|
|||||||
debug (1.10.0)
|
debug (1.10.0)
|
||||||
irb (~> 1.10)
|
irb (~> 1.10)
|
||||||
reline (>= 0.3.8)
|
reline (>= 0.3.8)
|
||||||
device_detector (1.1.3)
|
|
||||||
devise (4.9.4)
|
devise (4.9.4)
|
||||||
bcrypt (~> 3.0)
|
bcrypt (~> 3.0)
|
||||||
orm_adapter (~> 0.1)
|
orm_adapter (~> 0.1)
|
||||||
@ -390,7 +385,6 @@ GEM
|
|||||||
rubyzip (2.4.1)
|
rubyzip (2.4.1)
|
||||||
rufus-scheduler (3.9.2)
|
rufus-scheduler (3.9.2)
|
||||||
fugit (~> 1.1, >= 1.11.1)
|
fugit (~> 1.1, >= 1.11.1)
|
||||||
safely_block (0.4.1)
|
|
||||||
securerandom (0.4.1)
|
securerandom (0.4.1)
|
||||||
selenium-webdriver (4.28.0)
|
selenium-webdriver (4.28.0)
|
||||||
base64 (~> 0.2)
|
base64 (~> 0.2)
|
||||||
@ -491,7 +485,6 @@ PLATFORMS
|
|||||||
|
|
||||||
DEPENDENCIES
|
DEPENDENCIES
|
||||||
activeadmin (~> 3.2)
|
activeadmin (~> 3.2)
|
||||||
ahoy_matey (~> 5.2)
|
|
||||||
aws-sdk-s3 (~> 1.177)
|
aws-sdk-s3 (~> 1.177)
|
||||||
bootsnap
|
bootsnap
|
||||||
brakeman
|
brakeman
|
||||||
|
@ -1,31 +0,0 @@
|
|||||||
ActiveAdmin.register Ahoy::Event do
|
|
||||||
# See permitted parameters documentation:
|
|
||||||
# https://github.com/activeadmin/activeadmin/blob/master/docs/2-resource-customization.md#setting-up-strong-parameters
|
|
||||||
#
|
|
||||||
# Uncomment all parameters which should be permitted for assignment
|
|
||||||
#
|
|
||||||
# permit_params :visit_id, :user_id, :name, :properties, :time
|
|
||||||
#
|
|
||||||
# or
|
|
||||||
#
|
|
||||||
# permit_params do
|
|
||||||
# permitted = [:visit_id, :user_id, :name, :properties, :time]
|
|
||||||
# permitted << :other if params[:action] == 'create' && current_user.admin?
|
|
||||||
# permitted
|
|
||||||
# end
|
|
||||||
menu priority: 101, label: "事件统计"
|
|
||||||
|
|
||||||
actions :index
|
|
||||||
|
|
||||||
index do
|
|
||||||
column :id
|
|
||||||
column :name
|
|
||||||
column :time
|
|
||||||
column :properties
|
|
||||||
column :user_id
|
|
||||||
end
|
|
||||||
|
|
||||||
filter :name
|
|
||||||
filter :time
|
|
||||||
filter :properties
|
|
||||||
end
|
|
@ -1,34 +0,0 @@
|
|||||||
# app/admin/ahoy_management.rb
|
|
||||||
ActiveAdmin.register_page "Ahoy Management" do
|
|
||||||
menu label: "访问数据管理", parent: "系统管理"
|
|
||||||
|
|
||||||
content title: "访问数据管理" do
|
|
||||||
columns do
|
|
||||||
column do
|
|
||||||
panel "数据统计" do
|
|
||||||
attributes_table_for :ahoy do
|
|
||||||
row("总事件数") { Ahoy::Event.count }
|
|
||||||
row("总访问数") { Ahoy::Visit.count }
|
|
||||||
row("最早事件") { Ahoy::Event.minimum(:time)&.strftime("%Y-%m-%d %H:%M:%S") }
|
|
||||||
row("最早访问") { Ahoy::Visit.minimum(:started_at)&.strftime("%Y-%m-%d %H:%M:%S") }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
column do
|
|
||||||
panel "操作" do
|
|
||||||
div class: "buttons" do
|
|
||||||
button_to "立即清理旧数据", admin_ahoy_management_cleanup_path, method: :post,
|
|
||||||
data: { confirm: "确定要清理3个月前的数据吗?" },
|
|
||||||
class: "button"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
page_action :cleanup, method: :post do
|
|
||||||
CleanAhoyDataWorker.perform_async
|
|
||||||
redirect_to admin_ahoy_management_path, notice: "清理任务已加入队列"
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,35 +0,0 @@
|
|||||||
ActiveAdmin.register Ahoy::Visit do
|
|
||||||
# See permitted parameters documentation:
|
|
||||||
# https://github.com/activeadmin/activeadmin/blob/master/docs/2-resource-customization.md#setting-up-strong-parameters
|
|
||||||
#
|
|
||||||
# Uncomment all parameters which should be permitted for assignment
|
|
||||||
#
|
|
||||||
# permit_params :visit_token, :visitor_token, :user_id, :ip, :user_agent, :referrer, :referring_domain, :landing_page, :browser, :os, :device_type, :country, :region, :city, :latitude, :longitude, :utm_source, :utm_medium, :utm_term, :utm_content, :utm_campaign, :app_version, :os_version, :platform, :started_at
|
|
||||||
#
|
|
||||||
# or
|
|
||||||
#
|
|
||||||
# permit_params do
|
|
||||||
# permitted = [:visit_token, :visitor_token, :user_id, :ip, :user_agent, :referrer, :referring_domain, :landing_page, :browser, :os, :device_type, :country, :region, :city, :latitude, :longitude, :utm_source, :utm_medium, :utm_term, :utm_content, :utm_campaign, :app_version, :os_version, :platform, :started_at]
|
|
||||||
# permitted << :other if params[:action] == 'create' && current_user.admin?
|
|
||||||
# permitted
|
|
||||||
# end
|
|
||||||
|
|
||||||
menu priority: 100, label: "访问统计"
|
|
||||||
|
|
||||||
actions :index
|
|
||||||
|
|
||||||
index do
|
|
||||||
column :id
|
|
||||||
column :visitor_token
|
|
||||||
column :ip
|
|
||||||
column :user_agent
|
|
||||||
column :started_at
|
|
||||||
column :city
|
|
||||||
column :country
|
|
||||||
column :region
|
|
||||||
end
|
|
||||||
|
|
||||||
filter :started_at
|
|
||||||
filter :city
|
|
||||||
filter :country
|
|
||||||
end
|
|
@ -11,53 +11,6 @@ ActiveAdmin.register_page "Dashboard" do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
columns do
|
|
||||||
column do
|
|
||||||
panel "访问统计" do
|
|
||||||
para "总访问量: #{Ahoy::Visit.count}"
|
|
||||||
para "总事件数: #{Ahoy::Event.count}"
|
|
||||||
para "独立访客数: #{Ahoy::Visit.distinct.count(:visitor_token)}"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
column do
|
|
||||||
panel "热门城市" do
|
|
||||||
table_for City.by_popularity.limit(10) do
|
|
||||||
column("城市") { |city| link_to(city.name, admin_city_path(city)) }
|
|
||||||
column("访问量") { |city| city.view_count }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
column do
|
|
||||||
panel "热门天气艺术" do
|
|
||||||
table_for WeatherArt.by_popularity.limit(10) do
|
|
||||||
column("作品") { |art| link_to(art.to_s, admin_weather_art_path(art)) }
|
|
||||||
column("访问量") { |art| art.view_count }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
column do
|
|
||||||
panel "冷门活跃城市" do
|
|
||||||
table_for City.least_popular_active.limit(10) do
|
|
||||||
column("城市") { |city| link_to(city.name, admin_city_path(city)) }
|
|
||||||
column("访问量") { |city| city.view_count }
|
|
||||||
# column("状态") { |city| status_tag(city.active? ? "活跃" : "停用") }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# 添加一个事件列表面板
|
|
||||||
panel "最近事件" do
|
|
||||||
table_for Ahoy::Event.order(time: :desc).limit(10) do
|
|
||||||
column :time
|
|
||||||
column :name
|
|
||||||
column :properties
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Here is an example of a simple dashboard with columns and panels.
|
# Here is an example of a simple dashboard with columns and panels.
|
||||||
#
|
#
|
||||||
# columns do
|
# columns do
|
||||||
|
@ -1,15 +1,8 @@
|
|||||||
class ApplicationController < ActionController::Base
|
class ApplicationController < ActionController::Base
|
||||||
include SeoConcern
|
include SeoConcern
|
||||||
# Only allow modern browsers supporting webp images, web push, badges, import maps, CSS nesting, and CSS :has.
|
# Only allow modern browsers supporting webp images, web push, badges, import maps, CSS nesting, and CSS :has.
|
||||||
# allow_browser versions: :modern
|
allow_browser versions: :modern
|
||||||
before_action :set_locale
|
before_action :set_locale
|
||||||
after_action :track_action
|
|
||||||
|
|
||||||
protected
|
|
||||||
|
|
||||||
def track_action
|
|
||||||
ahoy.track "Viewed Application", request.path_parameters
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
class CitiesController < ApplicationController
|
class CitiesController < ApplicationController
|
||||||
def index
|
def index
|
||||||
@regions = Region.includes(:countries).order(:name)
|
@regions = Region.includes(:countries).order(:name)
|
||||||
@cities = City.includes(:country, country: :region).order(:name)
|
@cities = City.includes(:country, country: :region).active.order(:name)
|
||||||
|
|
||||||
if params[:region]
|
if params[:region]
|
||||||
@current_region = Region.friendly.find(params[:region])
|
@current_region = Region.friendly.find(params[:region])
|
||||||
@ -24,11 +24,6 @@ class CitiesController < ApplicationController
|
|||||||
|
|
||||||
def show
|
def show
|
||||||
@city = City.friendly.find(params[:id])
|
@city = City.friendly.find(params[:id])
|
||||||
ahoy.track "View City", {
|
|
||||||
city_id: @city.id,
|
|
||||||
name: @city.name,
|
|
||||||
event_type: "city_view"
|
|
||||||
}
|
|
||||||
|
|
||||||
set_meta_tags(
|
set_meta_tags(
|
||||||
title: @city.name,
|
title: @city.name,
|
||||||
|
@ -2,18 +2,6 @@ class WeatherArtsController < ApplicationController
|
|||||||
def show
|
def show
|
||||||
@city = City.friendly.find(params[:city_id])
|
@city = City.friendly.find(params[:city_id])
|
||||||
@weather_art = @city.weather_arts.friendly.find(params[:slug])
|
@weather_art = @city.weather_arts.friendly.find(params[:slug])
|
||||||
|
|
||||||
ahoy.track "View Weather Art", {
|
|
||||||
weather_art_id: @weather_art.id,
|
|
||||||
city_id: @weather_art.city_id,
|
|
||||||
event_type: "weather_art_view"
|
|
||||||
}
|
|
||||||
ahoy.track "View City", {
|
|
||||||
city_id: @city.id,
|
|
||||||
name: @city.name,
|
|
||||||
event_type: "city_view"
|
|
||||||
}
|
|
||||||
|
|
||||||
set_meta_tags(
|
set_meta_tags(
|
||||||
title: "#{@city.name} Weather Art - #{@weather_art.weather_date.strftime('%B %d, %Y')}",
|
title: "#{@city.name} Weather Art - #{@weather_art.weather_date.strftime('%B %d, %Y')}",
|
||||||
description: "#{@city.name}'s weather visualized through AI art. #{@weather_art.description} at #{@weather_art.temperature}°C.",
|
description: "#{@city.name}'s weather visualized through AI art. #{@weather_art.description} at #{@weather_art.temperature}°C.",
|
||||||
|
@ -1,14 +0,0 @@
|
|||||||
class Ahoy::Event < ApplicationRecord
|
|
||||||
# include Ahoy::QueryMethods
|
|
||||||
|
|
||||||
self.table_name = "ahoy_events"
|
|
||||||
|
|
||||||
belongs_to :visit
|
|
||||||
belongs_to :user, optional: true
|
|
||||||
|
|
||||||
serialize :properties, coder: JSON
|
|
||||||
|
|
||||||
def self.ransackable_attributes(auth_object = nil)
|
|
||||||
[ "id", "id_value", "name", "properties", "time", "user_id", "visit_id" ]
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,10 +0,0 @@
|
|||||||
class Ahoy::Visit < ApplicationRecord
|
|
||||||
self.table_name = "ahoy_visits"
|
|
||||||
|
|
||||||
has_many :events, class_name: "Ahoy::Event"
|
|
||||||
belongs_to :user, optional: true
|
|
||||||
|
|
||||||
def self.ransackable_attributes(auth_object = nil)
|
|
||||||
[ "app_version", "browser", "city", "country", "device_type", "id", "ip", "landing_page", "latitude", "longitude", "os", "os_version", "platform", "referrer", "referring_domain", "region", "started_at", "user_agent", "user_id", "utm_campaign", "utm_content", "utm_medium", "utm_source", "utm_term", "visit_token", "visitor_token" ]
|
|
||||||
end
|
|
||||||
end
|
|
@ -5,9 +5,6 @@ class City < ApplicationRecord
|
|||||||
|
|
||||||
has_many :weather_arts, dependent: :destroy
|
has_many :weather_arts, dependent: :destroy
|
||||||
|
|
||||||
has_many :visits, class_name: "Ahoy::Visit", foreign_key: :city_id
|
|
||||||
has_many :events, class_name: "Ahoy::Event", foreign_key: :city_id
|
|
||||||
|
|
||||||
delegate :region, to: :country
|
delegate :region, to: :country
|
||||||
|
|
||||||
validates :name, presence: true
|
validates :name, presence: true
|
||||||
@ -20,40 +17,6 @@ class City < ApplicationRecord
|
|||||||
scope :by_country, ->(country_id) { where(country_id: country_id) }
|
scope :by_country, ->(country_id) { where(country_id: country_id) }
|
||||||
scope :active, -> { where(active: true) }
|
scope :active, -> { where(active: true) }
|
||||||
|
|
||||||
scope :by_popularity, -> {
|
|
||||||
if ActiveRecord::Base.connection.adapter_name.downcase == "sqlite"
|
|
||||||
joins("LEFT JOIN ahoy_events ON json_extract(ahoy_events.properties, '$.city_id') = cities.id
|
|
||||||
AND json_extract(ahoy_events.properties, '$.event_type') = 'city_view'")
|
|
||||||
.group("cities.id")
|
|
||||||
.select("cities.*, COUNT(ahoy_events.id) as visit_count")
|
|
||||||
.order("visit_count DESC")
|
|
||||||
else
|
|
||||||
joins("LEFT JOIN ahoy_events ON (ahoy_events.properties::jsonb->>'city_id')::integer = cities.id
|
|
||||||
AND ahoy_events.properties::jsonb->>'event_type' = 'city_view'")
|
|
||||||
.group("cities.id")
|
|
||||||
.select("cities.*, COUNT(ahoy_events.id) as visit_count")
|
|
||||||
.order("visit_count DESC")
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
scope :least_popular_active, -> {
|
|
||||||
if ActiveRecord::Base.connection.adapter_name.downcase == "sqlite"
|
|
||||||
active
|
|
||||||
.joins("LEFT JOIN ahoy_events ON json_extract(ahoy_events.properties, '$.city_id') = cities.id
|
|
||||||
AND json_extract(ahoy_events.properties, '$.event_type') = 'city_view'")
|
|
||||||
.group("cities.id")
|
|
||||||
.select("cities.*, COUNT(ahoy_events.id) as visit_count")
|
|
||||||
.order("visit_count ASC, cities.name ASC")
|
|
||||||
else
|
|
||||||
active
|
|
||||||
.joins("LEFT JOIN ahoy_events ON (ahoy_events.properties::jsonb->>'city_id')::integer = cities.id
|
|
||||||
AND ahoy_events.properties::jsonb->>'event_type' = 'city_view'")
|
|
||||||
.group("cities.id")
|
|
||||||
.select("cities.*, COUNT(ahoy_events.id) as visit_count")
|
|
||||||
.order("visit_count ASC, cities.name ASC")
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
def to_s
|
def to_s
|
||||||
name
|
name
|
||||||
end
|
end
|
||||||
@ -102,12 +65,4 @@ class City < ApplicationRecord
|
|||||||
def latest_weather_art
|
def latest_weather_art
|
||||||
weather_arts.order(weather_date: :desc).first
|
weather_arts.order(weather_date: :desc).first
|
||||||
end
|
end
|
||||||
|
|
||||||
def view_count
|
|
||||||
if ActiveRecord::Base.connection.adapter_name.downcase == "sqlite"
|
|
||||||
Ahoy::Event.where("json_extract(properties, '$.event_type') = 'city_view' AND json_extract(properties, '$.city_id') = ?", self.id).count
|
|
||||||
else
|
|
||||||
Ahoy::Event.where("properties::jsonb->>'event_type' = 'city_view' AND (properties::jsonb->>'city_id')::integer = ?", self.id).count
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
@ -5,28 +5,9 @@ class WeatherArt < ApplicationRecord
|
|||||||
belongs_to :city
|
belongs_to :city
|
||||||
has_one_attached :image
|
has_one_attached :image
|
||||||
|
|
||||||
has_many :visits, class_name: "Ahoy::Visit", foreign_key: :weather_art_id
|
|
||||||
has_many :events, class_name: "Ahoy::Event", foreign_key: :weather_art_id
|
|
||||||
|
|
||||||
validates :weather_date, presence: true
|
validates :weather_date, presence: true
|
||||||
validates :city_id, presence: true
|
validates :city_id, presence: true
|
||||||
|
|
||||||
scope :by_popularity, -> {
|
|
||||||
if ActiveRecord::Base.connection.adapter_name.downcase == "sqlite"
|
|
||||||
joins("LEFT JOIN ahoy_events ON json_extract(ahoy_events.properties, '$.weather_art_id') = weather_arts.id
|
|
||||||
AND json_extract(ahoy_events.properties, '$.event_type') = 'weather_art_view'")
|
|
||||||
.group("weather_arts.id")
|
|
||||||
.select("weather_arts.*, COUNT(ahoy_events.id) as visit_count")
|
|
||||||
.order("visit_count DESC")
|
|
||||||
else
|
|
||||||
joins("LEFT JOIN ahoy_events ON (ahoy_events.properties::jsonb->>'weather_art_id')::integer = weather_arts.id
|
|
||||||
AND ahoy_events.properties::jsonb->>'event_type' = 'weather_art_view'")
|
|
||||||
.group("weather_arts.id")
|
|
||||||
.select("weather_arts.*, COUNT(ahoy_events.id) as visit_count")
|
|
||||||
.order("visit_count DESC")
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
def should_generate_new_friendly_id?
|
def should_generate_new_friendly_id?
|
||||||
weather_date_changed? || city_id_changed? || super
|
weather_date_changed? || city_id_changed? || super
|
||||||
end
|
end
|
||||||
@ -42,12 +23,4 @@ class WeatherArt < ApplicationRecord
|
|||||||
def self.ransackable_attributes(auth_object = nil)
|
def self.ransackable_attributes(auth_object = nil)
|
||||||
[ "city_id", "cloud", "created_at", "description", "feeling_temp", "humidity", "id", "id_value", "precipitation", "pressure", "prompt", "temperature", "updated_at", "visibility", "weather_date", "wind_scale", "wind_speed" ]
|
[ "city_id", "cloud", "created_at", "description", "feeling_temp", "humidity", "id", "id_value", "precipitation", "pressure", "prompt", "temperature", "updated_at", "visibility", "weather_date", "wind_scale", "wind_speed" ]
|
||||||
end
|
end
|
||||||
|
|
||||||
def view_count
|
|
||||||
if ActiveRecord::Base.connection.adapter_name.downcase == "sqlite"
|
|
||||||
Ahoy::Event.where("json_extract(properties, '$.event_type') = 'weather_art_view' AND json_extract(properties, '$.weather_art_id') = ?", self.id).count
|
|
||||||
else
|
|
||||||
Ahoy::Event.where("properties::jsonb->>'event_type' = 'weather_art_view' AND (properties::jsonb->>'weather_art_id')::integer = ?", self.id).count
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
@ -58,7 +58,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
<div class="text-center mt-12 mb-12">
|
<div class="text-center mt-12">
|
||||||
<%= link_to arts_path, class: "btn btn-primary btn-lg gap-2" do %>
|
<%= link_to arts_path, class: "btn btn-primary btn-lg gap-2" do %>
|
||||||
View All Weather Arts
|
View All Weather Arts
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
|
@ -76,15 +76,15 @@
|
|||||||
<!-- 页脚 -->
|
<!-- 页脚 -->
|
||||||
<footer class="footer footer-center p-8 bg-base-200 text-base-content">
|
<footer class="footer footer-center p-8 bg-base-200 text-base-content">
|
||||||
<div class="container mx-auto flex flex-col gap-2">
|
<div class="container mx-auto flex flex-col gap-2">
|
||||||
<div id="busuanzi_container" class="text-xs opacity-70">
|
<div id="busuanzi_container" class="text-xs opacity-70 hidden">
|
||||||
<div class="space-x-2">
|
<div class="space-x-2">
|
||||||
<span>Page Views: <span id="busuanzi_page_pv"></span></span>
|
<span>Page Views: <span id="busuanzi_value_page_pv"></span></span>
|
||||||
<span>|</span>
|
<span>|</span>
|
||||||
<span>Page Visitors: <span id="busuanzi_page_uv"></span></span>
|
<span>Page Visitors: <span id="busuanzi_value_page_uv"></span></span>
|
||||||
<span>|</span>
|
<span>|</span>
|
||||||
<span>Total Views: <span id="busuanzi_site_pv"></span></span>
|
<span>Total Views: <span id="busuanzi_value_site_pv"></span></span>
|
||||||
<span>|</span>
|
<span>|</span>
|
||||||
<span>Total Visitors: <span id="busuanzi_site_uv"></span></span>
|
<span>Total Visitors: <span id="busuanzi_value_site_uv"></span></span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -1,32 +0,0 @@
|
|||||||
# app/workers/clean_ahoy_data_worker.rb
|
|
||||||
class CleanAhoyDataWorker
|
|
||||||
include Sidekiq::Worker
|
|
||||||
|
|
||||||
sidekiq_options queue: :default, retry: false
|
|
||||||
|
|
||||||
def perform
|
|
||||||
cleanup_old_events
|
|
||||||
cleanup_old_visits
|
|
||||||
log_cleanup_results
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def cleanup_old_events
|
|
||||||
cutoff_date = 3.months.ago
|
|
||||||
deleted_events_count = Ahoy::Event.where("time < ?", cutoff_date).delete_all
|
|
||||||
Rails.logger.info "Deleted #{deleted_events_count} old Ahoy events"
|
|
||||||
end
|
|
||||||
|
|
||||||
def cleanup_old_visits
|
|
||||||
cutoff_date = 3.months.ago
|
|
||||||
deleted_visits_count = Ahoy::Visit.where("started_at < ?", cutoff_date).delete_all
|
|
||||||
Rails.logger.info "Deleted #{deleted_visits_count} old Ahoy visits"
|
|
||||||
end
|
|
||||||
|
|
||||||
def log_cleanup_results
|
|
||||||
Rails.logger.info "Ahoy cleanup completed at #{Time.current}"
|
|
||||||
Rails.logger.info "Remaining events: #{Ahoy::Event.count}"
|
|
||||||
Rails.logger.info "Remaining visits: #{Ahoy::Visit.count}"
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,14 +0,0 @@
|
|||||||
class Ahoy::Store < Ahoy::DatabaseStore
|
|
||||||
end
|
|
||||||
|
|
||||||
# set to true for JavaScript tracking
|
|
||||||
Ahoy.api = true
|
|
||||||
|
|
||||||
# set to true for geocoding (and add the geocoder gem to your Gemfile)
|
|
||||||
# we recommend configuring local geocoding as well
|
|
||||||
# see https://github.com/ankane/ahoy#geocoding
|
|
||||||
Ahoy.geocode = false
|
|
||||||
|
|
||||||
Ahoy.visit_duration = 30.minutes
|
|
||||||
Ahoy.server_side_visits = :when_needed
|
|
||||||
RETENTION_PERIOD = 3.months
|
|
@ -10,9 +10,3 @@ refresh_sitemap:
|
|||||||
queue: default
|
queue: default
|
||||||
description: "Refresh sitemap daily"
|
description: "Refresh sitemap daily"
|
||||||
enabled: true
|
enabled: true
|
||||||
|
|
||||||
clean_ahoy_data:
|
|
||||||
cron: '0 0 * * 0'
|
|
||||||
class: CleanAhoyDataWorker
|
|
||||||
queue: default
|
|
||||||
enabled: true
|
|
@ -1,61 +0,0 @@
|
|||||||
class CreateAhoyVisitsAndEvents < ActiveRecord::Migration[8.0]
|
|
||||||
def change
|
|
||||||
create_table :ahoy_visits do |t|
|
|
||||||
t.string :visit_token
|
|
||||||
t.string :visitor_token
|
|
||||||
|
|
||||||
# the rest are recommended but optional
|
|
||||||
# simply remove any you don't want
|
|
||||||
|
|
||||||
# user
|
|
||||||
t.references :user
|
|
||||||
|
|
||||||
# standard
|
|
||||||
t.string :ip
|
|
||||||
t.text :user_agent
|
|
||||||
t.text :referrer
|
|
||||||
t.string :referring_domain
|
|
||||||
t.text :landing_page
|
|
||||||
|
|
||||||
# technology
|
|
||||||
t.string :browser
|
|
||||||
t.string :os
|
|
||||||
t.string :device_type
|
|
||||||
|
|
||||||
# location
|
|
||||||
t.string :country
|
|
||||||
t.string :region
|
|
||||||
t.string :city
|
|
||||||
t.float :latitude
|
|
||||||
t.float :longitude
|
|
||||||
|
|
||||||
# utm parameters
|
|
||||||
t.string :utm_source
|
|
||||||
t.string :utm_medium
|
|
||||||
t.string :utm_term
|
|
||||||
t.string :utm_content
|
|
||||||
t.string :utm_campaign
|
|
||||||
|
|
||||||
# native apps
|
|
||||||
t.string :app_version
|
|
||||||
t.string :os_version
|
|
||||||
t.string :platform
|
|
||||||
|
|
||||||
t.datetime :started_at
|
|
||||||
end
|
|
||||||
|
|
||||||
add_index :ahoy_visits, :visit_token, unique: true
|
|
||||||
add_index :ahoy_visits, [ :visitor_token, :started_at ]
|
|
||||||
|
|
||||||
create_table :ahoy_events do |t|
|
|
||||||
t.references :visit
|
|
||||||
t.references :user
|
|
||||||
|
|
||||||
t.string :name
|
|
||||||
t.text :properties
|
|
||||||
t.datetime :time
|
|
||||||
end
|
|
||||||
|
|
||||||
add_index :ahoy_events, [ :name, :time ]
|
|
||||||
end
|
|
||||||
end
|
|
44
db/schema.rb
generated
44
db/schema.rb
generated
@ -10,7 +10,7 @@
|
|||||||
#
|
#
|
||||||
# It's strongly recommended that you check this file into your version control system.
|
# It's strongly recommended that you check this file into your version control system.
|
||||||
|
|
||||||
ActiveRecord::Schema[8.0].define(version: 2025_01_26_155239) do
|
ActiveRecord::Schema[8.0].define(version: 2025_01_23_155234) do
|
||||||
create_table "active_admin_comments", force: :cascade do |t|
|
create_table "active_admin_comments", force: :cascade do |t|
|
||||||
t.string "namespace"
|
t.string "namespace"
|
||||||
t.text "body"
|
t.text "body"
|
||||||
@ -65,48 +65,6 @@ ActiveRecord::Schema[8.0].define(version: 2025_01_26_155239) do
|
|||||||
t.index ["reset_password_token"], name: "index_admin_users_on_reset_password_token", unique: true
|
t.index ["reset_password_token"], name: "index_admin_users_on_reset_password_token", unique: true
|
||||||
end
|
end
|
||||||
|
|
||||||
create_table "ahoy_events", force: :cascade do |t|
|
|
||||||
t.integer "visit_id"
|
|
||||||
t.integer "user_id"
|
|
||||||
t.string "name"
|
|
||||||
t.text "properties"
|
|
||||||
t.datetime "time"
|
|
||||||
t.index ["name", "time"], name: "index_ahoy_events_on_name_and_time"
|
|
||||||
t.index ["user_id"], name: "index_ahoy_events_on_user_id"
|
|
||||||
t.index ["visit_id"], name: "index_ahoy_events_on_visit_id"
|
|
||||||
end
|
|
||||||
|
|
||||||
create_table "ahoy_visits", force: :cascade do |t|
|
|
||||||
t.string "visit_token"
|
|
||||||
t.string "visitor_token"
|
|
||||||
t.integer "user_id"
|
|
||||||
t.string "ip"
|
|
||||||
t.text "user_agent"
|
|
||||||
t.text "referrer"
|
|
||||||
t.string "referring_domain"
|
|
||||||
t.text "landing_page"
|
|
||||||
t.string "browser"
|
|
||||||
t.string "os"
|
|
||||||
t.string "device_type"
|
|
||||||
t.string "country"
|
|
||||||
t.string "region"
|
|
||||||
t.string "city"
|
|
||||||
t.float "latitude"
|
|
||||||
t.float "longitude"
|
|
||||||
t.string "utm_source"
|
|
||||||
t.string "utm_medium"
|
|
||||||
t.string "utm_term"
|
|
||||||
t.string "utm_content"
|
|
||||||
t.string "utm_campaign"
|
|
||||||
t.string "app_version"
|
|
||||||
t.string "os_version"
|
|
||||||
t.string "platform"
|
|
||||||
t.datetime "started_at"
|
|
||||||
t.index ["user_id"], name: "index_ahoy_visits_on_user_id"
|
|
||||||
t.index ["visit_token"], name: "index_ahoy_visits_on_visit_token", unique: true
|
|
||||||
t.index ["visitor_token", "started_at"], name: "index_ahoy_visits_on_visitor_token_and_started_at"
|
|
||||||
end
|
|
||||||
|
|
||||||
create_table "cities", force: :cascade do |t|
|
create_table "cities", force: :cascade do |t|
|
||||||
t.string "name"
|
t.string "name"
|
||||||
t.float "latitude"
|
t.float "latitude"
|
||||||
|
Loading…
Reference in New Issue
Block a user