chore: init commit

in worker.../repo/GITFOLDER.zip is the .git folder.
This commit is contained in:
2026-08-11 14:44:09 -04:00
parent 0012380fd3
commit 392781f7aa
781 changed files with 65944 additions and 0 deletions

View File

@@ -0,0 +1,39 @@
# frozen_string_literal: true
require_relative "boot"
require "rails/all"
# Explicitly require Action Text to ensure it's available
require "action_text/engine"
# Require the gems listed in Gemfile, including any gems
# you've limited to :test, :development, or :production.
Bundler.require(*Rails.groups)
module StocksInTheFuture
class Application < Rails::Application
# Initialize configuration defaults for originally generated Rails version.
config.load_defaults 8.0
# Please, add to the `ignore` list any other `lib` subdirectories that do
# not contain `.rb` files, or that should not be reloaded or eager loaded.
# Common ones are `templates`, `generators`, or `middleware`, for example.
config.autoload_lib(ignore: %w[assets tasks])
config.active_job.queue_adapter = :solid_queue
# Add form_builders, presenters, and facades to autoload paths
config.autoload_paths << Rails.root.join("app/form_builders")
config.autoload_paths << Rails.root.join("app/presenters")
config.autoload_paths << Rails.root.join("app/facades")
# Configuration for the application, engines, and railties goes here.
#
# These settings can be overridden in specific environments using the files
# in config/environments, which are processed later.
#
# config.time_zone = "Central Time (US & Canada)"
# config.eager_load_paths << Rails.root.join("extras")
end
end

View File

@@ -0,0 +1,6 @@
# frozen_string_literal: true
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
require "bundler/setup" # Set up gems listed in the Gemfile.
require "bootsnap/setup" # Speed up boot time by caching expensive operations.

View File

@@ -0,0 +1,10 @@
development:
adapter: async
test:
adapter: test
production:
adapter: redis
url: <%= ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" } %>
channel_prefix: stocks_in_the_future_production

View File

@@ -0,0 +1 @@
+t4eF/F0vdV1pk9gghYfxw7HxkXbIpxTE4+/N2j7q0hlYFV0j5Rz3B1QY87QxhjdPb1W1My1tBm/rXTUXwbVW2WRgmXaYYC27Fcwdb3USK18GZdcF75rOPATZwdx20OIx80xpmHAsRTI+bD7/30lYK4zCNPjVXM0yYY4brjWjWORTePvYNoUDczoeWyt99QU5hDo+wu0z8Sz1Lb9StkwsMbbW6Oaey9nlYAXWcNE9FhniUULPWAo4KQ9/bbN0w8TQZXq/i3t263lXmDXZlunzvSNXKvJYR6W/T2VkbcPJMICfXwK9bYGBFHhOMBjKX6OqkhsJMunD3jDytwyoWDQvSp0RxTiqKk2Y9vEiqJcBRlP+O1POxTaMNojuY7dHPJRefi8QPnH+to+skRL8i9C2PhGLf7e--ZcyPsl+b4W2V5DK4--UCwUPNYjrcfGLIepEVHHfg==

View File

@@ -0,0 +1,86 @@
# PostgreSQL. Versions 9.3 and up are supported.
#
# Install the pg driver:
# gem install pg
# On macOS with Homebrew:
# gem install pg -- --with-pg-config=/usr/local/bin/pg_config
# On Windows:
# gem install pg
# Choose the win32 build.
# Install PostgreSQL and put its /bin directory on your path.
#
# Configure Using Gemfile
# gem "pg"
#
default: &default
adapter: postgresql
encoding: unicode
# running postgres on docker requires specifying an explicit host
# host: 127.0.0.1
# For details on connection pooling, see Rails configuration guide
# https://guides.rubyonrails.org/configuring.html#database-pooling
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
development:
<<: *default
database: stocks_in_the_future_development
# The specified database role being used to connect to PostgreSQL.
# To create additional roles in PostgreSQL see `$ createuser --help`.
# When left blank, PostgreSQL will use the default role. This is
# the same name as the operating system user running Rails.
#username: stocks_in_the_future
# The password associated with the PostgreSQL role (username).
#password:
# Connect on a TCP socket. Omitted by default since the client uses a
# domain socket that doesn't need configuration. Windows does not have
# domain sockets, so uncomment these lines.
#host: localhost
# The TCP port the server listens on. Defaults to 5432.
# If your server runs on a different port number, change accordingly.
#port: 5432
# Schema search path. The server defaults to $user,public
#schema_search_path: myapp,sharedapp,public
# Minimum log levels, in increasing order:
# debug5, debug4, debug3, debug2, debug1,
# log, notice, warning, error, fatal, and panic
# Defaults to warning.
#min_messages: notice
# Warning: The database defined as "test" will be erased and
# re-generated from your development database when you run "rake".
# Do not set this db to the same as development or production.
test:
<<: *default
database: stocks_in_the_future_test
# As with config/credentials.yml, you never want to store sensitive information,
# like your database password, in your source code. If your source code is
# ever seen by anyone, they now have access to your database.
#
# Instead, provide the password or a full connection URL as an environment
# variable when you boot the app. For example:
#
# DATABASE_URL="postgres://myuser:mypass@localhost/somedatabase"
#
# If the connection URL is provided in the special DATABASE_URL environment
# variable, Rails will automatically merge its configuration values on top of
# the values provided in this file. Alternatively, you can specify a connection
# URL environment variable explicitly:
#
# production:
# url: <%= ENV["MY_APP_DATABASE_URL"] %>
#
# Read https://guides.rubyonrails.org/configuring.html#configuring-a-database
# for a full overview on how database connection configuration can be specified.
#
production:
<<: *default
database: stocks_in_the_future_production
username: stocks_in_the_future
password: <%= ENV["STOCKS_IN_THE_FUTURE_DATABASE_PASSWORD"] %>

View File

