diff --git a/.env.example b/.env.example new file mode 100644 index 00000000..2affe8dc --- /dev/null +++ b/.env.example @@ -0,0 +1 @@ +LITESTACK_DATA_PATH=./storage diff --git a/.gitignore b/.gitignore index 1119ea12..46b90bdf 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ # Ignore all environment files (except templates). /.env* !/.env*.erb +!/.env*.example # Ignore all logfiles and tempfiles. /log/* @@ -46,3 +47,7 @@ node_modules # https://vitejs.dev/guide/env-and-mode.html#env-files *.local + +# Ignore default Litestack SQLite databases. +/db/**/*.sqlite3 +/db/**/*.sqlite3-* diff --git a/.nvmrc b/.nvmrc new file mode 120000 index 00000000..070266a2 --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +.node-version \ No newline at end of file diff --git a/Gemfile b/Gemfile index 9b8f1665..a46c9b0f 100644 --- a/Gemfile +++ b/Gemfile @@ -5,8 +5,10 @@ ruby "3.3.0" gem "rails", "~> 7.1" # Bundle edge Rails instead: gem "rails", github: "rails/rails", branch: "main" gem "puma", ">= 5.0" # Use the Puma web server [https://github.com/puma/puma] -gem "sqlite3", "~> 1.4" # Use sqlite3 as the database for Active Record [https://github.com/sparklemotion/sqlite3-ruby] -gem "redis" # Use Redis adapter to run Action Cable in production [https://github.com/redis/redis-rb] +gem "sqlite3" # Use sqlite3 as the database for Active Record [https://github.com/sparklemotion/sqlite3-ruby] +gem "litestack" # All-in-one solution for SQLite data storage, caching, and background jobs [https://github.com/oldmoe/litestack] + +gem "rack", "~> 2.2", ">= 2.2.7" # litestack's liteboard depends on hanami-router which does not currently support rack 3.x as of Dec 2023 # Asset management gem "jsbundling-rails" # Bundle and transpile JavaScript [https://github.com/rails/jsbundling-rails] @@ -15,10 +17,9 @@ gem "stimulus-rails" # Hotwire's modest JavaScript framework [https://stimulus.h gem "turbo-rails" # Hotwire's SPA-like page accelerator [https://turbo.hotwired.dev] gem "vite_rails" # Leverage Vite to power the frontend of your Rails app [https://vite-ruby.netlify.app/guide/rails.html] -# Utitlies +# Utilities # gem "bcrypt", "~> 3.1.7" # Use Active Model has_secure_password [https://guides.rubyonrails.org/active_model_basics.html#securepassword] # gem "kredis" # Use Kredis to get higher-level data types in Redis [https://github.com/rails/kredis] -gem "dotenv-rails" # A Ruby gem to load environment variables from `.env` [https://github.com/bkeepers/dotenv] gem "jbuilder" # Build JSON APIs with ease [https://github.com/rails/jbuilder] gem "tzinfo-data", platforms: %i[windows jruby] # Windows does not include zoneinfo files, so bundle the tzinfo-data gem @@ -48,6 +49,7 @@ group :development, :test do gem "brakeman", require: false # A static analysis security vulnerability scanner for Ruby on Rails applications [https://github.com/presidentbeef/brakeman] gem "bundle-audit", require: false # Patch level verification for Bundler [https://github.com/rubysec/bundler-audit] gem "debug", platforms: %i[mri windows] # See https://guides.rubyonrails.org/debugging_rails_applications.html#debugging-with-the-debug-gem + gem "dotenv-rails" # A Ruby gem to load environment variables from `.env` [https://github.com/bkeepers/dotenv] gem "factory_bot_rails" # A library for setting up Ruby objects as test data [https://github.com/thoughtbot/factory_bot_rails] gem "faker", require: false # A library for generating fake data [https://github.com/faker-ruby/faker] gem "mail_interceptor" # Intercepts and forwards emails in non-production environments [https://github.com/bigbinary/mail_interceptor] diff --git a/Gemfile.lock b/Gemfile.lock index ab1f6ae8..ee6772fa 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -131,6 +131,11 @@ GEM i18n (>= 1.8.11, < 2) globalid (1.2.1) activesupport (>= 6.1) + hanami-router (2.0.2) + mustermann (~> 3.0) + mustermann-contrib (~> 3.0) + rack (~> 2.0) + hansi (0.2.1) hashdiff (1.0.1) i18n (1.14.1) concurrent-ruby (~> 1.0) @@ -154,6 +159,13 @@ GEM letter_opener (1.8.1) launchy (>= 2.2, < 3) lint_roller (1.1.0) + litestack (0.4.2) + erubi + hanami-router + oj + rack + sqlite3 + tilt loofah (2.22.0) crass (~> 1.0.2) nokogiri (>= 1.12.0) @@ -178,6 +190,11 @@ GEM mini_portile2 (2.8.5) minitest (5.20.0) msgpack (1.7.2) + mustermann (3.0.0) + ruby2_keywords (~> 0.0.1) + mustermann-contrib (3.0.0) + hansi (~> 0.2.0) + mustermann (= 3.0.0) mutex_m (0.2.0) net-imap (0.4.7) date @@ -192,6 +209,8 @@ GEM nokogiri (1.15.5) mini_portile2 (~> 2.8.2) racc (~> 1.4) + oj (3.16.3) + bigdecimal (>= 3.0) parallel (1.23.0) parser (3.2.2.4) ast (~> 2.4.1) @@ -203,18 +222,18 @@ GEM puma (6.4.0) nio4r (~> 2.0) racc (1.7.3) - rack (3.0.8) + rack (2.2.8) rack-mini-profiler (3.1.1) rack (>= 1.2.0) rack-proxy (0.7.7) rack - rack-session (2.0.0) - rack (>= 3.0.0) + rack-session (1.0.2) + rack (< 3) rack-test (2.1.0) rack (>= 1.3) - rackup (2.1.0) - rack (>= 3) - webrick (~> 1.8) + rackup (1.0.0) + rack (< 3) + webrick rails (7.1.2) actioncable (= 7.1.2) actionmailbox (= 7.1.2) @@ -257,10 +276,6 @@ GEM rdoc (6.6.0) psych (>= 4.0.0) redcarpet (3.6.0) - redis (5.0.5) - redis-client (>= 0.9.0) - redis-client (0.15.0) - connection_pool reek (6.1.4) kwalify (~> 0.7.0) parser (~> 3.2.0) @@ -326,8 +341,9 @@ GEM actionpack (>= 5.2) activesupport (>= 5.2) sprockets (>= 3.0.0) - sqlite3 (1.6.9) - mini_portile2 (~> 2.8.0) + sqlite3 (1.7.0-aarch64-linux) + sqlite3 (1.7.0-arm64-darwin) + sqlite3 (1.7.0-x86_64-linux) standard (1.32.0) language_server-protocol (~> 3.17.0.2) lint_roller (~> 1.0) @@ -346,6 +362,7 @@ GEM syntax_tree (6.2.0) prettier_print (>= 1.2.0) thor (1.3.0) + tilt (2.3.0) timeout (0.4.1) turbo-rails (1.5.0) actionpack (>= 6.0.0) @@ -398,20 +415,21 @@ DEPENDENCIES jbuilder jsbundling-rails letter_opener + litestack mail_interceptor markdown-rails puma (>= 5.0) + rack (~> 2.2, >= 2.2.7) rack-mini-profiler rails (~> 7.1) rails_best_practices - redis reek rouge rspec-rails selenium-webdriver sitepress-rails sprockets-rails - sqlite3 (~> 1.4) + sqlite3 standard stimulus-rails turbo-rails diff --git a/app/jobs/sample_job.rb b/app/jobs/sample_job.rb new file mode 100644 index 00000000..af93fb72 --- /dev/null +++ b/app/jobs/sample_job.rb @@ -0,0 +1,5 @@ +class SampleJob < ApplicationJob + def perform + Rails.logger.info "SampleJob is working!" + end +end diff --git a/bin/setup b/bin/setup index 71272c4d..ceb113ea 100755 --- a/bin/setup +++ b/bin/setup @@ -21,10 +21,10 @@ FileUtils.chdir APP_ROOT do # Install JavaScript dependencies system("yarn check --check-files") || system!("yarn install") - # puts "\n== Copying sample files ==" - # unless File.exist?("config/database.yml") - # FileUtils.cp "config/database.yml.sample", "config/database.yml" - # end + puts "\n== Copying sample files ==" + unless File.exist?(".env") + FileUtils.cp ".env.example", ".env" + end puts "\n== Preparing database ==" system! "bin/rails db:prepare" diff --git a/config/cable.yml b/config/cable.yml index c6d190ff..042fe987 100644 --- a/config/cable.yml +++ b/config/cable.yml @@ -1,11 +1,8 @@ development: - adapter: redis - url: redis://localhost:6379/1 + adapter: litecable test: adapter: test production: - adapter: redis - url: <%= ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" } %> - channel_prefix: joy_production + adapter: litecable diff --git a/config/database.yml b/config/database.yml index 796466ba..903869fa 100644 --- a/config/database.yml +++ b/config/database.yml @@ -4,22 +4,37 @@ # Ensure the SQLite 3 gem is defined in your Gemfile # gem "sqlite3" # +# `Litesupport.root.join("data.sqlite3")` stores +# application data in the path `./db/#{Rails.env}/data.sqlite3` +# +# `Litesupport.root(env).join(path)` stores +# application data in the path `./db/#{env}/#{path}` +# +# idle_timeout should be set to zero, to avoid recycling sqlite connections +# and losing the page cache +# default: &default - adapter: sqlite3 + adapter: litedb pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> - timeout: 5000 + idle_timeout: 0 development: <<: *default - database: storage/development.sqlite3 + database: <%= Litesupport.root("development").join("data.sqlite3") %> # 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: storage/test.sqlite3 + database: <%= Litesupport.root("test").join("data.sqlite3") %> +# Warning: Make sure your production database path is on a persistent +# volume, otherwise your application data could be deleted between deploys. +# +# You may also set the Litesupport.root in production via the +# `LITESTACK_DATA_PATH` environment variable. production: <<: *default - database: storage/production.sqlite3 + database: <%= Litesupport.root("production").join("data.sqlite3") %> + diff --git a/config/environments/development.rb b/config/environments/development.rb index 2e7fb486..b433c938 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -73,4 +73,7 @@ # Raise error when a before_action's only/except options reference missing actions config.action_controller.raise_on_missing_callback_actions = true + + # Use a real queuing backend for Active Job (and separate queues per environment). + config.active_job.queue_adapter = :litejob end diff --git a/config/environments/production.rb b/config/environments/production.rb index 6ed1f63f..8a2bc639 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -65,11 +65,12 @@ config.log_level = ENV.fetch("RAILS_LOG_LEVEL", "info") # Use a different cache store in production. - # config.cache_store = :mem_cache_store + config.cache_store = :litecache # Use a real queuing backend for Active Job (and separate queues per environment). # config.active_job.queue_adapter = :resque # config.active_job.queue_name_prefix = "joy_production" + config.active_job.queue_adapter = :litejob config.action_mailer.perform_caching = false diff --git a/config/litejob.yml b/config/litejob.yml new file mode 100644 index 00000000..3fafe415 --- /dev/null +++ b/config/litejob.yml @@ -0,0 +1,11 @@ +# path: '/queue.db' # where the database file resides +queues: + - [default, 1] # default queue with the lowest priority + - [urgent, 10, spawn] # this is not a default, a higher priority queue which will run every job in its own thread or fiber +workers: 5 # how many threads/fibers to spawn for queue processing +retries: 5 # how many times to retry a failed job before giving up +retry_delay: 60 # seconds +retry_delay_multiplier: 10 # 60 -> 600 -> 6000 and so on +dead_job_retention: 864000 # 10 days to keep completely faild jobs in the _dead queue +gc_sleep_interval: 7200 # 2 hours of sleep between checking for dead jobs that are ready to be buried forever +# logger: STDOUT # possible values are STDOUT, STDERR, NULL or a file location diff --git a/config/routes.rb b/config/routes.rb index 89b7e9ab..3ac32900 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,3 +1,5 @@ +require "litestack/liteboard/liteboard" + Rails.application.routes.draw do # Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html @@ -6,6 +8,8 @@ # Defines the root path route ("/") sitepress_root + mount Liteboard.app => "/liteboard" + # 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