Compare commits
6 Commits
b38c8cf814
...
41bcdba91d
Author | SHA1 | Date |
---|---|---|
Trevor Vallender | 41bcdba91d | |
Trevor Vallender | a3b1e08a2c | |
Trevor Vallender | 19bdbb12b5 | |
Trevor Vallender | 0463e47044 | |
Trevor Vallender | 0ef2d0b23c | |
Trevor Vallender | a456212ccf |
|
@ -0,0 +1 @@
|
||||||
|
ENABLE_SIGNUP=true
|
|
@ -10,6 +10,8 @@
|
||||||
# Ignore all environment files (except templates).
|
# Ignore all environment files (except templates).
|
||||||
/.env*
|
/.env*
|
||||||
!/.env*.erb
|
!/.env*.erb
|
||||||
|
!/.env.development
|
||||||
|
!/.env.test
|
||||||
|
|
||||||
# Ignore all logfiles and tempfiles.
|
# Ignore all logfiles and tempfiles.
|
||||||
/log/*
|
/log/*
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# A sample docker-setup hook
|
||||||
|
#
|
||||||
|
# Sets up a Docker network which can then be used by the application’s containers
|
||||||
|
|
||||||
|
ssh root@ttcompanion.com "docker network inspect kamal || docker network create kamal"
|
|
@ -0,0 +1,7 @@
|
||||||
|
#!/usr/bin/env ruby
|
||||||
|
|
||||||
|
# A sample docker-setup hook
|
||||||
|
#
|
||||||
|
# Sets up a Docker network which can then be used by the application’s containers
|
||||||
|
|
||||||
|
ssh user@example.com docker network create kamal
|
|
@ -0,0 +1,14 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# A sample post-deploy hook
|
||||||
|
#
|
||||||
|
# These environment variables are available:
|
||||||
|
# KAMAL_RECORDED_AT
|
||||||
|
# KAMAL_PERFORMER
|
||||||
|
# KAMAL_VERSION
|
||||||
|
# KAMAL_HOSTS
|
||||||
|
# KAMAL_ROLE (if set)
|
||||||
|
# KAMAL_DESTINATION (if set)
|
||||||
|
# KAMAL_RUNTIME
|
||||||
|
|
||||||
|
echo "$KAMAL_PERFORMER deployed $KAMAL_VERSION to $KAMAL_DESTINATION in $KAMAL_RUNTIME seconds"
|
|
@ -0,0 +1,3 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
echo "Rebooted Traefik on $KAMAL_HOSTS"
|
|
@ -0,0 +1,51 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# A sample pre-build hook
|
||||||
|
#
|
||||||
|
# Checks:
|
||||||
|
# 1. We have a clean checkout
|
||||||
|
# 2. A remote is configured
|
||||||
|
# 3. The branch has been pushed to the remote
|
||||||
|
# 4. The version we are deploying matches the remote
|
||||||
|
#
|
||||||
|
# These environment variables are available:
|
||||||
|
# KAMAL_RECORDED_AT
|
||||||
|
# KAMAL_PERFORMER
|
||||||
|
# KAMAL_VERSION
|
||||||
|
# KAMAL_HOSTS
|
||||||
|
# KAMAL_ROLE (if set)
|
||||||
|
# KAMAL_DESTINATION (if set)
|
||||||
|
|
||||||
|
if [ -n "$(git status --porcelain)" ]; then
|
||||||
|
echo "Git checkout is not clean, aborting..." >&2
|
||||||
|
git status --porcelain >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
first_remote=$(git remote)
|
||||||
|
|
||||||
|
if [ -z "$first_remote" ]; then
|
||||||
|
echo "No git remote set, aborting..." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
current_branch=$(git branch --show-current)
|
||||||
|
|
||||||
|
if [ -z "$current_branch" ]; then
|
||||||
|
echo "Not on a git branch, aborting..." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
remote_head=$(git ls-remote $first_remote --tags $current_branch | cut -f1)
|
||||||
|
|
||||||
|
if [ -z "$remote_head" ]; then
|
||||||
|
echo "Branch not pushed to remote, aborting..." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$KAMAL_VERSION" != "$remote_head" ]; then
|
||||||
|
echo "Version ($KAMAL_VERSION) does not match remote HEAD ($remote_head), aborting..." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
exit 0
|
|
@ -0,0 +1,47 @@
|
||||||
|
#!/usr/bin/env ruby
|
||||||
|
|
||||||
|
# A sample pre-connect check
|
||||||
|
#
|
||||||
|
# Warms DNS before connecting to hosts in parallel
|
||||||
|
#
|
||||||
|
# These environment variables are available:
|
||||||
|
# KAMAL_RECORDED_AT
|
||||||
|
# KAMAL_PERFORMER
|
||||||
|
# KAMAL_VERSION
|
||||||
|
# KAMAL_HOSTS
|
||||||
|
# KAMAL_ROLE (if set)
|
||||||
|
# KAMAL_DESTINATION (if set)
|
||||||
|
# KAMAL_RUNTIME
|
||||||
|
|
||||||
|
hosts = ENV["KAMAL_HOSTS"].split(",")
|
||||||
|
results = nil
|
||||||
|
max = 3
|
||||||
|
|
||||||
|
elapsed = Benchmark.realtime do
|
||||||
|
results = hosts.map do |host|
|
||||||
|
Thread.new do
|
||||||
|
tries = 1
|
||||||
|
|
||||||
|
begin
|
||||||
|
Socket.getaddrinfo(host, 0, Socket::AF_UNSPEC, Socket::SOCK_STREAM, nil, Socket::AI_CANONNAME)
|
||||||
|
rescue SocketError
|
||||||
|
if tries < max
|
||||||
|
puts "Retrying DNS warmup: #{host}"
|
||||||
|
tries += 1
|
||||||
|
sleep rand
|
||||||
|
retry
|
||||||
|
else
|
||||||
|
puts "DNS warmup failed: #{host}"
|
||||||
|
host
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
tries
|
||||||
|
end
|
||||||
|
end.map(&:value)
|
||||||
|
end
|
||||||
|
|
||||||
|
retries = results.sum - hosts.size
|
||||||
|
nopes = results.count { |r| r == max }
|
||||||
|
|
||||||
|
puts "Prewarmed %d DNS lookups in %.2f sec: %d retries, %d failures" % [ hosts.size, elapsed, retries, nopes ]
|
|
@ -0,0 +1,109 @@
|
||||||
|
#!/usr/bin/env ruby
|
||||||
|
|
||||||
|
# A sample pre-deploy hook
|
||||||
|
#
|
||||||
|
# Checks the Github status of the build, waiting for a pending build to complete for up to 720 seconds.
|
||||||
|
#
|
||||||
|
# Fails unless the combined status is "success"
|
||||||
|
#
|
||||||
|
# These environment variables are available:
|
||||||
|
# KAMAL_RECORDED_AT
|
||||||
|
# KAMAL_PERFORMER
|
||||||
|
# KAMAL_VERSION
|
||||||
|
# KAMAL_HOSTS
|
||||||
|
# KAMAL_COMMAND
|
||||||
|
# KAMAL_SUBCOMMAND
|
||||||
|
# KAMAL_ROLE (if set)
|
||||||
|
# KAMAL_DESTINATION (if set)
|
||||||
|
|
||||||
|
# Only check the build status for production deployments
|
||||||
|
if ENV["KAMAL_COMMAND"] == "rollback" || ENV["KAMAL_DESTINATION"] != "production"
|
||||||
|
exit 0
|
||||||
|
end
|
||||||
|
|
||||||
|
require "bundler/inline"
|
||||||
|
|
||||||
|
# true = install gems so this is fast on repeat invocations
|
||||||
|
gemfile(true, quiet: true) do
|
||||||
|
source "https://rubygems.org"
|
||||||
|
|
||||||
|
gem "octokit"
|
||||||
|
gem "faraday-retry"
|
||||||
|
end
|
||||||
|
|
||||||
|
MAX_ATTEMPTS = 72
|
||||||
|
ATTEMPTS_GAP = 10
|
||||||
|
|
||||||
|
def exit_with_error(message)
|
||||||
|
$stderr.puts message
|
||||||
|
exit 1
|
||||||
|
end
|
||||||
|
|
||||||
|
class GithubStatusChecks
|
||||||
|
attr_reader :remote_url, :git_sha, :github_client, :combined_status
|
||||||
|
|
||||||
|
def initialize
|
||||||
|
@remote_url = `git config --get remote.origin.url`.strip.delete_prefix("https://github.com/")
|
||||||
|
@git_sha = `git rev-parse HEAD`.strip
|
||||||
|
@github_client = Octokit::Client.new(access_token: ENV["GITHUB_TOKEN"])
|
||||||
|
refresh!
|
||||||
|
end
|
||||||
|
|
||||||
|
def refresh!
|
||||||
|
@combined_status = github_client.combined_status(remote_url, git_sha)
|
||||||
|
end
|
||||||
|
|
||||||
|
def state
|
||||||
|
combined_status[:state]
|
||||||
|
end
|
||||||
|
|
||||||
|
def first_status_url
|
||||||
|
first_status = combined_status[:statuses].find { |status| status[:state] == state }
|
||||||
|
first_status && first_status[:target_url]
|
||||||
|
end
|
||||||
|
|
||||||
|
def complete_count
|
||||||
|
combined_status[:statuses].count { |status| status[:state] != "pending"}
|
||||||
|
end
|
||||||
|
|
||||||
|
def total_count
|
||||||
|
combined_status[:statuses].count
|
||||||
|
end
|
||||||
|
|
||||||
|
def current_status
|
||||||
|
if total_count > 0
|
||||||
|
"Completed #{complete_count}/#{total_count} checks, see #{first_status_url} ..."
|
||||||
|
else
|
||||||
|
"Build not started..."
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
$stdout.sync = true
|
||||||
|
|
||||||
|
puts "Checking build status..."
|
||||||
|
attempts = 0
|
||||||
|
checks = GithubStatusChecks.new
|
||||||
|
|
||||||
|
begin
|
||||||
|
loop do
|
||||||
|
case checks.state
|
||||||
|
when "success"
|
||||||
|
puts "Checks passed, see #{checks.first_status_url}"
|
||||||
|
exit 0
|
||||||
|
when "failure"
|
||||||
|
exit_with_error "Checks failed, see #{checks.first_status_url}"
|
||||||
|
when "pending"
|
||||||
|
attempts += 1
|
||||||
|
end
|
||||||
|
|
||||||
|
exit_with_error "Checks are still pending, gave up after #{MAX_ATTEMPTS * ATTEMPTS_GAP} seconds" if attempts == MAX_ATTEMPTS
|
||||||
|
|
||||||
|
puts checks.current_status
|
||||||
|
sleep(ATTEMPTS_GAP)
|
||||||
|
checks.refresh!
|
||||||
|
end
|
||||||
|
rescue Octokit::NotFound
|
||||||
|
exit_with_error "Build status could not be found"
|
||||||
|
end
|
|
@ -0,0 +1,3 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
echo "Rebooting Traefik on $KAMAL_HOSTS..."
|
|
@ -13,16 +13,18 @@ ENV RAILS_ENV="production" \
|
||||||
BUNDLE_PATH="/usr/local/bundle" \
|
BUNDLE_PATH="/usr/local/bundle" \
|
||||||
BUNDLE_WITHOUT="development"
|
BUNDLE_WITHOUT="development"
|
||||||
|
|
||||||
|
RUN apt-get update -qq && \
|
||||||
|
apt-get install --no-install-recommends -y libvips libpq-dev
|
||||||
|
|
||||||
# Throw-away build stage to reduce size of final image
|
# Throw-away build stage to reduce size of final image
|
||||||
FROM base as build
|
FROM base as build
|
||||||
|
|
||||||
# Install packages needed to build gems
|
# Install packages needed to build gems
|
||||||
RUN apt-get update -qq && \
|
RUN apt-get update -qq && \
|
||||||
apt-get install --no-install-recommends -y build-essential git libvips pkg-config
|
apt-get install --no-install-recommends -y build-essential git pkg-config
|
||||||
|
|
||||||
# Install application gems
|
# Install application gems
|
||||||
COPY Gemfile Gemfile.lock ./
|
COPY Gemfile Gemfile.lock .ruby-version ./
|
||||||
RUN bundle install && \
|
RUN bundle install && \
|
||||||
rm -rf ~/.bundle/ "${BUNDLE_PATH}"/ruby/*/cache "${BUNDLE_PATH}"/ruby/*/bundler/gems/*/.git && \
|
rm -rf ~/.bundle/ "${BUNDLE_PATH}"/ruby/*/cache "${BUNDLE_PATH}"/ruby/*/bundler/gems/*/.git && \
|
||||||
bundle exec bootsnap precompile --gemfile
|
bundle exec bootsnap precompile --gemfile
|
||||||
|
|
1
Gemfile
1
Gemfile
|
@ -24,6 +24,7 @@ gem "active_storage_validations"
|
||||||
|
|
||||||
group :development, :test do
|
group :development, :test do
|
||||||
gem "debug", platforms: %i[ mri windows ]
|
gem "debug", platforms: %i[ mri windows ]
|
||||||
|
gem "dotenv"
|
||||||
end
|
end
|
||||||
|
|
||||||
group :development do
|
group :development do
|
||||||
|
|
|
@ -107,6 +107,7 @@ GEM
|
||||||
debug (1.9.2)
|
debug (1.9.2)
|
||||||
irb (~> 1.10)
|
irb (~> 1.10)
|
||||||
reline (>= 0.3.8)
|
reline (>= 0.3.8)
|
||||||
|
dotenv (3.1.2)
|
||||||
drb (2.2.1)
|
drb (2.2.1)
|
||||||
erubi (1.12.0)
|
erubi (1.12.0)
|
||||||
et-orbi (1.2.11)
|
et-orbi (1.2.11)
|
||||||
|
@ -330,6 +331,7 @@ DEPENDENCIES
|
||||||
bootsnap
|
bootsnap
|
||||||
capybara
|
capybara
|
||||||
debug
|
debug
|
||||||
|
dotenv
|
||||||
image_processing (~> 1.2)
|
image_processing (~> 1.2)
|
||||||
importmap-rails
|
importmap-rails
|
||||||
jbuilder
|
jbuilder
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
body {
|
||||||
|
font-family: Roboto, sans-serif;
|
||||||
|
}
|
|
@ -11,10 +11,13 @@ class SessionsController < ApplicationController
|
||||||
username: params[:username],
|
username: params[:username],
|
||||||
password: params[:password],
|
password: params[:password],
|
||||||
)
|
)
|
||||||
if Current.user
|
if Current.user && Current.user.verified?
|
||||||
session[:user_id] = Current.user.id
|
session[:user_id] = Current.user.id
|
||||||
flash[:notice] = t(".success", name: Current.user.first_name)
|
flash[:notice] = t(".success", name: Current.user.first_name)
|
||||||
redirect_to :root
|
redirect_to :root
|
||||||
|
elsif !Current.user.verified?
|
||||||
|
flash[:alert] = t(".not_verified")
|
||||||
|
render :new, status: :unprocessable_entity
|
||||||
else
|
else
|
||||||
flash[:alert] = t(".error")
|
flash[:alert] = t(".error")
|
||||||
render :new, status: :unprocessable_entity
|
render :new, status: :unprocessable_entity
|
||||||
|
|
|
@ -6,10 +6,14 @@ class UsersController < ApplicationController
|
||||||
before_action :ensure_self, only: [ :edit, :update ]
|
before_action :ensure_self, only: [ :edit, :update ]
|
||||||
|
|
||||||
def new
|
def new
|
||||||
|
redirect_to :root and return unless ActiveModel::Type::Boolean.new.cast(ENV.fetch("ENABLE_SIGNUP") { false })
|
||||||
|
|
||||||
@user = User.new
|
@user = User.new
|
||||||
end
|
end
|
||||||
|
|
||||||
def create
|
def create
|
||||||
|
redirect_to :root and return unless ActiveModel::Type::Boolean.new.cast(ENV.fetch("ENABLE_SIGNUP") { false })
|
||||||
|
|
||||||
@user = User.new(new_user_params)
|
@user = User.new(new_user_params)
|
||||||
if @user.save
|
if @user.save
|
||||||
token = @user.generate_token_for(:email_verification)
|
token = @user.generate_token_for(:email_verification)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class ApplicationMailer < ActionMailer::Base
|
class ApplicationMailer < ActionMailer::Base
|
||||||
default from: "from@example.com"
|
default from: "hello@ttcompanion.com"
|
||||||
layout "mailer"
|
layout "mailer"
|
||||||
end
|
end
|
||||||
|
|
|
@ -40,7 +40,7 @@ class CharacterSheetFeature < ApplicationRecord
|
||||||
return if order_index.present?
|
return if order_index.present?
|
||||||
|
|
||||||
if character_sheet_section.character_sheet_features.any?
|
if character_sheet_section.character_sheet_features.any?
|
||||||
self.order_index = character_sheet_section.character_sheet_features.order_index(:order_index).last.order_index + 1
|
self.order_index = character_sheet_section.character_sheet_features.order(:order_index).last.order_index + 1
|
||||||
else
|
else
|
||||||
self.order_index = 1
|
self.order_index = 1
|
||||||
end
|
end
|
||||||
|
|
|
@ -28,7 +28,9 @@
|
||||||
<% end %>
|
<% end %>
|
||||||
<% else %>
|
<% else %>
|
||||||
<li><%= link_to t("log_in"), login_path %></li>
|
<li><%= link_to t("log_in"), login_path %></li>
|
||||||
<li><%= link_to t("sign_up"), new_user_path %></li>
|
<% if ActiveModel::Type::Boolean.new.cast(ENV.fetch("ENABLE_SIGNUP") { false } ) %>
|
||||||
|
<li><%= link_to t("sign_up"), new_user_path %></li>
|
||||||
|
<% end %>
|
||||||
<% end %>
|
<% end %>
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
EI74C8uxmka2Mejn5kTdq8qxetc8d8SJd4w+VNKkQdMUa6NgzZnz6Tf7RJZaQnUUtHPfVtFiltrkPcIiOzY1a5meO1TeydtWl+5ij2vpKFDP9tpcmVswhWJYvkuf6LRh/L8Yy1HeMBh5zM94JVc+s2X6aqfMjd19ORJEenyR4C6VogSUMWOOs+C9f9+8z+O3hptwfRlj6AwM5k5sn6PEKaUVUrnXa7TRLvVnK5Sm7s2WXTrTI6I3n+CaGp2iNRUf2Y3w34YE3QydWJ4YsmoFByMQIqkUtmasBuGYGX7CG4umd2/uUD/HJNnB/BriwsqbSV8QM4ErVO+8pSzKu7RJGH2SVZdYiz8WeVvoKHXPJ8cwVrW4Q45beYtUFMCPiYkUl4oZbzx1h94k0+AM4rRf6OCut9BW--3rZrEMdP2rRRfpnL--GFSUvgc2V69L+ERe1xIkiQ==
|
xJrXZdCJWgTrxv8EZWehw4DFUnkkf1coQjXgio0p6rxvubZ2Wm/vPd9n1cpoHQTRoZ4ZrqyT2Py1EKcc7niantVlgI+C9vBuZeU+Ygv6ZgbysQi4YmvCPsc+bPXDaE9PocyzgUA7n/1G12AfqtBSbX8GebXJU8//F9YQrqllCxJz1srtpYE28nT2kz5tngJyKWYSA8uyOMqquL8JYgrQgu9C3lCC1gCnYhFbseYVRFC5C/eaL+oi5kbI9Zu2iQ36+o26TiDGe2Csb1ykiwBwA9Cd8Wg1D3t3iVLbMHabJomUNRY5B55vE1y9uVA8SWk3Qdp+iiW/IONFKTmjZICimN8xs2ucQPkFma3LxmQyU7jXd6l7mP2Lw0Ss+wS5FJz1N+fCSkOur6wWkLhPDfllULutg/KtafaveKs4JELkIsTp7hxXrcWG8nFxFYDLa6ZB75YmKW1hVKK9oEflUCvLsmC/D6gZuAUuhwvMkC41Fnpm/XWb+psEt4lZW2iWDXL39YtJGH5x7R7ZXsnBMHtBqOT0TxUPKmygbyJjSy+Up6wLKUl7tvBMIdfXxxmD9golHERxkd1ohtzgBv8=--/1t7r22sKt8YaALp--Afle+IqS5PjqmocwReZmKQ==
|
|
@ -3,18 +3,20 @@ default: &default
|
||||||
encoding: unicode
|
encoding: unicode
|
||||||
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
|
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
|
||||||
timeout: 5000
|
timeout: 5000
|
||||||
host: localhost
|
host: <%= ENV.fetch("DB_HOST") { "localhost" } %>
|
||||||
username: <%= ENV.fetch("POSTGRES_USER") { "postgres" } %>
|
username: <%= ENV.fetch("POSTGRES_USER") { "postgres" } %>
|
||||||
password: <%= ENV.fetch("POSTGRES_PASSWORD") { "postgres" } %>
|
|
||||||
|
|
||||||
development:
|
development:
|
||||||
<<: *default
|
<<: *default
|
||||||
database: tabletop_companion_development
|
database: tabletop_companion_development
|
||||||
|
password: postgres
|
||||||
|
|
||||||
test:
|
test:
|
||||||
<<: *default
|
<<: *default
|
||||||
database: tabletop_companion_test
|
database: tabletop_companion_test
|
||||||
|
password: postgres
|
||||||
|
|
||||||
production:
|
production:
|
||||||
<<: *default
|
<<: *default
|
||||||
database: tabletop_companion_production
|
database: tabletop_companion_production
|
||||||
|
password: <%= ENV.fetch("POSTGRES_PASSWORD") { "postgres" } %>
|
||||||
|
|
|
@ -0,0 +1,73 @@
|
||||||
|
service: ttcompanion
|
||||||
|
|
||||||
|
image: tsvallender/ttcompanion
|
||||||
|
|
||||||
|
traefik:
|
||||||
|
options:
|
||||||
|
publish:
|
||||||
|
- "443:443"
|
||||||
|
volume:
|
||||||
|
- "/letsencrypt/acme.json:/letsencrypt/acme.json" # To save the configuration file.
|
||||||
|
network: kamal
|
||||||
|
args:
|
||||||
|
entryPoints.web.address: ":80"
|
||||||
|
entryPoints.websecure.address: ":443"
|
||||||
|
entryPoints.web.http.redirections.entryPoint.to: websecure # We want to force https
|
||||||
|
entryPoints.web.http.redirections.entryPoint.scheme: https
|
||||||
|
entryPoints.web.http.redirections.entrypoint.permanent: true
|
||||||
|
certificatesResolvers.letsencrypt.acme.email: "trevor@ttcompanion.com"
|
||||||
|
certificatesResolvers.letsencrypt.acme.storage: "/letsencrypt/acme.json"
|
||||||
|
certificatesResolvers.letsencrypt.acme.httpchallenge: true
|
||||||
|
certificatesResolvers.letsencrypt.acme.httpchallenge.entrypoint: web #
|
||||||
|
|
||||||
|
servers:
|
||||||
|
web:
|
||||||
|
hosts:
|
||||||
|
- ttcompanion.com
|
||||||
|
labels:
|
||||||
|
traefik.http.routers.ttcompanion.entrypoints: websecure
|
||||||
|
traefik.http.routers.ttcompanion.rule: Host(`ttcompanion.com`)
|
||||||
|
traefik.http.routers.ttcompanion.tls.certresolver: letsencrypt
|
||||||
|
options:
|
||||||
|
network: kamal
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
- /srv/ttcompanion/storage:/rails/storage
|
||||||
|
|
||||||
|
registry:
|
||||||
|
username: tsvallender
|
||||||
|
password:
|
||||||
|
- KAMAL_REGISTRY_PASSWORD
|
||||||
|
|
||||||
|
env:
|
||||||
|
clear:
|
||||||
|
APPLICATION_HOST: ttcompanion.com
|
||||||
|
DB_HOST: ttcompanion-postgres
|
||||||
|
ENABLE_SIGNUP: false
|
||||||
|
RAILS_LOG_TO_STDOUT: true
|
||||||
|
RAILS_SERVE_STATIC_FILES: true
|
||||||
|
secret:
|
||||||
|
- RAILS_MASTER_KEY
|
||||||
|
- POSTGRES_PASSWORD
|
||||||
|
|
||||||
|
builder:
|
||||||
|
multiarch: false
|
||||||
|
|
||||||
|
accessories:
|
||||||
|
postgres:
|
||||||
|
image: postgres:16
|
||||||
|
host: ttcompanion.com
|
||||||
|
env:
|
||||||
|
clear:
|
||||||
|
POSTGRES_USER: postgres
|
||||||
|
secret:
|
||||||
|
- POSTGRES_PASSWORD
|
||||||
|
directories:
|
||||||
|
- /srv/ttcompanion/data:/var/lib/postgresql/data
|
||||||
|
options:
|
||||||
|
network: kamal
|
||||||
|
|
||||||
|
healthcheck:
|
||||||
|
log_lines: 1000
|
||||||
|
|
||||||
|
asset_path: /rails/public/assets
|
|
@ -74,6 +74,14 @@ Rails.application.configure do
|
||||||
# config.active_job.queue_name_prefix = "forg_production"
|
# config.active_job.queue_name_prefix = "forg_production"
|
||||||
|
|
||||||
config.action_mailer.perform_caching = false
|
config.action_mailer.perform_caching = false
|
||||||
|
config.action_mailer.delivery_method = :smtp
|
||||||
|
config.action_mailer.smtp_settings = {
|
||||||
|
address: "smtp.eu.mailgun.org",
|
||||||
|
port: 587,
|
||||||
|
domain: "ttcompanion.com",
|
||||||
|
user_name: "postmaster@mail.ttcompanion.com",
|
||||||
|
password: Rails.application.credentials.dig(:mailgun, :password),
|
||||||
|
}
|
||||||
|
|
||||||
# Ignore bad email addresses and do not raise email delivery errors.
|
# Ignore bad email addresses and do not raise email delivery errors.
|
||||||
# Set this to true and configure the email server for immediate delivery to raise delivery errors.
|
# Set this to true and configure the email server for immediate delivery to raise delivery errors.
|
||||||
|
@ -90,10 +98,11 @@ Rails.application.configure do
|
||||||
config.active_record.dump_schema_after_migration = false
|
config.active_record.dump_schema_after_migration = false
|
||||||
|
|
||||||
# Enable DNS rebinding protection and other `Host` header attacks.
|
# Enable DNS rebinding protection and other `Host` header attacks.
|
||||||
# config.hosts = [
|
config.hosts = [
|
||||||
# "example.com", # Allow requests from example.com
|
"localhost",
|
||||||
# /.*\.example\.com/ # Allow requests from subdomains like `www.example.com`
|
"ttcompanion.com",
|
||||||
# ]
|
/.*\.ttcompanion\.com/,
|
||||||
|
]
|
||||||
# Skip DNS rebinding protection for the default health check endpoint.
|
# Skip DNS rebinding protection for the default health check endpoint.
|
||||||
# config.host_authorization = { exclude: ->(request) { request.path == "/up" } }
|
# config.host_authorization = { exclude: ->(request) { request.path == "/up" } }
|
||||||
end
|
end
|
||||||
|
|
|
@ -137,6 +137,7 @@ en:
|
||||||
create:
|
create:
|
||||||
success: "Hello, %{name}!"
|
success: "Hello, %{name}!"
|
||||||
error: "Could not sign in. Please check your username and password."
|
error: "Could not sign in. Please check your username and password."
|
||||||
|
not_verified: Please check your email to verify your account and log in.
|
||||||
new:
|
new:
|
||||||
log_in: Log in
|
log_in: Log in
|
||||||
forgot_password: Forgotten your password?
|
forgot_password: Forgotten your password?
|
||||||
|
|
|
@ -14,4 +14,13 @@ class SignUpsTest < ApplicationSystemTestCase
|
||||||
click_on I18n.t("sessions.destroy.log_out")
|
click_on I18n.t("sessions.destroy.log_out")
|
||||||
assert_text I18n.t("sessions.destroy.success")
|
assert_text I18n.t("sessions.destroy.success")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "unverified users cannot log in" do
|
||||||
|
visit new_session_url
|
||||||
|
fill_in attr_name(User, :username), with: users(:unverified).username
|
||||||
|
fill_in attr_name(User, :password), with: "password"
|
||||||
|
click_button I18n.t("sessions.new.log_in")
|
||||||
|
|
||||||
|
assert_text I18n.t("sessions.create.not_verified")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue