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