@@ -0,0 +1,108 @@
# frozen_string_literal: true
lock "~> 3.19"
set :application, "stocks-in-the-future"
set :repo_url, "git@github.com:rubyforgood/stocks-in-the-future.git"
set :branch, :main
set :deploy_to, "/home/ubuntu/stocks-in-the-future"
set :keep_releases, 5
# rbenv - must match the version installed on the server
set :rbenv_type, :user
set :rbenv_ruby, "3.4.4"
set :rbenv_path, "/home/ubuntu/.rbenv"
# Shared files and directories that persist across releases
set :linked_files, %w[
config/database.yml
]
set :linked_dirs, %w[
log
tmp/pids
tmp/cache
tmp/sockets
storage
public/assets
]
set :bundle_without, %w[development test].join(" ")
set :bundle_flags, "--quiet"
# Forward local SSH agent so server can clone from GitHub
set :ssh_options, forward_agent: true
# Read env vars from /etc/stocks/env on the server so Capistrano tasks
# (assets:precompile, db:migrate, etc.) have access to them.
ENV_VAR_FILE = "/etc/stocks/env"
RBENV_BIN = "/home/ubuntu/.rbenv/bin/rbenv"
def run_rake_with_env(task)
# Source the env file directly so passwords with special chars never appear in the command string
"set -a && . #{ENV_VAR_FILE} && set +a && #{RBENV_BIN} exec bundle exec rake #{task}"
end
# Override deploy:assets:precompile - skip the gem's default
begin
Rake::Task["deploy:assets:precompile"].clear
rescue StandardError
nil
end
namespace :deploy do
namespace :assets do
task :precompile do
on roles(:app) do
within release_path do
# Delete credentials file if it exists (it's encrypted and causes issues)
execute :rm, "-f", "#{release_path}/config/credentials.yml.enc"
execute :sh, "-c '#{run_rake_with_env('assets:precompile')}'"
end
end
end
end
end
# Skip default migrate task entirely
set :migration_role, :none
# Add our own migration task after publishing
# Run on :app role since we don't have separate :db role in staging
namespace :deploy do
task :run_migrations do
on roles(:app) do
within release_path do
# Delete credentials file before migrations too
execute :rm, "-f", "#{release_path}/config/credentials.yml.enc"
execute :sh, "-c '#{run_rake_with_env('db:migrate')}'"
end
end
end
end
# Run our custom migrations after publishing
after "deploy:publishing", "deploy:run_migrations"
# Manually precompile assets with env vars after the app is deployed
# Manually precompile assets with env vars after the app is deployed
namespace :deploy do
task :restart do
on roles(:app) do
# Restart Puma
execute :sudo, "systemctl restart stocks"
# Fix permissions so nginx (www-data) can access the Puma socket
# These are needed after deploy because Capistrano recreates the shared directories
# Also fix /home and /home/ubuntu permissions for nginx to traverse
execute :sudo, "chmod o+x /home"
execute :sudo, "chmod o+x /home/ubuntu"
execute :sudo, "chmod o+x #{deploy_to}/shared/tmp"
execute :sudo, "chmod o+x #{deploy_to}/shared/tmp/sockets"
end
end
after :published, :restart
end

View File

@@ -0,0 +1,13 @@
# frozen_string_literal: true
server \
ENV.fetch("PRODUCTION_SERVER_IP", "PRODUCTION_IP_PLACEHOLDER"),
user: "ubuntu",
roles: %w[app db web],
ssh_options: {
keys: ["~/.ssh/production_web_ssh.pem"],
forward_agent: true
}
set :rails_env, "production"
set :branch, "main"

View File

@@ -0,0 +1,13 @@
# frozen_string_literal: true
server \
ENV.fetch("STAGING_SERVER_IP", "STAGING_IP_PLACEHOLDER"),
user: "ubuntu",
roles: %w[app db web],
ssh_options: {
keys: ["~/.ssh/staging_web_ssh.pem"],
forward_agent: true
}
set :rails_env, "staging"
set :branch, "main"

View File

@@ -0,0 +1,7 @@
# frozen_string_literal: true
# Load the Rails application.
require_relative "application"
# Initialize the Rails application.
Rails.application.initialize!

View File

@@ -0,0 +1,77 @@
# frozen_string_literal: true
require "active_support/core_ext/integer/time"
Rails.application.configure do
# Settings specified here will take precedence over those in config/application.rb.
# Make code changes take effect immediately without server restart.
config.enable_reloading = true
# Do not eager load code on boot.
config.eager_load = false
# Show full error reports.
config.consider_all_requests_local = true
# Enable server timing.
config.server_timing = true
# Enable/disable Action Controller caching. By default Action Controller caching is disabled.
# Run rails dev:cache to toggle Action Controller caching.
if Rails.root.join("tmp/caching-dev.txt").exist?
config.action_controller.perform_caching = true
config.action_controller.enable_fragment_cache_logging = true
config.public_file_server.headers = { "cache-control" => "public, max-age=#{2.days.to_i}" }
else
config.action_controller.perform_caching = false
end
# Change to :null_store to avoid any caching.
config.cache_store = :memory_store
# Store uploaded files on the local file system (see config/storage.yml for options).
config.active_storage.service = :local
# Don't care if the mailer can't send.
config.action_mailer.raise_delivery_errors = false
# Make template changes take effect immediately.
config.action_mailer.perform_caching = false
# Set localhost to be used by links generated in mailer templates.
config.action_mailer.default_url_options = { host: "localhost", port: 3000 }
# Print deprecation notices to the Rails logger.
config.active_support.deprecation = :log
# Raise an error on page load if there are pending migrations.
config.active_record.migration_error = :page_load
# Highlight code that triggered database queries in logs.
config.active_record.verbose_query_logs = true
# Append comments with runtime information tags to SQL queries in logs.
config.active_record.query_log_tags_enabled = true
# Highlight code that enqueued background job in logs.
config.active_job.verbose_enqueue_logs = true
# Use Solid Queue for Active Job in development
config.active_job.queue_adapter = :solid_queue
# Raises error for missing translations.
# config.i18n.raise_on_missing_translations = true
# Annotate rendered view with file names.
config.action_view.annotate_rendered_view_with_filenames = true
# Uncomment if you wish to allow Action Cable access from any origin.
# config.action_cable.disable_request_forgery_protection = true
# Raise error when a before_action's only/except options reference missing actions.
config.action_controller.raise_on_missing_callback_actions = true
# Apply autocorrection by RuboCop to files generated by `bin/rails generate`.
# config.generators.apply_rubocop_autocorrect_after_generate!
end

View File

