ad_space 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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