diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..00f9ff1b --- /dev/null +++ b/.dockerignore @@ -0,0 +1,29 @@ +/db/*.sqlite3 + +/node_modules +/coverage +/doc +/.git +/.github +/.bundle +/.yardoc + +/log/* +/tmp/* +!/log/.keep +!/tmp/.keep + +/tmp/pids/* +!/tmp/pids/.keep + +/storage/* +!/storage/.keep + +.byebug_history + +/config/master.key +/public/packs-test +LICENSE +/attachments +/docuseal +.DS_Store diff --git a/.gitignore b/.gitignore index 2e424bb8..95ad8c38 100644 --- a/.gitignore +++ b/.gitignore @@ -33,4 +33,5 @@ yarn-debug.log* .DS_Store /coverage -/docuseal-attachments +/attachments +/docuseal diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..a7ead7ce --- /dev/null +++ b/Dockerfile @@ -0,0 +1,50 @@ +FROM ruby:3.2.2-alpine as webpack + +ENV RAILS_ENV=production +ENV NODE_ENV=production + +WORKDIR /app + +RUN apk add --no-cache nodejs yarn git build-base && \ + gem install shakapacker + +COPY ./package.json ./yarn.lock ./ + +RUN yarn install --network-timeout 1000000 + +COPY ./bin/shakapacker ./bin/shakapacker +COPY ./config/webpack ./config/webpack +COPY ./config/shakapacker.yml ./config/shakapacker.yml +COPY ./postcss.config.js ./postcss.config.js ./ +COPY ./tailwind.config.js ./tailwind.config.js ./ +COPY ./tailwind.form.config.js ./tailwind.form.config.js ./ +COPY ./tailwind.application.config.js ./tailwind.application.config.js ./ +COPY ./app/javascript ./app/javascript +COPY ./app/views ./app/views + +RUN echo "gem 'shakapacker'" > Gemfile && ./bin/shakapacker + +FROM ruby:3.2.2-alpine as app + +ENV RAILS_ENV=production +ENV BUNDLE_WITHOUT="development:test" + +WORKDIR /app + +RUN apk add --no-cache build-base sqlite-dev libpq-dev vips-dev vips-poppler vips-heif libc6-compat ttf-freefont + +COPY ./Gemfile ./Gemfile.lock ./ + +RUN bundle update --bundler && bundle install && rm -rf ~/.bundle + +COPY . ./ + +COPY --from=webpack /app/public/packs ./public/packs + +RUN bundle exec bootsnap precompile --gemfile app/ lib/ + +WORKDIR /data/docuseal +ENV WORKDIR=/data/docuseal + +EXPOSE 3000 +CMD ["/app/bin/rails", "server"] diff --git a/Gemfile b/Gemfile index 91731bf6..51fbd5e9 100644 --- a/Gemfile +++ b/Gemfile @@ -9,8 +9,8 @@ gem 'aws-sdk-s3' gem 'azure-storage-blob' gem 'bootsnap', require: false gem 'devise' +gem 'dotenv', require: false gem 'faraday' -gem 'geoip' gem 'google-cloud-storage' gem 'hexapdf' gem 'image_processing' diff --git a/Gemfile.lock b/Gemfile.lock index 293e9ada..0bd3e1cd 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,67 +1,67 @@ GEM remote: https://rubygems.org/ specs: - actioncable (7.0.4.3) - actionpack (= 7.0.4.3) - activesupport (= 7.0.4.3) + actioncable (7.0.5) + actionpack (= 7.0.5) + activesupport (= 7.0.5) nio4r (~> 2.0) websocket-driver (>= 0.6.1) - actionmailbox (7.0.4.3) - actionpack (= 7.0.4.3) - activejob (= 7.0.4.3) - activerecord (= 7.0.4.3) - activestorage (= 7.0.4.3) - activesupport (= 7.0.4.3) + actionmailbox (7.0.5) + actionpack (= 7.0.5) + activejob (= 7.0.5) + activerecord (= 7.0.5) + activestorage (= 7.0.5) + activesupport (= 7.0.5) mail (>= 2.7.1) net-imap net-pop net-smtp - actionmailer (7.0.4.3) - actionpack (= 7.0.4.3) - actionview (= 7.0.4.3) - activejob (= 7.0.4.3) - activesupport (= 7.0.4.3) + actionmailer (7.0.5) + actionpack (= 7.0.5) + actionview (= 7.0.5) + activejob (= 7.0.5) + activesupport (= 7.0.5) mail (~> 2.5, >= 2.5.4) net-imap net-pop net-smtp rails-dom-testing (~> 2.0) - actionpack (7.0.4.3) - actionview (= 7.0.4.3) - activesupport (= 7.0.4.3) - rack (~> 2.0, >= 2.2.0) + actionpack (7.0.5) + actionview (= 7.0.5) + activesupport (= 7.0.5) + rack (~> 2.0, >= 2.2.4) rack-test (>= 0.6.3) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.0, >= 1.2.0) - actiontext (7.0.4.3) - actionpack (= 7.0.4.3) - activerecord (= 7.0.4.3) - activestorage (= 7.0.4.3) - activesupport (= 7.0.4.3) + actiontext (7.0.5) + actionpack (= 7.0.5) + activerecord (= 7.0.5) + activestorage (= 7.0.5) + activesupport (= 7.0.5) globalid (>= 0.6.0) nokogiri (>= 1.8.5) - actionview (7.0.4.3) - activesupport (= 7.0.4.3) + actionview (7.0.5) + activesupport (= 7.0.5) builder (~> 3.1) erubi (~> 1.4) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.1, >= 1.2.0) - activejob (7.0.4.3) - activesupport (= 7.0.4.3) + activejob (7.0.5) + activesupport (= 7.0.5) globalid (>= 0.3.6) - activemodel (7.0.4.3) - activesupport (= 7.0.4.3) - activerecord (7.0.4.3) - activemodel (= 7.0.4.3) - activesupport (= 7.0.4.3) - activestorage (7.0.4.3) - actionpack (= 7.0.4.3) - activejob (= 7.0.4.3) - activerecord (= 7.0.4.3) - activesupport (= 7.0.4.3) + activemodel (7.0.5) + activesupport (= 7.0.5) + activerecord (7.0.5) + activemodel (= 7.0.5) + activesupport (= 7.0.5) + activestorage (7.0.5) + actionpack (= 7.0.5) + activejob (= 7.0.5) + activerecord (= 7.0.5) + activesupport (= 7.0.5) marcel (~> 1.0) mini_mime (>= 1.1.0) - activesupport (7.0.4.3) + activesupport (7.0.5) concurrent-ruby (~> 1.0, >= 1.0.2) i18n (>= 1.6, < 2) minitest (>= 5.1) @@ -76,17 +76,17 @@ GEM activerecord (>= 5.0, < 7.1) request_store (~> 1.2) aws-eventstream (1.2.0) - aws-partitions (1.765.0) - aws-sdk-core (3.172.0) + aws-partitions (1.781.0) + aws-sdk-core (3.175.0) aws-eventstream (~> 1, >= 1.0.2) aws-partitions (~> 1, >= 1.651.0) aws-sigv4 (~> 1.5) jmespath (~> 1, >= 1.6.1) - aws-sdk-kms (1.64.0) - aws-sdk-core (~> 3, >= 3.165.0) + aws-sdk-kms (1.67.0) + aws-sdk-core (~> 3, >= 3.174.0) aws-sigv4 (~> 1.1) - aws-sdk-s3 (1.122.0) - aws-sdk-core (~> 3, >= 3.165.0) + aws-sdk-s3 (1.126.0) + aws-sdk-core (~> 3, >= 3.174.0) aws-sdk-kms (~> 1) aws-sigv4 (~> 1.4) aws-sigv4 (1.5.2) @@ -99,7 +99,7 @@ GEM faraday_middleware (~> 1.0, >= 1.0.0.rc1) net-http-persistent (~> 4.0) nokogiri (~> 1, >= 1.10.8) - bcrypt (3.1.18) + bcrypt (3.1.19) better_html (2.0.1) actionview (>= 6.0) activesupport (>= 6.0) @@ -108,13 +108,13 @@ GEM parser (>= 2.4) smart_properties bindex (0.8.1) - bootsnap (1.16.0) + bootsnap (1.15.0) msgpack (~> 1.2) builder (3.2.4) bullet (7.0.7) activesupport (>= 3.0.0) uniform_notifier (~> 1.11) - capybara (3.39.1) + capybara (3.39.2) addressable matrix mini_mime (>= 0.1.3) @@ -126,7 +126,7 @@ GEM cmdparse (3.0.7) coderay (1.1.3) concurrent-ruby (1.2.2) - connection_pool (2.4.0) + connection_pool (2.4.1) crack (0.4.5) rexml crass (1.0.6) @@ -150,6 +150,7 @@ GEM digest-crc (0.6.4) rake (>= 12.0.0, < 14.0.0) docile (1.4.0) + dotenv (2.8.1) erb_lint (0.4.0) activesupport better_html (>= 2.0.1) @@ -196,7 +197,6 @@ GEM webrick (~> 1.7) websocket-driver (>= 0.6, < 0.8) ffi (1.15.5) - geoip (1.6.4) geom2d (0.3.1) globalid (1.1.0) activesupport (>= 5.0) @@ -227,7 +227,7 @@ GEM google-cloud-core (~> 1.6) googleauth (>= 0.16.2, < 2.a) mini_mime (~> 1.0) - googleauth (1.5.2) + googleauth (1.6.0) faraday (>= 0.17.3, < 3.a) jwt (>= 1.4, < 3.0) memoist (~> 0.16) @@ -241,17 +241,18 @@ GEM openssl (>= 2.2.1) htmlentities (4.3.4) httpclient (2.8.3) - i18n (1.13.0) + i18n (1.14.1) concurrent-ruby (~> 1.0) image_processing (1.12.2) mini_magick (>= 4.9.5, < 5) ruby-vips (>= 2.0.17, < 3) io-console (0.6.0) - irb (1.6.4) + irb (1.7.0) reline (>= 0.3.0) jmespath (1.6.2) json (2.6.3) - jwt (2.7.0) + jwt (2.7.1) + language_server-protocol (3.17.0.3) launchy (2.5.2) addressable (~> 2.8) letter_opener (1.8.1) @@ -280,13 +281,14 @@ GEM method_source (1.0.0) mini_magick (4.12.0) mini_mime (1.1.2) - minitest (5.18.0) - msgpack (1.7.0) + mini_portile2 (2.8.2) + minitest (5.18.1) + msgpack (1.7.1) multi_json (1.15.0) multipart-post (2.3.0) net-http-persistent (4.0.2) connection_pool (~> 2.2) - net-imap (0.3.4) + net-imap (0.3.6) date net-protocol net-pop (0.1.2) @@ -296,16 +298,17 @@ GEM net-smtp (0.3.3) net-protocol nio4r (2.5.9) - nokogiri (1.15.0-arm64-darwin) + nokogiri (1.15.2-arm64-darwin) racc (~> 1.4) - oj (3.14.3) + oj (3.15.0) openssl (3.1.0) orm_adapter (0.5.0) os (1.1.4) pagy (6.0.4) parallel (1.23.0) - parser (3.2.2.1) + parser (3.2.2.3) ast (~> 2.4.1) + racc pg (1.5.3) premailer (1.21.0) addressable @@ -321,44 +324,45 @@ GEM pry-rails (0.3.9) pry (>= 0.10.4) public_suffix (5.0.1) - puma (6.2.2) + puma (6.3.0) nio4r (~> 2.0) - racc (1.6.2) + racc (1.7.1) rack (2.2.7) rack-proxy (0.7.6) rack rack-test (2.1.0) rack (>= 1.3) - rails (7.0.4.3) - actioncable (= 7.0.4.3) - actionmailbox (= 7.0.4.3) - actionmailer (= 7.0.4.3) - actionpack (= 7.0.4.3) - actiontext (= 7.0.4.3) - actionview (= 7.0.4.3) - activejob (= 7.0.4.3) - activemodel (= 7.0.4.3) - activerecord (= 7.0.4.3) - activestorage (= 7.0.4.3) - activesupport (= 7.0.4.3) + rails (7.0.5) + actioncable (= 7.0.5) + actionmailbox (= 7.0.5) + actionmailer (= 7.0.5) + actionpack (= 7.0.5) + actiontext (= 7.0.5) + actionview (= 7.0.5) + activejob (= 7.0.5) + activemodel (= 7.0.5) + activerecord (= 7.0.5) + activestorage (= 7.0.5) + activesupport (= 7.0.5) bundler (>= 1.15.0) - railties (= 7.0.4.3) + railties (= 7.0.5) rails-dom-testing (2.0.3) activesupport (>= 4.2.0) nokogiri (>= 1.6) - rails-html-sanitizer (1.5.0) - loofah (~> 2.19, >= 2.19.1) - railties (7.0.4.3) - actionpack (= 7.0.4.3) - activesupport (= 7.0.4.3) + rails-html-sanitizer (1.6.0) + loofah (~> 2.21) + nokogiri (~> 1.14) + railties (7.0.5) + actionpack (= 7.0.5) + activesupport (= 7.0.5) method_source rake (>= 12.2) thor (~> 1.0) zeitwerk (~> 2.5) rainbow (3.1.1) rake (13.0.6) - regexp_parser (2.8.0) - reline (0.3.3) + regexp_parser (2.8.1) + reline (0.3.5) io-console (~> 0.5) representable (3.2.0) declarative (< 0.1.0) @@ -379,7 +383,7 @@ GEM rspec-mocks (3.12.5) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.12.0) - rspec-rails (6.0.2) + rspec-rails (6.0.3) actionpack (>= 6.1) activesupport (>= 6.1) railties (>= 6.1) @@ -388,26 +392,27 @@ GEM rspec-mocks (~> 3.12) rspec-support (~> 3.12) rspec-support (3.12.0) - rubocop (1.51.0) + rubocop (1.53.0) json (~> 2.3) + language_server-protocol (>= 3.17.0) parallel (~> 1.10) - parser (>= 3.2.0.0) + parser (>= 3.2.2.3) rainbow (>= 2.2.2, < 4.0) regexp_parser (>= 1.8, < 3.0) rexml (>= 3.2.5, < 4.0) rubocop-ast (>= 1.28.0, < 2.0) ruby-progressbar (~> 1.7) unicode-display_width (>= 2.4.0, < 3.0) - rubocop-ast (1.28.1) + rubocop-ast (1.29.0) parser (>= 3.2.1.0) rubocop-capybara (2.18.0) rubocop (~> 1.41) rubocop-factory_bot (2.23.1) rubocop (~> 1.33) - rubocop-performance (1.17.1) + rubocop-performance (1.18.0) rubocop (>= 1.7.0, < 2.0) rubocop-ast (>= 0.4.0) - rubocop-rails (2.19.1) + rubocop-rails (2.20.2) activesupport (>= 4.2.0) rack (>= 1.1) rubocop (>= 1.33.0, < 2.0) @@ -420,7 +425,7 @@ GEM ffi (~> 1.12) ruby2_keywords (0.0.5) semantic_range (3.0.0) - shakapacker (6.6.0) + shakapacker (7.0.0) activesupport (>= 5.2) rack-proxy (>= 0.6.1) railties (>= 5.2) @@ -437,11 +442,12 @@ GEM simplecov-html (0.12.3) simplecov_json_formatter (0.1.4) smart_properties (1.17.0) - sqlite3 (1.6.3-arm64-darwin) + sqlite3 (1.5.4) + mini_portile2 (~> 2.8.0) strip_attributes (1.13.0) activemodel (>= 3.0, < 8.0) thor (1.2.2) - timeout (0.3.2) + timeout (0.4.0) trailblazer-option (0.1.2) turbo-rails (1.4.0) actionpack (>= 6.0.0) @@ -472,7 +478,6 @@ GEM xpath (3.2.0) nokogiri (~> 1.8) zeitwerk (2.6.8) - zip (2.0.2) PLATFORMS arm64-darwin-22 @@ -489,11 +494,11 @@ DEPENDENCIES cuprite debug devise + dotenv erb_lint factory_bot_rails faker faraday - geoip google-cloud-storage hexapdf image_processing @@ -520,7 +525,6 @@ DEPENDENCIES tzinfo-data web-console webmock - zip RUBY VERSION ruby 3.2.2p53 diff --git a/Procfile.dev b/Procfile.dev index 034c899b..b26b8715 100644 --- a/Procfile.dev +++ b/Procfile.dev @@ -1,2 +1,2 @@ web: PORT=3000 bundle exec rails s -p 3000 -webpacker: bundle exec ./bin/webpacker-dev-server +webpacker: bundle exec ./bin/shakapacker-dev-server diff --git a/app/javascript/application.js b/app/javascript/application.js index 08b51dce..2ee408b5 100644 --- a/app/javascript/application.js +++ b/app/javascript/application.js @@ -1,5 +1,8 @@ import '@hotwired/turbo-rails' +import { createApp, reactive } from 'vue' +import TemplateBuilder from './template_builder/builder' + import ToggleVisible from './elements/toggle_visible' import DisableHidden from './elements/disable_hidden' import TurboModal from './elements/turbo_modal' @@ -31,16 +34,8 @@ window.customElements.define('download-button', DownloadButton) window.customElements.define('set-origin-url', SetOriginUrl) window.customElements.define('template-builder', class extends HTMLElement { - async connectedCallback () { - const [ - { createApp, reactive }, - { default: TemplateBuilder } - ] = await Promise.all([ - import('vue'), - import('./template_builder/builder') - ]) - - this.appElem = this.children[0] + connectedCallback () { + this.appElem = document.createElement('div') this.app = createApp(TemplateBuilder, { template: reactive(JSON.parse(this.dataset.template)) @@ -55,5 +50,6 @@ window.customElements.define('template-builder', class extends HTMLElement { disconnectedCallback () { this.app?.unmount() + this.appElem?.remove() } }) diff --git a/app/views/templates/show.html.erb b/app/views/templates/show.html.erb index 48e3157d..f1b8892c 100644 --- a/app/views/templates/show.html.erb +++ b/app/views/templates/show.html.erb @@ -1,31 +1 @@ - - - - - - - <%= render 'shared/logo', width: 40, height: 40 %> - - - - <%= @template.name %> - - - - - - <%= svg_icon('users_plus', class: 'w-5') %> - Recipients - - - <%= svg_icon('device_floppy', class: 'w-5') %> - Save - - - - - <%= svg_icon('loader', class: 'animate-spin') %> - - - - + diff --git a/bin/shakapacker b/bin/shakapacker new file mode 100755 index 00000000..13a008dc --- /dev/null +++ b/bin/shakapacker @@ -0,0 +1,13 @@ +#!/usr/bin/env ruby + +ENV["RAILS_ENV"] ||= "development" +ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", __FILE__) + +require "bundler/setup" +require "shakapacker" +require "shakapacker/webpack_runner" + +APP_ROOT = File.expand_path("..", __dir__) +Dir.chdir(APP_ROOT) do + Shakapacker::WebpackRunner.run(ARGV) +end diff --git a/bin/shakapacker-dev-server b/bin/shakapacker-dev-server new file mode 100755 index 00000000..5ae88979 --- /dev/null +++ b/bin/shakapacker-dev-server @@ -0,0 +1,13 @@ +#!/usr/bin/env ruby + +ENV["RAILS_ENV"] ||= "development" +ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", __FILE__) + +require "bundler/setup" +require "shakapacker" +require "shakapacker/dev_server_runner" + +APP_ROOT = File.expand_path("..", __dir__) +Dir.chdir(APP_ROOT) do + Shakapacker::DevServerRunner.run(ARGV) +end diff --git a/bin/webpacker b/bin/webpacker deleted file mode 100755 index 47ef0c4c..00000000 --- a/bin/webpacker +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/env ruby - -require "pathname" -require "bundler/setup" -require "webpacker" -require "webpacker/webpack_runner" - -ENV["RAILS_ENV"] ||= "development" -ENV["NODE_ENV"] ||= ENV["RAILS_ENV"] -ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", Pathname.new(__FILE__).realpath) - -APP_ROOT = File.expand_path("..", __dir__) -Dir.chdir(APP_ROOT) do - Webpacker::WebpackRunner.run(ARGV) -end diff --git a/bin/webpacker-dev-server b/bin/webpacker-dev-server deleted file mode 100755 index 6629114a..00000000 --- a/bin/webpacker-dev-server +++ /dev/null @@ -1,18 +0,0 @@ -#!/usr/bin/env ruby - -ENV["RAILS_ENV"] ||= "development" -ENV["NODE_ENV"] ||= ENV["RAILS_ENV"] - -require "pathname" -ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", - Pathname.new(__FILE__).realpath) - -require "bundler/setup" - -require "webpacker" -require "webpacker/dev_server_runner" - -APP_ROOT = File.expand_path("..", __dir__) -Dir.chdir(APP_ROOT) do - Webpacker::DevServerRunner.run(ARGV) -end diff --git a/config/application.rb b/config/application.rb index cf26ca7c..7958022e 100644 --- a/config/application.rb +++ b/config/application.rb @@ -21,5 +21,7 @@ module DocuSeal config.active_storage.routes_prefix = '' config.action_view.frozen_string_literal = true + + config.middleware.insert_before ActionDispatch::Static, Rack::Deflater end end diff --git a/config/boot.rb b/config/boot.rb index c04863fa..66f988fa 100644 --- a/config/boot.rb +++ b/config/boot.rb @@ -2,5 +2,27 @@ ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) +if ENV['RAILS_ENV'] == 'production' && ENV['SECRET_KEY_BASE'].to_s.empty? + require 'dotenv' + require 'securerandom' + + dotenv_path = './docuseal.env' + + unless File.exist?(dotenv_path) + default_env = <<~TEXT + DATABASE_URL= # keep empty to use sqlite or specify postgresql database URL + SECRET_KEY_BASE=#{SecureRandom.hex(64)} + TEXT + + File.write(dotenv_path, default_env) + end + + database_url = ENV.fetch('DATABASE_URL', nil) + + Dotenv.load(dotenv_path) + + ENV['DATABASE_URL'] = ENV['DATABASE_URL'].to_s.empty? ? database_url : ENV.fetch('DATABASE_URL', nil) +end + require 'bundler/setup' # Set up gems listed in the Gemfile. require 'bootsnap/setup' # Speed up boot time by caching expensive operations. diff --git a/config/database.yml b/config/database.yml index 8d1479e8..e29f2ff9 100644 --- a/config/database.yml +++ b/config/database.yml @@ -11,5 +11,6 @@ test: database: docuseal_test production: - <<: *default - database: db/production.sqlite3 + adapter: <%= ENV['DATABASE_URL'].present? ? 'postgresql' : 'sqlite3' %> + encoding: unicode + database: <%= ENV['WORKDIR'] || '.' %>/db.sqlite3 diff --git a/config/environments/production.rb b/config/environments/production.rb index bdda6d41..e1b52f66 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -9,6 +9,11 @@ Rails.application.configure do # Code is not reloaded between requests. config.cache_classes = true + config.public_file_server.headers = { + 'Cache-Control' => 'public, s-maxage=31536000, max-age=15552000', + 'Expires' => 1.year.from_now.to_fs(:rfc822) + } + # 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. @@ -18,6 +23,7 @@ Rails.application.configure do # Full error reports are disabled and caching is turned on. config.consider_all_requests_local = false config.action_controller.perform_caching = true + config.active_record.sqlite3_production_warning = false # 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). @@ -75,12 +81,24 @@ Rails.application.configure do logger.formatter = config.log_formatter config.logger = ActiveSupport::TaggedLogging.new(logger) + encryption_secret = Digest::SHA256.hexdigest(ENV['SECRET_KEY_BASE'].to_s) + config.active_record.encryption = { - primary_key: ENV['SECRET_KEY_BASE'].first(32), - deterministic_key: ENV['SECRET_KEY_BASE'].last(32), - key_derivation_salt: ENV.fetch('SECRET_KEY_BASE', nil) + primary_key: encryption_secret.first(32), + deterministic_key: encryption_secret.last(32), + key_derivation_salt: encryption_secret } # Do not dump schema after migrations. config.active_record.dump_schema_after_migration = false + + config.lograge.enabled = true + config.lograge.formatter = Lograge::Formatters::Json.new + config.lograge.base_controller_class = ['ActionController::API', 'ActionController::Base'] + + config.lograge.custom_payload do |controller| + { + fwd: controller.request.ip + } + end end diff --git a/config/initializers/active_storage.rb b/config/initializers/active_storage.rb index dfcf4f1c..957d2319 100644 --- a/config/initializers/active_storage.rb +++ b/config/initializers/active_storage.rb @@ -6,6 +6,8 @@ ActiveSupport.on_load(:active_storage_attachment) do has_many_attached :preview_images end +ActiveStorage::LogSubscriber.detach_from(:active_storage) if Rails.env.production? + Rails.configuration.to_prepare do ActiveStorage::DiskController.after_action do response.set_header('Cache-Control', 'public, max-age=31536000') if action_name == 'show' diff --git a/config/initializers/migrate.rb b/config/initializers/migrate.rb new file mode 100644 index 00000000..074eed15 --- /dev/null +++ b/config/initializers/migrate.rb @@ -0,0 +1,3 @@ +# frozen_string_literal: true + +ActiveRecord::Tasks::DatabaseTasks.migrate if ENV['RAILS_ENV'] == 'production' diff --git a/config/puma.rb b/config/puma.rb index ce231999..7568bc79 100644 --- a/config/puma.rb +++ b/config/puma.rb @@ -6,7 +6,7 @@ # the maximum value specified for Puma. Default is set to 5 threads for minimum # and maximum; this matches the default thread size of Active Record. # -max_threads_count = ENV.fetch('RAILS_MAX_THREADS', 10) +max_threads_count = ENV.fetch('RAILS_MAX_THREADS', 15) min_threads_count = ENV.fetch('RAILS_MIN_THREADS') { max_threads_count } threads min_threads_count, max_threads_count @@ -24,7 +24,7 @@ port ENV.fetch('PORT', 3000) environment ENV.fetch('RAILS_ENV', 'development') # Specifies the `pidfile` that Puma will use. -pidfile ENV.fetch('PIDFILE', 'tmp/pids/server.pid') +# pidfile ENV.fetch('PIDFILE', 'tmp/pids/server.pid') # Specifies the number of `workers` to boot in clustered mode. # Workers are forked web server processes. If using threads and workers together diff --git a/config/webpacker.yml b/config/shakapacker.yml similarity index 94% rename from config/webpacker.yml rename to config/shakapacker.yml index 96e28c74..14ad9611 100644 --- a/config/webpacker.yml +++ b/config/shakapacker.yml @@ -5,13 +5,14 @@ default: &default public_root_path: public public_output_path: packs cache_path: tmp/webpacker - webpacker_precompile: true + shakapacker_precompile: true webpack_compile_output: true additional_paths: [] webpack_loader: 'babel' compiler_strategy: digest cache_manifest: false ensure_consistent_versioning: false + useContentHash: false development: <<: *default diff --git a/config/storage.yml b/config/storage.yml index cb97bc19..f704e8fa 100644 --- a/config/storage.yml +++ b/config/storage.yml @@ -1,11 +1,11 @@ local: service: Disk - root: <%= ENV['WORKDIR'] %>docuseal-attachments + root: <%= ENV['WORKDIR'] || '.' %>/attachments public: true disk: service: Disk - root: <%= ENV['WORKDIR'] %>docuseal-attachments + root: <%= ENV['WORKDIR'] || '.' %>/attachments public: true aws_s3: diff --git a/config/webpack/webpack.config.js b/config/webpack/webpack.config.js index d92c365e..8e6bc9a3 100644 --- a/config/webpack/webpack.config.js +++ b/config/webpack/webpack.config.js @@ -1,8 +1,8 @@ -const { webpackConfig, merge } = require('shakapacker') +const { globalMutableWebpackConfig, merge } = require('shakapacker') const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin const { VueLoaderPlugin } = require('vue-loader') -const configs = merge(webpackConfig, { +const configs = merge(globalMutableWebpackConfig, { resolve: { extensions: ['.css', '.scss', '.vue'] }, diff --git a/lib/submissions/generate_result_attachments.rb b/lib/submissions/generate_result_attachments.rb index 646695d7..8e27df20 100644 --- a/lib/submissions/generate_result_attachments.rb +++ b/lib/submissions/generate_result_attachments.rb @@ -7,7 +7,11 @@ module Submissions INFO_CREATOR = 'DocuSeal (https://www.docuseal.co)' + TEXT_LEFT_MARGIN = 1 + TEXT_TOP_MARGIN = 1 + A4_SIZE = [595, 842].freeze + SUPPORTED_IMAGE_TYPES = ['image/png', 'image/jpeg'].freeze module_function @@ -39,7 +43,16 @@ module Submissions case field['type'] when 'image', 'signature' attachment = submitter.attachments.find { |a| a.uuid == value } - io = StringIO.new(attachment.download) + + image_data = + if SUPPORTED_IMAGE_TYPES.include?(attachment.content_type) + attachment.download + else + Vips::Image.new_from_buffer(attachment.download, '') + .write_to_buffer('.jpg', Q: 40) + end + + io = StringIO.new(image_data) scale = [(area['w'] * width) / attachment.metadata['width'], (area['h'] * height) / attachment.metadata['height']].min @@ -86,9 +99,9 @@ module Submissions { Type: :Annot, Subtype: :Link, Rect: [ - area['x'] * width, + (area['x'] * width) + TEXT_LEFT_MARGIN, height - (area['y'] * height) - lines[...index].sum(&:height) + height_diff, - (area['x'] * width) + (area['w'] * width), + (area['x'] * width) + (area['w'] * width) + TEXT_LEFT_MARGIN, height - (area['y'] * height) - lines[..next_index].sum(&:height) + height_diff ], A: { Type: :Action, S: :URI, URI: attachment.url } @@ -99,7 +112,8 @@ module Submissions end layouter.fit(items, area['w'] * width, height_diff.positive? ? box_height : area['h'] * height) - .draw(canvas, area['x'] * width, height - (area['y'] * height) + height_diff) + .draw(canvas, (area['x'] * width) + TEXT_LEFT_MARGIN, + height - (area['y'] * height) + height_diff - TEXT_TOP_MARGIN) when 'checkbox' next unless value == true @@ -125,7 +139,8 @@ module Submissions height_diff = [0, box_height - (area['h'] * height)].max layouter.fit([text], area['w'] * width, height_diff.positive? ? box_height : area['h'] * height) - .draw(canvas, area['x'] * width, height - (area['y'] * height) + height_diff) + .draw(canvas, (area['x'] * width) + TEXT_LEFT_MARGIN, + height - (area['y'] * height) + height_diff - TEXT_TOP_MARGIN) end end end @@ -167,7 +182,7 @@ module Submissions def save_signed_pdf(pdf:, submitter:, cert:, uuid:, name:) io = StringIO.new - pdf.trailer[:Info][:Creator] = INFO_CREATOR + pdf.trailer.info[:Creator] = INFO_CREATOR pdf.sign(io, reason: "Signed by #{submitter.email} with docuseal.co", certificate: OpenSSL::X509::Certificate.new(cert['cert']), diff --git a/package.json b/package.json index 54560cdb..ec3d124c 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,7 @@ "postcss-loader": "^7.3.0", "sass": "^1.62.1", "sass-loader": "^13.2.2", - "shakapacker": "6.6.0", + "shakapacker": "7.0.0", "signature_pad": "^4.1.5", "tailwindcss": "^3.3.2", "terser-webpack-plugin": "5.3.8", diff --git a/yarn.lock b/yarn.lock index fd71b634..093d67fa 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4818,10 +4818,10 @@ setprototypeof@1.2.0: resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== -shakapacker@6.6.0: - version "6.6.0" - resolved "https://registry.yarnpkg.com/shakapacker/-/shakapacker-6.6.0.tgz#1e372a7ce6fa93f1a7bd1820737b8168679eb220" - integrity sha512-7sNnv8PXMlgm2Ob7vZOayLKu0+PPMN3q0HEyAlkFIJtHJt7wA3p1rObhlk0/OrNeBa4dio/9HiBUeEU7bZsHvw== +shakapacker@7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/shakapacker/-/shakapacker-7.0.0.tgz#788c96e86eb78e44ee84c3cc03b7c091e4621fc3" + integrity sha512-yL5lbCdgtI8nUxZHarL7X5aB40r069wAunHwb59Hgw1gPg6/nMuYo7ofNTGXg11v1eunwAdjx8EfYnk4XwX27Q== dependencies: glob "^7.2.0" js-yaml "^4.1.0"