From 071a04cca9b9738bf004fc25ba0e825b04c5a7b7 Mon Sep 17 00:00:00 2001 From: James Dinkel Date: Fri, 5 Jan 2024 19:50:37 -0600 Subject: [PATCH 01/15] Add authentication --- Gemfile | 2 + README.md | 4 +- app.rb | 12 + migrate/002_create_rodauth_accounts_tables.rb | 240 ++++++++++++++++++ migrate/003_create_rodauth_pw_hashes_table.rb | 75 ++++++ 5 files changed, 331 insertions(+), 2 deletions(-) create mode 100644 migrate/002_create_rodauth_accounts_tables.rb create mode 100644 migrate/003_create_rodauth_pw_hashes_table.rb diff --git a/Gemfile b/Gemfile index b668a39..b500e57 100644 --- a/Gemfile +++ b/Gemfile @@ -6,6 +6,8 @@ gem 'tilt' gem 'erubi' gem 'sequel' gem 'rake' +gem 'rodauth' +gem 'bcrypt' # Change to whatever database you plan to use gem 'sqlite3' diff --git a/README.md b/README.md index 159cce8..2056778 100644 --- a/README.md +++ b/README.md @@ -23,8 +23,8 @@ Will need ruby; install it via package manager or a ruby manager like rbenv/ruby With this example, will basically just ignore the project's Gemfile. Debian 12 has a pretty current ruby version so just using it. ``` -sudo apt install ruby ruby-rack puma ruby-erubi ruby-tilt ruby-sequel ruby-sqlite3 rake -sudo gem install roda rack-unreloader +sudo apt install ruby ruby-rack puma ruby-erubi ruby-tilt ruby-sequel ruby-sqlite3 rake ruby-bcrypt +sudo gem install roda rack-unreloader rodauth --no-document ``` #### Option 2: Bundler diff --git a/app.rb b/app.rb index 8c321e0..56fb898 100644 --- a/app.rb +++ b/app.rb @@ -7,8 +7,19 @@ class App < Roda plugin :render, escape: true plugin :route_csrf + #secret = ENV['SESSION_SECRET'] + secret = 'hgfde456789ijhgt67uhgfdswertgbvfghjhgfde456789ijhgt67uhgfdswertgbvfghj' + plugin :sessions, secret: secret + plugin :rodauth do + enable :login, :logout, :create_account + require_email_address_logins? false + require_login_confirmation? false + hmac_secret secret + end + route do |r| check_csrf! + r.rodauth r.root do view :index @@ -20,6 +31,7 @@ class App < Roda end r.on 'hello' do + rodauth.require_authentication r.is String do |name| @page_title = 'A Custom Greeting' @name = name.capitalize diff --git a/migrate/002_create_rodauth_accounts_tables.rb b/migrate/002_create_rodauth_accounts_tables.rb new file mode 100644 index 0000000..5de2e92 --- /dev/null +++ b/migrate/002_create_rodauth_accounts_tables.rb @@ -0,0 +1,240 @@ +# frozen_string_literal: true + +Sequel.migration do + up do + extension :date_arithmetic + + # Used by the account verification and close account features + create_table(:account_statuses) do + Integer :id, primary_key: true + String :name, null: false, unique: true + end + from(:account_statuses).import([:id, :name], [[1, 'Unverified'], [2, 'Verified'], [3, 'Closed']]) + + db = self + create_table(:accounts) do + primary_key :id, type: :Bignum + foreign_key :status_id, :account_statuses, null: false, default: 1 + if db.database_type == :postgres + citext :email, null: false + constraint :valid_email, email: /^[^,;@ \r\n]+@[^,@; \r\n]+\.[^,@; \r\n]+$/ + else + String :email, null: false + end + if db.supports_partial_indexes? + index :email, unique: true, where: {status_id: [1, 2]} + else + index :email, unique: true + end + end + + deadline_opts = proc do |days| + if database_type == :mysql + {null: false} + else + {null: false, default: Sequel.date_add(Sequel::CURRENT_TIMESTAMP, days: days)} + end + end + + # Used by the audit logging feature + json_type = case database_type + when :postgres + :jsonb + when :sqlite, :mysql + :json + else + String + end + create_table(:account_authentication_audit_logs) do + primary_key :id, type: :Bignum + foreign_key :account_id, :accounts, null: false, type: :Bignum + DateTime :at, null: false, default: Sequel::CURRENT_TIMESTAMP + String :message, null: false + column :metadata, json_type + index [:account_id, :at], name: :audit_account_at_idx + index :at, name: :audit_at_idx + end + + # Used by the password reset feature + create_table(:account_password_reset_keys) do + foreign_key :id, :accounts, primary_key: true, type: :Bignum + String :key, null: false + DateTime :deadline, deadline_opts[1] + DateTime :email_last_sent, null: false, default: Sequel::CURRENT_TIMESTAMP + end + + # Used by the jwt refresh feature + create_table(:account_jwt_refresh_keys) do + primary_key :id, type: :Bignum + foreign_key :account_id, :accounts, null: false, type: :Bignum + String :key, null: false + DateTime :deadline, deadline_opts[1] + index :account_id, name: :account_jwt_rk_account_id_idx + end + + # Used by the account verification feature + create_table(:account_verification_keys) do + foreign_key :id, :accounts, primary_key: true, type: :Bignum + String :key, null: false + DateTime :requested_at, null: false, default: Sequel::CURRENT_TIMESTAMP + DateTime :email_last_sent, null: false, default: Sequel::CURRENT_TIMESTAMP + end + + # Used by the verify login change feature + create_table(:account_login_change_keys) do + foreign_key :id, :accounts, primary_key: true, type: :Bignum + String :key, null: false + String :login, null: false + DateTime :deadline, deadline_opts[1] + end + + # Used by the remember me feature + create_table(:account_remember_keys) do + foreign_key :id, :accounts, primary_key: true, type: :Bignum + String :key, null: false + DateTime :deadline, deadline_opts[14] + end + + # Used by the lockout feature + create_table(:account_login_failures) do + foreign_key :id, :accounts, primary_key: true, type: :Bignum + Integer :number, null: false, default: 1 + end + create_table(:account_lockouts) do + foreign_key :id, :accounts, primary_key: true, type: :Bignum + String :key, null: false + DateTime :deadline, deadline_opts[1] + DateTime :email_last_sent + end + + # Used by the email auth feature + create_table(:account_email_auth_keys) do + foreign_key :id, :accounts, primary_key: true, type: :Bignum + String :key, null: false + DateTime :deadline, deadline_opts[1] + DateTime :email_last_sent, null: false, default: Sequel::CURRENT_TIMESTAMP + end + + # Used by the password expiration feature + create_table(:account_password_change_times) do + foreign_key :id, :accounts, primary_key: true, type: :Bignum + DateTime :changed_at, null: false, default: Sequel::CURRENT_TIMESTAMP + end + + # Used by the account expiration feature + create_table(:account_activity_times) do + foreign_key :id, :accounts, primary_key: true, type: :Bignum + DateTime :last_activity_at, null: false + DateTime :last_login_at, null: false + DateTime :expired_at + end + + # Used by the single session feature + create_table(:account_session_keys) do + foreign_key :id, :accounts, primary_key: true, type: :Bignum + String :key, null: false + end + + # Used by the active sessions feature + create_table(:account_active_session_keys) do + foreign_key :account_id, :accounts, type: :Bignum + String :session_id + Time :created_at, null: false, default: Sequel::CURRENT_TIMESTAMP + Time :last_use, null: false, default: Sequel::CURRENT_TIMESTAMP + primary_key [:account_id, :session_id] + end + + # Used by the webauthn feature + create_table(:account_webauthn_user_ids) do + foreign_key :id, :accounts, primary_key: true, type: :Bignum + String :webauthn_id, null: false + end + create_table(:account_webauthn_keys) do + foreign_key :account_id, :accounts, type: :Bignum + String :webauthn_id + String :public_key, null: false + Integer :sign_count, null: false + Time :last_use, null: false, default: Sequel::CURRENT_TIMESTAMP + primary_key [:account_id, :webauthn_id] + end + + # Used by the otp feature + create_table(:account_otp_keys) do + foreign_key :id, :accounts, primary_key: true, type: :Bignum + String :key, null: false + Integer :num_failures, null: false, default: 0 + Time :last_use, null: false, default: Sequel::CURRENT_TIMESTAMP + end + + # Used by the recovery codes feature + create_table(:account_recovery_codes) do + foreign_key :id, :accounts, type: :Bignum + String :code + primary_key [:id, :code] + end + + # Used by the sms codes feature + create_table(:account_sms_codes) do + foreign_key :id, :accounts, primary_key: true, type: :Bignum + String :phone_number, null: false + Integer :num_failures + String :code + DateTime :code_issued_at, null: false, default: Sequel::CURRENT_TIMESTAMP + end + + case database_type + when :postgres + user = get(Sequel.lit('current_user')) + '_password' + run "GRANT REFERENCES ON accounts TO #{user}" + when :mysql, :mssql + user = if database_type == :mysql + get(Sequel.lit('current_user')).sub(/_password@/, '@') + else + get(Sequel.function(:DB_NAME)) + end + run "GRANT SELECT, INSERT, UPDATE, DELETE ON account_statuses TO #{user}" + run "GRANT SELECT, INSERT, UPDATE, DELETE ON accounts TO #{user}" + run "GRANT SELECT, INSERT, UPDATE, DELETE ON account_authentication_audit_logs TO #{user}" + run "GRANT SELECT, INSERT, UPDATE, DELETE ON account_password_reset_keys TO #{user}" + run "GRANT SELECT, INSERT, UPDATE, DELETE ON account_jwt_refresh_keys TO #{user}" + run "GRANT SELECT, INSERT, UPDATE, DELETE ON account_verification_keys TO #{user}" + run "GRANT SELECT, INSERT, UPDATE, DELETE ON account_login_change_keys TO #{user}" + run "GRANT SELECT, INSERT, UPDATE, DELETE ON account_remember_keys TO #{user}" + run "GRANT SELECT, INSERT, UPDATE, DELETE ON account_login_failures TO #{user}" + run "GRANT SELECT, INSERT, UPDATE, DELETE ON account_email_auth_keys TO #{user}" + run "GRANT SELECT, INSERT, UPDATE, DELETE ON account_lockouts TO #{user}" + run "GRANT SELECT, INSERT, UPDATE, DELETE ON account_password_change_times TO #{user}" + run "GRANT SELECT, INSERT, UPDATE, DELETE ON account_activity_times TO #{user}" + run "GRANT SELECT, INSERT, UPDATE, DELETE ON account_session_keys TO #{user}" + run "GRANT SELECT, INSERT, UPDATE, DELETE ON account_active_session_keys TO #{user}" + run "GRANT SELECT, INSERT, UPDATE, DELETE ON account_webauthn_user_ids TO #{user}" + run "GRANT SELECT, INSERT, UPDATE, DELETE ON account_webauthn_keys TO #{user}" + run "GRANT SELECT, INSERT, UPDATE, DELETE ON account_otp_keys TO #{user}" + run "GRANT SELECT, INSERT, UPDATE, DELETE ON account_recovery_codes TO #{user}" + run "GRANT SELECT, INSERT, UPDATE, DELETE ON account_sms_codes TO #{user}" + end + end + + down do + drop_table(:account_sms_codes, + :account_recovery_codes, + :account_otp_keys, + :account_webauthn_keys, + :account_webauthn_user_ids, + :account_session_keys, + :account_active_session_keys, + :account_activity_times, + :account_password_change_times, + :account_email_auth_keys, + :account_lockouts, + :account_login_failures, + :account_remember_keys, + :account_login_change_keys, + :account_verification_keys, + :account_jwt_refresh_keys, + :account_password_reset_keys, + :account_authentication_audit_logs, + :accounts, + :account_statuses) + end +end diff --git a/migrate/003_create_rodauth_pw_hashes_table.rb b/migrate/003_create_rodauth_pw_hashes_table.rb new file mode 100644 index 0000000..565018b --- /dev/null +++ b/migrate/003_create_rodauth_pw_hashes_table.rb @@ -0,0 +1,75 @@ +# frozen_string_literal: true + +require 'rodauth/migrations' + +Sequel.migration do + up do + create_table(:account_password_hashes) do + foreign_key :id, :accounts, primary_key: true, type: :Bignum + String :password_hash, null: false + end + Rodauth.create_database_authentication_functions(self) + case database_type + when :postgres + user = get(Sequel.lit('current_user')).sub(/_password\z/, '') + run "REVOKE ALL ON account_password_hashes FROM public" + run "REVOKE ALL ON FUNCTION rodauth_get_salt(int8) FROM public" + run "REVOKE ALL ON FUNCTION rodauth_valid_password_hash(int8, text) FROM public" + run "GRANT INSERT, UPDATE, DELETE ON account_password_hashes TO #{user}" + run "GRANT SELECT(id) ON account_password_hashes TO #{user}" + run "GRANT EXECUTE ON FUNCTION rodauth_get_salt(int8) TO #{user}" + run "GRANT EXECUTE ON FUNCTION rodauth_valid_password_hash(int8, text) TO #{user}" + when :mysql + user = get(Sequel.lit('current_user')).sub(/_password@/, '@') + db_name = get(Sequel.function(:database)) + run "GRANT EXECUTE ON #{db_name}.* TO #{user}" + run "GRANT INSERT, UPDATE, DELETE ON account_password_hashes TO #{user}" + run "GRANT SELECT (id) ON account_password_hashes TO #{user}" + when :mssql + user = get(Sequel.function(:DB_NAME)) + run "GRANT EXECUTE ON rodauth_get_salt TO #{user}" + run "GRANT EXECUTE ON rodauth_valid_password_hash TO #{user}" + run "GRANT INSERT, UPDATE, DELETE ON account_password_hashes TO #{user}" + run "GRANT SELECT ON account_password_hashes(id) TO #{user}" + end + + # Used by the disallow_password_reuse feature + create_table(:account_previous_password_hashes) do + primary_key :id, type: :Bignum + foreign_key :account_id, :accounts, type: :Bignum + String :password_hash, null: false + end + Rodauth.create_database_previous_password_check_functions(self) + + case database_type + when :postgres + user = get(Sequel.lit('current_user')).sub(/_password\z/, '') + run "REVOKE ALL ON account_previous_password_hashes FROM public" + run "REVOKE ALL ON FUNCTION rodauth_get_previous_salt(int8) FROM public" + run "REVOKE ALL ON FUNCTION rodauth_previous_password_hash_match(int8, text) FROM public" + run "GRANT INSERT, UPDATE, DELETE ON account_previous_password_hashes TO #{user}" + run "GRANT SELECT(id, account_id) ON account_previous_password_hashes TO #{user}" + run "GRANT USAGE ON account_previous_password_hashes_id_seq TO #{user}" + run "GRANT EXECUTE ON FUNCTION rodauth_get_previous_salt(int8) TO #{user}" + run "GRANT EXECUTE ON FUNCTION rodauth_previous_password_hash_match(int8, text) TO #{user}" + when :mysql + user = get(Sequel.lit('current_user')).sub(/_password@/, '@') + db_name = get(Sequel.function(:database)) + run "GRANT EXECUTE ON #{db_name}.* TO #{user}" + run "GRANT INSERT, UPDATE, DELETE ON account_previous_password_hashes TO #{user}" + run "GRANT SELECT (id, account_id) ON account_previous_password_hashes TO #{user}" + when :mssql + user = get(Sequel.function(:DB_NAME)) + run "GRANT EXECUTE ON rodauth_get_previous_salt TO #{user}" + run "GRANT EXECUTE ON rodauth_previous_password_hash_match TO #{user}" + run "GRANT INSERT, UPDATE, DELETE ON account_previous_password_hashes TO #{user}" + run "GRANT SELECT ON account_previous_password_hashes(id, account_id) TO #{user}" + end + end + + down do + Rodauth.drop_database_previous_password_check_functions(self) + Rodauth.drop_database_authentication_functions(self) + drop_table(:account_previous_password_hashes, :account_password_hashes) + end +end From 9ae07fc30186feb3c5b042f627a92e550e2cc9d4 Mon Sep 17 00:00:00 2001 From: James Date: Thu, 11 Jan 2024 15:49:59 -0600 Subject: [PATCH 02/15] Update Readme.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 2056778..693377c 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,7 @@ Run the bundle command from the project's root directory ``` sudo apt install ruby ruby-bundler +cd /path/to/project/ bundle config set --local path 'vendor/bundle' bundle install ``` From 5a91e0d0ddc6a74bcac0f568f4946b599cbddcbb Mon Sep 17 00:00:00 2001 From: james Date: Fri, 1 Nov 2024 14:52:56 -0500 Subject: [PATCH 03/15] Expanded setup instructions in Readme. --- README.md | 40 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 159cce8..ca3abc2 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,17 @@ Next Roda app template iterations will add database, then authentication. ## Setup +### Get git + +``` +sudo apt install git +git config --global user.email "myemail@gmail.com" +git config --global user.name "Full Name" +git config --global credential.helper "cache" + +git clone https://path/to/project.git +``` + ### Prereq installs Will need ruby; install it via package manager or a ruby manager like rbenv/ruby-build. Will need the roda gem, and then an application server such as puma (recommended), gunicorn, or passenger. My examples will use system ruby and puma. @@ -25,18 +36,41 @@ With this example, will basically just ignore the project's Gemfile. Debian 12 ``` sudo apt install ruby ruby-rack puma ruby-erubi ruby-tilt ruby-sequel ruby-sqlite3 rake sudo gem install roda rack-unreloader + +cd my-project ``` -#### Option 2: Bundler +#### Option 2a: Bundler system package -Run the bundle command from the project's root directory +Run the bundle install command from the project's root directory ``` -sudo apt install ruby ruby-bundler +sudo apt install ruby ruby-bundler ruby-dev gcc pkgconf make g++ bundle config set --local path 'vendor/bundle' + +cd my-project bundle install ``` +#### Option 2b: Bundler system gem (recommended) + +Similar to above but might as well use the gem install to get the latest bundler. The Debian apt packaged bundler is currently a bit outdated and missing some features compared to the latest. + +``` +sudo apt install ruby ruby-dev gcc pkgconf make g++ +echo 'gem: --no-document' >> ~/.gemrc +sudo cp ~/.gemrc /root/ +sudo gem install bundler +bundle config set --local path 'vendor/bundle' + +cd my-project +bundle install +``` + +#### Option 3: Rbenv Ruby + +Todo + ## Run it In the project root directory: From c05c3d3a89973d75e657aea520b065fa6901714d Mon Sep 17 00:00:00 2001 From: james Date: Fri, 1 Nov 2024 15:10:17 -0500 Subject: [PATCH 04/15] Update views/layout.erb for bulma 1.0.2 --- views/layout.erb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/views/layout.erb b/views/layout.erb index c5ec841..3ab9311 100644 --- a/views/layout.erb +++ b/views/layout.erb @@ -1,16 +1,16 @@ - + <%= @page_title || "My Website" %> - +
-

