diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9b66b15 --- /dev/null +++ b/.gitignore @@ -0,0 +1,38 @@ +# See https://help.github.com/articles/ignoring-files for more about ignoring files. +# +# If you find yourself ignoring temporary files generated by your text editor +# or operating system, you probably want to add a global ignore instead: +# git config --global core.excludesfile '~/.gitignore_global' + +# Ignore bundler config. +/.bundle + +# Ignore all environment files (except templates). +/.env* +!/.env*.erb + +# Ignore all logfiles and tempfiles. +/log/* +/tmp/* +!/log/.keep +!/tmp/.keep + +# Ignore pidfiles, but keep the directory. +/tmp/pids/* +!/tmp/pids/ +!/tmp/pids/.keep + +# Ignore storage (uploaded files in development and any SQLite databases). +/storage/* +!/storage/.keep +/tmp/storage/* +!/tmp/storage/ +!/tmp/storage/.keep + +/public/assets + +# Ignore master key for decrypting credentials and more. +/config/master.key + +/app/assets/builds/* +!/app/assets/builds/.keep diff --git a/.rubocop.yml b/.rubocop.yml new file mode 100644 index 0000000..f9d86d4 --- /dev/null +++ b/.rubocop.yml @@ -0,0 +1,8 @@ +# Omakase Ruby styling for Rails +inherit_gem: { rubocop-rails-omakase: rubocop.yml } + +# Overwrite or add rules to create your own house style +# +# # Use `[a, [b, c]]` not `[ a, [ b, c ] ]` +# Layout/SpaceInsideArrayLiteralBrackets: +# Enabled: false diff --git a/.ruby-version b/.ruby-version new file mode 100644 index 0000000..a0891f5 --- /dev/null +++ b/.ruby-version @@ -0,0 +1 @@ +3.3.4 diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..9b79b56 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,65 @@ +# syntax = docker/dockerfile:1 + +# Make sure RUBY_VERSION matches the Ruby version in .ruby-version and Gemfile +ARG RUBY_VERSION=3.3.4 +FROM registry.docker.com/library/ruby:$RUBY_VERSION-slim as base + +# Rails app lives here +WORKDIR /rails + +# Set production environment +ENV RAILS_ENV="production" \ + BUNDLE_DEPLOYMENT="1" \ + BUNDLE_PATH="/usr/local/bundle" \ + BUNDLE_WITHOUT="development" + + +# Throw-away build stage to reduce size of final image +FROM --platform=$TARGETPLATFORM base as build + +# Install packages needed to build gems +RUN apt-get update -qq && \ + apt-get install --no-install-recommends -y build-essential git libvips pkg-config + +# Install application gems +COPY Gemfile Gemfile.lock .ruby-version ./ +RUN bundle install && \ + rm -rf ~/.bundle/ "${BUNDLE_PATH}"/ruby/*/cache "${BUNDLE_PATH}"/ruby/*/bundler/gems/*/.git + +# Copy application code +COPY . . + +# Precompiling assets for production without requiring secret RAILS_MASTER_KEY +RUN SECRET_KEY_BASE_DUMMY=1 ./bin/rails assets:precompile + + +# Final stage for app image +FROM base + +# Install packages needed for deployment +RUN apt-get update -qq && \ + apt-get install --no-install-recommends -y curl libjemalloc2 libsqlite3-0 libvips redis && \ + rm -rf /var/lib/apt/lists /var/cache/apt/archives + +# Copy built artifacts: gems, application +COPY --from=build "${BUNDLE_PATH}" "${BUNDLE_PATH}" +COPY --from=build /rails /rails + +# Run and own only the runtime files as a non-root user for security +RUN groupadd --system --gid 1000 rails && \ + useradd rails --uid 1000 --gid 1000 --create-home --shell /bin/bash && \ + chown -R rails:rails db log storage tmp +USER 1000:1000 + +# Set version and revision +ARG APP_VERSION +ENV APP_VERSION=$APP_VERSION +ARG GIT_REVISION +ENV GIT_REVISION=$GIT_REVISION + +# Entrypoint prepares the application. +ENTRYPOINT ["/rails/bin/docker-entrypoint"] + +# Start the server by default, this can be overwritten at runtime +EXPOSE 80 443 +CMD ["bin/boot"] diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..7dffb60 --- /dev/null +++ b/Gemfile @@ -0,0 +1,55 @@ +source "https://rubygems.org" + +ruby file: ".ruby-version" + +gem "rails", "~> 7.1" + +# Drivers +gem "sqlite3", "~> 1.4" +gem "redis", ">= 4.0.1" + +# Deployment +gem "puma", ">= 5.0" + +# Jobs +gem "resque", "~> 2.6.0" +gem "resque-pool", "~> 0.7.1" + +# Front-end +gem "propshaft" +gem "importmap-rails" +gem "turbo-rails" +gem "stimulus-rails" +gem "bootstrap", "~> 5.1" +gem "sassc-rails" +gem "simple_form", "~> 5.3" + +# Other +gem "sprockets-rails" +gem "useragent", github: "basecamp/useragent" +gem "jbuilder" +gem "redcarpet", "~> 3.6" +gem "rouge", "~> 4.2" +gem "bcrypt", "~> 3.1.7" +gem "image_processing", "~> 1.2" +gem "rqrcode" +gem "thruster" +gem "front_matter_parser" +gem "rails-i18n", "~> 7.0.0" + +group :development, :test do + gem "debug" + gem "faker", require: false + gem "brakeman", require: false + gem "rubocop-rails-omakase", require: false +end + +group :development do + gem "web-console" + gem "htmlbeautifier" +end + +group :test do + gem "capybara" + gem "selenium-webdriver" +end diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 0000000..04af754 --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,420 @@ +GIT + remote: https://github.com/basecamp/useragent.git + revision: 746e37ba93cef6399920d18ad88903db92ba4e30 + specs: + useragent (0.16.10) + +GEM + remote: https://rubygems.org/ + specs: + actioncable (7.2.0.beta3) + actionpack (= 7.2.0.beta3) + activesupport (= 7.2.0.beta3) + nio4r (~> 2.0) + websocket-driver (>= 0.6.1) + zeitwerk (~> 2.6) + actionmailbox (7.2.0.beta3) + actionpack (= 7.2.0.beta3) + activejob (= 7.2.0.beta3) + activerecord (= 7.2.0.beta3) + activestorage (= 7.2.0.beta3) + activesupport (= 7.2.0.beta3) + mail (>= 2.8.0) + actionmailer (7.2.0.beta3) + actionpack (= 7.2.0.beta3) + actionview (= 7.2.0.beta3) + activejob (= 7.2.0.beta3) + activesupport (= 7.2.0.beta3) + mail (>= 2.8.0) + rails-dom-testing (~> 2.2) + actionpack (7.2.0.beta3) + actionview (= 7.2.0.beta3) + activesupport (= 7.2.0.beta3) + nokogiri (>= 1.8.5) + racc + rack (>= 2.2.4) + rack-session (>= 1.0.1) + rack-test (>= 0.6.3) + rails-dom-testing (~> 2.2) + rails-html-sanitizer (~> 1.6) + useragent (~> 0.16) + actiontext (7.2.0.beta3) + actionpack (= 7.2.0.beta3) + activerecord (= 7.2.0.beta3) + activestorage (= 7.2.0.beta3) + activesupport (= 7.2.0.beta3) + globalid (>= 0.6.0) + nokogiri (>= 1.8.5) + actionview (7.2.0.beta3) + activesupport (= 7.2.0.beta3) + builder (~> 3.1) + erubi (~> 1.11) + rails-dom-testing (~> 2.2) + rails-html-sanitizer (~> 1.6) + activejob (7.2.0.beta3) + activesupport (= 7.2.0.beta3) + globalid (>= 0.3.6) + activemodel (7.2.0.beta3) + activesupport (= 7.2.0.beta3) + activerecord (7.2.0.beta3) + activemodel (= 7.2.0.beta3) + activesupport (= 7.2.0.beta3) + timeout (>= 0.4.0) + activestorage (7.2.0.beta3) + actionpack (= 7.2.0.beta3) + activejob (= 7.2.0.beta3) + activerecord (= 7.2.0.beta3) + activesupport (= 7.2.0.beta3) + marcel (~> 1.0) + activesupport (7.2.0.beta3) + base64 + bigdecimal + concurrent-ruby (~> 1.0, >= 1.3.1) + connection_pool (>= 2.2.5) + drb + i18n (>= 1.6, < 2) + logger (>= 1.4.2) + minitest (>= 5.1) + tzinfo (~> 2.0, >= 2.0.5) + addressable (2.8.6) + public_suffix (>= 2.0.2, < 6.0) + ast (2.4.2) + autoprefixer-rails (10.4.16.0) + execjs (~> 2) + base64 (0.2.0) + bcrypt (3.1.20) + bigdecimal (3.1.8) + bindex (0.8.1) + bootstrap (5.3.3) + autoprefixer-rails (>= 9.1.0) + popper_js (>= 2.11.8, < 3) + brakeman (6.1.2) + racc + builder (3.3.0) + capybara (3.40.0) + addressable + matrix + mini_mime (>= 0.1.3) + nokogiri (~> 1.11) + rack (>= 1.6.0) + rack-test (>= 0.6.3) + regexp_parser (>= 1.5, < 3.0) + xpath (~> 3.2) + chunky_png (1.4.0) + concurrent-ruby (1.3.3) + connection_pool (2.4.1) + crass (1.0.6) + date (3.3.4) + debug (1.9.2) + irb (~> 1.10) + reline (>= 0.3.8) + drb (2.2.1) + erubi (1.13.0) + execjs (2.9.1) + faker (3.4.1) + i18n (>= 1.8.11, < 2) + ffi (1.17.0-aarch64-linux-gnu) + ffi (1.17.0-arm-linux-gnu) + ffi (1.17.0-arm64-darwin) + ffi (1.17.0-x86-linux-gnu) + ffi (1.17.0-x86_64-darwin) + ffi (1.17.0-x86_64-linux-gnu) + front_matter_parser (1.0.1) + globalid (1.2.1) + activesupport (>= 6.1) + htmlbeautifier (1.4.3) + i18n (1.14.5) + concurrent-ruby (~> 1.0) + image_processing (1.12.2) + mini_magick (>= 4.9.5, < 5) + ruby-vips (>= 2.0.17, < 3) + importmap-rails (2.0.1) + actionpack (>= 6.0.0) + activesupport (>= 6.0.0) + railties (>= 6.0.0) + io-console (0.7.2) + irb (1.13.1) + rdoc (>= 4.0.0) + reline (>= 0.4.2) + jbuilder (2.12.0) + actionview (>= 5.0.0) + activesupport (>= 5.0.0) + json (2.7.2) + language_server-protocol (3.17.0.3) + logger (1.6.0) + loofah (2.22.0) + crass (~> 1.0.2) + nokogiri (>= 1.12.0) + mail (2.8.1) + mini_mime (>= 0.1.1) + net-imap + net-pop + net-smtp + marcel (1.0.4) + matrix (0.4.2) + mini_magick (4.13.0) + mini_mime (1.1.5) + minitest (5.23.1) + mono_logger (1.1.2) + multi_json (1.15.0) + mustermann (3.0.0) + ruby2_keywords (~> 0.0.1) + net-imap (0.4.14) + date + net-protocol + net-pop (0.1.2) + net-protocol + net-protocol (0.2.2) + timeout + net-smtp (0.5.0) + net-protocol + nio4r (2.7.3) + nokogiri (1.16.6-aarch64-linux) + racc (~> 1.4) + nokogiri (1.16.6-arm-linux) + racc (~> 1.4) + nokogiri (1.16.6-arm64-darwin) + racc (~> 1.4) + nokogiri (1.16.6-x86-linux) + racc (~> 1.4) + nokogiri (1.16.6-x86_64-darwin) + racc (~> 1.4) + nokogiri (1.16.6-x86_64-linux) + racc (~> 1.4) + parallel (1.25.1) + parser (3.3.3.0) + ast (~> 2.4.1) + racc + popper_js (2.11.8) + propshaft (0.8.0) + actionpack (>= 7.0.0) + activesupport (>= 7.0.0) + rack + railties (>= 7.0.0) + psych (5.1.2) + stringio + public_suffix (5.0.5) + puma (6.4.2) + nio4r (~> 2.0) + racc (1.8.0) + rack (3.1.3) + rack-protection (4.0.0) + base64 (>= 0.1.0) + rack (>= 3.0.0, < 4) + rack-session (2.0.0) + rack (>= 3.0.0) + rack-test (2.1.0) + rack (>= 1.3) + rackup (2.1.0) + rack (>= 3) + webrick (~> 1.8) + rails (7.2.0.beta3) + actioncable (= 7.2.0.beta3) + actionmailbox (= 7.2.0.beta3) + actionmailer (= 7.2.0.beta3) + actionpack (= 7.2.0.beta3) + actiontext (= 7.2.0.beta3) + actionview (= 7.2.0.beta3) + activejob (= 7.2.0.beta3) + activemodel (= 7.2.0.beta3) + activerecord (= 7.2.0.beta3) + activestorage (= 7.2.0.beta3) + activesupport (= 7.2.0.beta3) + bundler (>= 1.15.0) + railties (= 7.2.0.beta3) + rails-dom-testing (2.2.0) + activesupport (>= 5.0.0) + minitest + nokogiri (>= 1.6) + rails-html-sanitizer (1.6.0) + loofah (~> 2.21) + nokogiri (~> 1.14) + rails-i18n (7.0.9) + i18n (>= 0.7, < 2) + railties (>= 6.0.0, < 8) + railties (7.2.0.beta3) + actionpack (= 7.2.0.beta3) + activesupport (= 7.2.0.beta3) + irb (~> 1.13) + rackup (>= 1.0.0) + rake (>= 12.2) + thor (~> 1.0, >= 1.2.2) + zeitwerk (~> 2.6) + rainbow (3.1.1) + rake (13.2.1) + rdoc (6.7.0) + psych (>= 4.0.0) + redcarpet (3.6.0) + redis (5.2.0) + redis-client (>= 0.22.0) + redis-client (0.22.2) + connection_pool + redis-namespace (1.11.0) + redis (>= 4) + regexp_parser (2.9.2) + reline (0.5.9) + io-console (~> 0.5) + resque (2.6.0) + mono_logger (~> 1.0) + multi_json (~> 1.0) + redis-namespace (~> 1.6) + sinatra (>= 0.9.2) + resque-pool (0.7.1) + rake (>= 10.0, < 14.0) + resque (>= 1.22, < 3) + rexml (3.3.0) + strscan + rouge (4.3.0) + rqrcode (2.2.0) + chunky_png (~> 1.0) + rqrcode_core (~> 1.0) + rqrcode_core (1.2.0) + rubocop (1.64.1) + json (~> 2.3) + language_server-protocol (>= 3.17.0) + parallel (~> 1.10) + parser (>= 3.3.0.2) + rainbow (>= 2.2.2, < 4.0) + regexp_parser (>= 1.8, < 3.0) + rexml (>= 3.2.5, < 4.0) + rubocop-ast (>= 1.31.1, < 2.0) + ruby-progressbar (~> 1.7) + unicode-display_width (>= 2.4.0, < 3.0) + rubocop-ast (1.31.3) + parser (>= 3.3.1.0) + rubocop-minitest (0.35.0) + rubocop (>= 1.61, < 2.0) + rubocop-ast (>= 1.31.1, < 2.0) + rubocop-performance (1.21.0) + rubocop (>= 1.48.1, < 2.0) + rubocop-ast (>= 1.31.1, < 2.0) + rubocop-rails (2.25.0) + activesupport (>= 4.2.0) + rack (>= 1.1) + rubocop (>= 1.33.0, < 2.0) + rubocop-ast (>= 1.31.1, < 2.0) + rubocop-rails-omakase (1.0.0) + rubocop + rubocop-minitest + rubocop-performance + rubocop-rails + ruby-progressbar (1.13.0) + ruby-vips (2.2.1) + ffi (~> 1.12) + ruby2_keywords (0.0.5) + rubyzip (2.3.2) + sassc (2.4.0) + ffi (~> 1.9) + sassc-rails (2.1.2) + railties (>= 4.0.0) + sassc (>= 2.0) + sprockets (> 3.0) + sprockets-rails + tilt + selenium-webdriver (4.21.1) + base64 (~> 0.2) + rexml (~> 3.2, >= 3.2.5) + rubyzip (>= 1.2.2, < 3.0) + websocket (~> 1.0) + simple_form (5.3.1) + actionpack (>= 5.2) + activemodel (>= 5.2) + sinatra (4.0.0) + mustermann (~> 3.0) + rack (>= 3.0.0, < 4) + rack-protection (= 4.0.0) + rack-session (>= 2.0.0, < 3) + tilt (~> 2.0) + sprockets (4.2.1) + concurrent-ruby (~> 1.0) + rack (>= 2.2.4, < 4) + sprockets-rails (3.5.1) + actionpack (>= 6.1) + activesupport (>= 6.1) + sprockets (>= 3.0.0) + sqlite3 (1.7.3-aarch64-linux) + sqlite3 (1.7.3-arm-linux) + sqlite3 (1.7.3-arm64-darwin) + sqlite3 (1.7.3-x86-linux) + sqlite3 (1.7.3-x86_64-darwin) + sqlite3 (1.7.3-x86_64-linux) + stimulus-rails (1.3.3) + railties (>= 6.0.0) + stringio (3.1.1) + strscan (3.1.0) + thor (1.3.1) + thruster (0.1.4) + thruster (0.1.4-aarch64-linux) + thruster (0.1.4-arm64-darwin) + thruster (0.1.4-x86_64-darwin) + thruster (0.1.4-x86_64-linux) + tilt (2.3.0) + timeout (0.4.1) + turbo-rails (2.0.5) + actionpack (>= 6.0.0) + activejob (>= 6.0.0) + railties (>= 6.0.0) + tzinfo (2.0.6) + concurrent-ruby (~> 1.0) + unicode-display_width (2.5.0) + web-console (4.2.1) + actionview (>= 6.0.0) + activemodel (>= 6.0.0) + bindex (>= 0.4.0) + railties (>= 6.0.0) + webrick (1.8.1) + websocket (1.2.10) + websocket-driver (0.7.6) + websocket-extensions (>= 0.1.0) + websocket-extensions (0.1.5) + xpath (3.2.0) + nokogiri (~> 1.8) + zeitwerk (2.6.15) + +PLATFORMS + aarch64-linux + arm-linux + arm64-darwin + x86-linux + x86_64-darwin + x86_64-linux + +DEPENDENCIES + bcrypt (~> 3.1.7) + bootstrap (~> 5.1) + brakeman + capybara + debug + faker + front_matter_parser + htmlbeautifier + image_processing (~> 1.2) + importmap-rails + jbuilder + propshaft + puma (>= 5.0) + rails (~> 7.1) + rails-i18n (~> 7.0.0) + redcarpet (~> 3.6) + redis (>= 4.0.1) + resque (~> 2.6.0) + resque-pool (~> 0.7.1) + rouge (~> 4.2) + rqrcode + rubocop-rails-omakase + sassc-rails + selenium-webdriver + simple_form (~> 5.3) + sprockets-rails + sqlite3 (~> 1.4) + stimulus-rails + thruster + turbo-rails + useragent! + web-console + +RUBY VERSION + ruby 3.3.4p94 + +BUNDLED WITH + 2.5.6 diff --git a/Procfile b/Procfile new file mode 100644 index 0000000..20e1430 --- /dev/null +++ b/Procfile @@ -0,0 +1,3 @@ +web: bundle exec thrust bin/start-app +redis: redis-server config/redis.conf +workers: FORK_PER_JOB=false INTERVAL=0.1 bundle exec resque-pool diff --git a/Rakefile b/Rakefile new file mode 100644 index 0000000..9a5ea73 --- /dev/null +++ b/Rakefile @@ -0,0 +1,6 @@ +# Add your own tasks in files placed in lib/tasks ending in .rake, +# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. + +require_relative "config/application" + +Rails.application.load_tasks diff --git a/app/assets/config/manifest.js b/app/assets/config/manifest.js new file mode 100644 index 0000000..ddd546a --- /dev/null +++ b/app/assets/config/manifest.js @@ -0,0 +1,4 @@ +//= link_tree ../images +//= link_directory ../stylesheets .css +//= link_tree ../../javascript .js +//= link_tree ../../../vendor/javascript .js diff --git a/app/assets/images/.keep b/app/assets/images/.keep new file mode 100644 index 0000000..e69de29 diff --git a/app/assets/images/add.svg b/app/assets/images/add.svg new file mode 100644 index 0000000..29d9e50 --- /dev/null +++ b/app/assets/images/add.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss new file mode 100644 index 0000000..41f414c --- /dev/null +++ b/app/assets/stylesheets/application.scss @@ -0,0 +1,15 @@ +// 1. Include functions & variables first +@import "bootstrap/functions"; +@import "bootstrap/variables"; + +$custom-colors: ( + "bootstrap": #563d7c, + "simpleform": #00617f +); +$theme-colors: map-merge($theme-colors, $custom-colors); + +// Custom bootstrap variables must be set or imported *before* bootstrap. +@import "bootstrap"; + +// simple_form helpers +@import "simple_form-bootstrap/simple_form-bootstrap"; \ No newline at end of file diff --git a/app/assets/stylesheets/simple_form-bootstrap/_form_abbr.scss b/app/assets/stylesheets/simple_form-bootstrap/_form_abbr.scss new file mode 100644 index 0000000..5ae6d22 --- /dev/null +++ b/app/assets/stylesheets/simple_form-bootstrap/_form_abbr.scss @@ -0,0 +1,3 @@ +abbr[title] { + text-decoration: none; +} diff --git a/app/assets/stylesheets/simple_form-bootstrap/_form_button.scss b/app/assets/stylesheets/simple_form-bootstrap/_form_button.scss new file mode 100644 index 0000000..0ada16b --- /dev/null +++ b/app/assets/stylesheets/simple_form-bootstrap/_form_button.scss @@ -0,0 +1,4 @@ +// apply primary button style to buttons with only `btn` class +[class="btn"] { + @extend .btn-primary; +} diff --git a/app/assets/stylesheets/simple_form-bootstrap/_form_collection_label.scss b/app/assets/stylesheets/simple_form-bootstrap/_form_collection_label.scss new file mode 100644 index 0000000..2622283 --- /dev/null +++ b/app/assets/stylesheets/simple_form-bootstrap/_form_collection_label.scss @@ -0,0 +1,9 @@ +// optional vertical & horizontal form spacing +.form-check-inline { + .form-check-label { + &.collection_check_boxes, + &.collection_radio_buttons { + margin-right: .265625rem; + } + } +} diff --git a/app/assets/stylesheets/simple_form-bootstrap/_form_legend_clear.scss b/app/assets/stylesheets/simple_form-bootstrap/_form_legend_clear.scss new file mode 100644 index 0000000..a2c5300 --- /dev/null +++ b/app/assets/stylesheets/simple_form-bootstrap/_form_legend_clear.scss @@ -0,0 +1,10 @@ +// In bootstrap 5 legend floats left and requires the following element +// to be cleared. In a radio button or checkbox group the element after +// the legend will be the automatically generated hidden input; the fix +// in https://github.com/twbs/bootstrap/pull/30345 applies to the hidden +// input and has no visual effect. Here we try to fix matters by +// applying the clear to the div wrapping the first following radio button +// or checkbox. +legend ~ div.form-check:first-of-type { + clear: left; +} diff --git a/app/assets/stylesheets/simple_form-bootstrap/_form_multi_select.scss b/app/assets/stylesheets/simple_form-bootstrap/_form_multi_select.scss new file mode 100644 index 0000000..1fb7a1c --- /dev/null +++ b/app/assets/stylesheets/simple_form-bootstrap/_form_multi_select.scss @@ -0,0 +1,8 @@ +.form-select { + &.date, + &.datetime, + &.time { + &:first-of-type { margin-left: 0 !important; } + &:last-of-type { margin-right: 0 !important; } + } +} diff --git a/app/assets/stylesheets/simple_form-bootstrap/_simple_form-bootstrap.scss b/app/assets/stylesheets/simple_form-bootstrap/_simple_form-bootstrap.scss new file mode 100644 index 0000000..8d378ed --- /dev/null +++ b/app/assets/stylesheets/simple_form-bootstrap/_simple_form-bootstrap.scss @@ -0,0 +1,5 @@ +@import "form_abbr"; +@import "form_button"; +@import "form_collection_label"; +@import "form_legend_clear"; +@import "form_multi_select"; diff --git a/app/channels/application_cable/channel.rb b/app/channels/application_cable/channel.rb new file mode 100644 index 0000000..d672697 --- /dev/null +++ b/app/channels/application_cable/channel.rb @@ -0,0 +1,4 @@ +module ApplicationCable + class Channel < ActionCable::Channel::Base + end +end diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb new file mode 100644 index 0000000..0d95db2 --- /dev/null +++ b/app/controllers/application_controller.rb @@ -0,0 +1,4 @@ +class ApplicationController < ActionController::Base + # Only allow modern browsers supporting webp images, web push, badges, import maps, CSS nesting, and CSS :has. + allow_browser versions: :modern +end diff --git a/app/controllers/concerns/.keep b/app/controllers/concerns/.keep new file mode 100644 index 0000000..e69de29 diff --git a/app/controllers/home_controller.rb b/app/controllers/home_controller.rb new file mode 100644 index 0000000..95f2992 --- /dev/null +++ b/app/controllers/home_controller.rb @@ -0,0 +1,4 @@ +class HomeController < ApplicationController + def index + end +end diff --git a/app/helpers/.keep b/app/helpers/.keep new file mode 100644 index 0000000..e69de29 diff --git a/app/javascript/application.js b/app/javascript/application.js new file mode 100644 index 0000000..a66066e --- /dev/null +++ b/app/javascript/application.js @@ -0,0 +1,6 @@ +// Configure your import map in config/importmap.rb. Read more: https://github.com/rails/importmap-rails +import "@hotwired/turbo-rails" +import "controllers" + +import "popper" +import "bootstrap" diff --git a/app/javascript/controllers/application.js b/app/javascript/controllers/application.js new file mode 100644 index 0000000..1213e85 --- /dev/null +++ b/app/javascript/controllers/application.js @@ -0,0 +1,9 @@ +import { Application } from "@hotwired/stimulus" + +const application = Application.start() + +// Configure Stimulus development experience +application.debug = false +window.Stimulus = application + +export { application } diff --git a/app/javascript/controllers/hello_controller.js b/app/javascript/controllers/hello_controller.js new file mode 100644 index 0000000..5975c07 --- /dev/null +++ b/app/javascript/controllers/hello_controller.js @@ -0,0 +1,7 @@ +import { Controller } from "@hotwired/stimulus" + +export default class extends Controller { + connect() { + this.element.textContent = "Hello World!" + } +} diff --git a/app/javascript/controllers/index.js b/app/javascript/controllers/index.js new file mode 100644 index 0000000..54ad4ca --- /dev/null +++ b/app/javascript/controllers/index.js @@ -0,0 +1,11 @@ +// Import and register all your controllers from the importmap under controllers/* + +import { application } from "controllers/application" + +// Eager load all controllers defined in the import map under controllers/**/*_controller +import { eagerLoadControllersFrom } from "@hotwired/stimulus-loading" +eagerLoadControllersFrom("controllers", application) + +// Lazy load controllers as they appear in the DOM (remember not to preload controllers in import map!) +// import { lazyLoadControllersFrom } from "@hotwired/stimulus-loading" +// lazyLoadControllersFrom("controllers", application) diff --git a/app/jobs/application_job.rb b/app/jobs/application_job.rb new file mode 100644 index 0000000..d394c3d --- /dev/null +++ b/app/jobs/application_job.rb @@ -0,0 +1,7 @@ +class ApplicationJob < ActiveJob::Base + # Automatically retry jobs that encountered a deadlock + # retry_on ActiveRecord::Deadlocked + + # Most jobs are safe to ignore if the underlying records are no longer available + # discard_on ActiveJob::DeserializationError +end diff --git a/app/mailers/application_mailer.rb b/app/mailers/application_mailer.rb new file mode 100644 index 0000000..3c34c81 --- /dev/null +++ b/app/mailers/application_mailer.rb @@ -0,0 +1,4 @@ +class ApplicationMailer < ActionMailer::Base + default from: "from@example.com" + layout "mailer" +end diff --git a/app/models/.keep b/app/models/.keep new file mode 100644 index 0000000..e69de29 diff --git a/app/views/home/index.html.erb b/app/views/home/index.html.erb new file mode 100644 index 0000000..9118d6c --- /dev/null +++ b/app/views/home/index.html.erb @@ -0,0 +1 @@ +asdasd \ No newline at end of file diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb new file mode 100644 index 0000000..053e0dd --- /dev/null +++ b/app/views/layouts/application.html.erb @@ -0,0 +1,141 @@ + + + + <%= content_for(:title) || "Open GAS" %> + + + + + + + + + <%= csrf_meta_tags %> + <%= csp_meta_tag %> + + + + <%= stylesheet_link_tag "application", "data-turbo-track": "reload" %> + <%= javascript_importmap_tags %> + + +
+ +
+
+
+
+ <%= yield %> +
+ + + diff --git a/app/views/layouts/mailer.html.erb b/app/views/layouts/mailer.html.erb new file mode 100644 index 0000000..3aac900 --- /dev/null +++ b/app/views/layouts/mailer.html.erb @@ -0,0 +1,13 @@ + + + + + + + + + <%= yield %> + + diff --git a/app/views/layouts/mailer.text.erb b/app/views/layouts/mailer.text.erb new file mode 100644 index 0000000..37f0bdd --- /dev/null +++ b/app/views/layouts/mailer.text.erb @@ -0,0 +1 @@ +<%= yield %> diff --git a/bin/boot b/bin/boot new file mode 100755 index 0000000..5106115 --- /dev/null +++ b/bin/boot @@ -0,0 +1,54 @@ +#!/usr/bin/env ruby + +require "json" +require "yaml" + +class ProcessMonitor + SIGNALS = %w[ INT TERM CLD ] + + def initialize(procfile) + @procs = process_list(procfile) + handle_signals + + @procs.each &:start + @procs.each &:wait + end + + private + def process_list(procfile) + config = YAML.load_file(procfile) + config.map { |name, cmd| MonitoredProcess.new(name, cmd) } + end + + def handle_signals + SIGNALS.each do |signal| + Signal.trap(signal) do + @procs.each &:terminate + end + end + end +end + +class MonitoredProcess + def initialize(name, cmd) + @name, @cmd = name, cmd + end + + def start + @pid = Process.spawn(@cmd) + end + + def wait + Process.wait @pid + rescue Errno::ECHILD + nil + end + + def terminate + Process.kill "TERM", @pid + rescue Errno::ESRCH + nil + end +end + +ProcessMonitor.new("Procfile") diff --git a/bin/brakeman b/bin/brakeman new file mode 100755 index 0000000..ace1c9b --- /dev/null +++ b/bin/brakeman @@ -0,0 +1,7 @@ +#!/usr/bin/env ruby +require "rubygems" +require "bundler/setup" + +ARGV.unshift("--ensure-latest") + +load Gem.bin_path("brakeman", "brakeman") diff --git a/bin/bundle b/bin/bundle new file mode 100755 index 0000000..50da5fd --- /dev/null +++ b/bin/bundle @@ -0,0 +1,109 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# +# This file was generated by Bundler. +# +# The application 'bundle' is installed as part of a gem, and +# this file is here to facilitate running it. +# + +require "rubygems" + +m = Module.new do + module_function + + def invoked_as_script? + File.expand_path($0) == File.expand_path(__FILE__) + end + + def env_var_version + ENV["BUNDLER_VERSION"] + end + + def cli_arg_version + return unless invoked_as_script? # don't want to hijack other binstubs + return unless "update".start_with?(ARGV.first || " ") # must be running `bundle update` + bundler_version = nil + update_index = nil + ARGV.each_with_index do |a, i| + if update_index && update_index.succ == i && a.match?(Gem::Version::ANCHORED_VERSION_PATTERN) + bundler_version = a + end + next unless a =~ /\A--bundler(?:[= ](#{Gem::Version::VERSION_PATTERN}))?\z/ + bundler_version = $1 + update_index = i + end + bundler_version + end + + def gemfile + gemfile = ENV["BUNDLE_GEMFILE"] + return gemfile if gemfile && !gemfile.empty? + + File.expand_path("../Gemfile", __dir__) + end + + def lockfile + lockfile = + case File.basename(gemfile) + when "gems.rb" then gemfile.sub(/\.rb$/, ".locked") + else "#{gemfile}.lock" + end + File.expand_path(lockfile) + end + + def lockfile_version + return unless File.file?(lockfile) + lockfile_contents = File.read(lockfile) + return unless lockfile_contents =~ /\n\nBUNDLED WITH\n\s{2,}(#{Gem::Version::VERSION_PATTERN})\n/ + Regexp.last_match(1) + end + + def bundler_requirement + @bundler_requirement ||= + env_var_version || + cli_arg_version || + bundler_requirement_for(lockfile_version) + end + + def bundler_requirement_for(version) + return "#{Gem::Requirement.default}.a" unless version + + bundler_gem_version = Gem::Version.new(version) + + bundler_gem_version.approximate_recommendation + end + + def load_bundler! + ENV["BUNDLE_GEMFILE"] ||= gemfile + + activate_bundler + end + + def activate_bundler + gem_error = activation_error_handling do + gem "bundler", bundler_requirement + end + return if gem_error.nil? + require_error = activation_error_handling do + require "bundler/version" + end + return if require_error.nil? && Gem::Requirement.new(bundler_requirement).satisfied_by?(Gem::Version.new(Bundler::VERSION)) + warn "Activating bundler (#{bundler_requirement}) failed:\n#{gem_error.message}\n\nTo install the version of bundler this project requires, run `gem install bundler -v '#{bundler_requirement}'`" + exit 42 + end + + def activation_error_handling + yield + nil + rescue StandardError, LoadError => e + e + end +end + +m.load_bundler! + +if m.invoked_as_script? + load Gem.bin_path("bundler", "bundle") +end diff --git a/bin/docker-entrypoint b/bin/docker-entrypoint new file mode 100755 index 0000000..7ec4917 --- /dev/null +++ b/bin/docker-entrypoint @@ -0,0 +1,8 @@ +#!/bin/bash -e + +# Enable jemalloc for reduced memory usage and latency. +if [ -f /usr/lib/*/libjemalloc.so.2 ]; then + export LD_PRELOAD="$(echo /usr/lib/*/libjemalloc.so.2) $LD_PRELOAD" +fi + +exec "${@}" diff --git a/bin/importmap b/bin/importmap new file mode 100755 index 0000000..36502ab --- /dev/null +++ b/bin/importmap @@ -0,0 +1,4 @@ +#!/usr/bin/env ruby + +require_relative "../config/application" +require "importmap/commands" diff --git a/bin/rails b/bin/rails new file mode 100755 index 0000000..efc0377 --- /dev/null +++ b/bin/rails @@ -0,0 +1,4 @@ +#!/usr/bin/env ruby +APP_PATH = File.expand_path("../config/application", __dir__) +require_relative "../config/boot" +require "rails/commands" diff --git a/bin/rake b/bin/rake new file mode 100755 index 0000000..4fbf10b --- /dev/null +++ b/bin/rake @@ -0,0 +1,4 @@ +#!/usr/bin/env ruby +require_relative "../config/boot" +require "rake" +Rake.application.run diff --git a/bin/rubocop b/bin/rubocop new file mode 100755 index 0000000..40330c0 --- /dev/null +++ b/bin/rubocop @@ -0,0 +1,8 @@ +#!/usr/bin/env ruby +require "rubygems" +require "bundler/setup" + +# explicit rubocop config increases performance slightly while avoiding config confusion. +ARGV.unshift("--config", File.expand_path("../.rubocop.yml", __dir__)) + +load Gem.bin_path("rubocop", "rubocop") diff --git a/bin/setup b/bin/setup new file mode 100755 index 0000000..67b9d6c --- /dev/null +++ b/bin/setup @@ -0,0 +1,28 @@ +#!/usr/bin/env ruby +require "fileutils" + +APP_ROOT = File.expand_path("..", __dir__) +APP_NAME = "open-GAS" + +def system!(*args) + system(*args, exception: true) +end + +FileUtils.chdir APP_ROOT do + puts "== Installing dependencies ==" + system! "gem install bundler --conservative" + system!("bundle install") + + puts "\n== Preparing database ==" + system! "bin/rails db:prepare" + + puts "\n== Removing old logs and tempfiles ==" + system! "bin/rails log:clear tmp:clear" + + puts "\n== Restarting application server ==" + system! "bin/rails restart" + + puts "\n== Configuring puma-dev ==" + system "ln -nfs #{APP_ROOT} ~/.puma-dev/#{APP_NAME}" + system "curl -Is https://#{APP_NAME}.test/up | head -n 1" +end diff --git a/bin/start-app b/bin/start-app new file mode 100755 index 0000000..9a751c2 --- /dev/null +++ b/bin/start-app @@ -0,0 +1,5 @@ +#!/bin/bash -e + +rm -f tmp/pids/server.pid +./bin/rails db:prepare +./bin/rails server diff --git a/config.ru b/config.ru new file mode 100644 index 0000000..4a3c09a --- /dev/null +++ b/config.ru @@ -0,0 +1,6 @@ +# This file is used by Rack-based servers to start the application. + +require_relative "config/environment" + +run Rails.application +Rails.application.load_server diff --git a/config/application.rb b/config/application.rb new file mode 100644 index 0000000..e8332f6 --- /dev/null +++ b/config/application.rb @@ -0,0 +1,27 @@ +require_relative "boot" + +require "rails/all" + +# Require the gems listed in Gemfile, including any gems +# you've limited to :test, :development, or :production. +Bundler.require(*Rails.groups) + +module OpenGas + class Application < Rails::Application + # Initialize configuration defaults for originally generated Rails version. + config.load_defaults 7.2 + + # 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[rails_ext assets tasks]) + + # 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 diff --git a/config/boot.rb b/config/boot.rb new file mode 100644 index 0000000..2820116 --- /dev/null +++ b/config/boot.rb @@ -0,0 +1,3 @@ +ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) + +require "bundler/setup" # Set up gems listed in the Gemfile. diff --git a/config/cable.yml b/config/cable.yml new file mode 100644 index 0000000..e53d0b0 --- /dev/null +++ b/config/cable.yml @@ -0,0 +1,11 @@ +development: + adapter: redis + url: redis://localhost:6379/1 + +test: + adapter: test + +production: + adapter: redis + url: <%= ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" } %> + channel_prefix: opengas_production diff --git a/config/database.yml b/config/database.yml new file mode 100644 index 0000000..c9c3f44 --- /dev/null +++ b/config/database.yml @@ -0,0 +1,20 @@ +default: &default + adapter: sqlite3 + pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 10 } %> + retries: 100 + default_transaction_mode: immediate + +development: + primary: + <<: *default + database: storage/db/development.sqlite3 + +test: + primary: + <<: *default + database: storage/db/test.sqlite3 + +production: + primary: + <<: *default + database: storage/db/production.sqlite3 diff --git a/config/environment.rb b/config/environment.rb new file mode 100644 index 0000000..cac5315 --- /dev/null +++ b/config/environment.rb @@ -0,0 +1,5 @@ +# Load the Rails application. +require_relative "application" + +# Initialize the Rails application. +Rails.application.initialize! diff --git a/config/environments/development.rb b/config/environments/development.rb new file mode 100644 index 0000000..6af3ffe --- /dev/null +++ b/config/environments/development.rb @@ -0,0 +1,77 @@ +require "active_support/core_ext/integer/time" + +Rails.application.configure do + # Settings specified here will take precedence over those in config/application.rb. + + # In the development environment your application's code is reloaded any time + # it changes. This slows down response time but is perfect for development + # since you don't have to restart the web server when you make code changes. + 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 caching. By default caching is disabled. + # Run rails dev:cache to toggle 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.cache_store = :memory_store + config.public_file_server.headers = { "Cache-Control" => "public, max-age=#{2.days.to_i}" } + else + config.action_controller.perform_caching = false + + config.cache_store = :null_store + end + + # 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 + + config.action_mailer.perform_caching = false + + # Print deprecation notices to the Rails logger. + config.active_support.deprecation = :log + + # Raise exceptions for disallowed deprecations. + config.active_support.disallowed_deprecation = :raise + + # Tell Active Support which deprecation messages to disallow. + config.active_support.disallowed_deprecation_warnings = [] + + # 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 + + # Highlight code that enqueued background job in logs. + config.active_job.verbose_enqueue_logs = true + + # Suppress logger output for asset requests. + config.assets.quiet = true + + # 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 diff --git a/config/environments/production.rb b/config/environments/production.rb new file mode 100644 index 0000000..ed5a046 --- /dev/null +++ b/config/environments/production.rb @@ -0,0 +1,71 @@ +require "active_support/core_ext/integer/time" +require "active_support/core_ext/numeric/bytes" + +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. 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 + + # 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 + + # Enable serving of images, stylesheets, and JavaScripts from an asset server. + # config.asset_host = "http://assets.example.com" + + config.assets.compile = false + + # Store uploaded files on the local file system (see config/storage.yml for options). + config.active_storage.service = :local + + # Log to STDOUT by default + config.logger = ActiveSupport::Logger.new(STDOUT) + .tap { |logger| logger.formatter = ::Logger::Formatter.new } + .then { |logger| ActiveSupport::TaggedLogging.new(logger) } + + # Prepend all log lines with the following tags. + config.log_tags = [ :request_id ] + + # Info include generic and useful information about system operation, but avoids logging too much + # information to avoid inadvertent exposure of personally identifiable information (PII). Use "debug" + # for everything. + config.log_level = ENV.fetch("RAILS_LOG_LEVEL", "info") + + # Cache in memory for now + config.cache_store = :redis_cache_store + + # Assets are cacheable + config.public_file_server.headers = { + "Cache-Control" => "public, max-age=#{1.year.to_i}" + } + + # 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 + + # Always be SSL'ing (unless told not to) + config.assume_ssl = ENV["DISABLE_SSL"].blank? + config.force_ssl = ENV["DISABLE_SSL"].blank? + + # Don't log any deprecations. + config.active_support.report_deprecations = false + + # Do not dump schema after migrations. + config.active_record.dump_schema_after_migration = false + + # SQLite is good, actually + config.active_record.sqlite3_production_warning = false + + config.active_job.queue_adapter = :resque +end diff --git a/config/environments/test.rb b/config/environments/test.rb new file mode 100644 index 0000000..75f22c6 --- /dev/null +++ b/config/environments/test.rb @@ -0,0 +1,64 @@ +require "active_support/core_ext/integer/time" + +# 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=#{1.hour.to_i}" } + + # Show full error reports and disable caching. + config.consider_all_requests_local = true + config.action_controller.perform_caching = false + 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 + + config.action_mailer.perform_caching = false + + # 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 + + # Print deprecation notices to the stderr. + config.active_support.deprecation = :stderr + + # Raise exceptions for disallowed deprecations. + config.active_support.disallowed_deprecation = :raise + + # Tell Active Support which deprecation messages to disallow. + config.active_support.disallowed_deprecation_warnings = [] + + # 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 + + # Load test helpers + config.autoload_paths += %w[ test/test_helpers ] +end diff --git a/config/importmap.rb b/config/importmap.rb new file mode 100644 index 0000000..8124b68 --- /dev/null +++ b/config/importmap.rb @@ -0,0 +1,10 @@ +# 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 "bootstrap", to: "bootstrap.min.js", preload: true +pin "popper", to: "popper.js", preload: true diff --git a/config/initializers/active_storage.rb b/config/initializers/active_storage.rb new file mode 100644 index 0000000..675541b --- /dev/null +++ b/config/initializers/active_storage.rb @@ -0,0 +1,5 @@ +ActiveSupport.on_load(:active_storage_blob) do + ActiveStorage::DiskController.after_action only: :show do + expires_in 1.year, public: true + end +end diff --git a/config/initializers/assets.rb b/config/initializers/assets.rb new file mode 100644 index 0000000..97653ea --- /dev/null +++ b/config/initializers/assets.rb @@ -0,0 +1,8 @@ +# 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 +Rails.application.config.assets.precompile += %w[bootstrap.min.js popper.js] diff --git a/config/initializers/content_security_policy.rb b/config/initializers/content_security_policy.rb new file mode 100644 index 0000000..b3076b3 --- /dev/null +++ b/config/initializers/content_security_policy.rb @@ -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 diff --git a/config/initializers/enable_yjit.rb b/config/initializers/enable_yjit.rb new file mode 100644 index 0000000..8a44221 --- /dev/null +++ b/config/initializers/enable_yjit.rb @@ -0,0 +1,11 @@ +# Automatically enable YJIT as of Ruby 3.3, as it brings very +# sizeable performance improvements. + +# If you are deploying to a memory constrained environment +# you may want to delete this file, but otherwise it's free +# performance. +if defined? RubyVM::YJIT.enable + Rails.application.config.after_initialize do + RubyVM::YJIT.enable + end +end diff --git a/config/initializers/extensions.rb b/config/initializers/extensions.rb new file mode 100644 index 0000000..455b96d --- /dev/null +++ b/config/initializers/extensions.rb @@ -0,0 +1 @@ +Dir["#{Rails.root}/lib/rails_ext/*"].each { |path| require "rails_ext/#{File.basename(path)}" } diff --git a/config/initializers/filter_parameter_logging.rb b/config/initializers/filter_parameter_logging.rb new file mode 100644 index 0000000..c010b83 --- /dev/null +++ b/config/initializers/filter_parameter_logging.rb @@ -0,0 +1,8 @@ +# 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 +] diff --git a/config/initializers/permissions_policy.rb b/config/initializers/permissions_policy.rb new file mode 100644 index 0000000..7db3b95 --- /dev/null +++ b/config/initializers/permissions_policy.rb @@ -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 diff --git a/config/initializers/simple_form.rb b/config/initializers/simple_form.rb new file mode 100644 index 0000000..d268784 --- /dev/null +++ b/config/initializers/simple_form.rb @@ -0,0 +1,176 @@ +# frozen_string_literal: true +# +# Uncomment this and change the path if necessary to include your own +# components. +# See https://github.com/heartcombo/simple_form#custom-components to know +# more about custom components. +# Dir[Rails.root.join('lib/components/**/*.rb')].each { |f| require f } +# +# Use this setup block to configure all options available in SimpleForm. +SimpleForm.setup do |config| + # Wrappers are used by the form builder to generate a + # complete input. You can remove any component from the + # wrapper, change the order or even add your own to the + # stack. The options given below are used to wrap the + # whole input. + config.wrappers :default, class: :input, + hint_class: :field_with_hint, error_class: :field_with_errors, valid_class: :field_without_errors do |b| + ## Extensions enabled by default + # Any of these extensions can be disabled for a + # given input by passing: `f.input EXTENSION_NAME => false`. + # You can make any of these extensions optional by + # renaming `b.use` to `b.optional`. + + # Determines whether to use HTML5 (:email, :url, ...) + # and required attributes + b.use :html5 + + # Calculates placeholders automatically from I18n + # You can also pass a string as f.input placeholder: "Placeholder" + b.use :placeholder + + ## Optional extensions + # They are disabled unless you pass `f.input EXTENSION_NAME => true` + # to the input. If so, they will retrieve the values from the model + # if any exists. If you want to enable any of those + # extensions by default, you can change `b.optional` to `b.use`. + + # Calculates maxlength from length validations for string inputs + # and/or database column lengths + b.optional :maxlength + + # Calculate minlength from length validations for string inputs + b.optional :minlength + + # Calculates pattern from format validations for string inputs + b.optional :pattern + + # Calculates min and max from length validations for numeric inputs + b.optional :min_max + + # Calculates readonly automatically from readonly attributes + b.optional :readonly + + ## Inputs + # b.use :input, class: 'input', error_class: 'is-invalid', valid_class: 'is-valid' + b.use :label_input + b.use :hint, wrap_with: { tag: :span, class: :hint } + b.use :error, wrap_with: { tag: :span, class: :error } + + ## full_messages_for + # If you want to display the full error message for the attribute, you can + # use the component :full_error, like: + # + # b.use :full_error, wrap_with: { tag: :span, class: :error } + end + + # The default wrapper to be used by the FormBuilder. + config.default_wrapper = :default + + # Define the way to render check boxes / radio buttons with labels. + # Defaults to :nested for bootstrap config. + # inline: input + label + # nested: label > input + config.boolean_style = :nested + + # Default class for buttons + config.button_class = 'btn' + + # Method used to tidy up errors. Specify any Rails Array method. + # :first lists the first message for each field. + # Use :to_sentence to list all errors for each field. + # config.error_method = :first + + # Default tag used for error notification helper. + config.error_notification_tag = :div + + # CSS class to add for error notification helper. + config.error_notification_class = 'error_notification' + + # Series of attempts to detect a default label method for collection. + # config.collection_label_methods = [ :to_label, :name, :title, :to_s ] + + # Series of attempts to detect a default value method for collection. + # config.collection_value_methods = [ :id, :to_s ] + + # You can wrap a collection of radio/check boxes in a pre-defined tag, defaulting to none. + # config.collection_wrapper_tag = nil + + # You can define the class to use on all collection wrappers. Defaulting to none. + # config.collection_wrapper_class = nil + + # You can wrap each item in a collection of radio/check boxes with a tag, + # defaulting to :span. + # config.item_wrapper_tag = :span + + # You can define a class to use in all item wrappers. Defaulting to none. + # config.item_wrapper_class = nil + + # How the label text should be generated altogether with the required text. + # config.label_text = lambda { |label, required, explicit_label| "#{required} #{label}" } + + # You can define the class to use on all labels. Default is nil. + # config.label_class = nil + + # You can define the default class to be used on forms. Can be overridden + # with `html: { :class }`. Defaulting to none. + # config.default_form_class = nil + + # You can define which elements should obtain additional classes + # config.generate_additional_classes_for = [:wrapper, :label, :input] + + # Whether attributes are required by default (or not). Default is true. + # config.required_by_default = true + + # Tell browsers whether to use the native HTML5 validations (novalidate form option). + # These validations are enabled in SimpleForm's internal config but disabled by default + # in this configuration, which is recommended due to some quirks from different browsers. + # To stop SimpleForm from generating the novalidate option, enabling the HTML5 validations, + # change this configuration to true. + config.browser_validations = false + + # Custom mappings for input types. This should be a hash containing a regexp + # to match as key, and the input type that will be used when the field name + # matches the regexp as value. + # config.input_mappings = { /count/ => :integer } + + # Custom wrappers for input types. This should be a hash containing an input + # type as key and the wrapper that will be used for all inputs with specified type. + # config.wrapper_mappings = { string: :prepend } + + # Namespaces where SimpleForm should look for custom input classes that + # override default inputs. + # config.custom_inputs_namespaces << "CustomInputs" + + # Default priority for time_zone inputs. + # config.time_zone_priority = nil + + # Default priority for country inputs. + # config.country_priority = nil + + # When false, do not use translations for labels. + # config.translate_labels = true + + # Automatically discover new inputs in Rails' autoload path. + # config.inputs_discovery = true + + # Cache SimpleForm inputs discovery + # config.cache_discovery = !Rails.env.development? + + # Default class for inputs + # config.input_class = nil + + # Define the default class of the input wrapper of the boolean input. + config.boolean_label_class = 'checkbox' + + # Defines if the default input wrapper class should be included in radio + # collection wrappers. + # config.include_default_input_wrapper_class = true + + # Defines which i18n scope will be used in Simple Form. + # config.i18n_scope = 'simple_form' + + # Defines validation classes to the input_field. By default it's nil. + # config.input_field_valid_class = 'is-valid' + # config.input_field_error_class = 'is-invalid' +end diff --git a/config/initializers/sqlite3.rb b/config/initializers/sqlite3.rb new file mode 100644 index 0000000..f34bc1d --- /dev/null +++ b/config/initializers/sqlite3.rb @@ -0,0 +1,17 @@ +module SQLite3Configuration + private + def configure_connection + super + + if @config[:retries] + retries = self.class.type_cast_config_to_integer(@config[:retries]) + raw_connection.busy_handler do |count| + (count <= retries).tap { |result| sleep count * 0.001 if result } + end + end + end +end + +ActiveSupport.on_load :active_record do + ActiveRecord::ConnectionAdapters::SQLite3Adapter.prepend SQLite3Configuration +end diff --git a/config/initializers/version.rb b/config/initializers/version.rb new file mode 100644 index 0000000..3861cc4 --- /dev/null +++ b/config/initializers/version.rb @@ -0,0 +1,2 @@ +Rails.application.config.app_version = ENV.fetch("APP_VERSION", "0") +Rails.application.config.git_revision = ENV["GIT_REVISION"] diff --git a/config/locales/en.yml b/config/locales/en.yml new file mode 100644 index 0000000..6c349ae --- /dev/null +++ b/config/locales/en.yml @@ -0,0 +1,31 @@ +# Files in the config/locales directory are used for internationalization and +# are automatically loaded by Rails. If you want to use locales other than +# English, add the necessary files in this directory. +# +# To use the locales, use `I18n.t`: +# +# I18n.t "hello" +# +# In views, this is aliased to just `t`: +# +# <%= t("hello") %> +# +# To use a different locale, set it with `I18n.locale`: +# +# I18n.locale = :es +# +# This would use the information in config/locales/es.yml. +# +# To learn more about the API, please read the Rails Internationalization guide +# at https://guides.rubyonrails.org/i18n.html. +# +# Be aware that YAML interprets the following case-insensitive strings as +# booleans: `true`, `false`, `on`, `off`, `yes`, `no`. Therefore, these strings +# must be quoted to be interpreted as strings. For example: +# +# en: +# "yes": yup +# enabled: "ON" + +en: + hello: "Hello world" diff --git a/config/locales/simple_form.en.yml b/config/locales/simple_form.en.yml new file mode 100644 index 0000000..2374383 --- /dev/null +++ b/config/locales/simple_form.en.yml @@ -0,0 +1,31 @@ +en: + simple_form: + "yes": 'Yes' + "no": 'No' + required: + text: 'required' + mark: '*' + # You can uncomment the line below if you need to overwrite the whole required html. + # When using html, text and mark won't be used. + # html: '*' + error_notification: + default_message: "Please review the problems below:" + # Examples + # labels: + # defaults: + # password: 'Password' + # user: + # new: + # email: 'E-mail to sign in.' + # edit: + # email: 'E-mail.' + # hints: + # defaults: + # username: 'User name to sign in.' + # password: 'No special characters, please.' + # include_blanks: + # defaults: + # age: 'Rather not say' + # prompts: + # defaults: + # age: 'Select your age' diff --git a/config/puma.rb b/config/puma.rb new file mode 100644 index 0000000..022ebc8 --- /dev/null +++ b/config/puma.rb @@ -0,0 +1,18 @@ +threads_count = ENV.fetch("RAILS_MAX_THREADS", 3) +threads threads_count, threads_count + +rails_env = ENV.fetch("RAILS_ENV", "development") +environment rails_env + +case rails_env +when "production" + workers_count = Integer(ENV.fetch("WEB_CONCURRENCY") { (Concurrent.processor_count * 0.666).ceil }) + workers workers_count if workers_count > 1 + + preload_app! +when "development" + worker_timeout 3600 # Don't let worker die during debugger session +end + +port ENV.fetch("PORT", 3000) +plugin :tmp_restart diff --git a/config/redis.conf b/config/redis.conf new file mode 100644 index 0000000..804d865 --- /dev/null +++ b/config/redis.conf @@ -0,0 +1,4 @@ +port 6379 +daemonize no +appendonly no +save "" diff --git a/config/resque-pool.yml b/config/resque-pool.yml new file mode 100644 index 0000000..64f569b --- /dev/null +++ b/config/resque-pool.yml @@ -0,0 +1 @@ +default: <%= (Concurrent.processor_count * 0.5).ceil %> diff --git a/config/routes.rb b/config/routes.rb new file mode 100644 index 0000000..9b8dbe5 --- /dev/null +++ b/config/routes.rb @@ -0,0 +1,5 @@ +Rails.application.routes.draw do + root to: "home#index" + + get "up" => "rails/health#show", as: :rails_health_check +end diff --git a/config/storage.yml b/config/storage.yml new file mode 100644 index 0000000..4b7f70b --- /dev/null +++ b/config/storage.yml @@ -0,0 +1,35 @@ +test: + service: Disk + root: <%= Rails.root.join("tmp/storage") %> + +local: + service: Disk + root: <%= Rails.root.join("storage", "files") %> + public: true + +# 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 %> + +# mirror: +# service: Mirror +# primary: local +# mirrors: [ amazon, google, microsoft ] diff --git a/db/migrate/.keep b/db/migrate/.keep new file mode 100644 index 0000000..e69de29 diff --git a/db/seeds.rb b/db/seeds.rb new file mode 100644 index 0000000..4fbd6ed --- /dev/null +++ b/db/seeds.rb @@ -0,0 +1,9 @@ +# This file should ensure the existence of records required to run the application in every environment (production, +# development, test). The code here should be idempotent so that it can be executed at any point in every environment. +# The data can then be loaded with the bin/rails db:seed command (or created alongside the database with db:setup). +# +# Example: +# +# ["Action", "Comedy", "Drama", "Horror"].each do |genre_name| +# MovieGenre.find_or_create_by!(name: genre_name) +# end diff --git a/lib/assets/.keep b/lib/assets/.keep new file mode 100644 index 0000000..e69de29 diff --git a/lib/tasks/.keep b/lib/tasks/.keep new file mode 100644 index 0000000..e69de29 diff --git a/log/.keep b/log/.keep new file mode 100644 index 0000000..e69de29 diff --git a/public/404.html b/public/404.html new file mode 100644 index 0000000..2be3af2 --- /dev/null +++ b/public/404.html @@ -0,0 +1,67 @@ + + + + The page you were looking for doesn't exist (404) + + + + + + +
+
+

