staging infrastructure support

pull/544/head
Mikhael Rakauskas 4 months ago
parent c7c0cb17f7
commit 2be2ed47cf

@ -28,4 +28,3 @@
/attachments
/docuseal
.DS_Store
.env

@ -0,0 +1,6 @@
DB_HOST=host.docker.internal
DB_PASSWORD=postgres
DB_PORT=5432
DB_USERNAME=postgres
REDIS_URL=redis://host.docker.internal:6379/7
PORT=3000

@ -0,0 +1,9 @@
DB_HOST=
DB_POOL=25
DB_PORT=5432
DB_SSLCERT=
DB_SSLMODE=verify-full
REDIS_URL=
PORT=3000
S3_ATTACHMENTS_BUCKET=
ACTIVE_STORAGE_PUBLIC=true

@ -78,6 +78,8 @@ COPY ./public ./public
COPY ./tmp ./tmp
COPY LICENSE README.md Rakefile config.ru .version ./
COPY .version ./public/version
COPY ./.env.staging ./.env.staging
COPY ./config/rds-combined-ca-bundle.pem /config/rds-combined-ca-bundle.pem
COPY --from=download /fonts/GoNotoKurrent-Regular.ttf /fonts/GoNotoKurrent-Bold.ttf /fonts/DancingScript-Regular.otf /fonts/OFL.txt /fonts
COPY --from=download /fonts/FreeSans.ttf /usr/share/fonts/freefont
@ -85,6 +87,9 @@ COPY --from=download /pdfium-linux/lib/libpdfium.so /usr/lib/libpdfium.so
COPY --from=download /pdfium-linux/licenses/pdfium.txt /usr/lib/libpdfium-LICENSE.txt
COPY --from=webpack /app/public/packs ./public/packs
# Install AWS CLI and jq for secrets management
RUN apk add --no-cache curl jemalloc vips sqlite postgresql-client aws-cli jq bind-tools
RUN ln -s /fonts /app/public/fonts
RUN bundle exec bootsnap precompile --gemfile app/ lib/
@ -92,4 +97,4 @@ WORKDIR /data/docuseal
ENV WORKDIR=/data/docuseal
EXPOSE 3000
CMD ["/app/bin/bundle", "exec", "puma", "-C", "/app/config/puma.rb", "--dir", "/app"]
CMD ["/app/bin/bundle", "exec", "puma", "-C", "/app/config/puma.rb", "--dir", "/app"]