<%= @page_title || 'Page Title Placeholder' %>

- <% # maybe put a flash section here some day %> +

<%= @page_title || 'Page Title Placeholder' %>

+ <% # maybe put a flash section here some day, what Bulma calls the Hero component I think %> <%== yield %>
From 57462f98fcfc8fd7f6154025ad0af824e45fceff Mon Sep 17 00:00:00 2001 From: james Date: Fri, 1 Nov 2024 15:43:52 -0500 Subject: [PATCH 05/15] Update README.md --- README.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ca3abc2..8012f40 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,10 @@ Run the bundle install command from the project's root directory ``` sudo apt install ruby ruby-bundler ruby-dev gcc pkgconf make g++ -bundle config set --local path 'vendor/bundle' +echo 'gem: --no-document' >> ~/.gemrc +sudo cp ~/.gemrc /root/ +bundle config set --global path 'vendor/bundle' +sudo cp -r .bundle /root/ cd my-project bundle install @@ -61,7 +64,8 @@ sudo apt install ruby ruby-dev gcc pkgconf make g++ echo 'gem: --no-document' >> ~/.gemrc sudo cp ~/.gemrc /root/ sudo gem install bundler -bundle config set --local path 'vendor/bundle' +bundle config set --global path 'vendor/bundle' +sudo cp -r .bundle /root/ cd my-project bundle install From 10bcb49b56e4434b5df8d72bd0dd0f206123049f Mon Sep 17 00:00:00 2001 From: james Date: Fri, 1 Nov 2024 15:50:21 -0500 Subject: [PATCH 06/15] Update README.md --- README.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 8012f40..c36f07e 100644 --- a/README.md +++ b/README.md @@ -80,12 +80,18 @@ Todo In the project root directory: ``` +bundle exec puma + +# or if you did not use bundler to install puma... puma ``` This default to development mode. Run it in production mode with: ``` +RACK_ENV=production bundle exec puma + +# or if you did not install puma RACK_ENV=production puma ``` @@ -93,7 +99,7 @@ For development, just run it like that. For production, probably want to set up ### Run it with systemd in production -Copy the example myapp.service file to `/etc/systemd/system/` and edit accordingly. The example assumes a user named "myapp" with a group name "myapp", the application files are in `/opt/myapp/`, and puma is the system puma. +Copy the example myapp.service file to `/etc/systemd/system/` and edit accordingly. The example assumes a user named "myapp" with a group name "myapp", the application files are in `/opt/myapp/`, and puma is the system puma. If you installed puma with bundler, the exe will be at `/opt/myapp/vendor/bundle/ruby/3.1.0/bin/puma`. ### Notes From 3d679586d80a3763f4fa6844ad9c2981fccf510f Mon Sep 17 00:00:00 2001 From: james Date: Fri, 1 Nov 2024 16:59:26 -0500 Subject: [PATCH 07/15] Update views/greeting.erb --- views/greeting.erb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/greeting.erb b/views/greeting.erb index 9de747b..15c8d7a 100644 --- a/views/greeting.erb +++ b/views/greeting.erb @@ -1,3 +1,3 @@ -