The page you were looking for doesn't exist.

+

You may have mistyped the address or the page may have moved.

+
+

If you are the application owner check the logs for more information.

+
+ + diff --git a/public/422.html b/public/422.html new file mode 100644 index 0000000..c08eac0 --- /dev/null +++ b/public/422.html @@ -0,0 +1,67 @@ + + + + The change you wanted was rejected (422) + + + + + + +
+
+

The change you wanted was rejected.

+

Maybe you tried to change something you didn't have access to.

+
+

If you are the application owner check the logs for more information.

+
+ + diff --git a/public/500.html b/public/500.html new file mode 100644 index 0000000..78a030a --- /dev/null +++ b/public/500.html @@ -0,0 +1,66 @@ + + + + We're sorry, but something went wrong (500) + + + + + + +
+
+

We're sorry, but something went wrong.

+
+

If you are the application owner check the logs for more information.

+
+ + diff --git a/public/robots.txt b/public/robots.txt new file mode 100644 index 0000000..c19f78a --- /dev/null +++ b/public/robots.txt @@ -0,0 +1 @@ +# See https://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file diff --git a/script/admin/prepare-backup b/script/admin/prepare-backup new file mode 100755 index 0000000..e4e6d0a --- /dev/null +++ b/script/admin/prepare-backup @@ -0,0 +1,27 @@ +#!/usr/bin/env ruby +require File.expand_path("../../config/environment", File.dirname(__FILE__)) + +class Backup + class << self + def create + dest = SQLite3::Database.new(backup_filename) + backup = SQLite3::Backup.new(dest, "main", User.connection.raw_connection, "main") + + backup.step(-1) + backup.finish + dest.close + end + + private + def backup_filename + Rails.root.join("storage", "backups").tap(&:mkpath).join(primary_database_filename) + end + + def primary_database_filename + path = Rails.application.config.database_configuration[Rails.env]["primary"]["database"] + File.basename(path) + end + end +end + +Backup.create diff --git a/script/admin/reset-password b/script/admin/reset-password new file mode 100755 index 0000000..908a023 --- /dev/null +++ b/script/admin/reset-password @@ -0,0 +1,13 @@ +#!/usr/bin/env ruby +require File.expand_path("../../config/environment", File.dirname(__FILE__)) + +abort "Usage: #{$0} " unless ARGV.length == 2 + +email_address, password = ARGV +if user = User.find_by(email_address: email_address) + user.update!(password: password) + puts "Password has been reset" +else + puts "User not found" + exit -1 +end diff --git a/storage/.keep b/storage/.keep new file mode 100644 index 0000000..e69de29 diff --git a/test/application_system_test_case.rb b/test/application_system_test_case.rb new file mode 100644 index 0000000..c3f0fa4 --- /dev/null +++ b/test/application_system_test_case.rb @@ -0,0 +1,7 @@ +require "test_helper" + +class ApplicationSystemTestCase < ActionDispatch::SystemTestCase + include SystemTestHelper + + driven_by :selenium, using: :headless_chrome, screen_size: [ 1400, 1400 ] +end diff --git a/test/channels/.keep b/test/channels/.keep new file mode 100644 index 0000000..e69de29 diff --git a/test/controllers/.keep b/test/controllers/.keep new file mode 100644 index 0000000..e69de29 diff --git a/test/helpers/.keep b/test/helpers/.keep new file mode 100644 index 0000000..e69de29 diff --git a/test/integration/.keep b/test/integration/.keep new file mode 100644 index 0000000..e69de29 diff --git a/test/mailers/.keep b/test/mailers/.keep new file mode 100644 index 0000000..e69de29 diff --git a/test/models/.keep b/test/models/.keep new file mode 100644 index 0000000..e69de29 diff --git a/test/system/.keep b/test/system/.keep new file mode 100644 index 0000000..e69de29 diff --git a/test/test_helper.rb b/test/test_helper.rb new file mode 100644 index 0000000..37e5e0a --- /dev/null +++ b/test/test_helper.rb @@ -0,0 +1,15 @@ +ENV["RAILS_ENV"] ||= "test" +require_relative "../config/environment" +require "rails/test_help" + +module ActiveSupport + class TestCase + # Run tests in parallel with specified workers + parallelize(workers: :number_of_processors) + + # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order. + fixtures :all + + include SessionTestHelper + end +end diff --git a/test/test_helpers/session_test_helper.rb b/test/test_helpers/session_test_helper.rb new file mode 100644 index 0000000..15e9858 --- /dev/null +++ b/test/test_helpers/session_test_helper.rb @@ -0,0 +1,16 @@ +module SessionTestHelper + def parsed_cookies + ActionDispatch::Cookies::CookieJar.build(request, cookies.to_hash) + end + + def sign_in(user) + user = users(user) unless user.is_a? User + post session_url, params: { email_address: user.email_address, password: "secret123456" } + assert cookies[:session_token].present? + end + + def sign_out + delete session_url + assert_not cookies[:session_token].present? + end +end diff --git a/test/test_helpers/system_test_helper.rb b/test/test_helpers/system_test_helper.rb new file mode 100644 index 0000000..e0d0838 --- /dev/null +++ b/test/test_helpers/system_test_helper.rb @@ -0,0 +1,20 @@ +module SystemTestHelper + include ActionView::Helpers::JavaScriptHelper + + def sign_in(email_address, password = "secret123456") + visit new_session_url + + fill_in "email_address", with: email_address + fill_in "password", with: password + + click_on "log_in" + assert_selector "h2", text: "Handbook" + end + + def fill_house_editor(name, content) + execute_script <<~JS + const editor = document.querySelector("[name='#{name}']") + editor.value = "#{escape_javascript(content)}" + JS + end +end diff --git a/tmp/.keep b/tmp/.keep new file mode 100644 index 0000000..e69de29 diff --git a/tmp/pids/.keep b/tmp/pids/.keep new file mode 100644 index 0000000..e69de29 diff --git a/vendor/.keep b/vendor/.keep new file mode 100644 index 0000000..e69de29 diff --git a/vendor/javascript/.keep b/vendor/javascript/.keep new file mode 100644 index 0000000..e69de29