@@ -0,0 +1,94 @@
# frozen_string_literal: true
require "active_support/core_ext/integer/time"
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
# Eager load code on boot for better performance and memory savings (ignored by Rake tasks).
config.eager_load = true
# Full error reports are disabled.
config.consider_all_requests_local = false
# Turn on fragment caching in view templates.
config.action_controller.perform_caching = true
# Cache assets for far-future expiry since they are all digest stamped.
config.public_file_server.headers = { "cache-control" => "public, max-age=#{1.year.to_i}" }
# Enable serving of images, stylesheets, and JavaScripts from an asset server.
# config.asset_host = "http://assets.example.com"
# Store uploaded files on the local file system (see config/storage.yml for options).
config.active_storage.service = :heroku
# Assume all access to the app is happening through a SSL-terminating reverse proxy.
config.assume_ssl = true
# Set timezone for recurring job schedules (EST for "1am EST" schedule)
config.time_zone = "Eastern Time (US & Canada)"
# Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
# Disabled because SSL is terminated at the load balancer level (assume_ssl = true above handles this)
config.force_ssl = false
# Skip http-to-https redirect for the default health check endpoint.
# config.ssl_options = { redirect: { exclude: ->(request) { request.path == "/up" } } }
# Log to STDOUT with the current request id as a default log tag.
config.log_tags = [:request_id]
config.logger = ActiveSupport::TaggedLogging.logger($stdout)
# Change to "debug" to log everything (including potentially personally-identifiable information!)
config.log_level = ENV.fetch("RAILS_LOG_LEVEL", "info")
# Prevent health checks from clogging up the logs.
config.silence_healthcheck_path = "/up"
# Don't log any deprecations.
config.active_support.report_deprecations = false
# Replace the default in-process memory cache store with a durable alternative.
# config.cache_store = :mem_cache_store
# Replace the default in-process and non-durable queuing backend for Active Job.
config.active_job.queue_adapter = :solid_queue
config.action_mailer.raise_delivery_errors = true
config.action_mailer.default_url_options = {
host: ENV.fetch("APP_HOST", "app.sifonline.org"),
protocol: "https"
}
config.action_mailer.delivery_method = :smtp
config.action_mailer.smtp_settings = {
user_name: ENV.fetch("SES_SMTP_USERNAME"),
password: ENV.fetch("SES_SMTP_PASSWORD"),
address: ENV.fetch("SES_SMTP_ADDRESS", "email-smtp.us-east-1.amazonaws.com"),
port: ENV.fetch("SES_SMTP_PORT", 587),
authentication: :plain,
enable_starttls_auto: true
}
# 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
# Do not dump schema after migrations.
config.active_record.dump_schema_after_migration = false
# Only use :id for inspections in production.
config.active_record.attributes_for_inspect = [:id]
# Enable DNS rebinding protection and other `Host` header attacks.
# config.hosts = [
# "example.com", # Allow requests from example.com
# /.*\.example\.com/ # Allow requests from subdomains like `www.example.com`
# ]
#
# Skip DNS rebinding protection for the default health check endpoint.
# config.host_authorization = { exclude: ->(request) { request.path == "/up" } }
end

View File

@@ -0,0 +1,95 @@
# frozen_string_literal: true
require "active_support/core_ext/integer/time"
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
# Eager load code on boot for better performance and memory savings (ignored by Rake tasks).
config.eager_load = true
# Full error reports are disabled.
config.consider_all_requests_local = false
# Turn on fragment caching in view templates.
config.action_controller.perform_caching = true
# Cache assets for far-future expiry since they are all digest stamped.
config.public_file_server.headers = { "cache-control" => "public, max-age=#{1.year.to_i}" }
# Enable serving of images, stylesheets, and JavaScripts from an asset server.
# config.asset_host = "http://assets.example.com"
# Store uploaded files on the local file system (see config/storage.yml for options).
config.active_storage.service = :local
# Assume all access to the app is happening through a SSL-terminating reverse proxy.
config.assume_ssl = true
# Set timezone for recurring job schedules (EST for "1am EST" schedule)
config.time_zone = "Eastern Time (US & Canada)"
# Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
# Disabled because SSL is terminated at the load balancer level (assume_ssl = true above handles this)
config.force_ssl = false
# Skip http-to-https redirect for the default health check endpoint.
# config.ssl_options = { redirect: { exclude: ->(request) { request.path == "/up" } } }
# Log to STDOUT with the current request id as a default log tag.
config.log_tags = [:request_id]
$stdout.sync = true
config.logger = ActiveSupport::TaggedLogging.logger($stdout)
# Change to "debug" to log everything (including potentially personally-identifiable information!)
config.log_level = :debug
# Prevent health checks from clogging up the logs.
config.silence_healthcheck_path = "/up"
# Don't log any deprecations.
config.active_support.report_deprecations = false
# Replace the default in-process memory cache store with a durable alternative.
# config.cache_store = :mem_cache_store
# Replace the default in-process and non-durable queuing backend for Active Job.
config.active_job.queue_adapter = :solid_queue
config.action_mailer.raise_delivery_errors = true
config.action_mailer.default_url_options = {
host: ENV.fetch("APP_HOST", "staging.sifonline.org"),
protocol: "https"
}
config.action_mailer.delivery_method = :smtp
config.action_mailer.smtp_settings = {
user_name: ENV.fetch("SES_SMTP_USERNAME"),
password: ENV.fetch("SES_SMTP_PASSWORD"),
address: ENV.fetch("SES_SMTP_ADDRESS", "email-smtp.us-east-1.amazonaws.com"),
port: ENV.fetch("SES_SMTP_PORT", 587),
authentication: :plain,
enable_starttls_auto: true
}
# 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
# Do not dump schema after migrations.
config.active_record.dump_schema_after_migration = false
# Only use :id for inspections in production.
config.active_record.attributes_for_inspect = [:id]
# Enable DNS rebinding protection and other `Host` header attacks.
# config.hosts = [
# "example.com", # Allow requests from example.com
# /.*\.example\.com/ # Allow requests from subdomains like `www.example.com`
# ]
#
# Skip DNS rebinding protection for the default health check endpoint.
# config.host_authorization = { exclude: ->(request) { request.path == "/up" } }
end

View File

@@ -0,0 +1,57 @@
# frozen_string_literal: true
# The test environment is used exclusively to run your application's
# test suite. You never need to work with it otherwise. Remember that
# your test database is "scratch space" for the test suite and is wiped
# and recreated between test runs. Don't rely on the data there!
Rails.application.configure do
# Settings specified here will take precedence over those in config/application.rb.
# While tests run files are not watched, reloading is not necessary.
config.enable_reloading = false
# Eager loading loads your entire application. When running a single test locally,
# this is usually not necessary, and can slow down your test suite. However, it's
# recommended that you enable it in continuous integration systems to ensure eager
# loading is working properly before deploying your code.
config.eager_load = ENV["CI"].present?
# Configure public file server for tests with cache-control for performance.
config.public_file_server.headers = { "cache-control" => "public, max-age=3600" }
# Show full error reports.
config.consider_all_requests_local = true
config.cache_store = :null_store
# Render exception templates for rescuable exceptions and raise for other exceptions.
config.action_dispatch.show_exceptions = :rescuable
# Disable request forgery protection in test environment.
config.action_controller.allow_forgery_protection = false
# Store uploaded files on the local file system in a temporary directory.
config.active_storage.service = :test
# Tell Action Mailer not to deliver emails to the real world.
# The :test delivery method accumulates sent emails in the
# ActionMailer::Base.deliveries array.
config.action_mailer.delivery_method = :test
config.active_job.queue_adapter = :test
# Set host to be used by links generated in mailer templates.
config.action_mailer.default_url_options = { host: "example.com" }
# Print deprecation notices to the stderr.
config.active_support.deprecation = :stderr
# Raises error for missing translations.
# config.i18n.raise_on_missing_translations = true
# Annotate rendered view with file names.
# config.action_view.annotate_rendered_view_with_filenames = true
# Raise error when a before_action's only/except options reference missing actions.
config.action_controller.raise_on_missing_callback_actions = true
end

View File

