Add deterministic API key for uptime monitoring (#834)

* Preserve existing demo data by default

Add SKIP_CLEAR environment variable to demo_data rake tasks.
Defaults to true (preserving existing data). Set SKIP_CLEAR=0
to wipe data before generating new demo data.

https://claude.ai/code/session_01GcoMc2SH3czPrbeGkHbmpE

* Add deterministic instatus.com API key for demo data

Create a read-only API key named "instatus.com" with a fixed value
when generating demo data. This allows uptime monitoring tools to
use a hardcoded API key that doesn't change between demo data runs.

The key is idempotent - if it already exists, it will be reused.

https://claude.ai/code/session_01GcoMc2SH3czPrbeGkHbmpE

* OK to name instatus to a point

* Remove all Instatus references

* Rename to create_monitoring_api_key! and scope lookup to admin_user

- Rename create_instatus_api_key! to create_monitoring_api_key! (snake_case)
- Scope API key lookup to admin_user instead of global ApiKey lookup
- Each family's admin now has their own monitoring API key

https://claude.ai/code/session_01GcoMc2SH3czPrbeGkHbmpE

---------

Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
Juan José Mata
2026-01-30 12:29:46 +01:00
committed by GitHub
parent 04931e27eb
commit 02cd84568e
2 changed files with 42 additions and 6 deletions

View File

@@ -1,6 +1,10 @@
require "securerandom"
class Demo::Generator
# Deterministic API key for uptime monitoring
# This key is always the same so it can be hardcoded in monitoring tools
MONITORING_API_KEY = "demo_monitoring_key_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6"
# @param seed [Integer, String, nil] Seed value used to initialise the internal PRNG. If nil, the ENV variable DEMO_DATA_SEED will
# be honoured and default to a random seed when not present.
#
@@ -93,6 +97,9 @@ class Demo::Generator
puts "👥 Creating demo family..."
family = create_family_and_users!("Demo Family", email, onboarded: true, subscribed: true)
puts "🔑 Creating monitoring API key..."
create_monitoring_api_key!(family)
puts "📊 Creating realistic financial data..."
create_realistic_categories!(family)
create_realistic_accounts!(family)
@@ -181,6 +188,32 @@ class Demo::Generator
family
end
def create_monitoring_api_key!(family)
admin_user = family.users.find_by(role: "admin")
return unless admin_user
# Find existing key scoped to this admin user by the deterministic display_key value
existing_key = admin_user.api_keys.find_by(display_key: MONITORING_API_KEY)
if existing_key
puts " → Use existing monitoring API key"
return existing_key
end
# Revoke any existing web API keys for this user to avoid one-per-source validation error
admin_user.api_keys.active.where(source: "web").find_each(&:revoke!)
api_key = admin_user.api_keys.create!(
name: "monitoring",
key: MONITORING_API_KEY,
scopes: [ "read" ],
source: "web"
)
puts " → Created monitoring API key: #{MONITORING_API_KEY}"
api_key
end
def create_realistic_categories!(family)
# Income categories (3 total)
@salary_cat = family.categories.create!(name: "Salary", color: "#10b981", classification: "income")

View File

@@ -2,9 +2,10 @@ namespace :demo_data do
desc "Load empty demo dataset (no financial data)"
task empty: :environment do
start = Time.now
puts "🚀 Loading EMPTY demo data…"
skip_clear = ENV.fetch("SKIP_CLEAR", "1") == "1"
puts "🚀 Loading EMPTY demo data#{skip_clear ? ' (preserving existing data)' : ' (clearing existing data)'}"
Demo::Generator.new.generate_empty_data!
Demo::Generator.new.generate_empty_data!(skip_clear: skip_clear)
puts "✅ Done in #{(Time.now - start).round(2)}s"
end
@@ -12,9 +13,10 @@ namespace :demo_data do
desc "Load new-user demo dataset (family created but not onboarded)"
task new_user: :environment do
start = Time.now
puts "🚀 Loading NEW-USER demo data…"
skip_clear = ENV.fetch("SKIP_CLEAR", "1") == "1"
puts "🚀 Loading NEW-USER demo data#{skip_clear ? ' (preserving existing data)' : ' (clearing existing data)'}"
Demo::Generator.new.generate_new_user_data!
Demo::Generator.new.generate_new_user_data!(skip_clear: skip_clear)
puts "✅ Done in #{(Time.now - start).round(2)}s"
end
@@ -23,10 +25,11 @@ namespace :demo_data do
task default: :environment do
start = Time.now
seed = ENV.fetch("SEED", Random.new_seed)
puts "🚀 Loading FULL demo data (seed=#{seed})…"
skip_clear = ENV.fetch("SKIP_CLEAR", "1") == "1"
puts "🚀 Loading FULL demo data (seed=#{seed})#{skip_clear ? ' (preserving existing data)' : ' (clearing existing data)'}"
generator = Demo::Generator.new(seed: seed)
generator.generate_default_data!
generator.generate_default_data!(skip_clear: skip_clear)
validate_demo_data