ad_space 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +22 -0
  3. data/Gemfile.lock +240 -0
  4. data/MIT-LICENSE +20 -0
  5. data/README.md +92 -0
  6. data/Rakefile +8 -0
  7. data/ad_space.gemspec +25 -0
  8. data/app/assets/builds/application.js +7397 -0
  9. data/app/assets/builds/application.js.map +7 -0
  10. data/app/assets/config/ad_space_manifest.js +2 -0
  11. data/app/assets/javascript/ad_space/application.js +0 -0
  12. data/app/assets/stylesheets/ad_space/application.css +15 -0
  13. data/app/controllers/ad_space/advertisements_controller.rb +121 -0
  14. data/app/controllers/ad_space/api/v1/advertisements_controller.rb +36 -0
  15. data/app/controllers/ad_space/application_controller.rb +30 -0
  16. data/app/controllers/ad_space/dashboard_controller.rb +6 -0
  17. data/app/controllers/ad_space/slots_controller.rb +70 -0
  18. data/app/helpers/ad_space/advertisements_helper.rb +4 -0
  19. data/app/helpers/ad_space/application_helper.rb +15 -0
  20. data/app/helpers/ad_space/dashboard_helper.rb +4 -0
  21. data/app/helpers/ad_space/slots_helper.rb +4 -0
  22. data/app/javascript/application.js +3 -0
  23. data/app/javascript/controllers/application.js +9 -0
  24. data/app/javascript/controllers/hello_controller.js +153 -0
  25. data/app/javascript/controllers/image_upload_controller.js +46 -0
  26. data/app/javascript/controllers/index.js +11 -0
  27. data/app/jobs/ad_space/application_job.rb +4 -0
  28. data/app/mailers/ad_space/application_mailer.rb +6 -0
  29. data/app/models/ad_space/advertisement.rb +67 -0
  30. data/app/models/ad_space/application_record.rb +5 -0
  31. data/app/models/ad_space/breadcrumb.rb +14 -0
  32. data/app/models/ad_space/slot.rb +27 -0
  33. data/app/serializers/v1/advertisement_serializer.rb +30 -0
  34. data/app/validators/url_or_custom_scheme_validator.rb +20 -0
  35. data/app/views/ad_space/advertisements/_advertisement.html.erb +102 -0
  36. data/app/views/ad_space/advertisements/_form.html.erb +143 -0
  37. data/app/views/ad_space/advertisements/edit.html.erb +10 -0
  38. data/app/views/ad_space/advertisements/index.html.erb +15 -0
  39. data/app/views/ad_space/advertisements/new.html.erb +9 -0
  40. data/app/views/ad_space/advertisements/show.html.erb +13 -0
  41. data/app/views/ad_space/dashboard/index.html.erb +7 -0
  42. data/app/views/ad_space/slots/_form.html.erb +44 -0
  43. data/app/views/ad_space/slots/_slot.html.erb +30 -0
  44. data/app/views/ad_space/slots/edit.html.erb +10 -0
  45. data/app/views/ad_space/slots/index.html.erb +14 -0
  46. data/app/views/ad_space/slots/new.html.erb +9 -0
  47. data/app/views/ad_space/slots/show.html.erb +10 -0
  48. data/app/views/layouts/ad_space/application.html.erb +43 -0
  49. data/config/routes.rb +22 -0
  50. data/db/migrate/20231026152912_create_ad_space_slots.rb +11 -0
  51. data/db/migrate/20231026160758_create_ad_space_advertisements.rb +25 -0
  52. data/db/migrate/20231027070536_add_scheme_url_to_ad_space_advertisements.rb +10 -0
  53. data/db/migrate/20231027083935_add_target_version_code_to_ad_space_advertisements.rb +6 -0
  54. data/db/migrate/20231027115707_add_is_splash_to_ad_space_slots.rb +5 -0
  55. data/lib/ad_space/app.rb +16 -0
  56. data/lib/ad_space/configuration.rb +32 -0
  57. data/lib/ad_space/engine.rb +12 -0
  58. data/lib/ad_space/version.rb +3 -0
  59. data/lib/ad_space.rb +17 -0
  60. data/lib/tasks/ad_space_tasks.rake +4 -0
  61. metadata +161 -0
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module V1
4
+ class AdvertisementSerializer < ActiveModel::Serializer
5
+ attributes :uuid, :started_at, :ended_at, :slot, :ad_type, :visibility
6
+
7
+ attribute :scheme_url, if: -> { object.ad_type == :custom }
8
+ attribute :image_url, if: -> { object.ad_type == :custom }
9
+ attribute :dsp_type, if: -> { object.ad_type == :dsp }
10
+
11
+ attribute :splash_ui_type, if: -> { object.splash? }
12
+ attribute :splash_countdown, if: -> { object.splash? }
13
+
14
+ def slot
15
+ object.slot.uuid
16
+ end
17
+
18
+ def started_at
19
+ object.started_at.to_i
20
+ end
21
+
22
+ def ended_at
23
+ object.started_at.to_i
24
+ end
25
+
26
+ def image_url
27
+ object.cover_url(instance_options[:size])
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,20 @@
1
+ class UrlOrCustomSchemeValidator < ActiveModel::EachValidator
2
+ def validate_each(record, attribute, value)
3
+ unless valid_url?(value) || valid_custom_scheme?(value)
4
+ record.errors.add(attribute, "must be a valid URL or have the format 'xxx://xxxxx'")
5
+ end
6
+ end
7
+
8
+ private
9
+
10
+ def valid_url?(value)
11
+ uri = URI.parse(value)
12
+ uri.is_a?(URI::HTTP) || uri.is_a?(URI::HTTPS)
13
+ rescue URI::InvalidURIError
14
+ false
15
+ end
16
+
17
+ def valid_custom_scheme?(value)
18
+ value =~ /\A\w+:\/\/\w+\z/
19
+ end
20
+ end
@@ -0,0 +1,102 @@
1
+ <%= turbo_frame_tag advertisement do %>
2
+ <div id="<%= dom_id advertisement %>">
3
+ <p>
4
+ <strong>Name:</strong>
5
+ <%= advertisement.name %>
6
+ </p>
7
+
8
+ <p>
9
+ <strong>Uuid:</strong>
10
+ <%= advertisement.uuid %>
11
+ </p>
12
+
13
+ <p>
14
+ <strong>Slot:</strong>
15
+ <%= advertisement.slot_id %>
16
+ </p>
17
+
18
+ <p>
19
+ <strong>Published at:</strong>
20
+ <%= advertisement.published_at %>
21
+ </p>
22
+
23
+ <p>
24
+ <strong>Archived at:</strong>
25
+ <%= advertisement.archived_at %>
26
+ </p>
27
+
28
+ <p>
29
+ <strong>Started at:</strong>
30
+ <%= advertisement.started_at %>
31
+ </p>
32
+
33
+ <p>
34
+ <strong>Ended at:</strong>
35
+ <%= advertisement.ended_at %>
36
+ </p>
37
+
38
+ <p>
39
+ <strong>Platform:</strong>
40
+ <%= advertisement.platform %>
41
+ </p>
42
+
43
+ <p>
44
+ <strong>Archived at:</strong>
45
+ <%= advertisement.archived_at %>
46
+ </p>
47
+
48
+ <p>
49
+ <strong>Weight:</strong>
50
+ <%= advertisement.weight %>
51
+ </p>
52
+
53
+ <p>
54
+ <strong>Remark:</strong>
55
+ <%= advertisement.remark %>
56
+ </p>
57
+
58
+ <p>
59
+ <strong>Pv count:</strong>
60
+ <%= advertisement.pv_count %>
61
+ </p>
62
+
63
+ <p>
64
+ <strong>Uv count:</strong>
65
+ <%= advertisement.uv_count %>
66
+ </p>
67
+
68
+ <p>
69
+ <strong>Click count:</strong>
70
+ <%= advertisement.click_count %>
71
+ </p>
72
+
73
+ <% if advertisement.cover_sm.attached? %>
74
+ <p>
75
+ <strong>Small Cover Image:</strong>
76
+ <%= image_tag advertisement.cover_sm.url, style: "max-width: 200px; height: auto;" %>
77
+ </p>
78
+ <% end %>
79
+
80
+ <% if advertisement.cover_base.attached? %>
81
+ <p>
82
+ <strong>Base Cover Image:</strong>
83
+ <%= image_tag advertisement.cover_base.url, style: "max-width: 200px; height: auto;" %>
84
+ </p>
85
+ <% end %>
86
+
87
+ <% if advertisement.cover_md.attached? %>
88
+ <p>
89
+ <strong>Medium Cover Image:</strong>
90
+ <%= image_tag advertisement.cover_md.url, style: "max-width: 200px; height: auto;" %>
91
+ </p>
92
+ <% end %>
93
+
94
+ <% if advertisement.cover_lg.attached? %>
95
+ <p>
96
+ <strong>Large Cover Image:</strong>
97
+ <%= image_tag advertisement.cover_lg.url, style: "max-width: 200px; height: auto;" %>
98
+ </p>
99
+ <% end %>
100
+ </div>
101
+
102
+ <% end %>
@@ -0,0 +1,143 @@
1
+ <%= form_with(model: advertisement, html: { "data-controller" => "hello", "data-hello-is-dsp-ad-value" => advertisement.ad_type.blank? ? true : advertisement.ad_type == "dsp", "data-hello-is-splash-value" => advertisement.slot.blank? ? false : advertisement.slot.splash? }) do |form| %>
2
+ <% if advertisement.errors.any? %>
3
+ <div style="color: red">
4
+ <h2><%= pluralize(advertisement.errors.count, "error") %> prohibited this advertisement from being saved:</h2>
5
+
6
+ <ul>
7
+ <% advertisement.errors.each do |error| %>
8
+ <li><%= error.full_message %></li>
9
+ <% end %>
10
+ </ul>
11
+ </div>
12
+ <% end %>
13
+
14
+ <div>
15
+ <%= form.label :platform, style: "display: block" %>
16
+ <%= form.select :platform, AdSpace::Advertisement.platforms.keys.map { |platform| [platform.capitalize, platform] } %>
17
+ </div>
18
+
19
+ <div>
20
+ <%= form.label :name, style: "display: block" %>
21
+ <%= form.text_field :name %>
22
+ </div>
23
+
24
+ <div>
25
+ <%= form.label :slot_id, "Slot", style: "display: block" %>
26
+ <%= form.select :slot_id, options_for_select(
27
+ AdSpace::Slot.all.map { |s| [s.name, s.id, data: { is_splash: s.splash? }] },
28
+ advertisement.slot_id,
29
+ ), {}, data: { hello_target: "slot", action: "hello#handleSlotChange" } %>
30
+ </div>
31
+
32
+ <div>
33
+ <%= form.label :visibility, style: "display: block" %>
34
+ <%= form.select :visibility, AdSpace::Advertisement.visibilities.keys.map { |key| [key.humanize, key] } %>
35
+ </div>
36
+
37
+ <div>
38
+ <%= form.label :target_version_codes, style: "display: block" %>
39
+ <%= form.text_field :target_version_codes, placeholder: "Enter version codes separated by commas", value: form.object.target_version_codes.blank? ? "" : form.object.target_version_codes.join(",") %>
40
+ </div>
41
+
42
+ <div>
43
+ <%= form.label :started_at, style: "display: block" %>
44
+ <%= form.datetime_field :started_at %>
45
+ </div>
46
+
47
+ <div>
48
+ <%= form.label :ended_at, style: "display: block" %>
49
+ <%= form.datetime_field :ended_at %>
50
+ </div>
51
+
52
+ <div>
53
+ <%= form.label :ad_type, style: "display: block" %>
54
+ <%= form.select :ad_type,
55
+ AdSpace::Advertisement.ad_types.keys.map { |key| [key.humanize, key] },
56
+ {},
57
+ data: { hello_target: "adType", action: "hello#handleAdTypeChnage" }
58
+ %>
59
+ </div>
60
+
61
+ <div>
62
+ <%= form.label :dsp_type, style: "display: block" %>
63
+ <%= form.select :dsp_type, AdSpace::Advertisement.dsp_types.keys.map { |key| [key.humanize, key] } %>
64
+ </div>
65
+
66
+ <div data-hello-target="schemeUrl">
67
+ <%= form.label :scheme_url, style: "display: block" %>
68
+ <%= form.text_field :scheme_url %>
69
+ </div>
70
+
71
+ <%# 默认兜底图 %>
72
+ <div data-hello-target="baseCover" data-controller="image-upload", data-image-upload-url-value=<%= remove_cover_advertisement_path(advertisement, size: "base")%>>
73
+ <%= form.label :cover_base, "Base Cover Image", style: "display: block" %>
74
+ <%= form.file_field :cover_base, data: {image_upload_target: "input"} %>
75
+ <% if advertisement.cover_base.attached? %>
76
+ <%= image_tag advertisement.cover_base.url, style: "max-width: 200px; height: auto;", data: {image_upload_target: "preview"} %>
77
+ <button type="button" data-image-upload-target="removeButton">移除图片</button>
78
+ <% else %>
79
+ <img data-image-upload-target="preview" style="max-width: 200px; height: auto;">
80
+ <button type="button" data-image-upload-target="removeButton" style="display: none;">移除图片</button>
81
+ <% end %>
82
+ </div>
83
+
84
+ <div data-hello-target="splashCountdown">
85
+ <%= form.label :splash_countdown, style: "display: block" %>
86
+ <%= form.number_field :splash_countdown, min: 0, step: 1, value: form.object.splash_countdown %>
87
+ </div>
88
+
89
+ <div data-hello-target="splashUiType">
90
+ <%= form.label :splash_ui_type, style: "display: block" %>
91
+ <%= form.select :splash_ui_type, AdSpace::Advertisement.splash_ui_types.keys.map { |key| [key.humanize, key] } %>
92
+ </div>
93
+
94
+ <div data-hello-target="smCover" data-controller="image-upload", data-image-upload-url-value=<%= remove_cover_advertisement_path(advertisement, size: "sm")%>>
95
+ <%= form.label :cover_sm, "Small Cover Image", style: "display: block" %>
96
+ <%= form.file_field :cover_sm, data: {image_upload_target: "input"} %>
97
+ <% if advertisement.cover_sm.attached? %>
98
+ <%= image_tag advertisement.cover_sm.url, style: "max-width: 200px; height: auto;", data: {image_upload_target: "preview"} %>
99
+ <button type="button" data-image-upload-target="removeButton">移除图片</button>
100
+ <% else %>
101
+ <img data-image-upload-target="preview" style="max-width: 200px; height: auto;">
102
+ <button type="button" data-image-upload-target="removeButton" style="display: none;">移除图片</button>
103
+ <% end %>
104
+ </div>
105
+
106
+ <div data-hello-target="mdCover" data-controller="image-upload", data-image-upload-url-value=<%= remove_cover_advertisement_path(advertisement, size: "md")%>>
107
+ <%= form.label :cover_md, "Medium Cover Image", style: "display: block" %>
108
+ <%= form.file_field :cover_md, data: {image_upload_target: "input"} %>
109
+ <% if advertisement.cover_md.attached? %>
110
+ <%= image_tag advertisement.cover_md.url, style: "max-width: 200px; height: auto;", data: {image_upload_target: "preview"} %>
111
+ <button type="button" data-image-upload-target="removeButton">移除图片</button>
112
+ <% else %>
113
+ <img data-image-upload-target="preview" style="max-width: 200px; height: auto;">
114
+ <button type="button" data-image-upload-target="removeButton" style="display: none;">移除图片</button>
115
+ <% end %>
116
+ </div>
117
+
118
+ <div data-hello-target="lgCover" data-controller="image-upload", data-image-upload-url-value=<%= remove_cover_advertisement_path(advertisement, size: "lg")%>>
119
+ <%= form.label :cover_lg, "Large Cover Image", style: "display: block" %>
120
+ <%= form.file_field :cover_lg, data: {image_upload_target: "input"} %>
121
+ <% if advertisement.cover_lg.attached? %>
122
+ <%= image_tag advertisement.cover_lg.url, style: "max-width: 200px; height: auto;", data: {image_upload_target: "preview"} %>
123
+ <button type="button" data-image-upload-target="removeButton">移除图片</button>
124
+ <% else %>
125
+ <img data-image-upload-target="preview" style="max-width: 200px; height: auto;">
126
+ <button type="button" data-image-upload-target="removeButton" style="display: none;">移除图片</button>
127
+ <% end %>
128
+ </div>
129
+
130
+ <div>
131
+ <%= form.label :weight, style: "display: block" %>
132
+ <%= form.number_field :weight %>
133
+ </div>
134
+
135
+ <div>
136
+ <%= form.label :remark, style: "display: block" %>
137
+ <%= form.text_area :remark %>
138
+ </div>
139
+
140
+ <div>
141
+ <%= form.submit %>
142
+ </div>
143
+ <% end %>
@@ -0,0 +1,10 @@
1
+ <h1>Editing advertisement</h1>
2
+
3
+ <%= render "form", advertisement: @advertisement %>
4
+
5
+ <br>
6
+
7
+ <div>
8
+ <%= link_to "Show this advertisement", @advertisement %> |
9
+ <%= link_to "Back to advertisements", advertisements_path %>
10
+ </div>
@@ -0,0 +1,15 @@
1
+ <p style="color: green"><%= notice %></p>
2
+
3
+ <h1>Advertisements</h1>
4
+
5
+ <div id="advertisements">
6
+ <% @advertisements.each do |advertisement| %>
7
+ <%= render advertisement %>
8
+ <p>
9
+ <%= link_to "Show this advertisement", advertisement %>
10
+ </p>
11
+
12
+ <% end %>
13
+ </div>
14
+
15
+ <%= link_to "New advertisement", new_advertisement_path %>
@@ -0,0 +1,9 @@
1
+ <h1>New advertisement</h1>
2
+
3
+ <%= render "form", advertisement: @advertisement %>
4
+
5
+ <br>
6
+
7
+ <div>
8
+ <%= link_to "Back to advertisements", advertisements_path %>
9
+ </div>
@@ -0,0 +1,13 @@
1
+ <p style="color: green"><%= notice %></p>
2
+
3
+ <%= render @advertisement %>
4
+
5
+ <div>
6
+ <%= link_to "Edit this advertisement", edit_advertisement_path(@advertisement) %> |
7
+ <%= link_to "Back to advertisements", advertisements_path %>
8
+ <%= button_to "#{@advertisement.published? ? 'Unpublish' : 'Publish'} this advertisement", publish_advertisement_path(@advertisement) %>
9
+ <%= button_to "#{@advertisement.archived? ? 'Unarchive' : 'Archive'} this advertisement", archive_advertisement_path(@advertisement) %>
10
+
11
+ <br/>
12
+ <%= button_to "Destroy this advertisement", @advertisement, method: :delete %>
13
+ </div>
@@ -0,0 +1,7 @@
1
+ <h1>📺 AdSpace</h1>
2
+ <ul>
3
+ <li><%= link_to "广告", advertisements_path %></li>
4
+ <li><%= link_to "位置", slots_path %></li>
5
+ <li><%= link_to "Github", "https://github.com/42up/ad_space" %></li>
6
+ <li><%= link_to "Doc", "https://github.com/42up/ad_space/blob/main/README.md" %></li>
7
+ </ul>
@@ -0,0 +1,44 @@
1
+ <%= form_with(model: slot) do |form| %>
2
+ <% if slot.errors.any? %>
3
+ <div style="color: red">
4
+ <h2><%= pluralize(slot.errors.count, "error") %> prohibited this slot from being saved:</h2>
5
+
6
+ <ul>
7
+ <% slot.errors.each do |error| %>
8
+ <li><%= error.full_message %></li>
9
+ <% end %>
10
+ </ul>
11
+ </div>
12
+ <% end %>
13
+
14
+ <div>
15
+ <%= form.label :name, style: "display: block" %>
16
+ <%= form.text_field :name %>
17
+ </div>
18
+
19
+ <div>
20
+ <%= form.label :remark, style: "display: block" %>
21
+ <%= form.text_area :remark %>
22
+ </div>
23
+
24
+ <div data-controller="image-upload", data-image-upload-url-value=<%= remove_demo_slot_path(slot)%>>
25
+ <%= form.label :demo, "Demo", style: "display: block" %>
26
+ <%= form.file_field :demo, data: {image_upload_target: "input"} %>
27
+ <% if slot.demo.attached? %>
28
+ <%= image_tag slot.demo.url, style: "max-width: 200px; height: auto;", data: {image_upload_target: "preview"} %>
29
+ <button type="button" data-image-upload-target="removeButton">移除图片</button>
30
+ <% else %>
31
+ <img data-image-upload-target="preview" style="max-width: 200px; height: auto;">
32
+ <button type="button" data-image-upload-target="removeButton" style="display: none;">移除图片</button>
33
+ <% end %>
34
+ </div>
35
+
36
+ <div>
37
+ <%= form.label :is_splash, "Is Splash" %>
38
+ <%= form.check_box :is_splash, { checked: form.object.is_splash } %>
39
+ </div>
40
+
41
+ <div>
42
+ <%= form.submit %>
43
+ </div>
44
+ <% end %>
@@ -0,0 +1,30 @@
1
+ <div id="<%= dom_id slot %>">
2
+ <p>
3
+ <strong>Name:</strong>
4
+ <%= slot.name %>
5
+ </p>
6
+
7
+ <p>
8
+ <strong>Uuid:</strong>
9
+ <%= slot.uuid %>
10
+ </p>
11
+
12
+ <p>
13
+ <strong>Remark:</strong>
14
+ <%= slot.remark %>
15
+ </p>
16
+
17
+ <p>
18
+ <strong>Is Splash:</strong>
19
+ <%= slot.is_splash %>
20
+ </p>
21
+
22
+ <% if slot.demo.attached? %>
23
+ <p>
24
+ <strong>Demo:</strong>
25
+ <%= image_tag slot.demo.url, style: "max-width: 200px; height: auto;" %>
26
+ </p>
27
+ <% end %>
28
+
29
+
30
+ </div>
@@ -0,0 +1,10 @@
1
+ <h1>Editing slot</h1>
2
+
3
+ <%= render "form", slot: @slot %>
4
+
5
+ <br>
6
+
7
+ <div>
8
+ <%= link_to "Show this slot", @slot %> |
9
+ <%= link_to "Back to slots", slots_path %>
10
+ </div>
@@ -0,0 +1,14 @@
1
+ <p style="color: green"><%= notice %></p>
2
+
3
+ <h1>Slots</h1>
4
+
5
+ <div id="slots">
6
+ <% @slots.each do |slot| %>
7
+ <%= render slot %>
8
+ <p>
9
+ <%= link_to "Show this slot", slot %>
10
+ </p>
11
+ <% end %>
12
+ </div>
13
+
14
+ <%= link_to "New slot", new_slot_path %>
@@ -0,0 +1,9 @@
1
+ <h1>New slot</h1>
2
+
3
+ <%= render "form", slot: @slot %>
4
+
5
+ <br>
6
+
7
+ <div>
8
+ <%= link_to "Back to slots", slots_path %>
9
+ </div>
@@ -0,0 +1,10 @@
1
+ <p style="color: green"><%= notice %></p>
2
+
3
+ <%= render @slot %>
4
+
5
+ <div>
6
+ <%= link_to "Edit this slot", edit_slot_path(@slot) %> |
7
+ <%= link_to "Back to slots", slots_path %>
8
+
9
+ <%= button_to "Destroy this slot", @slot, method: :delete %>
10
+ </div>
@@ -0,0 +1,43 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title><%= breadcrumbs.map(&:name).reverse.append("📺 AdSpace").join(" | ") %></title>
5
+ <%= csrf_meta_tags %>
6
+ <%= csp_meta_tag %>
7
+
8
+ <% if AdSpace::PACKED %>
9
+ <%= javascript_include_tag "/ad_space-assets/application", "data-turbo-track": "reload", defer: true %>
10
+ <%= stylesheet_link_tag "/ad_space-assets/application", "data-turbo-track": "reload", defer: true %>
11
+ <% else %>
12
+ <%= javascript_include_tag "application", "data-turbo-track": "reload", defer: true %>
13
+ <%= stylesheet_link_tag "ad_space/application", "data-turbo-track": "reload", defer: true %>
14
+ <% end %>
15
+ </head>
16
+ <body>
17
+
18
+ <div>
19
+ 当前用户:
20
+ <b style="color: blue;"><%= user_identifier(_current_user) %></b>
21
+ </div>
22
+
23
+ <nav>
24
+ <ol style="display: flex; gap: 0.25rem; align-items: center; font-size: 0.875rem; list-style-type: none;">
25
+ <% breadcrumbs.each do |crumb| %>
26
+ <li>
27
+ <% if crumb.link? %>
28
+ <%= link_to crumb.name, crumb.path, style: "color: #6b7280;" %>
29
+ <% else %>
30
+ <span style="color: #111827;"><%= crumb.name %></span>
31
+ <% end %>
32
+ <% unless crumb == breadcrumbs.last %>
33
+ <span style="color: #6b7280;"> / </span>
34
+ <% end %>
35
+ </li>
36
+ <% end %>
37
+ </ol>
38
+ </nav>
39
+
40
+ <%= yield %>
41
+
42
+ </body>
43
+ </html>
data/config/routes.rb ADDED
@@ -0,0 +1,22 @@
1
+ AdSpace::Engine.routes.draw do
2
+ resources :advertisements do
3
+ member do
4
+ post :publish
5
+ post :archive
6
+ delete 'remove_cover/:size', to: 'advertisements#remove_cover', as: :remove_cover
7
+ end
8
+ end
9
+ resources :slots do
10
+ member do
11
+ delete 'remove_demo', to: 'slots#remove_demo', as: :remove_demo
12
+ end
13
+ end
14
+
15
+ namespace :api do
16
+ namespace :v1 do
17
+ get :ads, to: "advertisements#index"
18
+ end
19
+ end
20
+
21
+ root "dashboard#index"
22
+ end
@@ -0,0 +1,11 @@
1
+ class CreateAdSpaceSlots < ActiveRecord::Migration[7.1]
2
+ def change
3
+ create_table :ad_space_slots do |t|
4
+ t.string :name
5
+ t.string :uuid
6
+ t.text :remark
7
+
8
+ t.timestamps
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,25 @@
1
+ class CreateAdSpaceAdvertisements < ActiveRecord::Migration[7.1]
2
+ def change
3
+ create_table :ad_space_advertisements do |t|
4
+ t.string :name
5
+ t.string :uuid
6
+ t.belongs_to :slot, foreign_key: { to_table: :ad_space_slots }, null: false
7
+ t.datetime :published_at
8
+ t.datetime :started_at
9
+ t.datetime :ended_at
10
+ t.integer :platform
11
+ t.datetime :archived_at
12
+ t.integer :weight, default: 0
13
+ t.text :remark
14
+ t.integer :pv_count, default: 0
15
+ t.integer :uv_count, default: 0
16
+ t.integer :click_count, default: 0
17
+
18
+ t.timestamps
19
+ end
20
+
21
+ add_index :ad_space_advertisements, :platform
22
+ add_index :ad_space_advertisements, :published_at
23
+ add_index :ad_space_advertisements, [:started_at, :ended_at]
24
+ end
25
+ end
@@ -0,0 +1,10 @@
1
+ class AddSchemeUrlToAdSpaceAdvertisements < ActiveRecord::Migration[7.1]
2
+ def change
3
+ add_column :ad_space_advertisements, :ad_type, :integer
4
+ add_column :ad_space_advertisements, :visibility, :integer
5
+ add_column :ad_space_advertisements, :scheme_url, :string
6
+ add_column :ad_space_advertisements, :splash_ui_type, :integer
7
+ add_column :ad_space_advertisements, :splash_countdown, :integer, default: 5
8
+ add_column :ad_space_advertisements, :dsp_type, :integer
9
+ end
10
+ end
@@ -0,0 +1,6 @@
1
+ class AddTargetVersionCodeToAdSpaceAdvertisements < ActiveRecord::Migration[7.1]
2
+ def change
3
+ add_column :ad_space_advertisements, :target_version_codes, :bigint, array: true, default: [0]
4
+ add_index :ad_space_advertisements, :target_version_codes, using: :gin
5
+ end
6
+ end
@@ -0,0 +1,5 @@
1
+ class AddIsSplashToAdSpaceSlots < ActiveRecord::Migration[7.1]
2
+ def change
3
+ add_column :ad_space_slots, :is_splash, :boolean, default: false
4
+ end
5
+ end
@@ -0,0 +1,16 @@
1
+ module AdSpace
2
+ class App
3
+ class << self
4
+ def init(request:)
5
+ # Set the current host for ActiveStorage
6
+ begin
7
+ if defined?(ActiveStorage::Current)
8
+ ActiveStorage::Current.url_options = {protocol: request.protocol, host: request.host, port: request.port}
9
+ end
10
+ rescue => exception
11
+ Rails.logger.debug "[AdSpace] Failed to set ActiveStorage::Current.url_options, #{exception.inspect}"
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,32 @@
1
+ module AdSpace
2
+ class Configuration
3
+ attr_writer :app_name
4
+ attr_writer :root_path
5
+ attr_accessor :current_user
6
+
7
+ def initialize
8
+ @root_path = "/ad_space"
9
+ @app_name = ::Rails.application.class.to_s.split("::").first.underscore.humanize(keep_id_suffix: true)
10
+ end
11
+
12
+ def current_user_method(&block)
13
+ @current_user = block if block.present?
14
+ end
15
+
16
+ def current_user_method=(method)
17
+ @current_user = method if method.present?
18
+ end
19
+ end
20
+
21
+ def self.configuration
22
+ @configuration ||= Configuration.new
23
+ end
24
+
25
+ def self.configuration=(config)
26
+ @configuration = config
27
+ end
28
+
29
+ def self.configure
30
+ yield configuration
31
+ end
32
+ end