@@ -0,0 +1,12 @@
# frozen_string_literal: true
# Pin npm packages by running ./bin/importmap
pin "application"
pin "@hotwired/turbo-rails", to: "turbo.min.js"
pin "@hotwired/stimulus", to: "stimulus.min.js"
pin "@hotwired/stimulus-loading", to: "stimulus-loading.js"
pin_all_from "app/javascript/controllers", under: "controllers"
pin "trix"
pin "@rails/actiontext", to: "actiontext.esm.js"
pin "chart.js", to: "https://cdn.jsdelivr.net/npm/chart.js@4.5.0/dist/chart.umd.min.js"

View File

@@ -0,0 +1,8 @@
# frozen_string_literal: true
# Action Text configuration
Rails.application.configure do
config.after_initialize do
ActionText::RichText.table_name = "action_text_rich_texts"
end
end

View File

@@ -0,0 +1 @@
API_KEY = ENV.fetch("ALPHA_VANTAGE_API_KEY", "test-api-key")

View File

@@ -0,0 +1,7 @@
# Be sure to restart your server when you modify this file.
# Version of your assets, change this if you want to expire all your assets.
Rails.application.config.assets.version = "1.0"
# Add additional assets to the asset load path.
# Rails.application.config.assets.paths << Emoji.images_path

View File

@@ -0,0 +1,25 @@
# Be sure to restart your server when you modify this file.
# Define an application-wide content security policy.
# See the Securing Rails Applications Guide for more information:
# https://guides.rubyonrails.org/security.html#content-security-policy-header
# Rails.application.configure do
# config.content_security_policy do |policy|
# policy.default_src :self, :https
# policy.font_src :self, :https, :data
# policy.img_src :self, :https, :data
# policy.object_src :none
# policy.script_src :self, :https
# policy.style_src :self, :https
# # Specify URI for violation reports
# # policy.report_uri "/csp-violation-report-endpoint"
# end
#
# # Generate session nonces for permitted importmap, inline scripts, and inline styles.
# config.content_security_policy_nonce_generator = ->(request) { request.session.id.to_s }
# config.content_security_policy_nonce_directives = %w(script-src style-src)
#
# # Report violations without enforcing the policy.
# # config.content_security_policy_report_only = true
# end

View File

