Class: ActiveCanvas::Setting

Inherits:
ApplicationRecord show all
Defined in:
app/models/active_canvas/setting.rb

Constant Summary collapse

ENCRYPTED_KEYS =

Keys that should be encrypted in the database

%w[
  ai_openai_api_key
  ai_anthropic_api_key
  ai_openrouter_api_key
].freeze
CSS_FRAMEWORKS =
{
  "tailwind" => {
    name: "Tailwind CSS",
    url: "https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4",
    type: :script
  },
  "bootstrap5" => {
    name: "Bootstrap 5",
    url: "https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css",
    type: :stylesheet
  },
  "custom" => {
    name: "Custom CSS (no framework)",
    url: nil,
    type: nil
  }
}.freeze
DEFAULT_TAILWIND_CONFIG =

Tailwind Configuration

{
  theme: {
    extend: {
      colors: {},
      fontFamily: {}
    }
  }
}.freeze

Class Method Summary collapse

Class Method Details

.ai_anthropic_api_keyObject



193
194
195
# File 'app/models/active_canvas/setting.rb', line 193

def ai_anthropic_api_key
  get("ai_anthropic_api_key")
end

.ai_anthropic_api_key=(key) ⇒ Object



197
198
199
# File 'app/models/active_canvas/setting.rb', line 197

def ai_anthropic_api_key=(key)
  set("ai_anthropic_api_key", key.presence)
end

.ai_connection_modeObject

AI Connection Mode



235
236
237
# File 'app/models/active_canvas/setting.rb', line 235

def ai_connection_mode
  get("ai_connection_mode") || "server"
end

.ai_connection_mode=(value) ⇒ Object



239
240
241
# File 'app/models/active_canvas/setting.rb', line 239

def ai_connection_mode=(value)
  set("ai_connection_mode", value.presence || "server")
end

.ai_default_image_modelObject



218
219
220
# File 'app/models/active_canvas/setting.rb', line 218

def ai_default_image_model
  get("ai_default_image_model") || "dall-e-3"
end

.ai_default_image_model=(model) ⇒ Object



222
223
224
# File 'app/models/active_canvas/setting.rb', line 222

def ai_default_image_model=(model)
  set("ai_default_image_model", model.presence)
end

.ai_default_text_modelObject

AI Default Models



210
211
212
# File 'app/models/active_canvas/setting.rb', line 210

def ai_default_text_model
  get("ai_default_text_model") || "gpt-4o-mini"
end

.ai_default_text_model=(model) ⇒ Object



214
215
216
# File 'app/models/active_canvas/setting.rb', line 214

def ai_default_text_model=(model)
  set("ai_default_text_model", model.presence)
end

.ai_default_vision_modelObject



226
227
228
# File 'app/models/active_canvas/setting.rb', line 226

def ai_default_vision_model
  get("ai_default_vision_model") || "gpt-4o"
end

.ai_default_vision_model=(model) ⇒ Object



230
231
232
# File 'app/models/active_canvas/setting.rb', line 230

def ai_default_vision_model=(model)
  set("ai_default_vision_model", model.presence)
end

.ai_image_enabled=(enabled) ⇒ Object



256
257
258
# File 'app/models/active_canvas/setting.rb', line 256

def ai_image_enabled=(enabled)
  set("ai_image_enabled", enabled.to_s)
end

.ai_image_enabled?Boolean

Returns:

  • (Boolean)


252
253
254
# File 'app/models/active_canvas/setting.rb', line 252

def ai_image_enabled?
  get("ai_image_enabled") != "false"
end

.ai_openai_api_keyObject

AI API Keys



185
186
187
# File 'app/models/active_canvas/setting.rb', line 185

def ai_openai_api_key
  get("ai_openai_api_key")
end

.ai_openai_api_key=(key) ⇒ Object



189
190
191
# File 'app/models/active_canvas/setting.rb', line 189

def ai_openai_api_key=(key)
  set("ai_openai_api_key", key.presence)
end

.ai_openrouter_api_keyObject



201
202
203
# File 'app/models/active_canvas/setting.rb', line 201

def ai_openrouter_api_key
  get("ai_openrouter_api_key")
end

.ai_openrouter_api_key=(key) ⇒ Object



205
206
207
# File 'app/models/active_canvas/setting.rb', line 205

def ai_openrouter_api_key=(key)
  set("ai_openrouter_api_key", key.presence)
end

.ai_screenshot_enabled=(enabled) ⇒ Object



264
265
266
# File 'app/models/active_canvas/setting.rb', line 264

def ai_screenshot_enabled=(enabled)
  set("ai_screenshot_enabled", enabled.to_s)
end

.ai_screenshot_enabled?Boolean

Returns:

  • (Boolean)


260
261
262
# File 'app/models/active_canvas/setting.rb', line 260

def ai_screenshot_enabled?
  get("ai_screenshot_enabled") != "false"
end

.ai_text_enabled=(enabled) ⇒ Object



248
249
250
# File 'app/models/active_canvas/setting.rb', line 248

def ai_text_enabled=(enabled)
  set("ai_text_enabled", enabled.to_s)
end

.ai_text_enabled?Boolean

AI Feature Toggles

Returns:

  • (Boolean)


244
245
246
# File 'app/models/active_canvas/setting.rb', line 244

def ai_text_enabled?
  get("ai_text_enabled") != "false"
