web47core 2.0.1 → 2.2.15
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.
- checksums.yaml +4 -4
- data/README.md +111 -41
- data/app/controllers/status_controller.rb +8 -3
- data/app/helpers/core_form_helper.rb +6 -6
- data/app/helpers/core_helper.rb +1 -1
- data/app/helpers/core_link_helper.rb +47 -8
- data/app/helpers/core_nav_bar_helper.rb +5 -4
- data/app/helpers/core_table_helper.rb +80 -0
- data/app/helpers/model_modal_helper.rb +3 -3
- data/app/views/common/_create_actions.html.haml +12 -0
- data/app/views/common/_update_actions.html.haml +10 -0
- data/app/views/cron/_edit.html.haml +15 -17
- data/app/views/cron/_index.html.haml +74 -67
- data/app/views/delayed_job_metrics/_index.html.haml +27 -0
- data/app/views/delayed_job_metrics/index.html.haml +1 -0
- data/app/views/delayed_job_workers/_index.html.haml +27 -0
- data/app/views/delayed_job_workers/index.html.haml +1 -0
- data/app/views/delayed_jobs/_index.html.haml +47 -52
- data/app/views/delayed_jobs/_show.html.haml +15 -13
- data/app/views/system_configurations/_edit.html.haml +14 -9
- data/app/views/system_configurations/_show.html.haml +18 -12
- data/config/brakeman.ignore +26 -0
- data/config/brakeman.yml +2 -0
- data/config/locales/en.yml +21 -3
- data/lib/app/controllers/concerns/core_delayed_job_metrics_controller.rb +35 -0
- data/lib/app/controllers/concerns/core_delayed_job_workers_controller.rb +35 -0
- data/lib/app/controllers/concerns/core_delayed_jobs_controller.rb +2 -3
- data/lib/app/jobs/cron/command.rb +1 -4
- data/lib/app/jobs/cron/record_delayed_job_metrics.rb +25 -0
- data/lib/app/jobs/cron/restart_orphaned_delayed_jobs.rb +44 -0
- data/lib/app/jobs/cron/server.rb +32 -15
- data/lib/app/jobs/cron/switchboard_sync_configuration.rb +2 -0
- data/lib/app/jobs/cron/switchboard_sync_models.rb +2 -0
- data/lib/app/jobs/cron/trim_collection.rb +1 -1
- data/lib/app/jobs/cron/trim_delayed_job_metrics.rb +29 -0
- data/lib/app/jobs/cron/trim_delayed_job_workers.rb +39 -0
- data/lib/app/jobs/cron/trim_failed_delayed_jobs.rb +2 -0
- data/lib/app/models/api_token.rb +9 -0
- data/lib/app/models/command_job.rb +12 -11
- data/lib/app/models/concerns/api_tokenable.rb +38 -0
- data/lib/app/models/concerns/aws_configuration.rb +65 -0
- data/lib/app/models/concerns/cdn_url.rb +7 -0
- data/lib/app/models/concerns/core_smtp_configuration.rb +65 -0
- data/lib/app/models/concerns/core_system_configuration.rb +18 -201
- data/lib/app/models/concerns/delayed_job_configuration.rb +24 -0
- data/lib/app/models/concerns/email_able.rb +6 -2
- data/lib/app/models/concerns/google_sso_configuration.rb +32 -0
- data/lib/app/models/concerns/search_able.rb +16 -0
- data/lib/app/models/concerns/server_process_able.rb +69 -0
- data/lib/app/models/concerns/slack_configuration.rb +38 -0
- data/lib/app/models/concerns/standard_model.rb +5 -2
- data/lib/app/models/concerns/switchboard_configuration.rb +43 -0
- data/lib/app/models/concerns/twilio_configuration.rb +37 -0
- data/lib/app/models/concerns/zendesk_configuration.rb +92 -0
- data/lib/app/models/{delayed_job.rb → delayed/backend/delayed_job.rb} +41 -0
- data/lib/app/models/delayed/jobs/metric.rb +61 -0
- data/lib/app/models/delayed/jobs/run.rb +40 -0
- data/lib/app/models/delayed/jobs/worker.rb +43 -0
- data/lib/app/models/delayed/plugins/time_keeper.rb +33 -0
- data/lib/app/models/delayed/worker.rb +24 -0
- data/lib/app/models/email_notification.rb +2 -1
- data/lib/app/models/email_template.rb +5 -6
- data/lib/app/models/notification.rb +12 -2
- data/lib/app/models/sms_notification.rb +9 -6
- data/lib/app/models/template.rb +12 -12
- data/lib/web47core/version.rb +1 -1
- data/lib/web47core.rb +33 -9
- metadata +114 -69
@@ -1,22 +1,24 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
#
|
4
|
+
# The System configuration. Various configuration items that can be updated/defined at run time
|
5
|
+
#
|
6
|
+
# Use of this class allows you to simply ask for the configuration parameter directly without
|
7
|
+
# first having to get an instance of it.
|
8
|
+
#
|
9
|
+
# SystemConfiguration.queue_impl #=> 'RedisQueue'
|
10
|
+
#
|
11
|
+
# This method only is allowed for accessors, you should NEVER set values on the SystemConfiguration
|
12
|
+
# unless you are updating via the Admin or Stack UI, or during testing to setup a specific configuration
|
13
|
+
# for that.
|
14
|
+
#
|
3
15
|
module CoreSystemConfiguration
|
4
16
|
extend ActiveSupport::Concern
|
5
|
-
|
6
|
-
# The System configuration. Various configuration items that can be updated/defined at run time
|
7
|
-
#
|
8
|
-
# Use of this class allows you to simply ask for the configuration parameter directly without
|
9
|
-
# first having to get an instance of it.
|
10
|
-
#
|
11
|
-
# SystemConfiguration.queue_impl #=> 'RedisQueue'
|
12
|
-
#
|
13
|
-
# This method only is allowed for accessors, you should NEVER set values on the SystemConfiguration
|
14
|
-
# unless you are updating via the Admin or Stack UI, or during testing to setup a specific configuration
|
15
|
-
# for that.
|
16
|
-
#
|
17
|
+
|
17
18
|
def self.included(base)
|
18
19
|
base.class_eval do
|
19
20
|
attr_accessor :configuration
|
21
|
+
|
20
22
|
#
|
21
23
|
# Fields
|
22
24
|
#
|
@@ -28,79 +30,31 @@ module CoreSystemConfiguration
|
|
28
30
|
field :short_cache, { type: Integer, default: 1 }
|
29
31
|
field :medium_cache, { type: Integer, default: 5 }
|
30
32
|
field :long_cache, { type: Integer, default: 15 }
|
31
|
-
# SMTP configuration
|
32
|
-
field :default_email, type: String, default: 'support@app47.com'
|
33
|
-
field :support_email, type: String, default: 'support@app47.com'
|
34
|
-
field :smtp_name, type: String
|
35
|
-
field :smtp_address, type: String
|
36
|
-
field :smtp_domain, type: String
|
37
|
-
field :smtp_port, type: Integer, default: 587
|
38
|
-
field :smtp_user_name, type: String
|
39
|
-
field :smtp_password, type: String
|
40
|
-
field :smtp_enable_starttls_auto, type: Boolean
|
41
|
-
field :mailgun_api_key, type: String
|
42
|
-
# Twillio support
|
43
|
-
field :twilio_account_id, type: String
|
44
|
-
field :twilio_auth_token, type: String
|
45
|
-
field :twilio_phone_number, type: String
|
46
33
|
# URLs
|
47
34
|
field :base_url, type: String
|
48
35
|
field :cdn_url, type: String
|
49
|
-
|
50
|
-
field :slack_api_url, type: String
|
51
|
-
field :slack_support_channel, type: String, default: 'support'
|
52
|
-
field :slack_sales_channel, type: String, default: 'sales'
|
36
|
+
field :asset_cdn_url, type: String
|
53
37
|
# Time Zone Support
|
54
38
|
field :default_time_zone, type: String, default: 'US/Eastern'
|
55
|
-
# AWS
|
56
|
-
field :aws_region, type: String
|
57
|
-
field :aws_access_key_id, type: String
|
58
|
-
field :aws_secret_access_key, type: String
|
59
|
-
field :aws_auto_scaling_group_name, type: String
|
60
|
-
# Zendesk
|
61
|
-
field :zendesk_token, type: String
|
62
|
-
field :zendesk_base_url, type: String, default: 'https://app47.zendesk.com'
|
63
|
-
field :zendesk_documentation_path, type: String, default: 'hc'
|
64
|
-
field :zendesk_support_path, type: String, default: 'hc/en-us/requests'
|
65
|
-
field :zendesk_updates_path, type: String, default: 'hc'
|
66
|
-
# Switchboard
|
67
|
-
field :switchboard_base_url, type: String, default: 'https://switchboard.app47.com'
|
68
|
-
field :switchboard_stack_id, type: String
|
69
|
-
field :switchboard_stack_api_token, type: String
|
70
|
-
field :switchboard_last_sync_at, type: Time
|
71
39
|
# TTLs
|
72
40
|
field :user_model_audit_log_ttl, type: Integer, default: 365
|
73
|
-
field :slack_notification_ttl, type: Integer, default: 5
|
74
|
-
field :email_notification_ttl, type: Integer, default: 180
|
75
41
|
#
|
76
42
|
# Validations
|
77
43
|
#
|
78
44
|
validates :environment, presence: true, uniqueness: true
|
79
|
-
validates :slack_support_channel, presence: true
|
80
|
-
validates :slack_sales_channel, presence: true
|
81
45
|
validates :default_time_zone, presence: true
|
82
|
-
validates :switchboard_base_url, url: true
|
83
|
-
validates :zendesk_base_url, url: true
|
84
46
|
end
|
85
47
|
base.extend ClassMethods
|
86
48
|
end
|
87
49
|
|
50
|
+
#
|
51
|
+
# Class methods for SystemConfiguration
|
52
|
+
#
|
88
53
|
module ClassMethods
|
89
54
|
def configuration
|
90
55
|
SystemConfiguration.find_or_create_by!(environment: Rails.env)
|
91
56
|
end
|
92
57
|
|
93
|
-
def smtp_configuration
|
94
|
-
output = {}
|
95
|
-
config = configuration
|
96
|
-
fields = %w[name address domain port user_name password enable_starttls_auto]
|
97
|
-
fields.each do |field|
|
98
|
-
field_name = "smtp_#{field}".to_sym
|
99
|
-
output[field.to_sym] = config.send(field_name)
|
100
|
-
end
|
101
|
-
output
|
102
|
-
end
|
103
|
-
|
104
58
|
#
|
105
59
|
# NOTE: Currently ignored Codacy issue: When using 'method_missing', fall back on 'super'
|
106
60
|
#
|
@@ -121,18 +75,6 @@ module CoreSystemConfiguration
|
|
121
75
|
end
|
122
76
|
end
|
123
77
|
|
124
|
-
#
|
125
|
-
# Make sure the password doesn't get blanked out on an update
|
126
|
-
#
|
127
|
-
def secure_fields
|
128
|
-
super + %i[smtp_password
|
129
|
-
aws_access_secret
|
130
|
-
mailgun_api_key
|
131
|
-
switchboard_stack_api_token
|
132
|
-
twilio_auth_token
|
133
|
-
zendesk_token]
|
134
|
-
end
|
135
|
-
|
136
78
|
#
|
137
79
|
# Cache times in minutes
|
138
80
|
#
|
@@ -147,129 +89,4 @@ module CoreSystemConfiguration
|
|
147
89
|
def long_cache_time
|
148
90
|
long_cache.minutes
|
149
91
|
end
|
150
|
-
|
151
|
-
#
|
152
|
-
# Determine if SMTP is configured
|
153
|
-
#
|
154
|
-
def smtp_configured?
|
155
|
-
smtp_name.present? && smtp_address.present? && smtp_domain.present?
|
156
|
-
end
|
157
|
-
|
158
|
-
#
|
159
|
-
# Determine if mailgun is configured
|
160
|
-
#
|
161
|
-
def mail_gun_configured?
|
162
|
-
smtp_configured? && mailgun_api_key.present?
|
163
|
-
end
|
164
|
-
|
165
|
-
#
|
166
|
-
# Determine if AWS is configured
|
167
|
-
#
|
168
|
-
def aws_configured?
|
169
|
-
[aws_region.present?, aws_access_key_id.present?, aws_secret_access_key.present?].all?
|
170
|
-
end
|
171
|
-
|
172
|
-
#
|
173
|
-
# Determine if auto scaling group is configured
|
174
|
-
#
|
175
|
-
def aws_auto_scaling_configured?
|
176
|
-
aws_configured? && aws_auto_scaling_group_name.present?
|
177
|
-
end
|
178
|
-
|
179
|
-
#
|
180
|
-
# Return the zendesk documentation URL
|
181
|
-
#
|
182
|
-
def zendesk_documentation_url(user = nil)
|
183
|
-
zendesk_url(forward_to: zendesk_documentation_path, user: user)
|
184
|
-
end
|
185
|
-
|
186
|
-
#
|
187
|
-
# Return the zendesk support URL
|
188
|
-
#
|
189
|
-
def zendesk_requests_url(user = nil)
|
190
|
-
zendesk_url(forward_to: zendesk_support_path, user: user)
|
191
|
-
end
|
192
|
-
|
193
|
-
#
|
194
|
-
# Return the zendesk support URL
|
195
|
-
#
|
196
|
-
def zendesk_new_request_url(user = nil)
|
197
|
-
zendesk_url(forward_to: "#{zendesk_support_path}/new", user: user)
|
198
|
-
end
|
199
|
-
|
200
|
-
#
|
201
|
-
# Return the zendesk update URL
|
202
|
-
#
|
203
|
-
def zendesk_updates_url(user = nil)
|
204
|
-
zendesk_url(forward_to: zendesk_updates_path, user: user)
|
205
|
-
end
|
206
|
-
|
207
|
-
#
|
208
|
-
# Generate a Zendesk URL
|
209
|
-
#
|
210
|
-
# If a user is passed in and Zendesk is configured then return a JWT enabled URL for
|
211
|
-
# SSO authentication to Zendesk.
|
212
|
-
#
|
213
|
-
def zendesk_url(forward_to: zendesk_documentation_path, user: nil)
|
214
|
-
config = SystemConfiguration.configuration
|
215
|
-
path = if config.zendesk_configured? && user.present?
|
216
|
-
time_now = Time.now.to_i
|
217
|
-
jti = "#{time_now}/#{rand(36 ** 64).to_s(36)}"
|
218
|
-
payload = { jwt: JWT.encode({ iat: time_now, # Seconds since epoch, determine when this token is stale
|
219
|
-
jti: jti, # Unique token identifier, helps prevent replay attacks
|
220
|
-
name: user.name,
|
221
|
-
email: user.email }, config.zendesk_token),
|
222
|
-
return_to: CGI.escape([config.zendesk_base_url, forward_to].join('/')) }
|
223
|
-
['access/jwt', payload.to_query].join('?')
|
224
|
-
else
|
225
|
-
forward_to
|
226
|
-
end
|
227
|
-
[config.zendesk_base_url, path].join('/')
|
228
|
-
end
|
229
|
-
|
230
|
-
#
|
231
|
-
# Is zendesk configured?
|
232
|
-
#
|
233
|
-
def zendesk_configured?
|
234
|
-
[zendesk_token.present?,
|
235
|
-
zendesk_base_url.present?,
|
236
|
-
zendesk_documentation_path.present?,
|
237
|
-
zendesk_support_path.present?].all?
|
238
|
-
end
|
239
|
-
|
240
|
-
#
|
241
|
-
# Public: Determine if switchboard is configured
|
242
|
-
#
|
243
|
-
# Examples
|
244
|
-
#
|
245
|
-
# switchboard_configured?
|
246
|
-
# # => true || false
|
247
|
-
#
|
248
|
-
def switchboard_configured?
|
249
|
-
[switchboard_base_url.present?, switchboard_stack_api_token.present?, switchboard_stack_id.present?].all?
|
250
|
-
end
|
251
|
-
|
252
|
-
#
|
253
|
-
# Determine if twillio is configured at a system configuration
|
254
|
-
#
|
255
|
-
# Examples
|
256
|
-
#
|
257
|
-
# switchboard_configured?
|
258
|
-
# # => true || false
|
259
|
-
#
|
260
|
-
def twilio_configured?
|
261
|
-
[twilio_account_id.present?, twilio_auth_token.present?, twilio_phone_number.present?].all?
|
262
|
-
end
|
263
|
-
|
264
|
-
#
|
265
|
-
# Determine if Slack is configured
|
266
|
-
#
|
267
|
-
# Examples
|
268
|
-
#
|
269
|
-
# switchboard_configured?
|
270
|
-
# # => true || false
|
271
|
-
#
|
272
|
-
def slack_configured?
|
273
|
-
slack_api_url.present?
|
274
|
-
end
|
275
92
|
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
#
|
4
|
+
# The Slack Configuration to be used inside of SystemConfiguration
|
5
|
+
#
|
6
|
+
module DelayedJobConfiguration
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
|
9
|
+
def self.included(base)
|
10
|
+
base.class_eval do
|
11
|
+
#
|
12
|
+
# Fields
|
13
|
+
#
|
14
|
+
field :delayed_job_max_allowed_method, type: String, default: 'max'
|
15
|
+
field :delayed_job_max_allowed_factor, type: Integer, default: 5
|
16
|
+
field :delayed_job_restart_orphaned, type: Boolean, default: false
|
17
|
+
#
|
18
|
+
# Validations
|
19
|
+
#
|
20
|
+
validates :delayed_job_max_allowed_method, inclusion: { in: %w[max min avg] }
|
21
|
+
validates :delayed_job_max_allowed_factor, numericality: { greater_than: 0 }
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -61,7 +61,7 @@ module EmailAble
|
|
61
61
|
#
|
62
62
|
# Reset the bounced email
|
63
63
|
#
|
64
|
-
def reset_bounce_status
|
64
|
+
def reset_bounce_status(current_member = nil)
|
65
65
|
return unless SystemConfiguration.mail_gun_configured? && email_bounced?
|
66
66
|
|
67
67
|
reset_url = "https://api.mailgun.net/v3/#{SystemConfiguration.smtp_domain}/bounces/#{CGI.escape(email)}"
|
@@ -69,7 +69,11 @@ module EmailAble
|
|
69
69
|
rescue RestClient::Exception => error
|
70
70
|
log_error "Unable to reset email bounce status: #{inspect}", error
|
71
71
|
ensure
|
72
|
-
|
72
|
+
if current_member.present?
|
73
|
+
update_and_log current_member, email_bounced_at: nil, email_bounce_reason: nil
|
74
|
+
else
|
75
|
+
set email_bounced_at: nil, email_bounce_reason: nil
|
76
|
+
end
|
73
77
|
end
|
74
78
|
|
75
79
|
#
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
#
|
4
|
+
# Google SSO Configuration
|
5
|
+
#
|
6
|
+
module GoogleSsoConfiguration
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
|
9
|
+
def self.included(base)
|
10
|
+
base.class_eval do
|
11
|
+
#
|
12
|
+
# Fields
|
13
|
+
#
|
14
|
+
field :google_client_id, type: String
|
15
|
+
field :google_client_secret, type: String
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
#
|
20
|
+
# Make sure the password doesn't get blanked out on an update
|
21
|
+
#
|
22
|
+
def secure_fields
|
23
|
+
super + %i[google_client_secret]
|
24
|
+
end
|
25
|
+
|
26
|
+
#
|
27
|
+
# Determine if AWS is configured
|
28
|
+
#
|
29
|
+
def google_sso_configured?
|
30
|
+
google_client_id.present? && google_client_secret.present?
|
31
|
+
end
|
32
|
+
end
|
@@ -39,6 +39,22 @@ module SearchAble
|
|
39
39
|
end
|
40
40
|
end
|
41
41
|
|
42
|
+
def method_missing(method, *args)
|
43
|
+
if method.to_s.start_with? 'sorted_'
|
44
|
+
send(method.to_s.sub(/^sorted_/, '')).asc(:sort_text)
|
45
|
+
else
|
46
|
+
super
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def respond_to_missing?(method_name, include_private = false)
|
51
|
+
super || method_name.to_s.start_with?('sorted_')
|
52
|
+
end
|
53
|
+
|
54
|
+
def respond_to?(method, include_private = false)
|
55
|
+
super || method.to_s.start_with?('sorted_')
|
56
|
+
end
|
57
|
+
|
42
58
|
#
|
43
59
|
# Place holder to call to allow for work to be done before we gather up search text fields
|
44
60
|
#
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
#
|
4
|
+
# Mixin for objects that act as servers
|
5
|
+
#
|
6
|
+
module ServerProcessAble
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
|
9
|
+
def self.included(base)
|
10
|
+
base.class_eval do
|
11
|
+
#
|
12
|
+
# Fields
|
13
|
+
#
|
14
|
+
field :host_name, type: String
|
15
|
+
field :pid, type: Integer
|
16
|
+
field :running, type: Boolean, default: false
|
17
|
+
field :last_check_in_at, type: Time
|
18
|
+
#
|
19
|
+
# Validations
|
20
|
+
#
|
21
|
+
validates :host_name, presence: true
|
22
|
+
validates :pid, presence: true
|
23
|
+
end
|
24
|
+
base.extend ClassMethods
|
25
|
+
end
|
26
|
+
|
27
|
+
#
|
28
|
+
# Methods for the concrete class this concern is attached too, ways to find or create or just find the
|
29
|
+
# associated server.
|
30
|
+
#
|
31
|
+
module ClassMethods
|
32
|
+
#
|
33
|
+
# Find a record for this server
|
34
|
+
#
|
35
|
+
def find_or_create_server
|
36
|
+
find_or_create_by!(host_name: Socket.gethostname, pid: Process.pid)
|
37
|
+
end
|
38
|
+
|
39
|
+
#
|
40
|
+
# Find a worker, return nil if not found
|
41
|
+
#
|
42
|
+
def find_server
|
43
|
+
find_by(host_name: Socket.gethostname, pid: Process.pid)
|
44
|
+
rescue StandardError
|
45
|
+
nil
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
#
|
50
|
+
# Perform a check in for the server
|
51
|
+
#
|
52
|
+
def check_in
|
53
|
+
set(last_check_in_at: Time.now.utc)
|
54
|
+
end
|
55
|
+
|
56
|
+
#
|
57
|
+
# Stop the worker
|
58
|
+
#
|
59
|
+
def stop
|
60
|
+
set(running: false, last_check_in_at: Time.now.utc)
|
61
|
+
end
|
62
|
+
|
63
|
+
#
|
64
|
+
# Start the worker
|
65
|
+
#
|
66
|
+
def start
|
67
|
+
set(running: true, last_check_in_at: Time.now.utc)
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
#
|
4
|
+
# The Slack Configuration to be used inside of SystemConfiguration
|
5
|
+
#
|
6
|
+
module SlackConfiguration
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
|
9
|
+
def self.included(base)
|
10
|
+
base.class_eval do
|
11
|
+
#
|
12
|
+
# Fields
|
13
|
+
#
|
14
|
+
field :slack_api_url, type: String
|
15
|
+
field :slack_support_channel, type: String, default: 'support'
|
16
|
+
field :slack_sales_channel, type: String, default: 'sales'
|
17
|
+
field :slack_notification_ttl, type: Integer, default: 5
|
18
|
+
#
|
19
|
+
# Validations
|
20
|
+
#
|
21
|
+
validates :slack_api_url, url: true, allow_blank: true
|
22
|
+
validates :slack_support_channel, presence: true
|
23
|
+
validates :slack_sales_channel, presence: true
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
#
|
28
|
+
# Determine if Slack is configured
|
29
|
+
#
|
30
|
+
# Examples
|
31
|
+
#
|
32
|
+
# slack_configured??
|
33
|
+
# # => true || false
|
34
|
+
#
|
35
|
+
def slack_configured?
|
36
|
+
[slack_api_url.present?, slack_support_channel.present?, slack_sales_channel.present?].all?
|
37
|
+
end
|
38
|
+
end
|
@@ -42,9 +42,12 @@ module StandardModel
|
|
42
42
|
# Used by calling 'model.without_callback(*.args, &block) do'
|
43
43
|
def without_callback(*args, &_block)
|
44
44
|
skip_callback(*args)
|
45
|
-
yield
|
46
|
-
ensure
|
45
|
+
block_result = yield
|
47
46
|
set_callback(*args)
|
47
|
+
block_result
|
48
|
+
rescue StandardError
|
49
|
+
set_callback(*args)
|
50
|
+
nil
|
48
51
|
end
|
49
52
|
|
50
53
|
#
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
#
|
4
|
+
# Switchboard configuration, both the fields and methods needed to determine configuration
|
5
|
+
#
|
6
|
+
module SwitchboardConfiguration
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
#
|
9
|
+
# Switchboard configuration
|
10
|
+
#
|
11
|
+
def self.included(base)
|
12
|
+
base.class_eval do
|
13
|
+
# Switchboard
|
14
|
+
field :switchboard_base_url, type: String, default: 'https://switchboard.app47.com'
|
15
|
+
field :switchboard_stack_id, type: String
|
16
|
+
field :switchboard_stack_api_token, type: String
|
17
|
+
field :switchboard_last_sync_at, type: Time
|
18
|
+
#
|
19
|
+
# Validations
|
20
|
+
#
|
21
|
+
validates :switchboard_base_url, url: true
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
#
|
26
|
+
# Make sure the password doesn't get blanked out on an update
|
27
|
+
#
|
28
|
+
def secure_fields
|
29
|
+
super + %i[switchboard_stack_api_token]
|
30
|
+
end
|
31
|
+
|
32
|
+
#
|
33
|
+
# Public: Determine if switchboard is configured
|
34
|
+
#
|
35
|
+
# Examples
|
36
|
+
#
|
37
|
+
# switchboard_configured?
|
38
|
+
# # => true || false
|
39
|
+
#
|
40
|
+
def switchboard_configured?
|
41
|
+
[switchboard_base_url.present?, switchboard_stack_api_token.present?, switchboard_stack_id.present?].all?
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
#
|
4
|
+
# Twilio configuration, both fields and methods to determine configuration
|
5
|
+
#
|
6
|
+
module TwilioConfiguration
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
#
|
9
|
+
# The Twilio Configuration
|
10
|
+
#
|
11
|
+
def self.included(base)
|
12
|
+
base.class_eval do
|
13
|
+
field :twilio_account_id, type: String
|
14
|
+
field :twilio_auth_token, type: String
|
15
|
+
field :twilio_phone_number, type: String
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
#
|
20
|
+
# Make sure the password doesn't get blanked out on an update
|
21
|
+
#
|
22
|
+
def secure_fields
|
23
|
+
super + %i[twilio_auth_token]
|
24
|
+
end
|
25
|
+
|
26
|
+
#
|
27
|
+
# Determine if twilio is configured at a system configuration
|
28
|
+
#
|
29
|
+
# Examples
|
30
|
+
#
|
31
|
+
# twilio_configured??
|
32
|
+
# # => true || false
|
33
|
+
#
|
34
|
+
def twilio_configured?
|
35
|
+
[twilio_account_id.present?, twilio_auth_token.present?, twilio_phone_number.present?].all?
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
#
|
4
|
+
# Add zendesk capability to an object to integrate via JWT with Zendesk. Most commonly this will be added
|
5
|
+
# to SystemConfiguration, however this can also be added to a different object (i.e., switchboard).
|
6
|
+
#
|
7
|
+
module ZendeskConfiguration
|
8
|
+
extend ActiveSupport::Concern
|
9
|
+
def self.included(base)
|
10
|
+
base.class_eval do
|
11
|
+
#
|
12
|
+
# Fields
|
13
|
+
#
|
14
|
+
field :zendesk_token, type: String
|
15
|
+
field :zendesk_base_url, type: String, default: 'https://app47.zendesk.com'
|
16
|
+
field :zendesk_documentation_path, type: String, default: 'hc'
|
17
|
+
field :zendesk_support_path, type: String, default: 'hc/en-us/requests'
|
18
|
+
field :zendesk_updates_path, type: String, default: 'hc'
|
19
|
+
#
|
20
|
+
# Validations
|
21
|
+
#
|
22
|
+
validates :zendesk_base_url, url: true
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
#
|
27
|
+
# Make sure the password doesn't get blanked out on an update
|
28
|
+
#
|
29
|
+
def secure_fields
|
30
|
+
super + %i[zendesk_token]
|
31
|
+
end
|
32
|
+
|
33
|
+
#
|
34
|
+
# Return the zendesk documentation URL
|
35
|
+
#
|
36
|
+
def zendesk_documentation_url(user = nil)
|
37
|
+
zendesk_url(forward_to: zendesk_documentation_path, user: user)
|
38
|
+
end
|
39
|
+
|
40
|
+
#
|
41
|
+
# Return the zendesk support URL
|
42
|
+
#
|
43
|
+
def zendesk_requests_url(user = nil)
|
44
|
+
zendesk_url(forward_to: zendesk_support_path, user: user)
|
45
|
+
end
|
46
|
+
|
47
|
+
#
|
48
|
+
# Return the zendesk support URL
|
49
|
+
#
|
50
|
+
def zendesk_new_request_url(user = nil)
|
51
|
+
zendesk_url(forward_to: "#{zendesk_support_path}/new", user: user)
|
52
|
+
end
|
53
|
+
|
54
|
+
#
|
55
|
+
# Return the zendesk update URL
|
56
|
+
#
|
57
|
+
def zendesk_updates_url(user = nil)
|
58
|
+
zendesk_url(forward_to: zendesk_updates_path, user: user)
|
59
|
+
end
|
60
|
+
|
61
|
+
#
|
62
|
+
# Generate a Zendesk URL
|
63
|
+
#
|
64
|
+
# If a user is passed in and Zendesk is configured then return a JWT enabled URL for
|
65
|
+
# SSO authentication to Zendesk.
|
66
|
+
#
|
67
|
+
def zendesk_url(forward_to: zendesk_documentation_path, user: nil)
|
68
|
+
path = if zendesk_configured? && user.present?
|
69
|
+
time_now = Time.now.to_i
|
70
|
+
jti = "#{time_now}/#{rand(36**64).to_s(36)}"
|
71
|
+
payload = { jwt: JWT.encode({ iat: time_now, # Seconds since epoch, determine when this token is stale
|
72
|
+
jti: jti, # Unique token identifier, helps prevent replay attacks
|
73
|
+
name: user.name,
|
74
|
+
email: user.email }, zendesk_token),
|
75
|
+
return_to: CGI.escape([zendesk_base_url, forward_to].join('/')) }
|
76
|
+
['access/jwt', payload.to_query].join('?')
|
77
|
+
else
|
78
|
+
forward_to
|
79
|
+
end
|
80
|
+
[zendesk_base_url, path].join('/')
|
81
|
+
end
|
82
|
+
|
83
|
+
#
|
84
|
+
# Is zendesk configured?
|
85
|
+
#
|
86
|
+
def zendesk_configured?
|
87
|
+
[zendesk_token.present?,
|
88
|
+
zendesk_base_url.present?,
|
89
|
+
zendesk_documentation_path.present?,
|
90
|
+
zendesk_support_path.present?].all?
|
91
|
+
end
|
92
|
+
end
|