Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 55 additions & 4 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,6 +1,57 @@
# Solid Queue Configuration
# =============================================================================
# Environment Variables
# =============================================================================
# Copy this file to .env and fill in the values.
# These ENV vars are interchangeable with Rails encrypted credentials.
# Rails.app.creds checks ENV first, then falls back to encrypted credentials.
# Nested credential keys use "__" (double underscore) as separator.
#
# For example, credentials.dig(:stripe, :private_key) maps to STRIPE__PRIVATE_KEY
# =============================================================================

# --- Stripe Payments ---
STRIPE__PRIVATE_KEY=sk_test_
STRIPE__PUBLIC_KEY=pk_test_
STRIPE__WEBHOOK_RECEIVE_TEST_EVENTS=true
STRIPE__SIGNING_SECRET=whsec_

# --- OAuth: Google ---
GOOGLE_OAUTH2__CLIENT_ID=
GOOGLE_OAUTH2__CLIENT_SECRET=

# --- OAuth: GitHub ---
GITHUB__KEY=
GITHUB__SECRET=

# --- Telegram ---
TELEGRAM__BOT_NICKNAME=
TELEGRAM__BOT_SECRET=

# --- SMTP ---
SMTP__USER_NAME=
SMTP__PASSWORD=

# --- S3 Storage ---
S3__ACCESS_KEY_ID=
S3__SECRET_ACCESS_KEY=
S3__REGION=
S3__BUCKET=

# --- Active Record Encryption ---
ACTIVE_RECORD_ENCRYPTION__PRIMARY_KEY=
ACTIVE_RECORD_ENCRYPTION__DETERMINISTIC_KEY=
ACTIVE_RECORD_ENCRYPTION__KEY_DERIVATION_SALT=

# --- Tigris S3-compatible Storage (used via direct ENV) ---
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_ENDPOINT_URL_S3=
BUCKET_NAME=

# --- Application ---
SOLID_QUEUE_IN_PUMA=true
HOST=localhost:3000

# Avo License Key (if using Avo Pro)
AVO_LICENSE_KEY=''
OPENAI_API_KEY=''
# --- Third-party ---
AVO_LICENSE_KEY=
OPENAI_API_KEY=
2 changes: 1 addition & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ source "https://rubygems.org"
ruby file: ".ruby-version"

# Bundle edge Rails instead: gem "rails", github: "rails/rails", branch: "main"
gem "rails", "~> 8.1.1"
gem "rails", "~> 8.1.2"
# The modern asset pipeline for Rails [https://github.com/rails/propshaft]
gem "propshaft"
# Use postgresql as the database for Active Record
Expand Down
2 changes: 1 addition & 1 deletion app/controllers/organizations/subscriptions_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,6 @@ def sync_subscriptions
end

def require_billing_enabled
redirect_to organization_url(@organization) if Rails.application.credentials.dig(:stripe, :private_key).blank?
redirect_to organization_url(@organization) if Rails.app.creds.option(:stripe, :private_key).blank?
end
end
2 changes: 1 addition & 1 deletion app/models/connected_account.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ class ConnectedAccount < ApplicationRecord
validates :provider, presence: true
validates :uid, presence: true, uniqueness: { scope: :provider }

encrypts :access_token, :refresh_token if Rails.application.credentials.active_record_encryption.present?
encrypts :access_token, :refresh_token if Rails.app.creds.option(:active_record_encryption, :primary_key).present?