@@ -0,0 +1,313 @@
# frozen_string_literal: true
# Assuming you have not yet modified this file, each configuration option below
# is set to its default value. Note that some are commented out while others
# are not: uncommented lines are intended to protect your configuration from
# breaking changes in upgrades (i.e., in the event that future versions of
# Devise change the default values for those options).
#
# Use this hook to configure devise mailer, warden hooks and so forth.
# Many of these configuration options can be set straight in your model.
Devise.setup do |config|
# The secret key used by Devise. Devise uses this key to generate
# random tokens. Changing this key will render invalid all existing
# confirmation, reset password and unlock tokens in the database.
# Devise will use the `secret_key_base` as its `secret_key`
# by default. You can change it below and use your own secret key.
# config.secret_key = 'cab719eea451506a567fb939a0d765492c19f2724bfa70b076b3500c7d530279e065a882088b17ecd58f00878c481f9f97c4d348ab781eaf76f67f97eb229d8c'
# ==> Controller configuration
# Configure the parent class to the devise controllers.
# config.parent_controller = 'DeviseController'
# ==> Mailer Configuration
# Configure the e-mail address which will be shown in Devise::Mailer,
# note that it will be overwritten if you use your own mailer class
# with default "from" parameter.
config.mailer_sender = ENV.fetch("MAILER_SENDER", "no-reply@sifonline.org")
# Configure the class responsible to send e-mails.
# config.mailer = 'Devise::Mailer'
# Configure the parent class responsible to send e-mails.
# config.parent_mailer = 'ActionMailer::Base'
# ==> ORM configuration
# Load and configure the ORM. Supports :active_record (default) and
# :mongoid (bson_ext recommended) by default. Other ORMs may be
# available as additional gems.
require "devise/orm/active_record"
# ==> Configuration for any authentication mechanism
# Configure which keys are used when authenticating a user. The default is
# just :email. You can configure it to use [:username, :subdomain], so for
# authenticating a user, both parameters are required. Remember that those
# parameters are used only when authenticating and not when retrieving from
# session. If you need permissions, you should implement that in a before filter.
# You can also supply a hash where the value is a boolean determining whether
# or not authentication should be aborted when the value is not present.
config.authentication_keys = [:username]
# Configure parameters from the request object used for authentication. Each entry
# given should be a request method and it will automatically be passed to the
# find_for_authentication method and considered in your model lookup. For instance,
# if you set :request_keys to [:subdomain], :subdomain will be used on authentication.
# The same considerations mentioned for authentication_keys also apply to request_keys.
# config.request_keys = []
# Configure which authentication keys should be case-insensitive.
# These keys will be downcased upon creating or modifying a user and when used
# to authenticate or find a user. Default is :email.
config.case_insensitive_keys = %i[username]
# Configure which authentication keys should have whitespace stripped.
# These keys will have whitespace before and after removed upon creating or
# modifying a user and when used to authenticate or find a user. Default is :email.
config.strip_whitespace_keys = %i[username]
# Tell if authentication through request.params is enabled. True by default.
# It can be set to an array that will enable params authentication only for the
# given strategies, for example, `config.params_authenticatable = [:database]` will
# enable it only for database (email + password) authentication.
# config.params_authenticatable = true
# Tell if authentication through HTTP Auth is enabled. False by default.
# It can be set to an array that will enable http authentication only for the
# given strategies, for example, `config.http_authenticatable = [:database]` will
# enable it only for database authentication.
# For API-only applications to support authentication "out-of-the-box", you will likely want to
# enable this with :database unless you are using a custom strategy.
# The supported strategies are:
# :database = Support basic authentication with authentication key + password
# config.http_authenticatable = false
# If 401 status code should be returned for AJAX requests. True by default.
# config.http_authenticatable_on_xhr = true
# The realm used in Http Basic Authentication. 'Application' by default.
# config.http_authentication_realm = 'Application'
# It will change confirmation, password recovery and other workflows
# to behave the same regardless if the e-mail provided was right or wrong.
# Does not affect registerable.
# config.paranoid = true
# By default Devise will store the user in session. You can skip storage for
# particular strategies by setting this option.
# Notice that if you are skipping storage for all authentication paths, you
# may want to disable generating routes to Devise's sessions controller by
# passing skip: :sessions to `devise_for` in your config/routes.rb
config.skip_session_storage = [:http_auth]
# By default, Devise cleans up the CSRF token on authentication to
# avoid CSRF token fixation attacks. This means that, when using AJAX
# requests for sign in and sign up, you need to get a new CSRF token
# from the server. You can disable this option at your own risk.
# config.clean_up_csrf_token_on_authentication = true
# When false, Devise will not attempt to reload routes on eager load.
# This can reduce the time taken to boot the app but if your application
# requires the Devise mappings to be loaded during boot time the application
# won't boot properly.
# config.reload_routes = true
# ==> Configuration for :database_authenticatable
# For bcrypt, this is the cost for hashing the password and defaults to 12. If
# using other algorithms, it sets how many times you want the password to be hashed.
# The number of stretches used for generating the hashed password are stored
# with the hashed password. This allows you to change the stretches without
# invalidating existing passwords.
#
# Limiting the stretches to just one in testing will increase the performance of
# your test suite dramatically. However, it is STRONGLY RECOMMENDED to not use
# a value less than 10 in other environments. Note that, for bcrypt (the default
# algorithm), the cost increases exponentially with the number of stretches (e.g.
# a value of 20 is already extremely slow: approx. 60 seconds for 1 calculation).
config.stretches = Rails.env.test? ? 1 : 12
# Set up a pepper to generate the hashed password.
# config.pepper = '2d08ab5fea21cc87c21969791c18939164944ec55c211c92d71b938068edf866ae737d4bf6c5c97e4ee6fad76ec346d1882889fade5ac05e1749a60e0287a746'
# Send a notification to the original email when the user's email is changed.
# config.send_email_changed_notification = false
# Send a notification email when the user's password is changed.
# config.send_password_change_notification = false
# ==> Configuration for :confirmable
# A period that the user is allowed to access the website even without
# confirming their account. For instance, if set to 2.days, the user will be
# able to access the website for two days without confirming their account,
# access will be blocked just in the third day.
# You can also set it to nil, which will allow the user to access the website
# without confirming their account.
# Default is 0.days, meaning the user cannot access the website without
# confirming their account.
# config.allow_unconfirmed_access_for = 2.days
# A period that the user is allowed to confirm their account before their
# token becomes invalid. For example, if set to 3.days, the user can confirm
# their account within 3 days after the mail was sent, but on the fourth day
# their account can't be confirmed with the token any more.
# Default is nil, meaning there is no restriction on how long a user can take
# before confirming their account.
# config.confirm_within = 3.days
# If true, requires any email changes to be confirmed (exactly the same way as
# initial account confirmation) to be applied. Requires additional unconfirmed_email
# db field (see migrations). Until confirmed, new email is stored in
# unconfirmed_email column, and copied to email column on successful confirmation.
config.reconfirmable = true
# Defines which key will be used when confirming an account
# config.confirmation_keys = [:email]
# ==> Configuration for :rememberable
# The time the user will be remembered without asking for credentials again.
# config.remember_for = 2.weeks
# Invalidates all the remember me tokens when the user signs out.
config.expire_all_remember_me_on_sign_out = true
# If true, extends the user's remember period when remembered via cookie.
# config.extend_remember_period = false
# Options to be passed to the created cookie. For instance, you can set
# secure: true in order to force SSL only cookies.
# config.rememberable_options = {}
# ==> Configuration for :validatable
# Range for password length.
config.password_length = 6..128
# Email regex used to validate email formats. It simply asserts that
# one (and only one) @ exists in the given string. This is mainly
# to give user feedback and not to assert the e-mail validity.
config.email_regexp = /\A[^@\s]+@[^@\s]+\z/
# ==> Configuration for :timeoutable
# The time you want to timeout the user session without activity. After this
# time the user will be asked for credentials again. Default is 30 minutes.
# config.timeout_in = 30.minutes
# ==> Configuration for :lockable
# Defines which strategy will be used to lock an account.
# :failed_attempts = Locks an account after a number of failed attempts to sign in.
# :none = No lock strategy. You should handle locking by yourself.
# config.lock_strategy = :failed_attempts
# Defines which key will be used when locking and unlocking an account
# config.unlock_keys = [:email]
# Defines which strategy will be used to unlock an account.
# :email = Sends an unlock link to the user email
# :time = Re-enables login after a certain amount of time (see :unlock_in below)
# :both = Enables both strategies
# :none = No unlock strategy. You should handle unlocking by yourself.
# config.unlock_strategy = :both
# Number of authentication tries before locking an account if lock_strategy
# is failed attempts.
# config.maximum_attempts = 20
# Time interval to unlock the account if :time is enabled as unlock_strategy.
# config.unlock_in = 1.hour
# Warn on the last attempt before the account is locked.
# config.last_attempt_warning = true
# ==> Configuration for :recoverable
#
# Defines which key will be used when recovering the password for an account
# config.reset_password_keys = [:email]
# Time interval you can reset your password with a reset password key.
# Don't put a too small interval or your users won't have the time to
# change their passwords.
config.reset_password_within = 6.hours
# When set to false, does not sign a user in automatically after their password is
# reset. Defaults to true, so a user is signed in automatically after a reset.
# config.sign_in_after_reset_password = true
# ==> Configuration for :encryptable
# Allow you to use another hashing or encryption algorithm besides bcrypt (default).
# You can use :sha1, :sha512 or algorithms from others authentication tools as
# :clearance_sha1, :authlogic_sha512 (then you should set stretches above to 20
# for default behavior) and :restful_authentication_sha1 (then you should set
# stretches to 10, and copy REST_AUTH_SITE_KEY to pepper).
#
# Require the `devise-encryptable` gem when using anything other than bcrypt
# config.encryptor = :sha512
# ==> Scopes configuration
# Turn scoped views on. Before rendering "sessions/new", it will first check for
# "users/sessions/new". It's turned off by default because it's slower if you
# are using only default views.
# config.scoped_views = false
# Configure the default scope given to Warden. By default it's the first
# devise role declared in your routes (usually :user).
# config.default_scope = :user
# Set this configuration to false if you want /users/sign_out to sign out
# only the current scope. By default, Devise signs out all scopes.
# config.sign_out_all_scopes = true
# ==> Navigation configuration
# Lists the formats that should be treated as navigational. Formats like
# :html should redirect to the sign in page when the user does not have
# access, but formats like :xml or :json, should return 401.
#
# If you have any extra navigational formats, like :iphone or :mobile, you
# should add them to the navigational formats lists.
#
# The "*/*" below is required to match Internet Explorer requests.
# config.navigational_formats = ['*/*', :html, :turbo_stream]
# The default HTTP method used to sign out a resource. Default is :delete.
config.sign_out_via = :delete
# ==> OmniAuth
# Add a new OmniAuth provider. Check the wiki for more information on setting
# up on your models and hooks.
# config.omniauth :github, 'APP_ID', 'APP_SECRET', scope: 'user,public_repo'
# ==> Warden configuration
# If you want to use other strategies, that are not supported by Devise, or
# change the failure app, you can configure them inside the config.warden block.
#
# config.warden do |manager|
# manager.intercept_401 = false
# manager.default_strategies(scope: :user).unshift :some_external_strategy
# end
# ==> Mountable engine configurations
# When using Devise inside an engine, let's call it `MyEngine`, and this engine
# is mountable, there are some extra configurations to be taken into account.
# The following options are available, assuming the engine is mounted as:
#
# mount MyEngine, at: '/my_engine'
#
# The router that invoked `devise_for`, in the example above, would be:
# config.router_name = :my_engine
#
# When using OmniAuth, Devise cannot automatically set OmniAuth path,
# so you need to do it manually. For the users scope, it would be:
# config.omniauth_path_prefix = '/my_engine/users/auth'
# ==> Hotwire/Turbo configuration
# When using Devise with Hotwire/Turbo, the http status for error responses
# and some redirects must match the following. The default in Devise for existing
# apps is `200 OK` and `302 Found` respectively, but new apps are generated with
# these new defaults that match Hotwire/Turbo behavior.
# Note: These might become the new default in future versions of Devise.
config.responder.error_status = :unprocessable_content
config.responder.redirect_status = :see_other
# ==> Configuration for :registerable
# When set to false, does not sign a user in automatically after their password is
# changed. Defaults to true, so a user is signed in automatically after changing a password.
# config.sign_in_after_change_password = true
end