@ -0,0 +1,224 @@
#!/bin/sh -e
echo "=== CP Docuseal Staging Startup ==="
# Enable jemalloc for reduced memory usage and latency.
if [ -z "${LD_PRELOAD+x}" ]; then
LD_PRELOAD=$(find /usr/lib -name libjemalloc.so.2 -print -quit)
export LD_PRELOAD
fi
check_aws_setup() {
if [ -z "$AWS_REGION" ]; then
echo "ERROR: AWS_REGION environment variable is not set"
exit 1
fi
if ! command -v aws &> /dev/null; then
echo "ERROR: AWS CLI is not installed. Please install it to proceed."
exit 1
fi
}
# Function to fetch secrets from AWS Secrets Manager
fetch_db_credentials() {
echo "Fetching database credentials from AWS Secrets Manager..."
if [ -z "$DB_SECRETS_NAME" ]; then
echo "ERROR: DB_SECRETS_NAME environment variable is not set"
exit 1
fi
# Fetch the secret
echo "Retrieving secret: $DB_SECRETS_NAME"
SECRET_JSON=$(aws secretsmanager get-secret-value \
--region "$AWS_REGION" \
--secret-id "$DB_SECRETS_NAME" \
--query SecretString \
--output text)
if [ $? -ne 0 ]; then
echo "ERROR: Failed to retrieve secrets from AWS Secrets Manager"
exit 1
fi
# Parse JSON and export environment variables
export DB_USERNAME=$(echo "$SECRET_JSON" | jq -r '.username')
export DB_PASSWORD=$(echo "$SECRET_JSON" | jq -r '.password')
# Validate that we got the credentials
if [ "$DB_USERNAME" = "null" ] || [ "$DB_PASSWORD" = "null" ] || [ -z "$DB_USERNAME" ] || [ -z "$DB_PASSWORD" ]; then
echo "ERROR: Failed to parse database credentials from secrets"
echo "Expected JSON format: {\"username\": \"...\", \"password\": \"...\"}"
exit 1
fi
# Write credentials to .env.staging file
echo "Writing database credentials to .env.staging..."
# Remove existing DB_USERNAME and DB_PASSWORD lines if they exist
if [ -f "./.env.staging" ]; then
echo "Removing existing DB_USERNAME and DB_PASSWORD from .env.staging"
grep -v "^DB_USERNAME=" ./.env.staging > ./.env.staging.tmp || true
grep -v "^DB_PASSWORD=" ./.env.staging.tmp > ./.env.staging || true
rm -f ./.env.staging.tmp
fi
# Append the new credentials
echo "DB_USERNAME=$DB_USERNAME" >> ./.env.staging
echo "DB_PASSWORD=$DB_PASSWORD" >> ./.env.staging
echo "✓ Database credentials successfully retrieved and written to .env.staging"
}
# Function to fetch encryption key from AWS Secrets Manager and write to config/master.key
fetch_encryption_key() {
echo "Fetching encryption key from AWS Secrets Manager..."
ENCRYPTION_SECRET_NAME="cpdocuseal/encryption_key"
if [ -z "$AWS_REGION" ]; then
echo "ERROR: AWS_REGION environment variable is not set"
exit 1
fi
# Fetch the secret value (assume it's a plain string, not JSON)
ENCRYPTION_KEY=$(aws secretsmanager get-secret-value \
--region "$AWS_REGION" \
--secret-id "$ENCRYPTION_SECRET_NAME" \
--query SecretString \
--output text)
if [ $? -ne 0 ] || [ -z "$ENCRYPTION_KEY" ] || [ "$ENCRYPTION_KEY" = "null" ]; then
echo "ERROR: Failed to retrieve encryption key from AWS Secrets Manager"
exit 1
fi
# Write the key to config/master.key
echo -n "$ENCRYPTION_KEY" > config/master.key
chmod 600 config/master.key
echo "✓ Encryption key written to config/master.key"
}
fetch_env_variables() {
echo "Fetching environment variables from AWS Secrets Manager..."
if [ -z "$CP_VARIABLES_NAME" ]; then
echo "ERROR: CP_VARIABLES_NAME environment variable is not set"
exit 1
fi
# Fetch the secret
echo "Retrieving secret: $CP_VARIABLES_NAME"
SECRET_JSON=$(aws secretsmanager get-secret-value \
--region "$AWS_REGION" \
--secret-id "$CP_VARIABLES_NAME" \
--query SecretString \
--output text)
if [ $? -ne 0 ]; then
echo "ERROR: Failed to retrieve secrets from AWS Secrets Manager"
exit 1
fi
export DB_HOST=$(echo "$SECRET_JSON" | jq -r '.host')
export REDIS_URL=$(echo "$SECRET_JSON" | jq -r '.redis_url')
export S3_ATTACHMENTS_BUCKET=$(echo "$SECRET_JSON" | jq -r '.s3_attachments_bucket')
export DB_SSLCERT=$(echo "$SECRET_JSON" | jq -r '.ssl_cert_location')
# Validate that we got the values
if [ "$DB_HOST" = "null" ] || [ "$REDIS_URL" = "null" ] || [ "$S3_ATTACHMENTS_BUCKET" = "null" ] || [ "$DB_SSLCERT" = "null" ] || [ -z "$DB_HOST" ] || [ -z "$REDIS_URL" ] || [ -z "$S3_ATTACHMENTS_BUCKET" ] || [ -z "$DB_SSLCERT" ]; then
echo "ERROR: Failed to parse variables from secrets"
echo "Expected JSON format: {\"key\": \"...\", ...}"
exit 1
fi
# Write variables to .env.staging file
echo "Writing environment variables to .env.staging..."
# Remove existing DB_HOST, REDIS_URL, and S3_ATTACHMENTS_BUCKET lines if they exist
if [ -f "./.env.staging" ]; then
echo "Removing existing variables from .env.staging"
grep -v "^DB_HOST=" ./.env.staging > ./.env.staging.tmp || true
grep -v "^REDIS_URL=" ./.env.staging.tmp > ./.env.staging || true
grep -v "^S3_ATTACHMENTS_BUCKET=" ./.env.staging.tmp > ./.env.staging || true
grep -v "^DB_SSLCERT=" ./.env.staging.tmp > ./.env.staging || true
rm -f ./.env.staging.tmp
fi
# Append the new credentials
echo "DB_HOST=$DB_HOST" >> ./.env.staging
echo "REDIS_URL=$REDIS_URL" >> ./.env.staging
echo "S3_ATTACHMENTS_BUCKET=$S3_ATTACHMENTS_BUCKET" >> ./.env.staging
echo "DB_SSLCERT=$DB_SSLCERT" >> ./.env.staging
echo "✓ Environment variables successfully retrieved and written to .env.staging"
}
# Function to setup database
setup_database() {
echo "Running database migrations..."
./bin/rails db:migrate
if [ $? -eq 0 ]; then
echo "✓ Database migrations completed successfully"
else
echo "ERROR: Database migrations failed"
exit 1
fi
}
set_environment() {
if [ -f "./.env.staging" ]; then
echo "Setting environment variables from .env.staging"
set -a
. ./.env.staging
set +a
fi
}
# Main execution
main() {
local command=${1:-api}
cd ../../app/
set_environment
check_aws_setup
echo "Starting CP Docuseal in staging mode..."
echo "Command: $command"
echo "Rails Environment: ${RAILS_ENV:-staging}"
# Fetch database credentials from Secrets Manager
fetch_db_credentials
# Fetch encryption key and write to config/master.key
fetch_encryption_key
# Fetch other environment variables from Secrets Manager
fetch_env_variables
# Load updated environment variables
set_environment
# Setup and migrate database (only for API command)
if [ "$command" = "api" ]; then
setup_database
echo "=== Startup Complete - Starting Rails Server ==="
echo "Database Host: ${DB_HOST:-not set}"
echo "Database Port: ${DB_PORT:-not set}"
# Start the Rails server
exec ./bin/rails server -b 0.0.0.0 -p "${PORT:-3000}"
elif [ "$command" = "sidekiq" ]; then
echo "=== Startup Complete - Starting Sidekiq ==="
# Start Sidekiq
exec bundle exec sidekiq -q default
else
echo "ERROR: Unknown command '$command'. Use 'api' or 'sidekiq'"
exit 1
fi
}
# Execute main function with all arguments
main "$@"