+

Hello, <%= @name %>!

From 15e6c8dff89f251974bb01bf87c83e3c14654287 Mon Sep 17 00:00:00 2001 From: james Date: Fri, 1 Nov 2024 17:00:55 -0500 Subject: [PATCH 08/15] Update views/index.erb --- views/index.erb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/index.erb b/views/index.erb index 31b3b2d..25704e6 100644 --- a/views/index.erb +++ b/views/index.erb @@ -1,3 +1,3 @@ -

+

Welcome to my new page! Running in <%= ENV['RACK_ENV'] %> mode!

From 9f00a34f7db27f465046ef3e62be01d2b41f5dad Mon Sep 17 00:00:00 2001 From: james Date: Fri, 1 Nov 2024 17:01:38 -0500 Subject: [PATCH 09/15] Update views/layout.erb --- views/layout.erb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/views/layout.erb b/views/layout.erb index 3ab9311..b18438a 100644 --- a/views/layout.erb +++ b/views/layout.erb @@ -9,8 +9,9 @@
-

<%= @page_title || 'Page Title Placeholder' %>

- <% # maybe put a flash section here some day, what Bulma calls the Hero component I think %> +

+ <%= @page_title || 'Page Title Placeholder' %> +

<%== yield %>
From 4f3d003a35529d61c98d1f942dca0ebe7b478f66 Mon Sep 17 00:00:00 2001 From: james Date: Fri, 1 Nov 2024 17:03:48 -0500 Subject: [PATCH 10/15] Update views/users.erb --- views/users.erb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/users.erb b/views/users.erb index 13cc1e7..7fd2caf 100644 --- a/views/users.erb +++ b/views/users.erb @@ -1,5 +1,5 @@ <% for u in @users do %> -

