kcppayments_rails 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 (130) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +5 -0
  3. data/README.md +49 -0
  4. data/Rakefile +4 -0
  5. data/lib/generators/kcppayments_rails/install/install_generator.rb +21 -0
  6. data/lib/generators/kcppayments_rails/install/templates/kcp_controller.js +54 -0
  7. data/lib/generators/kcppayments_rails/install/templates/kcppayments_rails.rb +14 -0
  8. data/lib/kcppayments_rails/client.rb +49 -0
  9. data/lib/kcppayments_rails/configuration.rb +24 -0
  10. data/lib/kcppayments_rails/engine.rb +21 -0
  11. data/lib/kcppayments_rails/helpers/kcp_helper.rb +33 -0
  12. data/lib/kcppayments_rails/version.rb +5 -0
  13. data/lib/kcppayments_rails.rb +26 -0
  14. data/sig/kcppayments_rails.rbs +4 -0
  15. data/test_app/.dockerignore +51 -0
  16. data/test_app/.github/dependabot.yml +12 -0
  17. data/test_app/.github/workflows/ci.yml +55 -0
  18. data/test_app/.gitignore +87 -0
  19. data/test_app/.kamal/hooks/docker-setup.sample +3 -0
  20. data/test_app/.kamal/hooks/post-app-boot.sample +3 -0
  21. data/test_app/.kamal/hooks/post-deploy.sample +14 -0
  22. data/test_app/.kamal/hooks/post-proxy-reboot.sample +3 -0
  23. data/test_app/.kamal/hooks/pre-app-boot.sample +3 -0
  24. data/test_app/.kamal/hooks/pre-build.sample +51 -0
  25. data/test_app/.kamal/hooks/pre-connect.sample +47 -0
  26. data/test_app/.kamal/hooks/pre-deploy.sample +122 -0
  27. data/test_app/.kamal/hooks/pre-proxy-reboot.sample +3 -0
  28. data/test_app/.kamal/secrets +17 -0
  29. data/test_app/.rubocop.yml +8 -0
  30. data/test_app/.ruby-version +1 -0
  31. data/test_app/Dockerfile +72 -0
  32. data/test_app/Gemfile +60 -0
  33. data/test_app/Gemfile.lock +370 -0
  34. data/test_app/README.md +181 -0
  35. data/test_app/Rakefile +6 -0
  36. data/test_app/app/assets/images/.keep +0 -0
  37. data/test_app/app/assets/stylesheets/application.css +10 -0
  38. data/test_app/app/controllers/application_controller.rb +4 -0
  39. data/test_app/app/controllers/concerns/.keep +0 -0
  40. data/test_app/app/controllers/orders_controller.rb +31 -0
  41. data/test_app/app/controllers/payments_controller.rb +120 -0
  42. data/test_app/app/helpers/application_helper.rb +2 -0
  43. data/test_app/app/helpers/orders_helper.rb +2 -0
  44. data/test_app/app/helpers/payments_helper.rb +2 -0
  45. data/test_app/app/javascript/application.js +3 -0
  46. data/test_app/app/javascript/controllers/application.js +9 -0
  47. data/test_app/app/javascript/controllers/hello_controller.js +7 -0
  48. data/test_app/app/javascript/controllers/index.js +4 -0
  49. data/test_app/app/javascript/controllers/kcp_controller.js +310 -0
  50. data/test_app/app/jobs/application_job.rb +7 -0
  51. data/test_app/app/mailers/application_mailer.rb +4 -0
  52. data/test_app/app/models/application_record.rb +3 -0
  53. data/test_app/app/models/concerns/.keep +0 -0
  54. data/test_app/app/models/order.rb +67 -0
  55. data/test_app/app/views/layouts/application.html.erb +28 -0
  56. data/test_app/app/views/layouts/mailer.html.erb +13 -0
  57. data/test_app/app/views/layouts/mailer.text.erb +1 -0
  58. data/test_app/app/views/orders/create.html.erb +2 -0
  59. data/test_app/app/views/orders/index.html.erb +41 -0
  60. data/test_app/app/views/orders/new.html.erb +44 -0
  61. data/test_app/app/views/orders/show.html.erb +36 -0
  62. data/test_app/app/views/payments/_receipt_links.html.erb +31 -0
  63. data/test_app/app/views/payments/callback.html.erb +2 -0
  64. data/test_app/app/views/payments/create.html.erb +2 -0
  65. data/test_app/app/views/payments/failure.html.erb +20 -0
  66. data/test_app/app/views/payments/new.html.erb +145 -0
  67. data/test_app/app/views/payments/receipt.html.erb +156 -0
  68. data/test_app/app/views/payments/success.html.erb +34 -0
  69. data/test_app/app/views/pwa/manifest.json.erb +22 -0
  70. data/test_app/app/views/pwa/service-worker.js +26 -0
  71. data/test_app/bin/brakeman +7 -0
  72. data/test_app/bin/dev +2 -0
  73. data/test_app/bin/docker-entrypoint +14 -0
  74. data/test_app/bin/importmap +4 -0
  75. data/test_app/bin/jobs +6 -0
  76. data/test_app/bin/kamal +16 -0
  77. data/test_app/bin/rails +4 -0
  78. data/test_app/bin/rake +4 -0
  79. data/test_app/bin/rubocop +8 -0
  80. data/test_app/bin/setup +65 -0
  81. data/test_app/bin/thrust +5 -0
  82. data/test_app/config/application.rb +42 -0
  83. data/test_app/config/boot.rb +4 -0
  84. data/test_app/config/cable.yml +17 -0
  85. data/test_app/config/cache.yml +16 -0
  86. data/test_app/config/credentials.yml.enc +1 -0
  87. data/test_app/config/database.yml +41 -0
  88. data/test_app/config/deploy.yml +116 -0
  89. data/test_app/config/environment.rb +5 -0
  90. data/test_app/config/environments/development.rb +74 -0
  91. data/test_app/config/environments/production.rb +90 -0
  92. data/test_app/config/environments/test.rb +53 -0
  93. data/test_app/config/importmap.rb +7 -0
  94. data/test_app/config/initializers/assets.rb +7 -0
  95. data/test_app/config/initializers/content_security_policy.rb +25 -0
  96. data/test_app/config/initializers/filter_parameter_logging.rb +8 -0
  97. data/test_app/config/initializers/inflections.rb +16 -0
  98. data/test_app/config/initializers/kcppayments_rails.rb +29 -0
  99. data/test_app/config/locales/en.yml +31 -0
  100. data/test_app/config/master.key +1 -0
  101. data/test_app/config/puma.rb +41 -0
  102. data/test_app/config/queue.yml +18 -0
  103. data/test_app/config/recurring.yml +15 -0
  104. data/test_app/config/routes.rb +29 -0
  105. data/test_app/config/storage.yml +34 -0
  106. data/test_app/config.ru +6 -0
  107. data/test_app/db/cable_schema.rb +11 -0
  108. data/test_app/db/cache_schema.rb +14 -0
  109. data/test_app/db/migrate/20250827075913_create_orders.rb +16 -0
  110. data/test_app/db/migrate/20250827121258_add_kcp_fields_to_orders.rb +6 -0
  111. data/test_app/db/queue_schema.rb +129 -0
  112. data/test_app/db/schema.rb +28 -0
  113. data/test_app/db/seeds.rb +80 -0
  114. data/test_app/lib/tasks/.keep +0 -0
  115. data/test_app/log/.keep +0 -0
  116. data/test_app/public/.well-known/appspecific/com.chrome.devtools.json +1 -0
  117. data/test_app/public/400.html +114 -0
  118. data/test_app/public/404.html +114 -0
  119. data/test_app/public/406-unsupported-browser.html +114 -0
  120. data/test_app/public/422.html +114 -0
  121. data/test_app/public/500.html +114 -0
  122. data/test_app/public/icon.png +0 -0
  123. data/test_app/public/icon.svg +3 -0
  124. data/test_app/public/robots.txt +1 -0
  125. data/test_app/script/.keep +0 -0
  126. data/test_app/tmp/.keep +0 -0
  127. data/test_app/tmp/restart.txt +0 -0
  128. data/test_app/vendor/.keep +0 -0
  129. data/test_app/vendor/javascript/.keep +0 -0
  130. metadata +184 -0