View File

@@ -0,0 +1,19 @@
# Be sure to restart your server when you modify this file.
# Configure parameters to be partially matched (e.g. passw matches password) and filtered from the log file.
# Use this to limit dissemination of sensitive information.
# See the ActiveSupport::ParameterFilter documentation for supported notations and behaviors.
Rails.application.config.filter_parameters += [
:passw,
:email,
:secret,
:token,
:_key,
:crypt,
:salt,
:certificate,
:otp,
:ssn,
:cvv,
:cvc
]

View File

@@ -0,0 +1,16 @@
# Be sure to restart your server when you modify this file.
# Add new inflection rules using the following format. Inflections
# are locale specific, and you may define rules for as many different
# locales as you wish. All of these examples are active by default:
# ActiveSupport::Inflector.inflections(:en) do |inflect|
# inflect.plural /^(ox)$/i, "\\1en"
# inflect.singular /^(ox)en/i, "\\1"
# inflect.irregular "person", "people"
# inflect.uncountable %w( fish sheep )
# end
# These inflection rules are supported but not enabled by default:
# ActiveSupport::Inflector.inflections(:en) do |inflect|
# inflect.acronym "RESTful"
# end

View File

@@ -0,0 +1,3 @@
# frozen_string_literal: true
ActiveSupport.on_load(:action_view) { include LucideRails::LucideHelper } if defined?(LucideRails::LucideHelper)

View File

@@ -0,0 +1,13 @@
# Be sure to restart your server when you modify this file.
# Define an application-wide HTTP permissions policy. For further
# information see: https://developers.google.com/web/updates/2018/06/feature-policy
# Rails.application.config.permissions_policy do |policy|
# policy.camera :none
# policy.gyroscope :none
# policy.microphone :none
# policy.usb :none
# policy.fullscreen :self
# policy.payment :self, "https://secure.example.com"
# end

View File

@@ -0,0 +1,33 @@
# frozen_string_literal: true
# Mark existing migrations as safe
# rubocop:disable Style/NumericLiterals
StrongMigrations.start_after = 20250701200226
# rubocop:enable Style/NumericLiterals
# Set timeouts for migrations
# If you use PgBouncer in transaction mode, delete these lines and set timeouts on the database user
StrongMigrations.lock_timeout = 10.seconds
StrongMigrations.statement_timeout = 1.hour
# Analyze tables after indexes are added
# Outdated statistics can sometimes hurt performance
StrongMigrations.auto_analyze = true
# Set the version of the production database
# so the right checks are run in development
# StrongMigrations.target_version = 10
# Add custom checks
# StrongMigrations.add_check do |method, args|
# if method == :add_index && args[0].to_s == "users"
# stop! "No more indexes on the users table"
# end
# end
# Remove invalid indexes when rerunning migrations
# StrongMigrations.remove_invalid_indexes = true
# Make some operations safe by default
# See https://github.com/ankane/strong_migrations#safe-by-default
# StrongMigrations.safe_by_default = true

View File

@@ -0,0 +1,65 @@
# Additional translations at https://github.com/heartcombo/devise/wiki/I18n
en:
devise:
confirmations:
confirmed: "Your email address has been successfully confirmed."
send_instructions: "You will receive an email with instructions for how to confirm your email address in a few minutes."
send_paranoid_instructions: "If your email address exists in our database, you will receive an email with instructions for how to confirm your email address in a few minutes."
failure:
already_authenticated: "You are already signed in."
inactive: "Your account is not activated yet."
invalid: "Invalid %{authentication_keys} or password."
locked: "Your account is locked."
last_attempt: "You have one more attempt before your account is locked."
not_found_in_database: "Invalid %{authentication_keys} or password."
timeout: "Your session expired. Please sign in again to continue."
unauthenticated: "You need to sign in or sign up before continuing."
unconfirmed: "You have to confirm your email address before continuing."
mailer:
confirmation_instructions:
subject: "Confirmation instructions"
reset_password_instructions:
subject: "Reset password instructions"
unlock_instructions:
subject: "Unlock instructions"
email_changed:
subject: "Email Changed"
password_change:
subject: "Password Changed"
omniauth_callbacks:
failure: "Could not authenticate you from %{kind} because \"%{reason}\"."
success: "Successfully authenticated from %{kind} account."
passwords:
no_token: "You can't access this page without coming from a password reset email. If you do come from a password reset email, please make sure you used the full URL provided."
send_instructions: "You will receive an email with instructions on how to reset your password in a few minutes."
send_paranoid_instructions: "If your email address exists in our database, you will receive a password recovery link at your email address in a few minutes."
updated: "Your password has been changed successfully. You are now signed in."
updated_not_active: "Your password has been changed successfully."
registrations:
destroyed: "Bye! Your account has been successfully cancelled. We hope to see you again soon."
signed_up: "Welcome! You have signed up successfully."
signed_up_but_inactive: "You have signed up successfully. However, we could not sign you in because your account is not yet activated."
signed_up_but_locked: "You have signed up successfully. However, we could not sign you in because your account is locked."
signed_up_but_unconfirmed: "A message with a confirmation link has been sent to your email address. Please follow the link to activate your account."
update_needs_confirmation: "You updated your account successfully, but we need to verify your new email address. Please check your email and follow the confirmation link to confirm your new email address."
updated: "Your account has been updated successfully."
updated_but_not_signed_in: "Your account has been updated successfully, but since your password was changed, you need to sign in again."
sessions:
signed_in: "Signed in successfully."
signed_out: "Signed out successfully."
already_signed_out: "Signed out successfully."
unlocks:
send_instructions: "You will receive an email with instructions for how to unlock your account in a few minutes."
send_paranoid_instructions: "If your account exists, you will receive an email with instructions for how to unlock it in a few minutes."
unlocked: "Your account has been unlocked successfully. Please sign in to continue."
errors:
messages:
already_confirmed: "was already confirmed, please try signing in"
confirmation_period_expired: "needs to be confirmed within %{period}, please request a new one"
expired: "has expired, please request a new one"
not_found: "not found"
not_locked: "was not locked"
not_saved:
one: "1 error prohibited this %{resource} from being saved:"
other: "%{count} errors prohibited this %{resource} from being saved:"