PROVIDER_CONFIG = {
google_oauth2: {
Expand Down
2 changes: 1 addition & 1 deletion app/models/stripe_price.rb
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ def configured_price_ids
end

def stripe_configured?
Rails.application.credentials.dig(:stripe, :private_key).present?
Rails.app.creds.option(:stripe, :private_key).present?
end
end
end
2 changes: 1 addition & 1 deletion app/views/shared/_navbar.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@

<% if Current.membership.admin? %>
<%= nav_link t("organizations.edit.title"), edit_organization_path(Current.organization), icon: "svg/cog-6-tooth.svg", active: [["organizations"], ["edit"]] %>
<%= nav_link t("organizations.subscriptions.index.title"), organization_subscriptions_path(Current.organization), icon: subscription_status_label(Current.organization) if Rails.application.credentials.dig(:stripe, :private_key).present? %>
<%= nav_link t("organizations.subscriptions.index.title"), organization_subscriptions_path(Current.organization), icon: subscription_status_label(Current.organization) if Rails.app.creds.option(:stripe, :private_key).present? %>
<% end %>
</ul>
</div>
Expand Down
2 changes: 1 addition & 1 deletion app/views/shared/_sidebar.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@

<% if Current.membership.admin? %>
<%= nav_link t("organizations.edit.title"), edit_organization_path(Current.organization), icon: "svg/cog-6-tooth.svg", active: [["organizations"], ["edit"]] %>
<%= nav_link t("organizations.subscriptions.index.title"), organization_subscriptions_path(Current.organization), icon: subscription_status_label(Current.organization) if Rails.application.credentials.dig(:stripe, :private_key).present? %>
<%= nav_link t("organizations.subscriptions.index.title"), organization_subscriptions_path(Current.organization), icon: subscription_status_label(Current.organization) if Rails.app.creds.option(:stripe, :private_key).present? %>
<% end %>
</ul>
</details>
Expand Down
2 changes: 1 addition & 1 deletion app/views/shared/_sidebar_links.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@

<%= nav_link t("organizations.dashboard.index.title"), organization_dashboard_path(Current.organization), icon: "svg/chart-bar.svg" %>

<%= nav_link t("organizations.dashboard.paywalled_page.title"), organization_paywalled_page_path(Current.organization), icon: "🔐" if Rails.application.credentials.dig(:stripe, :private_key).present? %>
<%= nav_link t("organizations.dashboard.paywalled_page.title"), organization_paywalled_page_path(Current.organization), icon: "🔐" if Rails.app.creds.option(:stripe, :private_key).present? %>

<%= nav_link "Projects", organization_projects_path(Current.organization), icon: "svg/question-mark-circle.svg" if policy(Project).index? %>
10 changes: 5 additions & 5 deletions config/environments/production.rb
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,10 @@
# Set host to be used by links generated in mailer templates.
config.action_mailer.default_url_options = { host: ENV.fetch("HOST", "example.com") }

# Specify outgoing SMTP server. Remember to add smtp/* credentials via bin/rails credentials:edit.
# Specify outgoing SMTP server. Set via credentials or ENV (SMTP__USER_NAME, SMTP__PASSWORD).
# config.action_mailer.smtp_settings = {
# user_name: Rails.application.credentials.dig(:smtp, :user_name),
# password: Rails.application.credentials.dig(:smtp, :password),
# user_name: Rails.app.creds.option(:smtp, :user_name),
# password: Rails.app.creds.option(:smtp, :password),
# address: "smtp.example.com",
# port: 587,
# authentication: :plain
Expand Down Expand Up @@ -95,8 +95,8 @@
# config.action_mailer.smtp_settings = {
# port: 587,
# address: "email-smtp.eu-central-1.amazonaws.com",
# user_name: Rails.application.credentials.dig(:smtp, :user_name),
# password: Rails.application.credentials.dig(:smtp, :password),
# user_name: Rails.app.creds.option(:smtp, :user_name),
# password: Rails.app.creds.option(:smtp, :password),
# authentication: :plain,
# enable_starttls_auto: true
# }
Expand Down
47 changes: 47 additions & 0 deletions config/initializers/app_credentials.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# frozen_string_literal: true

# Backport of Rails 8.2 combined credentials (Rails.app.creds).
# Checks ENV first, then falls back to encrypted credentials.
# ENV keys are derived by uppercasing and joining nested keys with "__" (double underscore).
#
# Examples:
# Rails.app.creds.option(:stripe, :private_key)
# # => ENV["STRIPE__PRIVATE_KEY"] || credentials.dig(:stripe, :private_key)
#
# Rails.app.creds.require(:stripe, :private_key)
# # => same, but raises KeyError if missing from both
#
# TODO: Remove this file after upgrading to Rails 8.2+

unless Rails.respond_to?(:app)
class AppCreds
def option(*keys, default: nil)
env_value(*keys) || Rails.application.credentials.dig(*keys) || default
end

def require(*keys)
value = option(*keys)
raise KeyError, "Missing credential: #{keys.map(&:to_s).join('.')}" if value.blank?
value
end

private

def env_value(*keys)
env_key = keys.map { |k| k.to_s.upcase }.join("__")
ENV[env_key].presence
end
end

class AppContainer
def creds
@creds ||= AppCreds.new
end

def credentials
Rails.application.credentials
end
end

Rails.define_singleton_method(:app) { @_app ||= AppContainer.new }
end
16 changes: 8 additions & 8 deletions config/initializers/omniauth.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,14 @@
providers << :developer if Rails.env.development?

# Google OAuth2
if Rails.application.credentials.dig(:google_oauth2, :client_id).present? &&
Rails.application.credentials.dig(:google_oauth2, :client_secret).present?
if Rails.app.creds.option(:google_oauth2, :client_id).present? &&
Rails.app.creds.option(:google_oauth2, :client_secret).present?
providers << :google_oauth2
end

# GitHub
if Rails.application.credentials.dig(:github, :key).present? &&
Rails.application.credentials.dig(:github, :secret).present?
if Rails.app.creds.option(:github, :key).present? &&
Rails.app.creds.option(:github, :secret).present?
providers << :github
end

Expand All @@ -34,13 +34,13 @@
provider :developer
when :google_oauth2
provider :google_oauth2,
Rails.application.credentials.dig(:google_oauth2, :client_id),
Rails.application.credentials.dig(:google_oauth2, :client_secret),
Rails.app.creds.option(:google_oauth2, :client_id),
Rails.app.creds.option(:google_oauth2, :client_secret),
scope: "email,profile"
when :github
provider :github,
Rails.application.credentials.dig(:github, :key),
Rails.application.credentials.dig(:github, :secret),
Rails.app.creds.option(:github, :key),
Rails.app.creds.option(:github, :secret),
scope: "user:email"
end
end
Expand Down
10 changes: 5 additions & 5 deletions config/storage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ local:
service: Disk
root: <%= Rails.root.join("storage") %>

# Use bin/rails credentials:edit to set the AWS secrets (as aws:access_key_id|secret_access_key)
# Use bin/rails credentials:edit or set ENV vars (S3__ACCESS_KEY_ID, etc.)
# amazon:
# service: S3
# access_key_id: <%= Rails.application.credentials.dig(:s3, :access_key_id) %>
# secret_access_key: <%= Rails.application.credentials.dig(:s3, :secret_access_key) %>
# region: <%= Rails.application.credentials.dig(:s3, :region) %>
# bucket: <%= Rails.application.credentials.dig(:s3, :bucket) %>-<%= Rails.env %>
# access_key_id: <%= Rails.app.creds.option(:s3, :access_key_id) %>
# secret_access_key: <%= Rails.app.creds.option(:s3, :secret_access_key) %>
# region: <%= Rails.app.creds.option(:s3, :region) %>
# bucket: <%= Rails.app.creds.option(:s3, :bucket) %>-<%= Rails.env %>

# Remember not to checkin your GCS keyfile to a repository
# google:
Expand Down
2 changes: 1 addition & 1 deletion db/seeds.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
organization = Organization.create!(name: 'Buzzsprout', owner: user, privacy_setting: :private)
organization.logo.attach(io: Rails.root.join('test/fixtures/files/buzzsprout-logo.png').open, filename: 'buzzsprout.png')

# if Rails.application.credentials.dig(:stripe, :private_key).present?
# if Rails.app.creds.option(:stripe, :private_key).present?
# product = Stripe::Product.create(name: "Pro plan")
# Stripe::Price.create(
# product: product.id,
Expand Down
4 changes: 4 additions & 0 deletions lib/templates/rails/credentials/credentials.yml.tt
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# Rails encrypted credentials. These are interchangeable with ENV variables.
# Rails.app.creds checks ENV first, then falls back to these values.
# See .env.example for the corresponding ENV variable names.
#
# Used as the base secret for all MessageVerifiers in Rails, including the one protecting cookies.
secret_key_base: <%= secret_key_base %>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

class Organizations::SubscriptionsControllerTest < ActionDispatch::IntegrationTest
setup do
skip "Stripe credentials not configured" if Rails.application.credentials.dig(:stripe, :private_key).blank?
skip "Stripe credentials not configured" if Rails.app.creds.option(:stripe, :private_key).blank?
@organization = organizations(:one)
@user = users(:one)
sign_in @user
Expand Down
Loading