+

Hello, <%= u.name %>! You're #<%= u.id %>!

<% end %> From 0cd5093976a73f793a182b12894d827385775fb5 Mon Sep 17 00:00:00 2001 From: james Date: Sat, 2 Nov 2024 22:36:20 -0500 Subject: [PATCH 11/15] Update .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index cb44c0a..4530e79 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /.rake_tasks~ /temp.db +/vendor/ \ No newline at end of file From 2024a8367bc31fca7ae044ed063fce1c2a9ec074 Mon Sep 17 00:00:00 2001 From: james Date: Sat, 2 Nov 2024 23:08:12 -0500 Subject: [PATCH 12/15] link to latest bulma 1.x --- views/layout.erb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/layout.erb b/views/layout.erb index b18438a..4869da1 100644 --- a/views/layout.erb +++ b/views/layout.erb @@ -4,7 +4,7 @@ <%= @page_title || "My Website" %> - +
From a9f4b05b1625c0616b48d80ab652fd82757bb571 Mon Sep 17 00:00:00 2001 From: james Date: Mon, 4 Nov 2024 23:25:40 -0600 Subject: [PATCH 13/15] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c36f07e..aee5c23 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ cd my-project Run the bundle install command from the project's root directory ``` -sudo apt install ruby ruby-bundler ruby-dev gcc pkgconf make g++ +sudo apt install ruby ruby-bundler ruby-dev gcc pkgconf make g++ libyaml-dev echo 'gem: --no-document' >> ~/.gemrc sudo cp ~/.gemrc /root/ bundle config set --global path 'vendor/bundle' @@ -60,7 +60,7 @@ bundle install Similar to above but might as well use the gem install to get the latest bundler. The Debian apt packaged bundler is currently a bit outdated and missing some features compared to the latest. ``` -sudo apt install ruby ruby-dev gcc pkgconf make g++ +sudo apt install ruby ruby-dev gcc pkgconf make g++ libyaml-dev echo 'gem: --no-document' >> ~/.gemrc sudo cp ~/.gemrc /root/ sudo gem install bundler From 77bdebd0b1be71f8856ad9c59e780a920cde1097 Mon Sep 17 00:00:00 2001 From: james Date: Thu, 7 Nov 2024 17:46:21 -0600 Subject: [PATCH 14/15] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index aee5c23..7994bbc 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ cd my-project Run the bundle install command from the project's root directory ``` -sudo apt install ruby ruby-bundler ruby-dev gcc pkgconf make g++ libyaml-dev +sudo apt install ruby ruby-bundler ruby-dev gcc pkgconf make g++ libyaml-dev libffi-dev echo 'gem: --no-document' >> ~/.gemrc sudo cp ~/.gemrc /root/ bundle config set --global path 'vendor/bundle' @@ -60,7 +60,7 @@ bundle install Similar to above but might as well use the gem install to get the latest bundler. The Debian apt packaged bundler is currently a bit outdated and missing some features compared to the latest. ``` -sudo apt install ruby ruby-dev gcc pkgconf make g++ libyaml-dev +sudo apt install ruby ruby-dev gcc pkgconf make g++ libyaml-dev # zlib1g-dev libffi-dev #(for rails stuff) echo 'gem: --no-document' >> ~/.gemrc sudo cp ~/.gemrc /root/ sudo gem install bundler From f4780106996c444622d36e2b8d41ca6a86f87643 Mon Sep 17 00:00:00 2001 From: james Date: Wed, 25 Jun 2025 22:42:56 -0500 Subject: [PATCH 15/15] Update .gitignore --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 4530e79..aa03a48 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /.rake_tasks~ /temp.db -/vendor/ \ No newline at end of file +/vendor/ +/bin/ \ No newline at end of file