@ -1,7 +1,6 @@
default: &default
adapter: postgresql
encoding: unicode
pool: <%= ENV.fetch('RAILS_MAX_THREADS', 15).to_i + ENV.fetch('SIDEKIQ_THREADS', 5).to_i %>
development:
adapter: postgresql
@ -22,27 +21,25 @@ test:
host: localhost
production:
<% if !ENV['DATABASE_HOST'].to_s.empty? %>
<<: *default
host: <%= ENV['DATABASE_HOST'] %>
port: <%= ENV['DATABASE_PORT'] %>
username: <%= ENV['DATABASE_USER'] %>
password: <%= ENV['DATABASE_PASSWORD'] %>
database: <%= ENV['DATABASE_NAME'] %>
<% if !ENV['DATABASE_SEARCH_PATH'].to_s.empty? %>
search_path: <%= ENV['DATABASE_SEARCH_PATH'] %>
<% end %>
<% elsif ENV['DATABASE_URL'].to_s.empty? %>
adapter: sqlite3
database: <%= ENV['WORKDIR'] || '.' %>/db.sqlite3
pool: <%= ENV.fetch('RAILS_MAX_THREADS', 15).to_i + ENV.fetch('SIDEKIQ_THREADS', 5).to_i %>
timeout: 5000
<% elsif ENV['DATABASE_URL'].match?(/\Apostgres/) %>
host: <%= ENV['DB_HOST'] %>
port: <%= ENV['DB_PORT'] %>
pool: <%= ENV['DB_POOL'] || 25 %>
username: <%= ENV['DB_USERNAME'] %>
password: <%= ENV['DB_PASSWORD'] %>
database: <%= ENV['DB_NAME'] %>
sslmode: <%= ENV['DB_SSLMODE'] %>
sslrootcert: <%= ENV['DB_SSLCERT'] %>
staging:
<<: *default
url: <%= ENV['DATABASE_URL'] %>
<% elsif ENV['DATABASE_URL'].match?(/\Amysql/) %>
adapter: mysql2
encoding: utf8mb4
pool: <%= ENV.fetch('RAILS_MAX_THREADS', 15).to_i + ENV.fetch('SIDEKIQ_THREADS', 5).to_i %>
url: <%= ENV['DATABASE_URL'] %>
<% end %>
host: <%= ENV['DB_HOST'] %>
port: <%= ENV['DB_PORT'] %>
pool: <%= ENV['DB_POOL'] || 25 %>
username: <%= ENV['DB_USERNAME'] %>
password: <%= ENV['DB_PASSWORD'] %>
database: <%= ENV['DB_NAME'] %>
sslmode: <%= ENV['DB_SSLMODE'] %>
sslrootcert: <%= ENV['DB_SSLCERT'] %>
variables:
statement_timeout: 120000

