brainzlab 0.1.2 → 0.1.3

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 (94) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +6 -21
  3. data/README.md +16 -2
  4. data/lib/brainzlab/beacon/client.rb +38 -40
  5. data/lib/brainzlab/beacon/provisioner.rb +1 -1
  6. data/lib/brainzlab/beacon.rb +15 -15
  7. data/lib/brainzlab/configuration.rb +92 -90
  8. data/lib/brainzlab/context.rb +2 -3
  9. data/lib/brainzlab/cortex/client.rb +29 -31
  10. data/lib/brainzlab/cortex/provisioner.rb +1 -1
  11. data/lib/brainzlab/cortex.rb +7 -11
  12. data/lib/brainzlab/dendrite/client.rb +42 -44
  13. data/lib/brainzlab/dendrite/provisioner.rb +1 -1
  14. data/lib/brainzlab/dendrite.rb +4 -4
  15. data/lib/brainzlab/devtools/data/collector.rb +22 -22
  16. data/lib/brainzlab/devtools/middleware/asset_server.rb +14 -14
  17. data/lib/brainzlab/devtools/middleware/database_handler.rb +52 -55
  18. data/lib/brainzlab/devtools/middleware/debug_panel.rb +19 -19
  19. data/lib/brainzlab/devtools/middleware/error_page.rb +45 -44
  20. data/lib/brainzlab/devtools/renderers/debug_panel_renderer.rb +39 -35
  21. data/lib/brainzlab/devtools/renderers/error_page_renderer.rb +13 -9
  22. data/lib/brainzlab/devtools.rb +11 -11
  23. data/lib/brainzlab/flux/buffer.rb +3 -3
  24. data/lib/brainzlab/flux/client.rb +14 -16
  25. data/lib/brainzlab/flux/provisioner.rb +13 -13
  26. data/lib/brainzlab/flux.rb +8 -8
  27. data/lib/brainzlab/instrumentation/action_mailer.rb +14 -13
  28. data/lib/brainzlab/instrumentation/active_record.rb +13 -15
  29. data/lib/brainzlab/instrumentation/aws.rb +43 -39
  30. data/lib/brainzlab/instrumentation/dalli.rb +20 -20
  31. data/lib/brainzlab/instrumentation/delayed_job.rb +27 -29
  32. data/lib/brainzlab/instrumentation/elasticsearch.rb +23 -24
  33. data/lib/brainzlab/instrumentation/excon.rb +27 -27
  34. data/lib/brainzlab/instrumentation/faraday.rb +3 -4
  35. data/lib/brainzlab/instrumentation/good_job.rb +28 -28
  36. data/lib/brainzlab/instrumentation/grape.rb +24 -24
  37. data/lib/brainzlab/instrumentation/graphql.rb +24 -23
  38. data/lib/brainzlab/instrumentation/httparty.rb +13 -14
  39. data/lib/brainzlab/instrumentation/mongodb.rb +7 -7
  40. data/lib/brainzlab/instrumentation/net_http.rb +6 -6
  41. data/lib/brainzlab/instrumentation/redis.rb +14 -21
  42. data/lib/brainzlab/instrumentation/resque.rb +23 -24
  43. data/lib/brainzlab/instrumentation/sidekiq.rb +29 -28
  44. data/lib/brainzlab/instrumentation/solid_queue.rb +37 -41
  45. data/lib/brainzlab/instrumentation/stripe.rb +36 -37
  46. data/lib/brainzlab/instrumentation/typhoeus.rb +19 -17
  47. data/lib/brainzlab/instrumentation.rb +20 -20
  48. data/lib/brainzlab/nerve/client.rb +38 -40
  49. data/lib/brainzlab/nerve/provisioner.rb +1 -1
  50. data/lib/brainzlab/nerve.rb +6 -6
  51. data/lib/brainzlab/pulse/client.rb +15 -11
  52. data/lib/brainzlab/pulse/instrumentation.rb +61 -57
  53. data/lib/brainzlab/pulse/propagation.rb +28 -28
  54. data/lib/brainzlab/pulse/provisioner.rb +12 -12
  55. data/lib/brainzlab/pulse/tracer.rb +3 -3
  56. data/lib/brainzlab/pulse.rb +13 -13
  57. data/lib/brainzlab/rails/log_formatter.rb +127 -121
  58. data/lib/brainzlab/rails/log_subscriber.rb +70 -76
  59. data/lib/brainzlab/rails/railtie.rb +66 -89
  60. data/lib/brainzlab/recall/buffer.rb +1 -1
  61. data/lib/brainzlab/recall/client.rb +14 -10
  62. data/lib/brainzlab/recall/logger.rb +16 -18
  63. data/lib/brainzlab/recall/provisioner.rb +16 -16
  64. data/lib/brainzlab/recall.rb +11 -13
  65. data/lib/brainzlab/reflex/breadcrumbs.rb +2 -2
  66. data/lib/brainzlab/reflex/client.rb +14 -10
  67. data/lib/brainzlab/reflex/provisioner.rb +12 -12
  68. data/lib/brainzlab/reflex.rb +29 -29
  69. data/lib/brainzlab/sentinel/client.rb +40 -42
  70. data/lib/brainzlab/sentinel/provisioner.rb +1 -1
  71. data/lib/brainzlab/sentinel.rb +5 -5
  72. data/lib/brainzlab/signal/client.rb +12 -14
  73. data/lib/brainzlab/signal/provisioner.rb +12 -12
  74. data/lib/brainzlab/signal.rb +7 -7
  75. data/lib/brainzlab/synapse/client.rb +42 -44
  76. data/lib/brainzlab/synapse/provisioner.rb +1 -1
  77. data/lib/brainzlab/synapse.rb +6 -6
  78. data/lib/brainzlab/utilities/circuit_breaker.rb +37 -41
  79. data/lib/brainzlab/utilities/health_check.rb +53 -55
  80. data/lib/brainzlab/utilities/log_formatter.rb +38 -40
  81. data/lib/brainzlab/utilities/rate_limiter.rb +5 -5
  82. data/lib/brainzlab/utilities.rb +4 -4
  83. data/lib/brainzlab/vault/cache.rb +1 -1
  84. data/lib/brainzlab/vault/client.rb +39 -41
  85. data/lib/brainzlab/vault/provisioner.rb +1 -1
  86. data/lib/brainzlab/vault.rb +19 -25
  87. data/lib/brainzlab/version.rb +1 -1
  88. data/lib/brainzlab/vision/client.rb +20 -20
  89. data/lib/brainzlab/vision/provisioner.rb +21 -21
  90. data/lib/brainzlab/vision.rb +17 -19
  91. data/lib/brainzlab-sdk.rb +1 -1
  92. data/lib/brainzlab.rb +22 -24
  93. data/lib/generators/brainzlab/install/install_generator.rb +29 -27
  94. metadata +1 -1
