From 5afb2dd9631b2a20e0cfcfcf199b027eb17d14a6 Mon Sep 17 00:00:00 2001 From: Marco Foschian Date: Thu, 19 Feb 2026 17:33:59 +0100 Subject: [PATCH 1/3] Dockerized production backend --- .dockerignore | 48 +++++++++++ .env.example | 29 +++++-- .gitignore | 6 +- Dockerfile | 127 ++++++++++++++++++++++++++++++ app/mailers/application_mailer.rb | 2 +- bin/docker-entrypoint | 14 ++++ compose.yaml | 26 ++++++ config/initializers/mailer.rb | 15 ++-- 8 files changed, 249 insertions(+), 18 deletions(-) create mode 100644 .dockerignore create mode 100644 Dockerfile create mode 100644 bin/docker-entrypoint create mode 100644 compose.yaml diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..cd06bac8 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,48 @@ +# See https://docs.docker.com/engine/reference/builder/#dockerignore-file for more about ignoring files. + +# Ignore git directory. +/.git/ +/.gitignore + +# Ignore bundler config. +/.bundle + +# Ignore all environment files. +/.env* +!/.env + +# Ignore all default key files. +/config/master.key +/config/credentials/*.key + +# Ignore all logfiles and tempfiles. +/log/* +/tmp/* +!/log/.keep +!/tmp/.keep + +# Ignore pidfiles, but keep the directory. +/tmp/pids/* +!/tmp/pids/.keep + +# Ignore storage (uploaded files in development and any SQLite databases). +/storage/* +!/storage/.keep +/tmp/storage/* +!/tmp/storage/.keep + +# Ignore assets. +/node_modules/ +/app/assets/builds/* +!/app/assets/builds/.keep +/public/assets + +# Ignore CI service files. +/.github + +# Ignore development files +/.devcontainer + +# Ignore Docker-related files +/.dockerignore +/Dockerfile* diff --git a/.env.example b/.env.example index aa19614f..beb6b760 100644 --- a/.env.example +++ b/.env.example @@ -3,12 +3,14 @@ # ============================================================================= # Copy this file as .env and fill in your values. +DEFAULT_LOCALE="it" + # ----------------------------------------------------------------------------- # Application Settings # ----------------------------------------------------------------------------- # Rails environment (development, test, production) -RAILS_ENV= +RAILS_ENV=production # Default host for URL generation DEFAULT_HOST= @@ -19,25 +21,25 @@ DEFAULT_HOST= # Enable Pundit authorization (true to enable role-based access control) # Leave empty or false to disable authentication -ENABLE_AUTHENTICATION= +ENABLE_AUTHENTICATION=true # Devise secret key for encryption (generate with: rails secret) # REQUIRED in production -DEVISE_SECRET_KEY= +DEVISE_SECRET_KEY={{devise_secret_key}} # Devise pepper for additional password security (generate with: rails secret) # REQUIRED in production -DEVISE_PEPPER= +DEVISE_PEPPER={{devise_pepper}} # ----------------------------------------------------------------------------- # LDAP Configuration (optional - for LDAP authentication) # ----------------------------------------------------------------------------- # Enable LDAP debug logging (true/false) -DEVISE_LDAP_LOGGER= +#DEVISE_LDAP_LOGGER= # Use admin credentials to bind to LDAP (true/false) -DEVISE_LDAP_LOGGER_ADMIN_BIND= +#DEVISE_LDAP_LOGGER_ADMIN_BIND= # ----------------------------------------------------------------------------- # CORS (Cross-Origin Resource Sharing) @@ -77,4 +79,17 @@ SANITIZED_ALLOWED_ATTRIBUTES="href style" # ----------------------------------------------------------------------------- # Generate with: rails secret -# SECRET_KEY_BASE= +SECRET_KEY_BASE={{secret_key_base}} + + +# ----------------------------------------------------------------------------- +# DB secrets +# ----------------------------------------------------------------------------- +POSTGRES_USER= +POSTGRES_PASSWORD= +POSTGRES_DB= + +# ----------------------------------------------------------------------------- +# RAILS env +# ----------------------------------------------------------------------------- +RAILS_MASTER_KEY={{rails_master_key}} \ No newline at end of file diff --git a/.gitignore b/.gitignore index fe08d7bb..6986f535 100644 --- a/.gitignore +++ b/.gitignore @@ -42,9 +42,9 @@ /config/credentials.yml.enc # Docker files -.dockerignore -docker-compose.yml -Dockerfile +#.dockerignore +#docker-compose.yml +#Dockerfile # Others /public/uploads/* diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..a6bb6025 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,127 @@ +# syntax=docker/dockerfile:1 +# check=error=true + +# This Dockerfile is designed for production, not development. +# docker build -t app . +# docker run -d -p 80:80 -e RAILS_MASTER_KEY= --name app app + +# For a containerized dev environment, see Dev Containers: https://guides.rubyonrails.org/getting_started_with_devcontainer.html + +# Make sure RUBY_VERSION matches the Ruby version in .ruby-version +ARG RUBY_VERSION=3.4.8 +# FROM dhi.io/ruby:$RUBY_VERSION-dev AS base +FROM docker.io/library/ruby:$RUBY_VERSION-slim AS base + +# In case of proxy invoke the composer with: proxy_url=http://yourproxy.org:port docker compose up --build +# If you don'nt have a proxy comment the following lines +ARG proxy_url +ENV HTTP_PROXY=${proxy_url} +ENV HTTPS_PROXY=${proxy_url} +ENV NO_PROXY=localhost,127.0.0.1,.protezionecivile.fvg.it +ENV http_proxy=${HTTP_PROXY} +ENV https_proxy=${HTTPS_PROXY} +ENV no_proxy=${NO_PROXY} + +# # Source - https://stackoverflow.com/a/78336399 +# # Posted by Jose Diaz-Gonzalez +# # Retrieved 2026-02-18, License - CC BY-SA 4.0 + +# # set the argument default to a dummy value +# ARG SECRET_KEY_BASE=1 + +# # assign it to an environment variable +# # we can wrap the variable in brackets +# ENV SECRET_KEY_BASE ${SECRET_KEY_BASE} + + +# Rails app lives here +WORKDIR /rails + +# Install base packages +# Replace libpq-dev with sqlite3 if using SQLite, or libmysqlclient-dev if using MySQL +RUN apt-get update -qq && \ + apt-get install --no-install-recommends -y curl libjemalloc2 libvips libpq-dev && \ + rm -rf /var/lib/apt/lists /var/cache/apt/archives + +# 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 base AS build + +# Install packages needed to build gems +RUN apt-get update -qq && \ + apt-get install --no-install-recommends -y build-essential curl git pkg-config libyaml-dev && \ + rm -rf /var/lib/apt/lists /var/cache/apt/archives + + +# Install JavaScript dependencies and Node.js for asset compilation +# +# Uncomment the following lines if you are using NodeJS need to compile assets +# +# ARG NODE_VERSION=18.12.0 +# ARG YARN_VERSION=1.22.19 +# ENV PATH=/usr/local/node/bin:$PATH +# RUN curl -sL https://github.com/nodenv/node-build/archive/master.tar.gz | tar xz -C /tmp/ && \ +# /tmp/node-build-master/bin/node-build "${NODE_VERSION}" /usr/local/node && \ +# npm install -g yarn@$YARN_VERSION && \ +# npm install -g mjml && \ +# rm -rf /tmp/node-build-master + +# 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 && \ + bundle exec bootsnap precompile --gemfile + +# Install node modules +# +# Uncomment the following lines if you are using NodeJS need to compile assets +# +# COPY package.json yarn.lock ./ +# RUN --mount=type=cache,id=yarn,target=/rails/.cache/yarn YARN_CACHE_FOLDER=/rails/.cache/yarn \ +# yarn install --frozen-lockfile + +# Copy application code +COPY . . + +# Precompile bootsnap code for faster boot times +RUN bundle exec bootsnap precompile app/ lib/ + +# 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 + +# Copy built artifacts: gems, application +COPY --from=build "${BUNDLE_PATH}" "${BUNDLE_PATH}" +COPY --from=build /rails /rails + + +# Generate secrets +# RUN mv .env.base .env +RUN secret=$(bin/rails secret) && sed -i "s/{{devise_secret_key}}/$secret/g" .env +RUN secret=$(bin/rails secret) && sed -i "s/{{devise_pepper}}/$secret/g" .env +RUN secret=$(bin/rails secret) && sed -i "s/{{secret_key_base}}/$secret/g" .env +RUN secret=$(bin/rails secret) && sed -i "s/{{rails_master_key}}/$secret/g" .env + + +# 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 tmp +# chown -R rails:rails db log storage tmp +USER 1000:1000 + +# Entrypoint prepares the database. +ENTRYPOINT ["/rails/bin/docker-entrypoint"] + +# Start server via Thruster by default, this can be overwritten at runtime +# EXPOSE 80 +# CMD ["./bin/thrust", "./bin/rails", "server"] +EXPOSE 3000 +CMD ["./bin/rails", "server"] diff --git a/app/mailers/application_mailer.rb b/app/mailers/application_mailer.rb index 6b360268..5c452475 100644 --- a/app/mailers/application_mailer.rb +++ b/app/mailers/application_mailer.rb @@ -1,4 +1,4 @@ class ApplicationMailer < ActionMailer::Base - default from: Rails.application.credentials.email_from + default from: ENV.fetch("EMAIL_FROM", nil) layout 'mailer' end diff --git a/bin/docker-entrypoint b/bin/docker-entrypoint new file mode 100644 index 00000000..33c5c413 --- /dev/null +++ b/bin/docker-entrypoint @@ -0,0 +1,14 @@ +#!/bin/bash -e + +# Enable jemalloc for reduced memory usage and latency. +if [ -z "${LD_PRELOAD+x}" ]; then + LD_PRELOAD=$(find /usr/lib -name libjemalloc.so.2 -print -quit) + export LD_PRELOAD +fi + +# If running the rails server then create or migrate existing database +if [ "${@: -2:1}" == "./bin/rails" ] && [ "${@: -1:1}" == "server" ]; then + ./bin/rails db:prepare +fi + +exec "${@}" \ No newline at end of file diff --git a/compose.yaml b/compose.yaml new file mode 100644 index 00000000..fee7f859 --- /dev/null +++ b/compose.yaml @@ -0,0 +1,26 @@ +services: + pia-db: + image: postgres:16 + container_name: pia-db + env_file: + - .env + volumes: + - pia-db-data:/var/lib/postgresql/data + restart: unless-stopped + + web: + # run with proxy_url=my-url docker compose up --build + build: + context: . + args: + proxy_url: ${proxy_url} + env_file: + - .env + environment: + - DATABASE_HOST=pia-db + ports: + - "3000:3000" + restart: unless-stopped + +volumes: + pia-db-data: diff --git a/config/initializers/mailer.rb b/config/initializers/mailer.rb index 9bcbbeb0..aa9fb650 100644 --- a/config/initializers/mailer.rb +++ b/config/initializers/mailer.rb @@ -1,9 +1,10 @@ ActionMailer::Base.smtp_settings = { - address: Rails.application.credentials.smtp_address, - port: Rails.application.credentials.smtp_port, - domain: Rails.application.credentials.smtp_domain, - user_name: Rails.application.credentials.smtp_user_name, - password: Rails.application.credentials.smtp_password, - authentication: Rails.application.credentials.smtp_authentication, - enable_starttls_auto: Rails.application.credentials.smtp_enable_starttls_auto + address: ENV.fetch("SMTP_ADDRESS", "localhost"), + port: ENV.fetch("SMTP_PORT", 587), + openssl_verify_mode: 'none', + user_name: ENV.fetch("SMTP_USERNAME", nil), + password: ENV.fetch("SMTP_PASSWORD", nil), + domain: ENV.fetch("SMTP_DOMAIN", nil), + # enable_starttls_auto: Rails.application.credentials.smtp_enable_starttls_auto, + # authentication: :plain, } From d2f12cc7db8f70f54cfab604b97fdcb947f6245e Mon Sep 17 00:00:00 2001 From: Marco Foschian Date: Mon, 23 Feb 2026 10:24:23 +0100 Subject: [PATCH 2/3] feat: added rake task for clientId/secret and firsta administrator user generation --- lib/tasks/prepare.rake | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 lib/tasks/prepare.rake diff --git a/lib/tasks/prepare.rake b/lib/tasks/prepare.rake new file mode 100644 index 00000000..c2a2324e --- /dev/null +++ b/lib/tasks/prepare.rake @@ -0,0 +1,29 @@ +namespace :prepare do + desc "Prepare the app for deployment by precompiling assets and cleaning up old files" + task :app, [:username] => [:environment] do |task, args| + + puts "Register the following informations and keep then safe, you will need them to configure the PIA client:" + + # Create a Doorkeeper application for the PIA client + Doorkeeper::Application.create(name: "PIA", redirect_uri: "urn:ietf:wg:oauth:2.0:oob", scopes: ["read", "write"]) + item = Doorkeeper::Application.select(:uid, :secret).last + puts "PIA Client ID: #{item.uid}" + puts "PIA Client Secret: #{item.secret}" + + if args.has_key?(:username) + + puts "Creating an admin user with the provided email address..." + + username = args[:username] + password = [*'0'..'9', *'a'..'z', *'A'..'Z', *'!'..'?'].sample(16).join + User.create( email: username, password: password,password_confirmation:password) + user = User.find_by(email: username) + user.update(is_technical_admin: true, is_functional_admin: true, is_user: true) + user.unlock_access! + + puts "Admin user created with email: #{username} and password: #{password}" + + end + + end +end \ No newline at end of file From b55ca3617939dafdc6170d6471b697a21ae42a4a Mon Sep 17 00:00:00 2001 From: Marco Foschian Date: Mon, 23 Feb 2026 10:41:52 +0100 Subject: [PATCH 3/3] docs: docker usage --- Docker.md | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 Docker.md diff --git a/Docker.md b/Docker.md new file mode 100644 index 00000000..006e9fd8 --- /dev/null +++ b/Docker.md @@ -0,0 +1,55 @@ +# Docker version usage + +## Image build + +### With proxy between server and internet + +``` +proxy_url=http://yourproxy.url.org:port docker compose up --build +``` + +### Without proxy + +- open Dockerfile and comment the following lines: + +``` +ARG proxy_url +ENV HTTP_PROXY=${proxy_url} +ENV HTTPS_PROXY=${proxy_url} +ENV NO_PROXY=localhost,127.0.0.1,.protezionecivile.fvg.it +ENV http_proxy=${HTTP_PROXY} +ENV https_proxy=${HTTPS_PROXY} +ENV no_proxy=${NO_PROXY} +``` + +run the build with the command + +``` +proxy_url=http://yourproxy.url.org:port docker compose up --build +``` + +Leave the console (and the containers) running after the command + +## App prepare + +1. connect to the running container with: + +``` +docker exec -it pia-back-web-1 bash +``` + +2. Run the rake task for creating client secrets and admin user: + +``` +RAILS_ENV=production rake prepare:app[admin] +``` + +You can change the admin username with whaterver you need (i.e. : me@myorg.com) + +## Start backend + +Stop the console-running containers (Ctrl-C) and re-run them in daemon mode + +``` +docker compose up -d +```