View File

@@ -0,0 +1,200 @@
---
en:
activerecord:
attributes:
portfolio_transaction:
reasons:
administrative_adjustments: Administrative Adjustment
attendance_earnings: Earnings from Attendance
awards: Award
grade_earnings: Earnings from Grades
math_earnings: Earnings from Math
reading_earnings: Earnings from Reading
transaction_fees: Transaction Fees
user:
type: Role
errors:
models:
stock:
attributes:
company_website:
invalid: must be a valid HTTP or HTTPS URL
admin:
announcements:
create:
notice: Announcement created successfully.
destroy:
notice: Announcement deleted successfully.
update:
notice: Announcement updated successfully.
classrooms:
create:
notice: Classroom created successfully.
destroy:
notice: Classroom deleted successfully.
update:
notice: Classroom updated successfully.
grades:
create:
notice: Grade created successfully.
destroy:
notice: Grade deleted successfully.
update:
notice: Grade updated successfully.
portfolio_transactions:
create:
notice: Portfolio transaction created successfully.
destroy:
notice: Portfolio transaction deleted successfully.
update:
notice: Portfolio transaction updated successfully.
school_years:
create:
notice: School year created successfully.
destroy:
delete_restricted: Cannot delete school year because it has associated records.
Please remove all classrooms and quarters first.
notice: School year deleted successfully.
update:
notice: School year updated successfully.
schools:
create:
notice: School created successfully.
destroy:
notice: School deleted successfully.
update:
notice: School updated successfully.
stocks:
create:
notice: Stock created successfully.
destroy:
notice: Stock deleted successfully.
update:
notice: Stock updated successfully.
students:
add_transaction:
errors:
amount_blank: Amount must be present
reason_blank: Reason must be present
transaction_type_blank: Transaction Type must be present
notice: Transaction added successfully.
create:
notice: 'Student %{username} created successfully. Password: %{password}'
destroy:
notice: Student %{username} archived successfully.
import:
errors:
no_file: Please select a CSV file
no_students: No students found in CSV file
restore:
notice: Student %{username} restored successfully.
update:
notice: Student updated successfully.
teachers:
create:
notice: Teacher created successfully. Password reset email has been sent.
deactivations:
create:
notice: Teacher %{username} deactivated successfully.
destroy:
must_be_deactivated: Teacher must be deactivated before permanent deletion.
notice: Teacher %{username} permanently deleted.
reactivations:
create:
notice: Teacher %{username} reactivated successfully.
update:
notice: Teacher updated successfully.
users:
create:
notice: User created successfully.
destroy:
notice: User deleted successfully.
update:
notice: User updated successfully.
years:
create:
notice: Year created successfully.
destroy:
notice: Year deleted successfully.
update:
notice: Year updated successfully.
announcements:
create:
notice: Announcement was successfully created.
destroy:
notice: Announcement was successfully destroyed.
not_found: Announcement not found.
update:
notice: Announcement was successfully updated.
application:
access_denied:
admin_required: Access denied. Admin privileges required.
no_access: You do not have access to this page.
classrooms:
archived:
alert: This classroom has been archived and is no longer accessible.
create:
notice: Classroom was successfully created.
destroy:
notice: Classroom was successfully destroyed.
toggle_trading:
alert: Unable to update trading status.
disabled: Trading has been disabled for this classroom.
enabled: Trading has been enabled for this classroom.
update:
notice: Classroom was successfully updated.
fields:
portfolio_path:
index:
portfolio: Portfolio
grade_books:
finalize:
already_completed: Cannot finalize because it's already completed.
incomplete: 'Cannot finalize: Some entries are incomplete.'
notice: Grade book finalized. Funds have been distributed.
helpers:
label:
portfolio_transaction:
amount_cents: Amount
student:
portfolio_path: Portfolio Link
orders:
cancel:
confirm: Are you sure you want to cancel this order? This action cannot be undone.
pending_only: Only pending orders can be canceled
success: Order was successfully canceled
unauthorized: You can only cancel your own orders
create:
notice: Order was successfully created.
destroy:
notice: Order was successfully destroyed.
update:
notice: Order was successfully updated.
schools:
create:
notice: School was successfully created.
destroy:
notice: School was successfully destroyed.
update:
notice: School was successfully updated.
students:
add_transaction:
errors:
amount_blank: Amount must be present
reason_blank: Reason must be present
transaction_type_blank: Transaction Type must be present
success: Transaction added to student successfully.
create:
notice: 'Student %{username} created successfully. Initial password: %{password}'
destroy:
notice: Student %{username} deleted successfully.
generate_password:
notice: 'New password generated for %{username}: %{password}'
not_found: Student not found
reset_password:
notice: 'Password reset for %{username}. New password: %{password}'
update:
notice: Student updated successfully.
teachers:
create:
notice: Teacher created successfully. An account setup email has been sent.

View File

@@ -0,0 +1,48 @@
# frozen_string_literal: true
# This configuration file will be evaluated by Puma. The top-level methods that
# are invoked here are part of Puma's configuration DSL. For more information
# about methods provided by the DSL, see https://puma.io/puma/Puma/DSL.html.
#
# Puma starts a configurable number of processes (workers) and each process
# serves each request in a thread from an internal thread pool.
#
# You can control the number of workers using ENV["WEB_CONCURRENCY"]. You
# should only set this value when you want to run 2 or more workers. The
# default is already 1.
#
# The ideal number of threads per worker depends both on how much time the
# application spends waiting for IO operations and on how much you wish to
# prioritize throughput over latency.
#
# As a rule of thumb, increasing the number of threads will increase how much
# traffic a given process can handle (throughput), but due to CRuby's
# Global VM Lock (GVL) it has diminishing returns and will degrade the
# response time (latency) of the application.
#
# The default is set to 3 threads as it's deemed a decent compromise between
# throughput and latency for the average Rails application.
#
# Any libraries that use a connection pool or another resource pool should
# be configured to provide at least as many connections as the number of
# threads. This includes Active Record's `pool` parameter in `database.yml`.
threads_count = ENV.fetch("RAILS_MAX_THREADS", 3)
threads threads_count, threads_count
# Bind to unix socket when PUMA_SOCKET is set (production/staging behind nginx),
# otherwise bind to PORT for local development.
if ENV["PUMA_SOCKET"]
bind "unix://#{ENV['PUMA_SOCKET']}"
else
port ENV.fetch("PORT", 3000)
end
# Allow puma to be restarted by `bin/rails restart` command.
plugin :tmp_restart
# Run the Solid Queue supervisor inside of Puma for single-server deployments
plugin :solid_queue if ENV["SOLID_QUEUE_IN_PUMA"]
# Specify the PID file. Defaults to tmp/pids/server.pid in development.
# In other environments, only set the PID file if requested.
pidfile ENV["PIDFILE"] if ENV["PIDFILE"]