@@ -1,23 +1,23 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "net/http"
4
- require "json"
5
- require "uri"
6
- require "cgi"
3
+ require 'net/http'
4
+ require 'json'
5
+ require 'uri'
6
+ require 'cgi'
7
7
 
8
8
  module BrainzLab
9
9
  module Dendrite
10
10
  class Client
11
11
  def initialize(config)
12
12
  @config = config
13
- @base_url = config.dendrite_url || "https://dendrite.brainzlab.ai"
13
+ @base_url = config.dendrite_url || 'https://dendrite.brainzlab.ai'
14
14
  end
15
15
 
16
16
  # Connect a repository
17
- def connect_repository(url:, name: nil, branch: "main", **options)
17
+ def connect_repository(url:, name: nil, branch: 'main', **options)
18
18
  response = request(
19
19
  :post,
20
- "/api/v1/repositories",
20
+ '/api/v1/repositories',
21
21
  body: {
22
22
  url: url,
23
23
  name: name,
@@ -30,7 +30,7 @@ module BrainzLab
30
30
 
31
31
  JSON.parse(response.body, symbolize_names: true)
32
32
  rescue StandardError => e
33
- log_error("connect_repository", e)
33
+ log_error('connect_repository', e)
34
34
  nil
35
35
  end
36
36
 
@@ -39,7 +39,7 @@ module BrainzLab
39
39
  response = request(:post, "/api/v1/repositories/#{repo_id}/sync")
40
40
  response.is_a?(Net::HTTPSuccess) || response.is_a?(Net::HTTPAccepted)
41
41
  rescue StandardError => e
42
- log_error("sync_repository", e)
42
+ log_error('sync_repository', e)
43
43
  false
44
44
  end
45
45
 
@@ -51,20 +51,20 @@ module BrainzLab
51
51
 
52
52
  JSON.parse(response.body, symbolize_names: true)
53
53
  rescue StandardError => e
54
- log_error("get_repository", e)
54
+ log_error('get_repository', e)
55
55
  nil
56
56
  end
57
57
 
58
58
  # List repositories
59
59
  def list_repositories
60
- response = request(:get, "/api/v1/repositories")
60
+ response = request(:get, '/api/v1/repositories')
61
61
 
62
62
  return [] unless response.is_a?(Net::HTTPSuccess)
63
63
 
64
64
  data = JSON.parse(response.body, symbolize_names: true)
65
65
  data[:repositories] || []
66
66
  rescue StandardError => e
67
- log_error("list_repositories", e)
67
+ log_error('list_repositories', e)
68
68
  []
69
69
  end
70
70
 
@@ -76,7 +76,7 @@ module BrainzLab
76
76
 
77
77
  JSON.parse(response.body, symbolize_names: true)
78
78
  rescue StandardError => e
79
- log_error("get_wiki", e)
79
+ log_error('get_wiki', e)
80
80
  nil
81
81
  end
82
82
 
@@ -88,7 +88,7 @@ module BrainzLab
88
88
 
89
89
  JSON.parse(response.body, symbolize_names: true)
90
90
  rescue StandardError => e
91
- log_error("get_wiki_page", e)
91
+ log_error('get_wiki_page', e)
92
92
  nil
93
93
  end
94
94
 
@@ -96,7 +96,7 @@ module BrainzLab
96
96
  def search(repo_id, query, limit: 10)
97
97
  response = request(
98
98
  :get,
99
- "/api/v1/search",
99
+ '/api/v1/search',
100
100
  params: { repo_id: repo_id, q: query, limit: limit }
101
101
  )
102
102
 
@@ -105,7 +105,7 @@ module BrainzLab
105
105
  data = JSON.parse(response.body, symbolize_names: true)
106
106
  data[:results] || []
107
107
  rescue StandardError => e
108
- log_error("search", e)
108
+ log_error('search', e)
109
109
  []
110
110
  end
111
111
 
@@ -113,7 +113,7 @@ module BrainzLab
113
113
  def ask(repo_id, question, session_id: nil)
114
114
  response = request(
115
115
  :post,
116
- "/api/v1/chat",
116
+ '/api/v1/chat',
117
117
  body: {
118
118
  repo_id: repo_id,
119
119
  question: question,
@@ -125,7 +125,7 @@ module BrainzLab
125
125
 
126
126
  JSON.parse(response.body, symbolize_names: true)
127
127
  rescue StandardError => e
128
- log_error("ask", e)
128
+ log_error('ask', e)
129
129
  nil
130
130
  end
131
131
 
@@ -133,7 +133,7 @@ module BrainzLab
133
133
  def explain(repo_id, path, symbol: nil)
134
134
  response = request(
135
135
  :post,
136
- "/api/v1/explain",
136
+ '/api/v1/explain',
137
137
  body: {
138
138
  repo_id: repo_id,
139
139
  path: path,
@@ -145,7 +145,7 @@ module BrainzLab
145
145
 
146
146
  JSON.parse(response.body, symbolize_names: true)
147
147
  rescue StandardError => e
148
- log_error("explain", e)
148
+ log_error('explain', e)
149
149
  nil
150
150
  end
151
151
 
@@ -153,10 +153,10 @@ module BrainzLab
153
153
  def generate_diagram(repo_id, type:, scope: nil)
154
154
  response = request(
155
155
  :post,
156
- "/api/v1/diagrams",
156
+ '/api/v1/diagrams',
157
157
  body: {
158
158
  repo_id: repo_id,
159
- type: type, # class, er, sequence, architecture
159
+ type: type, # class, er, sequence, architecture
160
160
  scope: scope
161
161
  }
162
162
  )
@@ -165,21 +165,21 @@ module BrainzLab
165
165
 
166
166
  JSON.parse(response.body, symbolize_names: true)
167
167
  rescue StandardError => e
168
- log_error("generate_diagram", e)
168
+ log_error('generate_diagram', e)
169
169
  nil
170
170
  end
171
171
 
172
172
  def provision(project_id:, app_name:)
173
173
  response = request(
174
174
  :post,
175
- "/api/v1/projects/provision",
175
+ '/api/v1/projects/provision',
176
176
  body: { project_id: project_id, app_name: app_name },
177
177
  use_service_key: true
178
178
  )
179
179
 
180
180
  response.is_a?(Net::HTTPSuccess) || response.is_a?(Net::HTTPCreated)
181
181
  rescue StandardError => e
182
- log_error("provision", e)
182
+ log_error('provision', e)
183
183
  false
184
184
  end
185
185
 
@@ -188,34 +188,32 @@ module BrainzLab
188
188
  def request(method, path, headers: {}, body: nil, params: nil, use_service_key: false)
189
189
  uri = URI.parse("#{@base_url}#{path}")
190
190
 
191
- if params
192
- uri.query = URI.encode_www_form(params)
193
- end
191
+ uri.query = URI.encode_www_form(params) if params
194
192
 
195
193
  http = Net::HTTP.new(uri.host, uri.port)
196
- http.use_ssl = uri.scheme == "https"
194
+ http.use_ssl = uri.scheme == 'https'
197
195
  http.open_timeout = 10
198
- http.read_timeout = 60 # Longer timeout for AI operations
196
+ http.read_timeout = 60 # Longer timeout for AI operations
199
197
 
200
198
  request = case method
201
- when :get
202
- Net::HTTP::Get.new(uri)
203
- when :post
204
- Net::HTTP::Post.new(uri)
205
- when :put
206
- Net::HTTP::Put.new(uri)
207
- when :delete
208
- Net::HTTP::Delete.new(uri)
209
- end
210
-
211
- request["Content-Type"] = "application/json"
212
- request["Accept"] = "application/json"
199
+ when :get
200
+ Net::HTTP::Get.new(uri)
201
+ when :post
202
+ Net::HTTP::Post.new(uri)
203
+ when :put
204
+ Net::HTTP::Put.new(uri)
205
+ when :delete
206
+ Net::HTTP::Delete.new(uri)
207
+ end
208
+
209
+ request['Content-Type'] = 'application/json'
210
+ request['Accept'] = 'application/json'
213
211
 
214
212
  if use_service_key
215
- request["X-Service-Key"] = @config.dendrite_master_key || @config.secret_key
213
+ request['X-Service-Key'] = @config.dendrite_master_key || @config.secret_key
216
214
  else
217
215
  auth_key = @config.dendrite_api_key || @config.secret_key
218
- request["Authorization"] = "Bearer #{auth_key}" if auth_key
216
+ request['Authorization'] = "Bearer #{auth_key}" if auth_key
219
217
  end
220
218
 
221
219
  headers.each { |k, v| request[k] = v }
@@ -37,7 +37,7 @@ module BrainzLab
37
37
  end
38
38
 
39
39
  def detect_project_id
40
- ENV["BRAINZLAB_PROJECT_ID"]
40
+ ENV.fetch('BRAINZLAB_PROJECT_ID', nil)
41
41
  end
42
42
  end
43
43
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "dendrite/client"
4
- require_relative "dendrite/provisioner"
3
+ require_relative 'dendrite/client'
4
+ require_relative 'dendrite/provisioner'
5
5
 
6
6
  module BrainzLab
7
7
  module Dendrite
@@ -19,13 +19,13 @@ module BrainzLab
19
19
  # branch: "main"
20
20
  # )
21
21
  #
22
- def connect(url, name: nil, branch: "main", **options)
22
+ def connect(url, name: nil, branch: 'main', **)
23
23
  return nil unless enabled?
24
24
 
25
25
  ensure_provisioned!
26
26
  return nil unless BrainzLab.configuration.dendrite_valid?
27
27
 
28
- client.connect_repository(url: url, name: name, branch: branch, **options)
28
+ client.connect_repository(url: url, name: name, branch: branch, **)
29
29
  end
30
30
 
31
31
  # Trigger documentation sync for a repository
@@ -50,7 +50,7 @@ module BrainzLab
50
50
  logs: data[:logs] || [],
51
51
  memory: build_memory_data(data),
52
52
  user: context&.user,
53
- breadcrumbs: context&.breadcrumbs&.to_a || []
53
+ breadcrumbs: context&.breadcrumbs.to_a
54
54
  }
55
55
  end
56
56
 
@@ -98,15 +98,15 @@ module BrainzLab
98
98
 
99
99
  def build_request_data(data, context)
100
100
  env = data[:env] || {}
101
- request = env["action_dispatch.request"] || (defined?(ActionDispatch::Request) ? ActionDispatch::Request.new(env) : nil)
101
+ env['action_dispatch.request'] || (defined?(ActionDispatch::Request) ? ActionDispatch::Request.new(env) : nil)
102
102
 
103
103
  {
104
- method: context&.request_method || env["REQUEST_METHOD"],
105
- path: context&.request_path || env["PATH_INFO"],
106
- url: context&.request_url || env["REQUEST_URI"],
104
+ method: context&.request_method || env['REQUEST_METHOD'],
105
+ path: context&.request_path || env['PATH_INFO'],
106
+ url: context&.request_url || env['REQUEST_URI'],
107
107
  params: context&.request_params || {},
108
108
  headers: extract_headers(env),
109
- request_id: context&.request_id || env["action_dispatch.request_id"]
109
+ request_id: context&.request_id || env['action_dispatch.request_id']
110
110
  }
111
111
  end
112
112
 
@@ -149,8 +149,8 @@ module BrainzLab
149
149
  def extract_headers(env)
150
150
  headers = {}
151
151
  env.each do |key, value|
152
- if key.start_with?("HTTP_")
153
- header_name = key.sub("HTTP_", "").split("_").map(&:capitalize).join("-")
152
+ if key.start_with?('HTTP_')
153
+ header_name = key.sub('HTTP_', '').split('_').map(&:capitalize).join('-')
154
154
  headers[header_name] = value
155
155
  end
156
156
  end
@@ -160,23 +160,23 @@ module BrainzLab
160
160
  def subscribe_to_events
161
161
  return unless defined?(ActiveSupport::Notifications)
162
162
 
163
- @sql_subscriber = ActiveSupport::Notifications.subscribe("sql.active_record") do |*args|
163
+ @sql_subscriber = ActiveSupport::Notifications.subscribe('sql.active_record') do |*args|
164
164
  event = ActiveSupport::Notifications::Event.new(*args)
165
- next if event.payload[:name] == "SCHEMA"
166
- next if event.payload[:sql]&.start_with?("PRAGMA")
165
+ next if event.payload[:name] == 'SCHEMA'
166
+ next if event.payload[:sql]&.start_with?('PRAGMA')
167
167
 
168
168
  add_sql_query(
169
169
  name: event.payload[:name],
170
170
  duration: event.duration,
171
171
  sql: event.payload[:sql],
172
- cached: event.payload[:cached] || event.payload[:name] == "CACHE",
172
+ cached: event.payload[:cached] || event.payload[:name] == 'CACHE',
173
173
  source: extract_source(caller)
174
174
  )
175
175
  end
176
176
 
177
177
  @view_subscriber = ActiveSupport::Notifications.subscribe(/render_.+\.action_view/) do |*args|
178
178
  event = ActiveSupport::Notifications::Event.new(*args)
179
- type = event.name.include?("partial") ? :partial : :template
179
+ type = event.name.include?('partial') ? :partial : :template
180
180
 
181
181
  add_view(
182
182
  type: type,
@@ -214,11 +214,11 @@ module BrainzLab
214
214
  def normalize_sql(sql)
215
215
  return nil unless sql
216
216
 
217
- sql.gsub(/\b\d+\b/, "?")
218
- .gsub(/'[^']*'/, "?")
219
- .gsub(/\$\d+/, "?")
220
- .gsub(%r{/\*.*?\*/}, "")
221
- .gsub(/\s+/, " ")
217
+ sql.gsub(/\b\d+\b/, '?')
218
+ .gsub(/'[^']*'/, '?')
219
+ .gsub(/\$\d+/, '?')
220
+ .gsub(%r{/\*.*?\*/}, '')
221
+ .gsub(/\s+/, ' ')
222
222
  .strip
223
223
  end
224
224
 
@@ -230,11 +230,11 @@ module BrainzLab
230
230
 
231
231
  def extract_source(backtrace)
232
232
  backtrace.each do |line|
233
- next if line.include?("/brainzlab")
234
- next if line.include?("/gems/")
235
- next if line.include?("/ruby/")
233
+ next if line.include?('/brainzlab')
234
+ next if line.include?('/gems/')
235
+ next if line.include?('/ruby/')
236
236
 
237
- if line.include?("/app/")
237
+ if line.include?('/app/')
238
238
  match = line.match(%r{(app/[^:]+:\d+)})
239
239
  return match[1] if match
240
240
  end
@@ -5,11 +5,11 @@ module BrainzLab
5
5
  module Middleware
6
6
  class AssetServer
7
7
  MIME_TYPES = {
8
- ".css" => "text/css; charset=utf-8",
9
- ".js" => "application/javascript; charset=utf-8",
10
- ".svg" => "image/svg+xml",
11
- ".png" => "image/png",
12
- ".woff2" => "font/woff2"
8
+ '.css' => 'text/css; charset=utf-8',
9
+ '.js' => 'application/javascript; charset=utf-8',
10
+ '.svg' => 'image/svg+xml',
11
+ '.png' => 'image/png',
12
+ '.woff2' => 'font/woff2'
13
13
  }.freeze
14
14
 
15
15
  def initialize(app)
@@ -19,11 +19,11 @@ module BrainzLab
19
19
  def call(env)
20
20
  return @app.call(env) unless DevTools.enabled?
21
21
 
22
- path = env["PATH_INFO"]
22
+ path = env['PATH_INFO']
23
23
  asset_prefix = DevTools.asset_path
24
24
 
25
25
  if path.start_with?("#{asset_prefix}/")
26
- serve_asset(path.sub("#{asset_prefix}/", ""))
26
+ serve_asset(path.sub("#{asset_prefix}/", ''))
27
27
  else
28
28
  @app.call(env)
29
29
  end
@@ -33,29 +33,29 @@ module BrainzLab
33
33
 
34
34
  def serve_asset(relative_path)
35
35
  # Prevent directory traversal
36
- return not_found if relative_path.include?("..")
36
+ return not_found if relative_path.include?('..')
37
37
 
38
38
  file_path = File.join(DevTools::ASSETS_PATH, relative_path)
39
39
  return not_found unless File.exist?(file_path)
40
40
 
41
41
  ext = File.extname(relative_path)
42
- content_type = MIME_TYPES[ext] || "application/octet-stream"
42
+ content_type = MIME_TYPES[ext] || 'application/octet-stream'
43
43
  content = File.read(file_path)
44
44
 
45
45
  [
46
46
  200,
47
47
  {
48
- "Content-Type" => content_type,
49
- "Content-Length" => content.bytesize.to_s,
50
- "Cache-Control" => "public, max-age=31536000",
51
- "X-Content-Type-Options" => "nosniff"
48
+ 'Content-Type' => content_type,
49
+ 'Content-Length' => content.bytesize.to_s,
50
+ 'Cache-Control' => 'public, max-age=31536000',
51
+ 'X-Content-Type-Options' => 'nosniff'
52
52
  },
53
53
  [content]
54
54
  ]
55
55
  end
56
56
 
57
57
  def not_found
58
- [404, { "Content-Type" => "text/plain" }, ["Not Found"]]
58
+ [404, { 'Content-Type' => 'text/plain' }, ['Not Found']]
59
59
  end
60
60
  end
61
61
  end
@@ -4,7 +4,7 @@ module BrainzLab
4
4
  module DevTools
5
5
  module Middleware
6
6
  class DatabaseHandler
7
- ENDPOINT = "/_brainzlab/devtools/database"
7
+ ENDPOINT = '/_brainzlab/devtools/database'
8
8
 
9
9
  def initialize(app)
10
10
  @app = app
@@ -22,67 +22,65 @@ module BrainzLab
22
22
  return false unless DevTools.enabled?
23
23
  return false unless DevTools.allowed_environment?
24
24
  return false unless DevTools.allowed_ip?(extract_ip(env))
25
- return false unless env["PATH_INFO"] == ENDPOINT
26
- return false unless env["REQUEST_METHOD"] == "POST"
25
+ return false unless env['PATH_INFO'] == ENDPOINT
26
+ return false unless env['REQUEST_METHOD'] == 'POST'
27
27
 
28
28
  true
29
29
  end
30
30
 
31
31
  def extract_ip(env)
32
- forwarded = env["HTTP_X_FORWARDED_FOR"]
33
- return forwarded.split(",").first.strip if forwarded
32
+ forwarded = env['HTTP_X_FORWARDED_FOR']
33
+ return forwarded.split(',').first.strip if forwarded
34
34
 
35
- env["REMOTE_ADDR"]
35
+ env['REMOTE_ADDR']
36
36
  end
37
37
 
38
38
  def handle_database_request(env)
39
- begin
40
- body = env["rack.input"].read
41
- env["rack.input"].rewind
42
- params = JSON.parse(body)
43
- action = params["action"]
44
-
45
- result = case action
46
- when "migrate"
47
- run_migrations
48
- when "status"
49
- migration_status
50
- when "create"
51
- create_database
52
- when "rollback"
53
- rollback_migration
54
- else
55
- { success: false, output: "Unknown action: #{action}" }
56
- end
57
-
58
- json_response(result)
59
- rescue => e
60
- json_response({ success: false, output: "Error: #{e.message}\n\n#{e.backtrace&.first(10)&.join("\n")}" })
61
- end
39
+ body = env['rack.input'].read
40
+ env['rack.input'].rewind
41
+ params = JSON.parse(body)
42
+ action = params['action']
43
+
44
+ result = case action
45
+ when 'migrate'
46
+ run_migrations
47
+ when 'status'
48
+ migration_status
49
+ when 'create'
50
+ create_database
51
+ when 'rollback'
52
+ rollback_migration
53
+ else
54
+ { success: false, output: "Unknown action: #{action}" }
55
+ end
56
+
57
+ json_response(result)
58
+ rescue StandardError => e
59
+ json_response({ success: false, output: "Error: #{e.message}\n\n#{e.backtrace&.first(10)&.join("\n")}" })
62
60
  end
63
61
 
64
62
  def run_migrations
65
- return not_available("Rails") unless defined?(Rails)
63
+ return not_available('Rails') unless defined?(::Rails)
66
64
 
67
65
  output = capture_output do
68
66
  ActiveRecord::MigrationContext.new(
69
- Rails.root.join("db/migrate"),
67
+ ::Rails.root.join('db/migrate'),
70
68
  ActiveRecord::SchemaMigration
71
69
  ).migrate
72
70
  end
73
71
 
74
- { success: true, output: output.presence || "All migrations completed successfully!" }
75
- rescue => e
72
+ { success: true, output: output.presence || 'All migrations completed successfully!' }
73
+ rescue StandardError => e
76
74
  { success: false, output: "Migration failed:\n#{e.message}\n\n#{e.backtrace&.first(10)&.join("\n")}" }
77
75
  end
78
76
 
79
77
  def migration_status
80
- return not_available("Rails") unless defined?(Rails)
78
+ return not_available('Rails') unless defined?(::Rails)
81
79
 
82
80
  output = StringIO.new
83
81
 
84
82
  context = ActiveRecord::MigrationContext.new(
85
- Rails.root.join("db/migrate"),
83
+ ::Rails.root.join('db/migrate'),
86
84
  ActiveRecord::SchemaMigration
87
85
  )
88
86
 
@@ -90,55 +88,54 @@ module BrainzLab
90
88
  migrations = context.migrations
91
89
 
92
90
  output.puts "database: #{ActiveRecord::Base.connection_db_config.database}"
93
- output.puts ""
94
- output.puts " Status Migration ID Migration Name"
95
- output.puts "-" * 60
91
+ output.puts ''
92
+ output.puts ' Status Migration ID Migration Name'
93
+ output.puts '-' * 60
96
94
 
97
95
  migrations.each do |migration|
98
- status = migrated.include?(migration.version) ? " up" : " down"
96
+ status = migrated.include?(migration.version) ? ' up' : ' down'
99
97
  output.puts " #{status} #{migration.version} #{migration.name}"
100
98
  end
101
99
 
102
100
  pending = migrations.reject { |m| migrated.include?(m.version) }
101
+ output.puts ''
103
102
  if pending.any?
104
- output.puts ""
105
103
  output.puts "#{pending.count} pending migration(s)"
106
104
  else
107
- output.puts ""
108
- output.puts "All migrations are up to date!"
105
+ output.puts 'All migrations are up to date!'
109
106
  end
110
107
 
111
108
  { success: true, output: output.string }
112
- rescue => e
109
+ rescue StandardError => e
113
110
  { success: false, output: "Failed to check status:\n#{e.message}" }
114
111
  end
115
112
 
116
113
  def create_database
117
- return not_available("Rails") unless defined?(Rails)
114
+ return not_available('Rails') unless defined?(::Rails)
118
115
 
119
116
  output = capture_output do
120
117
  ActiveRecord::Tasks::DatabaseTasks.create_current
121
118
  end
122
119
 
123
- { success: true, output: output.presence || "Database created successfully!" }
120
+ { success: true, output: output.presence || 'Database created successfully!' }
124
121
  rescue ActiveRecord::DatabaseAlreadyExists
125
- { success: true, output: "Database already exists." }
126
- rescue => e
122
+ { success: true, output: 'Database already exists.' }
123
+ rescue StandardError => e
127
124
  { success: false, output: "Failed to create database:\n#{e.message}" }
128
125
  end
129
126
 
130
127
  def rollback_migration
131
- return not_available("Rails") unless defined?(Rails)
128
+ return not_available('Rails') unless defined?(::Rails)
132
129
 
133
130
  output = capture_output do
134
131
  ActiveRecord::MigrationContext.new(
135
- Rails.root.join("db/migrate"),
132
+ ::Rails.root.join('db/migrate'),
136
133
  ActiveRecord::SchemaMigration
137
134
  ).rollback
138
135
  end
139
136
 
140
- { success: true, output: output.presence || "Rollback completed!" }
141
- rescue => e
137
+ { success: true, output: output.presence || 'Rollback completed!' }
138
+ rescue StandardError => e
142
139
  { success: false, output: "Rollback failed:\n#{e.message}" }
143
140
  end
144
141
 
@@ -166,10 +163,10 @@ module BrainzLab
166
163
  [
167
164
  200,
168
165
  {
169
- "Content-Type" => "application/json; charset=utf-8",
170
- "Content-Length" => body.bytesize.to_s,
171
- "Cache-Control" => "no-store",
172
- "X-Content-Type-Options" => "nosniff"
166
+ 'Content-Type' => 'application/json; charset=utf-8',
167
+ 'Content-Length' => body.bytesize.to_s,
168
+ 'Cache-Control' => 'no-store',
169
+ 'X-Content-Type-Options' => 'nosniff'
173
170
  },
174
171
  [body]
175
172
  ]