end

.api_key_configured?(key) ⇒ Boolean

Check if an API key is configured (without revealing the value)

Returns:

  • (Boolean)


74
75
76
77
78
79
# File 'app/models/active_canvas/setting.rb', line 74

def api_key_configured?(key)
  setting = find_by(key: key)
  return false unless setting

  setting.encrypted_value.present? || setting.value.present?
end

.css_frameworkObject



148
149
150
# File 'app/models/active_canvas/setting.rb', line 148

def css_framework
  get("css_framework") || ActiveCanvas.config.css_framework.to_s
end

.css_framework=(framework) ⇒ Object



152
153
154
# File 'app/models/active_canvas/setting.rb', line 152

def css_framework=(framework)
  set("css_framework", framework.presence)
end

.css_framework_configObject



156
157
158
# File 'app/models/active_canvas/setting.rb', line 156

def css_framework_config
  CSS_FRAMEWORKS[css_framework] || CSS_FRAMEWORKS["custom"]
end

.css_framework_typeObject



164
165
166
# File 'app/models/active_canvas/setting.rb', line 164

def css_framework_type
  css_framework_config[:type]
end

.css_framework_urlObject



160
161
162
# File 'app/models/active_canvas/setting.rb', line 160

def css_framework_url
  css_framework_config[:url]
end

.get(key) ⇒ Object



31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# File 'app/models/active_canvas/setting.rb', line 31

def get(key)
  key = key.to_s
  cache = Current.settings_cache
  return cache[key] if cache.key?(key)

  setting = find_by(key: key)
  value = if setting.nil?
    nil
  elsif ENCRYPTED_KEYS.include?(key) && setting.encrypted_value.present?
    decrypt_value(setting.encrypted_value)
  else
    setting.value
  end

  cache[key] = value
end

.global_cssObject



168
169
170
# File 'app/models/active_canvas/setting.rb', line 168

def global_css
  get("global_css") || ""
end

.global_css=(css) ⇒ Object



172
173
174
# File 'app/models/active_canvas/setting.rb', line 172

def global_css=(css)
  set("global_css", css)
end

.global_jsObject



176
177
178
# File 'app/models/active_canvas/setting.rb', line 176

def global_js
  get("global_js") || ""
end

.global_js=(js) ⇒ Object



180
181
182
# File 'app/models/active_canvas/setting.rb', line 180

def global_js=(js)
  set("global_js", js)
end

.homepageObject



141
142
143
144
145
146
# File 'app/models/active_canvas/setting.rb', line 141

def homepage
  page_id = homepage_page_id
  return nil unless page_id&.positive?

  Page.published.find_by(id: page_id)
end

.homepage_page_idObject



133
134
135
# File 'app/models/active_canvas/setting.rb', line 133

def homepage_page_id
  get("homepage_page_id")&.to_i
end

.homepage_page_id=(page_id) ⇒ Object



137
138
139
# File 'app/models/active_canvas/setting.rb', line 137

def homepage_page_id=(page_id)
  set("homepage_page_id", page_id.presence)
end

.masked_api_key(key) ⇒ Object

Get a masked version of the API key for display (last 4 chars only)



82
83
84
85
86
87
# File 'app/models/active_canvas/setting.rb', line 82

def masked_api_key(key)
  value = get(key)
  return nil if value.blank?

  mask_value(value)
end

.set(key, value) ⇒ Object



48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# File 'app/models/active_canvas/setting.rb', line 48

def set(key, value)
  retried = false
  begin
    setting = find_or_initialize_by(key: key)

    if ENCRYPTED_KEYS.include?(key.to_s) && value.present?
      setting.encrypted_value = encrypt_value(value)
      setting.value = nil # Clear plain text value
    else
      setting.value = value
      setting.encrypted_value = nil if ENCRYPTED_KEYS.include?(key.to_s)
    end

    setting.save!
    Current.settings_cache[key.to_s] = value
    value
  rescue ActiveRecord::RecordNotUnique
    # Another process inserted the same key between find_or_initialize_by
    # and save!. Reload and retry once; the second pass will UPDATE.
    raise if retried
    retried = true
    retry
  end
end

.tailwind_compiled_mode?Boolean

Returns:

  • (Boolean)


278
279
280
# File 'app/models/active_canvas/setting.rb', line 278

def tailwind_compiled_mode?
  css_framework == "tailwind" && ActiveCanvas::TailwindCompiler.available?
end

.tailwind_configObject



282
283
284
285
286
287
288
289
# File 'app/models/active_canvas/setting.rb', line 282

def tailwind_config
  raw = get("tailwind_config")
  return DEFAULT_TAILWIND_CONFIG.deep_dup if raw.blank?

  JSON.parse(raw).deep_symbolize_keys
rescue JSON::ParserError
  DEFAULT_TAILWIND_CONFIG.deep_dup
end

.tailwind_config=(config) ⇒ Object



291
292
293
294
295
296
297
298
299
300
301
# File 'app/models/active_canvas/setting.rb', line 291

def tailwind_config=(config)
  value = case config
  when String
            config
  when Hash
            config.to_json
  else
            config.to_s
  end
  set("tailwind_config", value)
end

.tailwind_config_jsObject



303
304
305
# File 'app/models/active_canvas/setting.rb', line 303

def tailwind_config_js
  tailwind_config.to_json
end