@@ -0,0 +1,145 @@
1
+ <h1>KCP 결제</h1>
2
+
3
+ <div style="background-color: #f9f9f9; padding: 20px; border-radius: 5px; margin-bottom: 20px;">
4
+ <h2>주문 정보</h2>
5
+ <p><strong>주문번호:</strong> <%= @order.order_no %></p>
6
+ <p><strong>상품명:</strong> <%= @order.product_name %></p>
7
+ <p><strong>결제금액:</strong> <%= number_to_currency(@order.amount, unit: "₩", format: "%u%n", delimiter: ",", precision: 0) %></p>
8
+ <% if @order.tax_free_amount.present? && @order.tax_free_amount > 0 %>
9
+ <p><strong>면세금액:</strong> <%= number_to_currency(@order.tax_free_amount, unit: "₩", format: "%u%n", delimiter: ",", precision: 0) %></p>
10
+ <% end %>
11
+ <p><strong>구매자명:</strong> <%= @order.buyer_name %></p>
12
+ <p><strong>구매자 이메일:</strong> <%= @order.buyer_email %></p>
13
+ </div>
14
+
15
+ <!-- KCP 결제 스크립트 로드 -->
16
+ <%= kcp_script_tag %>
17
+
18
+ <!-- KCP 결제 폼 -->
19
+ <form name="kcp_pay_form"
20
+ id="kcp_pay_form"
21
+ method="post"
22
+ action="<%= callback_payments_url %>"
23
+ target="_self"
24
+ data-controller="kcp"
25
+ data-kcp-order-id-value="<%= @order.order_no %>"
26
+ data-kcp-amount-value="<%= @order.amount %>"
27
+ data-kcp-buyer-name-value="<%= @order.buyer_name %>"
28
+ data-kcp-buyer-email-value="<%= @order.buyer_email %>"
29
+ data-kcp-product-name-value="<%= @order.product_name %>"
30
+ data-kcp-return-url-value="<%= callback_payments_url %>"
31
+ data-kcp-tax-free-amount-value="<%= @order.tax_free_amount %>">
32
+
33
+ <!-- CSRF 토큰 -->
34
+ <%= hidden_field_tag :authenticity_token, form_authenticity_token %>
35
+
36
+ <!-- KCP 필수 hidden 필드들 -->
37
+ <%= hidden_field_tag :site_cd, KcppaymentsRails.configuration.site_cd %>
38
+ <%= hidden_field_tag :site_name, "테스트몰" %>
39
+ <%= hidden_field_tag :ordr_idxx, @order.order_no %>
40
+ <%= hidden_field_tag :good_name, @order.product_name %>
41
+ <%= hidden_field_tag :good_mny, @order.amount %>
42
+ <%= hidden_field_tag :curr_cd, "410" %>
43
+ <%= hidden_field_tag :shop_user_id, @order.buyer_email %>
44
+ <%= hidden_field_tag :buyr_name, @order.buyer_name %>
45
+ <%= hidden_field_tag :buyr_mail, @order.buyer_email %>
46
+ <%= hidden_field_tag :buyr_tel1, "010" %>
47
+ <%= hidden_field_tag :buyr_tel2, "1234" %>
48
+ <%= hidden_field_tag :buyr_tel3, "5678" %>
49
+ <%= hidden_field_tag :mod_type, "0" %>
50
+ <%= hidden_field_tag :mod_ip, request.remote_ip %>
51
+ <%= hidden_field_tag :mod_desc, "주문변경" %>
52
+
53
+ <!-- 결제 처리를 위한 추가 필드 -->
54
+ <%= hidden_field_tag :Ret_URL, callback_payments_url %>
55
+ <%= hidden_field_tag :ReturnURL, callback_payments_url %>
56
+ <%= hidden_field_tag :approval_key, "" %>
57
+ <%= hidden_field_tag :traceNo, "" %>
58
+
59
+ <!-- 수령자 정보 -->
60
+ <%= hidden_field_tag :rcvr_name, @order.buyer_name %>
61
+ <%= hidden_field_tag :rcvr_tel1, "010" %>
62
+ <%= hidden_field_tag :rcvr_tel2, "1234" %>
63
+ <%= hidden_field_tag :rcvr_tel3, "5678" %>
64
+ <%= hidden_field_tag :rcvr_mail, @order.buyer_email %>
65
+ <%= hidden_field_tag :rcvr_zipx, "12345" %>
66
+ <%= hidden_field_tag :rcvr_add1, "서울시 강남구" %>
67
+ <%= hidden_field_tag :rcvr_add2, "테스트 주소" %>
68
+
69
+ <!-- 결제 옵션 -->
70
+ <%= hidden_field_tag :quotaopt, "12" %>
71
+ <%= hidden_field_tag :escw_used, "N" %>
72
+ <%= hidden_field_tag :card_cop, "10300" %>
73
+ <%= hidden_field_tag :kcp_cert, "true" %>
74
+ <%= hidden_field_tag :disp_tax_yn, "N" %>
75
+ <%= hidden_field_tag :vcnt_expire_term, "" %>
76
+ <%= hidden_field_tag :vcnt_expire_term_time, "" %>
77
+ <%= hidden_field_tag :bask_cntx, "0" %>
78
+ <%= hidden_field_tag :eng_flag, "N" %>
79
+ <%= hidden_field_tag :user_agent, "" %>
80
+ <%= hidden_field_tag :ipgm_name, "kcp" %>
81
+ <%= hidden_field_tag :epay_no, "" %>
82
+
83
+ <!-- KCP JavaScript가 요구하는 추가 필수 필드들 -->
84
+ <%= hidden_field_tag :encoding_trans, "UTF-8" %>
85
+ <%= hidden_field_tag :req_tx, "pay" %>
86
+ <%= hidden_field_tag :shop_name, "테스트몰" %>
87
+ <%= hidden_field_tag :lang_flag, "KOR" %>
88
+ <%= hidden_field_tag :site_logo, "" %>
89
+ <%= hidden_field_tag :req_ip, request.remote_ip %>
90
+ <%= hidden_field_tag :complex_yn, "N" %>
91
+ <%= hidden_field_tag :kcp_noti, "" %>
92
+ <%= hidden_field_tag :van_code, "" %>
93
+
94
+ <!-- 세금 관련 -->
95
+ <% if @order.tax_free_amount.present? && @order.tax_free_amount > 0 %>
96
+ <%= hidden_field_tag :tax_flag, "TG03" %>
97
+ <%= hidden_field_tag :comm_tax_mny, (@order.amount - @order.tax_free_amount) %>
98
+ <%= hidden_field_tag :comm_free_mny, @order.tax_free_amount %>
99
+ <% else %>
100
+ <%= hidden_field_tag :tax_flag, "TG01" %>
101
+ <%= hidden_field_tag :comm_tax_mny, @order.amount %>
102
+ <%= hidden_field_tag :comm_free_mny, "0" %>
103
+ <% end %>
104
+
105
+ <!-- KCP 처리 결과 필드들 -->
106
+ <%= hidden_field_tag :res_cd, "" %>
107
+ <%= hidden_field_tag :res_msg, "" %>
108
+ <%= hidden_field_tag :tno, "" %>
109
+ <%= hidden_field_tag :trace_no, "" %>
110
+ <%= hidden_field_tag :enc_info, "" %>
111
+ <%= hidden_field_tag :enc_data, "" %>
112
+ <%= hidden_field_tag :ret_pay_method, "" %>
113
+ <%= hidden_field_tag :trad_date, "" %>
114
+ <%= hidden_field_tag :trad_time, "" %>
115
+ <%= hidden_field_tag :bank_name, "" %>
116
+ <%= hidden_field_tag :bank_issu, "" %>
117
+
118
+ <!-- 결제 수단 선택 -->
119
+ <%= hidden_field_tag :pay_method, "100000000000" %>
120
+
121
+ <div style="margin-bottom: 20px;">
122
+ <h3>결제 수단 선택</h3>
123
+ <label>
124
+ <%= radio_button_tag "pay_method_display", "card", true, onclick: "document.getElementsByName('pay_method')[0].value='100000000000'" %>
125
+ 신용카드
126
+ </label>
127
+ <label style="margin-left: 20px;">
128
+ <%= radio_button_tag "pay_method_display", "transfer", false, onclick: "document.getElementsByName('pay_method')[0].value='010000000000'" %>
129
+ 계좌이체
130
+ </label>
131
+ <label style="margin-left: 20px;">
132
+ <%= radio_button_tag "pay_method_display", "vbank", false, onclick: "document.getElementsByName('pay_method')[0].value='001000000000'" %>
133
+ 가상계좌
134
+ </label>
135
+ </div>
136
+
137
+ <div>
138
+ <%= button_tag "결제하기",
139
+ type: "button",
140
+ data: { action: "click->kcp#requestPayment" },
141
+ style: "padding: 10px 30px; background-color: #FF5722; color: white; border: none; cursor: pointer; font-size: 16px; border-radius: 3px;" %>
142
+
143
+ <%= link_to "취소", order_path(@order), style: "margin-left: 10px;" %>
144
+ </div>
145
+ </form>
@@ -0,0 +1,156 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>결제 영수증</title>
5
+ <meta charset="utf-8">
6
+ <style>
7
+ body {
8
+ font-family: Arial, sans-serif;
9
+ margin: 20px;
10
+ background-color: white;
11
+ }
12
+ .receipt-container {
13
+ max-width: 600px;
14
+ margin: 0 auto;
15
+ border: 1px solid #ddd;
16
+ padding: 30px;
17
+ background-color: white;
18
+ }
19
+ .header {
20
+ text-align: center;
21
+ border-bottom: 2px solid #333;
22
+ padding-bottom: 20px;
23
+ margin-bottom: 30px;
24
+ }
25
+ .header h1 {
26
+ color: #333;
27
+ margin: 0;
28
+ font-size: 28px;
29
+ }
30
+ .receipt-info {
31
+ margin-bottom: 30px;
32
+ }
33
+ .info-row {
34
+ display: flex;
35
+ justify-content: space-between;
36
+ padding: 8px 0;
37
+ border-bottom: 1px dotted #ccc;
38
+ }
39
+ .info-label {
40
+ font-weight: bold;
41
+ color: #555;
42
+ }
43
+ .info-value {
44
+ color: #333;
45
+ }
46
+ .amount {
47
+ font-size: 20px;
48
+ font-weight: bold;
49
+ color: #2e7d32;
50
+ }
51
+ .footer {
52
+ margin-top: 40px;
53
+ padding-top: 20px;
54
+ border-top: 1px solid #ddd;
55
+ text-align: center;
56
+ color: #666;
57
+ font-size: 14px;
58
+ }
59
+ .print-btn {
60
+ text-align: center;
61
+ margin: 20px 0;
62
+ }
63
+ .print-btn button {
64
+ padding: 10px 30px;
65
+ background-color: #4CAF50;
66
+ color: white;
67
+ border: none;
68
+ border-radius: 5px;
69
+ cursor: pointer;
70
+ font-size: 16px;
71
+ }
72
+ @media print {
73
+ .print-btn {
74
+ display: none;
75
+ }
76
+ }
77
+ </style>
78
+ </head>
79
+ <body>
80
+ <div class="receipt-container">
81
+ <% if @order.nil? %>
82
+ <div class="header">
83
+ <h1>❌ 오류</h1>
84
+ <p>주문 정보를 찾을 수 없습니다.</p>
85
+ </div>
86
+ <script>
87
+ alert('주문 정보를 찾을 수 없습니다.');
88
+ window.close();
89
+ </script>
90
+ <% else %>
91
+ <div class="header">
92
+ <h1>🧾 결제 영수증</h1>
93
+ <p>Receipt / 領収書</p>
94
+ </div>
95
+
96
+ <div class="receipt-info">
97
+ <div class="info-row">
98
+ <span class="info-label">주문번호 (Order No.):</span>
99
+ <span class="info-value"><%= @order.order_no %></span>
100
+ </div>
101
+ <div class="info-row">
102
+ <span class="info-label">결제일시 (Payment Date):</span>
103
+ <span class="info-value"><%= @order.updated_at.strftime("%Y년 %m월 %d일 %H:%M") %></span>
104
+ </div>
105
+ <div class="info-row">
106
+ <span class="info-label">상품명 (Product):</span>
107
+ <span class="info-value"><%= @order.product_name %></span>
108
+ </div>
109
+ <div class="info-row">
110
+ <span class="info-label">구매자 (Buyer):</span>
111
+ <span class="info-value"><%= @order.buyer_name %></span>
112
+ </div>
113
+ <div class="info-row">
114
+ <span class="info-label">이메일 (Email):</span>
115
+ <span class="info-value"><%= @order.buyer_email %></span>
116
+ </div>
117
+ <% if @order.payment_method.present? %>
118
+ <div class="info-row">
119
+ <span class="info-label">결제수단 (Payment Method):</span>
120
+ <span class="info-value"><%= @order.payment_method %></span>
121
+ </div>
122
+ <% end %>
123
+ <div class="info-row">
124
+ <span class="info-label">결제상태 (Status):</span>
125
+ <span class="info-value">
126
+ <% if @order.status == "paid" %>
127
+ <span style="color: #2e7d32;">✓ 결제완료 (Paid)</span>
128
+ <% else %>
129
+ <span style="color: #d32f2f;"><%= @order.status %></span>
130
+ <% end %>
131
+ </span>
132
+ </div>
133
+ <div class="info-row" style="font-size: 18px; font-weight: bold; border-bottom: 2px solid #333; padding: 15px 0; margin-top: 20px;">
134
+ <span class="info-label">결제금액 (Amount):</span>
135
+ <span class="info-value amount"><%= number_to_currency(@order.amount, unit: "₩", format: "%u%n", delimiter: ",", precision: 0) %></span>
136
+ </div>
137
+ </div>
138
+
139
+ <div class="print-btn">
140
+ <button onclick="window.print()">🖨️ 인쇄하기</button>
141
+ </div>
142
+
143
+ <div class="footer">
144
+ <p>KCP Payments Rails - 전자결제 서비스</p>
145
+ <p>이 영수증은 전자결제 완료를 증명하는 문서입니다.</p>
146
+ <p>This receipt serves as proof of electronic payment completion.</p>
147
+ </div>
148
+ <% end %>
149
+ </div>
150
+
151
+ <script>
152
+ // 자동 프린트 (선택사항)
153
+ // window.onload = function() { window.print(); }
154
+ </script>
155
+ </body>
156
+ </html>
@@ -0,0 +1,34 @@
1
+ <h1 style="color: green;">✓ 결제 성공</h1>
2
+
3
+ <% if @demo_mode %>
4
+ <div style="background-color: #fff3cd; padding: 15px; border-radius: 5px; border: 1px solid #ffc107; margin-bottom: 20px;">
5
+ <h4 style="color: #856404; margin-top: 0;">🧪 데모 모드</h4>
6
+ <p style="color: #856404; margin-bottom: 0;">
7
+ 이는 젬 기능을 테스트하기 위한 데모 결제입니다.<br>
8
+ 실제 KCP 계정을 설정하면 실제 결제가 처리됩니다.
9
+ </p>
10
+ </div>
11
+ <% end %>
12
+
13
+ <div style="background-color: #e8f5e9; padding: 20px; border-radius: 5px; border: 1px solid #4CAF50;">
14
+ <h2><%= @demo_mode ? "데모 결제가 성공적으로 완료되었습니다!" : "결제가 성공적으로 완료되었습니다!" %></h2>
15
+
16
+ <div style="margin-top: 20px;">
17
+ <p><strong>주문번호:</strong> <%= @order.order_no %></p>
18
+ <p><strong>상품명:</strong> <%= @order.product_name %></p>
19
+ <p><strong>결제금액:</strong> <%= number_to_currency(@order.amount, unit: "₩", format: "%u%n", delimiter: ",", precision: 0) %></p>
20
+ <p><strong>구매자:</strong> <%= @order.buyer_name %> (<%= @order.buyer_email %>)</p>
21
+ <% if @order.payment_method.present? %>
22
+ <p><strong>결제수단:</strong> <%= @order.payment_method %></p>
23
+ <% end %>
24
+ </div>
25
+ </div>
26
+
27
+ <div style="margin-top: 20px;">
28
+ <%= link_to "주문 상세보기", order_path(@order),
29
+ style: "padding: 10px 20px;margin-right: 10px; background-color: #4CAF50; color: white; text-decoration: none; border-radius: 3px;" %>
30
+
31
+ <%= render 'receipt_links', order: @order %>
32
+
33
+ <%= link_to "주문 목록", orders_path, style: "margin-left: 10px; margin-top: 15px; display: inline-block;" %>
34
+ </div>
@@ -0,0 +1,22 @@
1
+ {
2
+ "name": "TestApp",
3
+ "icons": [
4
+ {
5
+ "src": "/icon.png",
6
+ "type": "image/png",
7
+ "sizes": "512x512"
8
+ },
9
+ {
10
+ "src": "/icon.png",
11
+ "type": "image/png",
12
+ "sizes": "512x512",
13
+ "purpose": "maskable"
14
+ }
15
+ ],
16
+ "start_url": "/",
17
+ "display": "standalone",
18
+ "scope": "/",
19
+ "description": "TestApp.",
20
+ "theme_color": "red",
21
+ "background_color": "red"
22
+ }
@@ -0,0 +1,26 @@
1
+ // Add a service worker for processing Web Push notifications:
2
+ //
3
+ // self.addEventListener("push", async (event) => {
4
+ // const { title, options } = await event.data.json()
5
+ // event.waitUntil(self.registration.showNotification(title, options))
6
+ // })
7
+ //
8
+ // self.addEventListener("notificationclick", function(event) {
9
+ // event.notification.close()
10
+ // event.waitUntil(
11
+ // clients.matchAll({ type: "window" }).then((clientList) => {
12
+ // for (let i = 0; i < clientList.length; i++) {
13
+ // let client = clientList[i]
14
+ // let clientPath = (new URL(client.url)).pathname
15
+ //
16
+ // if (clientPath == event.notification.data.path && "focus" in client) {
17
+ // return client.focus()
18
+ // }
19
+ // }
20
+ //
21
+ // if (clients.openWindow) {
22
+ // return clients.openWindow(event.notification.data.path)
23
+ // }
24
+ // })
25
+ // )
26
+ // })
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+ require "rubygems"
3
+ require "bundler/setup"
4
+
5
+ ARGV.unshift("--ensure-latest")
6
+
7
+ load Gem.bin_path("brakeman", "brakeman")
data/test_app/bin/dev ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env ruby
2
+ exec "./bin/rails", "server", *ARGV
@@ -0,0 +1,14 @@
1
+ #!/bin/bash -e
2
+
3
+ # Enable jemalloc for reduced memory usage and latency.
4
+ if [ -z "${LD_PRELOAD+x}" ]; then
5
+ LD_PRELOAD=$(find /usr/lib -name libjemalloc.so.2 -print -quit)
6
+ export LD_PRELOAD
7
+ fi
8
+
9
+ # If running the rails server then create or migrate existing database
10
+ if [ "${@: -2:1}" == "./bin/rails" ] && [ "${@: -1:1}" == "server" ]; then
11
+ ./bin/rails db:prepare
12
+ fi
13
+
14
+ exec "${@}"
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require_relative "../config/application"
4
+ require "importmap/commands"
data/test_app/bin/jobs ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require_relative "../config/environment"
4
+ require "solid_queue/cli"
5
+
6
+ SolidQueue::Cli.start(ARGV)
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ #
5
+ # This file was generated by Bundler.
6
+ #
7
+ # The application 'kamal' is installed as part of a gem, and
8
+ # this file is here to facilitate running it.
9
+ #
10
+
11
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
12
+
13
+ require "rubygems"
14
+ require "bundler/setup"
15
+
16
+ load Gem.bin_path("kamal", "kamal")
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+ APP_PATH = File.expand_path("../config/application", __dir__)
3
+ require_relative "../config/boot"
4
+ require "rails/commands"
data/test_app/bin/rake ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+ require_relative "../config/boot"
3
+ require "rake"
4
+ Rake.application.run
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+ require "rubygems"
3
+ require "bundler/setup"
4
+
5
+ # explicit rubocop config increases performance slightly while avoiding config confusion.
6
+ ARGV.unshift("--config", File.expand_path("../.rubocop.yml", __dir__))
7
+
8
+ load Gem.bin_path("rubocop", "rubocop")
@@ -0,0 +1,65 @@
1
+ #!/usr/bin/env ruby
2
+ require "fileutils"
3
+
4
+ APP_ROOT = File.expand_path("..", __dir__)
5
+
6
+ def system!(*args)
7
+ system(*args, exception: true)
8
+ end
9
+
10
+ FileUtils.chdir APP_ROOT do
11
+ # This script is a way to set up or update your development environment automatically.
12
+ # This script is idempotent, so that you can run it at any time and get an expectable outcome.
13
+ # Add necessary setup steps to this file.
14
+
15
+ puts "== Installing dependencies =="
16
+ system("bundle check") || system!("bundle install")
17
+
18
+ # puts "\n== Copying sample files =="
19
+ # unless File.exist?("config/database.yml")
20
+ # FileUtils.cp "config/database.yml.sample", "config/database.yml"
21
+ # end
22
+
23
+ puts "\n== Preparing database =="
24
+ system! "bin/rails db:prepare"
25
+
26
+ puts "\n== Removing old logs and tempfiles =="
27
+ system! "bin/rails log:clear tmp:clear"
28
+
29
+ unless ARGV.include?("--skip-server")
30
+ puts "\n== Starting development server =="
31
+ STDOUT.flush # flush the output before exec(2) so that it displays
32
+ exec "bin/dev"
33
+ end
34
+ end
35
+
36
+
37
+ def command_exists?(command)
38
+ system("command -v #{command} > /dev/null 2>&1")
39
+ end
40
+
41
+ # Install puma-dev if it's not installed
42
+
43
+ unless command_exists?('brew')
44
+ puts "Homebrew is not installed. Please install Homebrew first."
45
+ exit 1
46
+ end
47
+
48
+ unless command_exists?('puma-dev')
49
+ puts "Installing puma-dev..."
50
+ system('brew install puma/puma/puma-dev')
51
+
52
+ puts "Running puma-dev setup..."
53
+ system('sudo puma-dev -setup')
54
+
55
+ puts "Installing puma-dev DNS settings..."
56
+ system('puma-dev -install')
57
+ end
58
+
59
+ # Link the current app to puma-dev
60
+
61
+ puts "\n== Configuring Puma-dev to host https://app_name.test =="
62
+ app_name = File.basename(APP_ROOT)
63
+
64
+ system! "puma-dev link -n #{app_name}"
65
+ puts "puma-dev setup complete for #{app_name}.test"
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+ require "rubygems"
3
+ require "bundler/setup"
4
+
5
+ load Gem.bin_path("thruster", "thrust")
@@ -0,0 +1,42 @@
1
+ require_relative "boot"
2
+
3
+ require "rails"
4
+ # Pick the frameworks you want:
5
+ require "active_model/railtie"
6
+ require "active_job/railtie"
7
+ require "active_record/railtie"
8
+ require "active_storage/engine"
9
+ require "action_controller/railtie"
10
+ require "action_mailer/railtie"
11
+ require "action_mailbox/engine"
12
+ require "action_text/engine"
13
+ require "action_view/railtie"
14
+ require "action_cable/engine"
15
+ # require "rails/test_unit/railtie"
16
+
17
+ # Require the gems listed in Gemfile, including any gems
18
+ # you've limited to :test, :development, or :production.
19
+ Bundler.require(*Rails.groups)
20
+
21
+ module TestApp
22
+ class Application < Rails::Application
23
+ # Initialize configuration defaults for originally generated Rails version.
24
+ config.load_defaults 8.0
25
+
26
+ # Please, add to the `ignore` list any other `lib` subdirectories that do
27
+ # not contain `.rb` files, or that should not be reloaded or eager loaded.
28
+ # Common ones are `templates`, `generators`, or `middleware`, for example.
29
+ config.autoload_lib(ignore: %w[assets tasks])
30
+
31
+ # Configuration for the application, engines, and railties goes here.
32
+ #
33
+ # These settings can be overridden in specific environments using the files
34
+ # in config/environments, which are processed later.
35
+ #
36
+ # config.time_zone = "Central Time (US & Canada)"
37
+ # config.eager_load_paths << Rails.root.join("extras")
38
+
39
+ # Don't generate system test files.
40
+ config.generators.system_tests = nil
41
+ end
42
+ end
@@ -0,0 +1,4 @@
1
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
2
+
3
+ require "bundler/setup" # Set up gems listed in the Gemfile.
4
+ require "bootsnap/setup" # Speed up boot time by caching expensive operations.
@@ -0,0 +1,17 @@
1
+ # Async adapter only works within the same process, so for manually triggering cable updates from a console,
2
+ # and seeing results in the browser, you must do so from the web console (running inside the dev process),
3
+ # not a terminal started via bin/rails console! Add "console" to any action or any ERB template view
4
+ # to make the web console appear.
5
+ development:
6
+ adapter: async
7
+
8
+ test:
9
+ adapter: test
10
+
11
+ production:
12
+ adapter: solid_cable
13
+ connects_to:
14
+ database:
15
+ writing: cable
16
+ polling_interval: 0.1.seconds
17
+ message_retention: 1.day
@@ -0,0 +1,16 @@
1
+ default: &default
2
+ store_options:
3
+ # Cap age of oldest cache entry to fulfill retention policies
4
+ # max_age: <%= 60.days.to_i %>
5
+ max_size: <%= 256.megabytes %>
6
+ namespace: <%= Rails.env %>
7
+
8
+ development:
9
+ <<: *default
10
+
11
+ test:
12
+ <<: *default
13
+
14
+ production:
15
+ database: cache
16
+ <<: *default
@@ -0,0 +1 @@
1
+ jCrJjZ8dSCROAGK7oEG5gfpi98YevpYNRCsK6Yf/MtF99C3wkuHwQdjQlvyFhT280PUYEyIoqENpQSV0xA+SR1RkVEX3uXeEfTZUEDI9XJln2v4vTKYNbDu3yjVX4b9G0Gg9Fd7MKYX85xLcL+TyBQfI/vftgTSFftDHaJOnlQ3prMeQIM2r/F4uMQ4FT+n+G/dcAHM7nkO3noPiDykaYvhgWDKgPY7C5+4faKx4uXQPXIlUFkiltTyuN9X5h5RuCHaaroUSz7ms3a0+gRUnFdy1udSL2ZxJJqNlRf7PlEtxxW7zuL/Ir8/oiZxXlVQeqg0MjBPhzc4KFKpkFRU3JW1iD5S1GMRoBh89q5+x3KrolyNKbVeTzUUQoOlbXyFXtbotULcR2Nm6tOTvG6OTEwpjIUslnbma/5okY8ux4kNagPJCU3M3RveER5uxJO8lbRG/i3G098OuEkSniPbxeXJT33xxuwGueC7AP8cNnEOsHyNMJr/JVgpC--idPG+LloS0T+ROJB--0YsDaiZ3JwOCTPyOTPgCow==
@@ -0,0 +1,41 @@
1
+ # SQLite. Versions 3.8.0 and up are supported.
2
+ # gem install sqlite3
3
+ #
4
+ # Ensure the SQLite 3 gem is defined in your Gemfile
5
+ # gem "sqlite3"
6
+ #
7
+ default: &default
8
+ adapter: sqlite3
9
+ pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
10
+ timeout: 5000
11
+
12
+ development:
13
+ <<: *default
14
+ database: storage/development.sqlite3
15
+
16
+ # Warning: The database defined as "test" will be erased and
17
+ # re-generated from your development database when you run "rake".
18
+ # Do not set this db to the same as development or production.
19
+ test:
20
+ <<: *default
21
+ database: storage/test.sqlite3
22
+
23
+
24
+ # Store production database in the storage/ directory, which by default
25
+ # is mounted as a persistent Docker volume in config/deploy.yml.
26
+ production:
27
+ primary:
28
+ <<: *default
29
+ database: storage/production.sqlite3
30
+ cache:
31
+ <<: *default
32
+ database: storage/production_cache.sqlite3
33
+ migrations_paths: db/cache_migrate
34
+ queue:
35
+ <<: *default
36
+ database: storage/production_queue.sqlite3
37
+ migrations_paths: db/queue_migrate
38
+ cable:
39
+ <<: *default
40
+ database: storage/production_cable.sqlite3
41
+ migrations_paths: db/cable_migrate