View File

@@ -0,0 +1,23 @@
default: &default
dispatchers:
- polling_interval: 1
batch_size: 500
workers:
- queues: "*"
threads: 3
processes: <%= ENV.fetch("JOB_CONCURRENCY", 1) %>
polling_interval: 0.1
scheduler:
polling_interval: 5
development:
<<: *default
test:
<<: *default
staging:
<<: *default
production:
<<: *default

View File

@@ -0,0 +1,35 @@
development: &default
daily_order_execution:
class: OrderExecutionJob
queue: default
# Every 15 minutes, every day
schedule: "*/15 * * * *"
daily_stock_prices_update:
class: StockPricesUpdateJob
queue: default
# Due to limitations of solid queue we need to use cron format for these schedules
# This is Monday through Friday at 9:00 PM EST (2:00 AM UTC Tues-Sat)
schedule: "0 2 * * 2-6"
monthly_portfolio_snapshot:
class: MonthlyPortfolioSnapshotJob
queue: default
# This is the last day of the month each month
schedule: "0 23 L * *"
# Stock attribute updates - runs weekly on Saturdays
weekly_stock_attribute_update:
class: StockAttributeUpdateJob
queue: default
# This is Saturday at 11:00 PM ET (4:00 AM UTC Sunday)
schedule: "0 4 * * 6"
test:
# No recurring jobs in test environment
staging:
<<: *default
production:
<<: *default

View File

@@ -0,0 +1,87 @@
# frozen_string_literal: true
Rails.application.routes.draw do
# Reveal health status on /up that returns 200 if the app boots with no exceptions, otherwise 500.
# Can be used by load balancers and uptime monitors to verify that the app is live.
get "up" => "rails/health#show", :as => :rails_health_check
root "home#index"
devise_for :users
devise_scope :user do
get "users/sign_up", to: redirect("/")
end
resources :users do
resources :portfolios, only: :show
end
resources :classrooms, except: [:destroy] do
member do
patch :toggle_trading
end
resources :grade_books, only: %i[show update] do
member do
post :finalize
end
end
resources :students, except: %i[index show] do
member do
patch :reset_password
patch :generate_password
end
end
resources :classroom_enrollments, only: %i[create destroy] do
member do
patch :unenroll
end
end
end
# In-house admin (formerly admin_v2 at /admin-new, now at /admin)
namespace :admin do
root "dashboard#index"
resources :component_demo, only: %i[index show] do
collection do
get :form
end
end
resources :announcements
resources :classrooms, except: [:destroy] do
member do
patch :toggle_archive
end
end
resources :schools
resources :school_years
resources :stocks
resources :students do
collection do
post :import
get :template
end
member do
patch :restore
post :add_transaction
end
end
resources :teachers do
resource :deactivation, only: [:create], controller: "teachers/deactivations"
resource :reactivation, only: [:create], controller: "teachers/reactivations"
end
resources :users
resources :portfolio_transactions, except: [:index]
end
resources :orders do
member do
patch :cancel
end
end
resources :portfolios, only: :show
resources :stocks, only: %i[show index]
resources :announcements, only: :show
end

View File

@@ -0,0 +1,39 @@
test:
service: Disk
root: <%= Rails.root.join("tmp/storage") %>
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)
# amazon:
# service: S3
# access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %>
# secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %>
# region: us-east-1
# bucket: your_own_bucket-<%= Rails.env %>
# Remember not to checkin your GCS keyfile to a repository
# google:
# service: GCS
# project: your_project
# credentials: <%= Rails.root.join("path/to/gcs.keyfile") %>
# bucket: your_own_bucket-<%= Rails.env %>
# Use bin/rails credentials:edit to set the Azure Storage secret (as azure_storage:storage_access_key)
# microsoft:
# service: AzureStorage
# storage_account_name: your_account_name
# storage_access_key: <%= Rails.application.credentials.dig(:azure_storage, :storage_access_key) %>
# container: your_container_name-<%= Rails.env %>
# For Heroku - using local with no attachments for now
heroku:
service: Disk
root: <%= Rails.root.join("tmp/storage") %>
# mirror:
# service: Mirror
# primary: local
# mirrors: [ amazon, google, microsoft ]

View File

@@ -0,0 +1,91 @@
const defaultTheme = require("tailwindcss/defaultTheme");
module.exports = {
darkMode: ["class"],
content: [
"./public/*.html",
"./app/helpers/**/*.rb",
"./app/javascript/**/*.js",
"./app/views/**/*.{erb,haml,html,slim}",
],
theme: {
container: {
center: true,
padding: "2rem",
screens: {
"2xl": "1400px",
},
},
extend: {
colors: {
border: "hsl(var(--border))",
input: "hsl(var(--input))",
ring: "hsl(var(--ring))",
background: "hsl(var(--background))",
foreground: "hsl(var(--foreground))",
primary: {
DEFAULT: "hsl(var(--primary))",
foreground: "hsl(var(--primary-foreground))",
},
secondary: {
DEFAULT: "hsl(var(--secondary))",
foreground: "hsl(var(--secondary-foreground))",
},
destructive: {
DEFAULT: "hsl(var(--destructive))",
foreground: "hsl(var(--destructive-foreground))",
},
success: {
DEFAULT: "hsl(var(--success))",
foreground: "hsl(var(--success-foreground))",
},
info: {
DEFAULT: "hsl(var(--info))",
foreground: "hsl(var(--info-foreground))",
},
attention: {
DEFAULT: "var(--yellow-50)",
foreground: "hsl(var(--attention-foreground))",
},
muted: {
DEFAULT: "hsl(var(--muted))",
foreground: "hsl(var(--muted-foreground))",
},
accent: {
DEFAULT: "hsl(var(--accent))",
foreground: "hsl(var(--accent-foreground))",
},
popover: {
DEFAULT: "hsl(var(--popover))",
foreground: "hsl(var(--popover-foreground))",
},
card: {
DEFAULT: "hsl(var(--card))",
foreground: "hsl(var(--card-foreground))",
},
},
borderRadius: {
lg: "var(--radius)",
md: "calc(var(--radius) - 2px)",
sm: "calc(var(--radius) - 4px)",
},
fontFamily: {
sans: ["var(--font-sans)", ...defaultTheme.fontFamily.sans],
},
keyframes: {
"accordion-down": {
from: { height: 0 },
to: { height: "var(--radix-accordion-content-height)" },
},
"accordion-up": {
from: { height: "var(--radix-accordion-content-height)" },
to: { height: 0 },
},
},
animation: {
"accordion-down": "accordion-down 0.2s ease-out",
"accordion-up": "accordion-up 0.2s ease-out",
},
},
},
};