@ -1,6 +1,6 @@
# frozen_string_literal: true
if ENV['RAILS_ENV'] == 'production'
if ENV['RAILS_ENV'] == 'production' || ENV['RAILS_ENV'] == 'staging'
if !ENV['AWS_SECRET_MANAGER_ID'].to_s.empty?
require 'aws-sdk-secretsmanager'
@ -43,11 +43,11 @@ if ENV['DATABASE_URL'].to_s.split('@').last.to_s.split('/').first.to_s.include?(
url = Addressable::URI.parse(ENV.fetch('DATABASE_URL', ''))
ENV['DATABASE_HOST'] = url.host
ENV['DATABASE_PORT'] = (url.port || 5432).to_s
ENV['DATABASE_USER'] = url.user
ENV['DATABASE_PASSWORD'] = url.password
ENV['DATABASE_NAME'] = url.path.to_s.delete_prefix('/')
ENV['DB_HOST'] = url.host
ENV['DB_PORT'] = (url.port || 5432).to_s
ENV['DB_USERNAME'] = url.user
ENV['DB_PASSWORD'] = url.password
ENV['DB_NAME'] = url.path.to_s.delete_prefix('/')
ENV.delete('DATABASE_URL')
end

@ -0,0 +1,164 @@
# frozen_string_literal: true
require 'active_support/core_ext/integer/time'
require 'active_support/core_ext/string'
Rails.backtrace_cleaner.remove_silencers!
Rails.application.configure do
# Settings specified here will take precedence over those in config/application.rb.
# Code is not reloaded between requests.
config.enable_reloading = false
config.public_file_server.headers = {
'cache-control' => 'public, s-maxage=31536000, max-age=15552000',
'Expires' => 1.year.from_now.to_fs(:rfc822)
}
# Eager load code on boot. This eager loads most of Rails and
# your application in memory, allowing both threaded web servers
# and those relying on copy on write to perform better.
# Rake tasks automatically ignore this option for performance.
config.eager_load = true
# Full error reports are disabled and caching is turned on.
config.consider_all_requests_local = false
config.action_controller.perform_caching = true
config.active_job.queue_adapter = :sidekiq
# Ensures that a master key has been made available in either ENV["RAILS_MASTER_KEY"]
# or in config/master.key. This key is used to decrypt credentials (and other encrypted files).
# config.require_master_key = true
# Disable serving static files from the `/public` folder by default since
# Apache or NGINX already handles this.
config.public_file_server.enabled = true
# Store uploaded files on the local file system (see config/storage.yml for options).
config.active_storage.service = :aws_s3
config.active_storage.resolve_model_to_route = :rails_storage_proxy if ENV['ACTIVE_STORAGE_PUBLIC'] != 'true'
config.active_storage.service_urls_expire_in = ENV.fetch('PRESIGNED_URLS_EXPIRE_MINUTES', '240').to_i.minutes
# Mount Action Cable outside main process or domain.
# config.action_cable.mount_path = nil
# config.action_cable.url = "wss://example.com/cable"
# config.action_cable.allowed_request_origins = [ "http://example.com", /http:\/\/example.*/ ]
# Assume all access to the app is happening through a SSL-terminating reverse proxy.
config.assume_ssl = ENV['FORCE_SSL'].present? && ENV['FORCE_SSL'] != 'false'
# Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
config.force_ssl = ENV['FORCE_SSL'].present? && ENV['FORCE_SSL'] != 'false'
# Include generic and useful information about system operation, but avoid logging too much
# information to avoid inadvertent exposure of personally identifiable information (PII).
config.log_level = :info
# Prepend all log lines with the following tags.
config.log_tags = [:request_id]
config.cache_store = :memory_store
config.action_mailer.perform_caching = false
# Ignore bad email addresses and do not raise email delivery errors.
# Set this to true and configure the email server for immediate delivery to raise delivery errors.
config.action_mailer.raise_delivery_errors = false
if ENV['SMTP_ADDRESS']
config.action_mailer.delivery_method = :smtp
config.action_mailer.smtp_settings = {
address: ENV.fetch('SMTP_ADDRESS', nil),
port: ENV.fetch('SMTP_PORT', 587),
domain: ENV.fetch('SMTP_DOMAIN', nil),
user_name: ENV.fetch('SMTP_USERNAME', nil),
password: ENV.fetch('SMTP_PASSWORD', nil),
authentication: ENV.fetch('SMTP_PASSWORD', nil).present? ? ENV.fetch('SMTP_AUTHENTICATION', 'plain') : nil,
enable_starttls_auto: ENV['SMTP_ENABLE_STARTTLS_AUTO'] != 'false'
}.compact
end
# Enable locale fallbacks for I18n (makes lookups for any locale fall back to
# the I18n.default_locale when a translation cannot be found).
config.i18n.fallbacks = true
# Don't log any deprecations.
config.active_support.report_deprecations = false
# Use default logging formatter so that PID and timestamp are not suppressed.
config.log_formatter = Logger::Formatter.new
# Use a different logger for distributed setups.
# require "syslog/logger"
# config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new "app-name")
logger = ActiveSupport::Logger.new($stdout)
logger.formatter = config.log_formatter
config.logger = ActiveSupport::TaggedLogging.new(logger)
encryption_secret = ENV['ENCRYPTION_SECRET'].presence || Digest::SHA256.hexdigest(ENV['SECRET_KEY_BASE'].to_s)
config.active_record.encryption = {
primary_key: encryption_secret.first(32),
deterministic_key: encryption_secret.last(32),
key_derivation_salt: Digest::SHA256.hexdigest(encryption_secret)
}
ActiveRecord::Encryption.configure(**config.active_record.encryption)
# Do not dump schema after migrations.
config.active_record.dump_schema_after_migration = false
config.lograge.enabled = true
config.lograge.base_controller_class = ['ActionController::API', 'ActionController::Base']
config.lograge.formatter = ->(data) { data.except(:path, :location).to_json }
config.lograge.custom_payload do |controller|
params =
begin
controller.request.try(:params) || {}
rescue StandardError
{}
end
resource = controller.instance_variable_get(:@submitter) ||
controller.instance_variable_get(:@submission) ||
controller.instance_variable_get(:@template) ||
controller.instance_variable_get(:@record)
current_user = controller.instance_variable_get(:@current_user)
{
host: controller.request.host,
fwd: controller.request.remote_ip,
params: {
id: params[:id],
template_id: params[:template_id],
submission_id: params[:submission_id],
submitter_id: params[:submitter_id],
sig: (params[:signed_uuid] || params[:signed_id]).to_s.split('--').first,
slug: (params[:slug] ||
params[:submitter_slug] ||
params[:submission_slug] ||
params[:submit_form_slug] ||
params[:template_slug]).to_s.first(5)
}.compact_blank,
uid: current_user.try(:id),
aid: current_user.try(:account_id),
rid: resource.try(:id),
raid: resource.try(:account_id)
}
end
config.host_authorization = { exclude: ->(request) { request.path == "/up" } }
[
/.*\.careerplug\.org\Z/,
/.*\.careerplug\.com\Z/,
/.*\.cpstaging\d\.click\Z/,
/.*\.cpstaging\d+\.name\Z/
].each { |hrexp| config.hosts << hrexp }
end

@ -0,0 +1,124 @@
-----BEGIN CERTIFICATE-----
MIIEBjCCAu6gAwIBAgIJAMc0ZzaSUK51MA0GCSqGSIb3DQEBCwUAMIGPMQswCQYD
VQQGEwJVUzEQMA4GA1UEBwwHU2VhdHRsZTETMBEGA1UECAwKV2FzaGluZ3RvbjEi
MCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNlcywgSW5jLjETMBEGA1UECwwKQW1h
em9uIFJEUzEgMB4GA1UEAwwXQW1hem9uIFJEUyBSb290IDIwMTkgQ0EwHhcNMTkw
ODIyMTcwODUwWhcNMjQwODIyMTcwODUwWjCBjzELMAkGA1UEBhMCVVMxEDAOBgNV
BAcMB1NlYXR0bGUxEzARBgNVBAgMCldhc2hpbmd0b24xIjAgBgNVBAoMGUFtYXpv
biBXZWIgU2VydmljZXMsIEluYy4xEzARBgNVBAsMCkFtYXpvbiBSRFMxIDAeBgNV
BAMMF0FtYXpvbiBSRFMgUm9vdCAyMDE5IENBMIIBIjANBgkqhkiG9w0BAQEFAAOC
AQ8AMIIBCgKCAQEArXnF/E6/Qh+ku3hQTSKPMhQQlCpoWvnIthzX6MK3p5a0eXKZ
oWIjYcNNG6UwJjp4fUXl6glp53Jobn+tWNX88dNH2n8DVbppSwScVE2LpuL+94vY
0EYE/XxN7svKea8YvlrqkUBKyxLxTjh+U/KrGOaHxz9v0l6ZNlDbuaZw3qIWdD/I
6aNbGeRUVtpM6P+bWIoxVl/caQylQS6CEYUk+CpVyJSkopwJlzXT07tMoDL5WgX9
O08KVgDNz9qP/IGtAcRduRcNioH3E9v981QO1zt/Gpb2f8NqAjUUCUZzOnij6mx9
McZ+9cWX88CRzR0vQODWuZscgI08NvM69Fn2SQIDAQABo2MwYTAOBgNVHQ8BAf8E
BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUc19g2LzLA5j0Kxc0LjZa
pmD/vB8wHwYDVR0jBBgwFoAUc19g2LzLA5j0Kxc0LjZapmD/vB8wDQYJKoZIhvcN
AQELBQADggEBAHAG7WTmyjzPRIM85rVj+fWHsLIvqpw6DObIjMWokpliCeMINZFV
ynfgBKsf1ExwbvJNzYFXW6dihnguDG9VMPpi2up/ctQTN8tm9nDKOy08uNZoofMc
NUZxKCEkVKZv+IL4oHoeayt8egtv3ujJM6V14AstMQ6SwvwvA93EP/Ug2e4WAXHu
cbI1NAbUgVDqp+DRdfvZkgYKryjTWd/0+1fS8X1bBZVWzl7eirNVnHbSH2ZDpNuY
0SBd8dj5F6ld3t58ydZbrTHze7JJOd8ijySAp4/kiu9UfZWuTPABzDa/DSdz9Dk/
zPW4CXXvhLmE02TA9/HeCw3KEHIwicNuEfw=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIEBzCCAu+gAwIBAgICJVUwDQYJKoZIhvcNAQELBQAwgY8xCzAJBgNVBAYTAlVT
MRAwDgYDVQQHDAdTZWF0dGxlMRMwEQYDVQQIDApXYXNoaW5ndG9uMSIwIAYDVQQK
DBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJbmMuMRMwEQYDVQQLDApBbWF6b24gUkRT
MSAwHgYDVQQDDBdBbWF6b24gUkRTIFJvb3QgMjAxOSBDQTAeFw0xOTA5MTkxODE2
NTNaFw0yNDA4MjIxNzA4NTBaMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECAwKV2Fz
aGluZ3RvbjEQMA4GA1UEBwwHU2VhdHRsZTEiMCAGA1UECgwZQW1hem9uIFdlYiBT
ZXJ2aWNlcywgSW5jLjETMBEGA1UECwwKQW1hem9uIFJEUzElMCMGA1UEAwwcQW1h
em9uIFJEUyB1cy1lYXN0LTEgMjAxOSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEP
ADCCAQoCggEBAM3i/k2u6cqbMdcISGRvh+m+L0yaSIoOXjtpNEoIftAipTUYoMhL
InXGlQBVA4shkekxp1N7HXe1Y/iMaPEyb3n+16pf3vdjKl7kaSkIhjdUz3oVUEYt
i8Z/XeJJ9H2aEGuiZh3kHixQcZczn8cg3dA9aeeyLSEnTkl/npzLf//669Ammyhs
XcAo58yvT0D4E0D/EEHf2N7HRX7j/TlyWvw/39SW0usiCrHPKDLxByLojxLdHzso
QIp/S04m+eWn6rmD+uUiRteN1hI5ncQiA3wo4G37mHnUEKo6TtTUh+sd/ku6a8HK
glMBcgqudDI90s1OpuIAWmuWpY//8xEG2YECAwEAAaNmMGQwDgYDVR0PAQH/BAQD
AgEGMBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFPqhoWZcrVY9mU7tuemR
RBnQIj1jMB8GA1UdIwQYMBaAFHNfYNi8ywOY9CsXNC42WqZg/7wfMA0GCSqGSIb3
DQEBCwUAA4IBAQB6zOLZ+YINEs72heHIWlPZ8c6WY8MDU+Be5w1M+BK2kpcVhCUK
PJO4nMXpgamEX8DIiaO7emsunwJzMSvavSPRnxXXTKIc0i/g1EbiDjnYX9d85DkC
E1LaAUCmCZBVi9fIe0H2r9whIh4uLWZA41oMnJx/MOmo3XyMfQoWcqaSFlMqfZM4
0rNoB/tdHLNuV4eIdaw2mlHxdWDtF4oH+HFm+2cVBUVC1jXKrFv/euRVtsTT+A6i
h2XBHKxQ1Y4HgAn0jACP2QSPEmuoQEIa57bEKEcZsBR8SDY6ZdTd2HLRIApcCOSF
MRM8CKLeF658I0XgF8D5EsYoKPsA+74Z+jDH
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIID/zCCAuegAwIBAgIRAPVSMfFitmM5PhmbaOFoGfUwDQYJKoZIhvcNAQELBQAw
gZcxCzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJ
bmMuMRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTEwMC4GA1UEAwwn
QW1hem9uIFJEUyB1cy1lYXN0LTEgUm9vdCBDQSBSU0EyMDQ4IEcxMRAwDgYDVQQH
DAdTZWF0dGxlMCAXDTIxMDUyNTIyMzQ1N1oYDzIwNjEwNTI1MjMzNDU3WjCBlzEL
MAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4x
EzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTAwLgYDVQQDDCdBbWF6
b24gUkRTIHVzLWVhc3QtMSBSb290IENBIFJTQTIwNDggRzExEDAOBgNVBAcMB1Nl
YXR0bGUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDu9H7TBeGoDzMr
dxN6H8COntJX4IR6dbyhnj5qMD4xl/IWvp50lt0VpmMd+z2PNZzx8RazeGC5IniV
5nrLg0AKWRQ2A/lGGXbUrGXCSe09brMQCxWBSIYe1WZZ1iU1IJ/6Bp4D2YEHpXrW
bPkOq5x3YPcsoitgm1Xh8ygz6vb7PsvJvPbvRMnkDg5IqEThapPjmKb8ZJWyEFEE
QRrkCIRueB1EqQtJw0fvP4PKDlCJAKBEs/y049FoOqYpT3pRy0WKqPhWve+hScMd
6obq8kxTFy1IHACjHc51nrGII5Bt76/MpTWhnJIJrCnq1/Uc3Qs8IVeb+sLaFC8K
DI69Sw6bAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFE7PCopt
lyOgtXX0Y1lObBUxuKaCMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsFAAOC
AQEAFj+bX8gLmMNefr5jRJfHjrL3iuZCjf7YEZgn89pS4z8408mjj9z6Q5D1H7yS
jNETVV8QaJip1qyhh5gRzRaArgGAYvi2/r0zPsy+Tgf7v1KGL5Lh8NT8iCEGGXwF
g3Ir+Nl3e+9XUp0eyyzBIjHtjLBm6yy8rGk9p6OtFDQnKF5OxwbAgip42CD75r/q
p421maEDDvvRFR4D+99JZxgAYDBGqRRceUoe16qDzbMvlz0A9paCZFclxeftAxv6
QlR5rItMz/XdzpBJUpYhdzM0gCzAzdQuVO5tjJxmXhkSMcDP+8Q+Uv6FA9k2VpUV
E/O5jgpqUJJ2Hc/5rs9VkAPXeA==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
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
0Ws=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIICrjCCAjSgAwIBAgIRAPAlEk8VJPmEzVRRaWvTh2AwCgYIKoZIzj0EAwMwgZYx
CzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJbmMu
MRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTEvMC0GA1UEAwwmQW1h
em9uIFJEUyB1cy1lYXN0LTEgUm9vdCBDQSBFQ0MzODQgRzExEDAOBgNVBAcMB1Nl
YXR0bGUwIBcNMjEwNTI1MjI0MTU1WhgPMjEyMTA1MjUyMzQxNTVaMIGWMQswCQYD
VQQGEwJVUzEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNlcywgSW5jLjETMBEG
A1UECwwKQW1hem9uIFJEUzELMAkGA1UECAwCV0ExLzAtBgNVBAMMJkFtYXpvbiBS
RFMgdXMtZWFzdC0xIFJvb3QgQ0EgRUNDMzg0IEcxMRAwDgYDVQQHDAdTZWF0dGxl
MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEx5xjrup8II4HOJw15NTnS3H5yMrQGlbj
EDA5MMGnE9DmHp5dACIxmPXPMe/99nO7wNdl7G71OYPCgEvWm0FhdvVUeTb3LVnV
BnaXt32Ek7/oxGk1T+Df03C+W0vmuJ+wo0IwQDAPBgNVHRMBAf8EBTADAQH/MB0G
A1UdDgQWBBTGXmqBWN/1tkSea4pNw0oHrjk2UDAOBgNVHQ8BAf8EBAMCAYYwCgYI
KoZIzj0EAwMDaAAwZQIxAIqqZWCSrIkZ7zsv/FygtAusW6yvlL935YAWYPVXU30m
jkMFLM+/RJ9GMvnO8jHfCgIwB+whlkcItzE9CRQ6CsMo/d5cEHDUu/QW6jSIh9BR
OGh9pTYPVkUbBiKPA7lVVhre
-----END CERTIFICATE-----

@ -6,3 +6,6 @@ test:
production:
secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>
staging:
secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>

@ -41,6 +41,10 @@ test:
compile: true
public_output_path: packs-test
staging:
<<: *default
compile: false
production:
<<: *default
compile: false

@ -13,21 +13,6 @@ aws_s3:
upload:
cache_control: 'public, max-age=31536000'
google:
service: GCS
credentials: <%= ENV['GCS_CREDENTIALS'] || '{}' %>
project: <%= ENV['GCS_PROJECT'] %>
bucket: <%= ENV['GCS_BUCKET'] %>
public: <%= ENV['ACTIVE_STORAGE_PUBLIC'] == 'true' %>
cache_control: "public, max-age=31536000"
azure:
service: AzureStorage
storage_account_name: <%= ENV['AZURE_STORAGE_ACCOUNT_NAME'] %>
storage_access_key: <%= ENV['AZURE_STORAGE_ACCESS_KEY'] %>
container: <%= ENV['AZURE_CONTAINER'] %>
public: <%= ENV['ACTIVE_STORAGE_PUBLIC'] == 'true' %>
test:
service: Disk
root: <%= Rails.root.join("tmp/storage") %>

Loading…
Cancel
Save