diff --git a/.dockerignore b/.dockerignore index 745841df6..13f7bad94 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,4 +1,17 @@ -.git/* -.gitignore -log/* -tmp/* +secrets.env +.git +tmp +!tmp/pids +log +public/assets +public/packs +.bundle + +db/*.sqlite3 +db/*.sqlite3-* + +storage +config/master.key +config/credentials/*.key + +node_modules diff --git a/.gitignore b/.gitignore index 03d45aca4..9313310e1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ +stringer_dev +secrets.env +.byebug* *.gem *.rbc .bundle diff --git a/.ruby-version b/.ruby-version index ff365e06b..944880fa1 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -3.1.3 +3.2.0 diff --git a/Dockerfile b/Dockerfile index 307350dfb..e45db3e15 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,42 +1,106 @@ -FROM ruby:3.1.3 +# syntax = docker/dockerfile:experimental -ENV RACK_ENV=production -ENV PORT=8080 +# Dockerfile used to build a deployable image for a Rails application. +# Adjust as required. +# +# Common adjustments you may need to make over time: +# * Modify version numbers for Ruby, Bundler, and other products. +# * Add library packages needed at build time for your gems, node modules. +# * Add deployment packages needed by your application +# * Add (often fake) secrets needed to compile your assets -EXPOSE 8080 +####################################################################### + +# Learn more about the chosen Ruby stack, Fullstaq Ruby, here: +# https://github.com/evilmartians/fullstaq-ruby-docker. +# +# We recommend using the highest patch level for better security and +# performance. + +ARG RUBY_VERSION=3.1.3 +ARG VARIANT=bullseye +FROM ruby:${RUBY_VERSION}-${VARIANT} as base + +LABEL fly_launch_runtime="rails" + +ARG BUNDLER_VERSION=2.3.26 + +ARG RAILS_ENV=production +ARG BUNDLE_WITHOUT=development:test +ARG BUNDLE_PATH=vendor/bundle +ENV RAILS_ENV=${RAILS_ENV} +ENV BUNDLE_PATH ${BUNDLE_PATH} +ENV BUNDLE_WITHOUT ${BUNDLE_WITHOUT} +ENV RAILS_SERVE_STATIC_FILES true +ENV RAILS_LOG_TO_STDOUT true + +RUN mkdir /app WORKDIR /app -ADD Gemfile Gemfile.lock /app/ -RUN bundle install +RUN mkdir -p tmp/pids + +####################################################################### + +# install packages only needed at build time + +FROM base as build_deps + +ARG BUILD_PACKAGES="git build-essential libpq-dev wget vim curl gzip xz-utils libsqlite3-dev" +ENV BUILD_PACKAGES ${BUILD_PACKAGES} -RUN apt-get update \ - && apt-get install -y --no-install-recommends \ - supervisor locales nodejs \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* +RUN --mount=type=cache,id=dev-apt-cache,sharing=locked,target=/var/cache/apt \ + --mount=type=cache,id=dev-apt-lib,sharing=locked,target=/var/lib/apt \ + apt-get update -qq && \ + apt-get install --no-install-recommends -y ${BUILD_PACKAGES} \ + && rm -rf /var/lib/apt/lists /var/cache/apt/archives -RUN DEBIAN_FRONTEND=noninteractive dpkg-reconfigure locales \ - && locale-gen C.UTF-8 \ - && /usr/sbin/update-locale LANG=C.UTF-8 +####################################################################### -ENV LC_ALL C.UTF-8 +# install gems -ENV SUPERCRONIC_URL=https://github.com/aptible/supercronic/releases/download/v0.1.3/supercronic-linux-amd64 \ - SUPERCRONIC=supercronic-linux-amd64 \ - SUPERCRONIC_SHA1SUM=96960ba3207756bb01e6892c978264e5362e117e +FROM build_deps as gems -RUN curl -fsSLO "$SUPERCRONIC_URL" \ - && echo "${SUPERCRONIC_SHA1SUM} ${SUPERCRONIC}" | sha1sum -c - \ - && chmod +x "$SUPERCRONIC" \ - && mv "$SUPERCRONIC" "/usr/local/bin/${SUPERCRONIC}" \ - && ln -s "/usr/local/bin/${SUPERCRONIC}" /usr/local/bin/supercronic +RUN gem update --system --no-document && \ + gem install -N bundler -v ${BUNDLER_VERSION} -ADD docker/supervisord.conf /etc/supervisord.conf -ADD docker/start.sh /app/ -ADD . /app +COPY Gemfile* ./ +RUN bundle install && rm -rf vendor/bundle/ruby/*/cache + +####################################################################### + +# install deployment packages + +FROM base + +ARG DEPLOY_PACKAGES="postgresql-client file vim curl gzip libsqlite3-0 nodejs" +ENV DEPLOY_PACKAGES=${DEPLOY_PACKAGES} + +RUN --mount=type=cache,id=prod-apt-cache,sharing=locked,target=/var/cache/apt \ + --mount=type=cache,id=prod-apt-lib,sharing=locked,target=/var/lib/apt \ + apt-get update -qq && \ + apt-get install --no-install-recommends -y \ + ${DEPLOY_PACKAGES} \ + && rm -rf /var/lib/apt/lists /var/cache/apt/archives + +# copy installed gems +COPY --from=gems /app /app +COPY --from=gems /usr/local/lib/ruby /usr/local/lib/ruby +COPY --from=gems /usr/local/bundle /usr/local/bundle + +####################################################################### + +# Deploy your application +COPY . . + +EXPOSE 8080 -RUN useradd -m stringer -RUN chown -R stringer:stringer /app -USER stringer +ENV WEB_CONCURRENCY 1 +ENV MAX_THREADS 2 +ENV RACK_ENV production +ENV PUMA_WORKER_TIMEOUT 25 +ENV PUMA_WORKER_SHUTDOWN_TIMEOUT 25 +ENV DATABASE_URL "" +ARG SERVER_COMMAND="bundle exec puma --debug -p 8080 config.ru" -CMD /app/start.sh +ENV SERVER_COMMAND ${SERVER_COMMAND} +CMD ${SERVER_COMMAND} diff --git a/Gemfile b/Gemfile index 6174e4a96..ee032e337 100644 --- a/Gemfile +++ b/Gemfile @@ -1,8 +1,17 @@ -ruby_version_file = File.expand_path(".ruby-version", __dir__) -ruby File.read(ruby_version_file).chomp if File.readable?(ruby_version_file) +ruby "3.2.0" + source "https://rubygems.org" +gem "sassc" +gem "sassc-rails" + +group :production do + gem "pg" + gem "puma" +end + group :development do + gem "sqlite3" gem "rubocop", require: false gem "rubocop-rails", require: false gem "rubocop-rake", require: false @@ -13,7 +22,6 @@ group :development, :test do gem "capybara" gem "coveralls_reborn", require: false gem "faker" - gem "pry-byebug" gem "rack-test" gem "rspec" gem "rspec-html-matchers" @@ -24,27 +32,28 @@ end gem "activerecord" gem "bcrypt" -gem "delayed_job" -gem "delayed_job_active_record" + gem "feedbag" gem "feedjira" gem "httparty" gem "i18n" gem "loofah" + gem "nokogiri" -gem "pg" -gem "puma", "~> 6.0" gem "rack-protection" -gem "racksh" gem "rack-ssl" gem "rake" -gem "sass" +gem "racksh" + gem "sinatra" gem "sinatra-activerecord" + gem "sinatra-contrib" + gem "sinatra-flash" gem "sprockets" gem "sprockets-helpers" + gem "thread" gem "uglifier" gem "will_paginate" diff --git a/Gemfile.lock b/Gemfile.lock index d3a14d15e..4a925957c 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,6 +1,19 @@ GEM remote: https://rubygems.org/ specs: + actionpack (7.0.4) + actionview (= 7.0.4) + activesupport (= 7.0.4) + rack (~> 2.0, >= 2.2.0) + rack-test (>= 0.6.3) + rails-dom-testing (~> 2.0) + rails-html-sanitizer (~> 1.0, >= 1.2.0) + actionview (7.0.4) + activesupport (= 7.0.4) + builder (~> 3.1) + erubi (~> 1.4) + rails-dom-testing (~> 2.0) + rails-html-sanitizer (~> 1.1, >= 1.2.0) activemodel (7.0.4) activesupport (= 7.0.4) activerecord (7.0.4) @@ -15,7 +28,7 @@ GEM public_suffix (>= 2.0.2, < 6.0) ast (2.4.2) bcrypt (3.1.18) - byebug (11.1.3) + builder (3.2.4) capybara (3.38.0) addressable matrix @@ -25,23 +38,18 @@ GEM rack-test (>= 0.6.3) regexp_parser (>= 1.5, < 3.0) xpath (~> 3.2) - coderay (1.1.3) concurrent-ruby (1.1.10) - coveralls_reborn (0.25.0) - simplecov (>= 0.18.1, < 0.22.0) - term-ansicolor (~> 1.6) - thor (>= 0.20.3, < 2.0) - tins (~> 1.16) + coveralls_reborn (0.26.0) + simplecov (~> 0.22.0) + term-ansicolor (~> 1.7) + thor (~> 1.2) + tins (~> 1.32) crass (1.0.6) - delayed_job (4.1.11) - activesupport (>= 3.0, < 8.0) - delayed_job_active_record (4.1.7) - activerecord (>= 3.0, < 8.0) - delayed_job (>= 3.0, < 5) diff-lcs (1.5.0) docile (1.4.0) + erubi (1.12.0) execjs (2.8.1) - faker (3.0.0) + faker (3.1.0) i18n (>= 1.8.11, < 2) feedbag (1.0.0) nokogiri (~> 1.8, >= 1.8.2) @@ -49,47 +57,38 @@ GEM loofah (>= 2.3.1) sax-machine (>= 1.0) ffi (1.15.5) - httparty (0.20.0) - mime-types (~> 3.0) + httparty (0.21.0) + mini_mime (>= 1.0.0) multi_xml (>= 0.5.2) i18n (1.12.0) concurrent-ruby (~> 1.0) - json (2.6.2) - loofah (2.19.0) + json (2.6.3) + loofah (2.19.1) crass (~> 1.0.2) nokogiri (>= 1.5.9) matrix (0.4.2) method_source (1.0.0) - mime-types (3.4.1) - mime-types-data (~> 3.2015) - mime-types-data (3.2022.0105) mini_mime (1.1.2) - mini_portile2 (2.8.0) - minitest (5.16.3) + mini_portile2 (2.8.1) + minitest (5.17.0) multi_json (1.15.0) multi_xml (0.6.0) mustermann (3.0.0) ruby2_keywords (~> 0.0.1) nio4r (2.5.8) - nokogiri (1.13.9) + nokogiri (1.13.10) mini_portile2 (~> 2.8.0) racc (~> 1.4) parallel (1.22.1) - parser (3.1.3.0) + parser (3.2.0.0) ast (~> 2.4.1) pg (1.4.5) - pry (0.14.1) - coderay (~> 1.1) - method_source (~> 1.0) - pry-byebug (3.10.1) - byebug (~> 11.0) - pry (>= 0.13, < 0.15) - public_suffix (5.0.0) - puma (6.0.0) + public_suffix (5.0.1) + puma (6.0.2) nio4r (~> 2.0) - racc (1.6.1) - rack (2.2.4) - rack-protection (3.0.4) + racc (1.6.2) + rack (2.2.5) + rack-protection (3.0.5) rack rack-ssl (1.4.1) rack @@ -98,11 +97,20 @@ GEM racksh (1.0.0) rack (>= 1.0) rack-test (>= 0.5) + rails-dom-testing (2.0.3) + activesupport (>= 4.2.0) + nokogiri (>= 1.6) + rails-html-sanitizer (1.4.4) + loofah (~> 2.19, >= 2.19.1) + railties (7.0.4) + actionpack (= 7.0.4) + activesupport (= 7.0.4) + method_source + rake (>= 12.2) + thor (~> 1.0) + zeitwerk (~> 2.5) rainbow (3.1.1) rake (13.0.6) - rb-fsevent (0.11.2) - rb-inotify (0.10.1) - ffi (~> 1.0) regexp_parser (2.6.1) rexml (3.2.5) rspec (3.12.0) @@ -111,73 +119,82 @@ GEM rspec-mocks (~> 3.12.0) rspec-core (3.12.0) rspec-support (~> 3.12.0) - rspec-expectations (3.12.0) + rspec-expectations (3.12.2) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.12.0) rspec-html-matchers (0.10.0) nokogiri (~> 1) rspec (>= 3.0.0.a) - rspec-mocks (3.12.0) + rspec-mocks (3.12.2) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.12.0) rspec-support (3.12.0) - rubocop (1.39.0) + rubocop (1.42.0) json (~> 2.3) parallel (~> 1.10) parser (>= 3.1.2.1) rainbow (>= 2.2.2, < 4.0) regexp_parser (>= 1.8, < 3.0) rexml (>= 3.2.5, < 4.0) - rubocop-ast (>= 1.23.0, < 2.0) + rubocop-ast (>= 1.24.1, < 2.0) ruby-progressbar (~> 1.7) unicode-display_width (>= 1.4.0, < 3.0) - rubocop-ast (1.24.0) + rubocop-ast (1.24.1) parser (>= 3.1.1.0) - rubocop-rails (2.17.3) + rubocop-rails (2.17.4) activesupport (>= 4.2.0) rack (>= 1.1) rubocop (>= 1.33.0, < 2.0) rubocop-rake (0.6.0) rubocop (~> 1.0) - rubocop-rspec (2.15.0) + rubocop-rspec (2.16.0) rubocop (~> 1.33) ruby-progressbar (1.11.0) ruby2_keywords (0.0.5) - sass (3.7.4) - sass-listen (~> 4.0.0) - sass-listen (4.0.0) - rb-fsevent (~> 0.9, >= 0.9.4) - rb-inotify (~> 0.9, >= 0.9.7) + 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 sax-machine (1.3.2) shotgun (0.9.2) rack (>= 1.0) - simplecov (0.21.2) + simplecov (0.22.0) docile (~> 1.1) simplecov-html (~> 0.11) simplecov_json_formatter (~> 0.1) simplecov-html (0.12.3) simplecov_json_formatter (0.1.4) - sinatra (3.0.4) + sinatra (3.0.5) mustermann (~> 3.0) rack (~> 2.2, >= 2.2.4) - rack-protection (= 3.0.4) + rack-protection (= 3.0.5) tilt (~> 2.0) sinatra-activerecord (2.0.26) activerecord (>= 4.1) sinatra (>= 1.0) - sinatra-contrib (3.0.4) + sinatra-contrib (3.0.5) multi_json mustermann (~> 3.0) - rack-protection (= 3.0.4) - sinatra (= 3.0.4) + rack-protection (= 3.0.5) + sinatra (= 3.0.5) tilt (~> 2.0) sinatra-flash (0.3.0) sinatra (>= 1.0.0) - sprockets (4.1.1) + sprockets (4.2.0) concurrent-ruby (~> 1.0) - rack (> 1, < 3) + rack (>= 2.2.4, < 4) sprockets-helpers (1.4.0) sprockets (>= 2.2) + sprockets-rails (3.4.2) + actionpack (>= 5.2) + activesupport (>= 5.2) + sprockets (>= 3.0.0) + sqlite3 (1.5.4) + mini_portile2 (~> 2.8.0) sync (0.5.0) term-ansicolor (1.7.1) tins (~> 1.0) @@ -191,21 +208,22 @@ GEM concurrent-ruby (~> 1.0) uglifier (4.2.0) execjs (>= 0.3.0, < 3) - unicode-display_width (2.3.0) + unicode-display_width (2.4.2) will_paginate (3.3.1) xpath (3.2.0) nokogiri (~> 1.8) + zeitwerk (2.6.6) PLATFORMS - ruby + arm64-darwin-21 + arm64-darwin-22 + x86_64-linux DEPENDENCIES activerecord bcrypt capybara coveralls_reborn - delayed_job - delayed_job_active_record faker feedbag feedjira @@ -214,8 +232,7 @@ DEPENDENCIES loofah nokogiri pg - pry-byebug - puma (~> 6.0) + puma rack-protection rack-ssl rack-test @@ -227,7 +244,8 @@ DEPENDENCIES rubocop-rails rubocop-rake rubocop-rspec - sass + sassc + sassc-rails shotgun simplecov sinatra @@ -236,13 +254,14 @@ DEPENDENCIES sinatra-flash sprockets sprockets-helpers + sqlite3 thread timecop uglifier will_paginate RUBY VERSION - ruby 3.1.3 + ruby 3.2.0p0 BUNDLED WITH - 2.2.33 + 2.3.26 diff --git a/Procfile b/Procfile deleted file mode 100644 index cceb33ddf..000000000 --- a/Procfile +++ /dev/null @@ -1,2 +0,0 @@ -web: bundle exec puma -p $PORT -C ./config/puma.rb -console: bundle exec racksh diff --git a/Rakefile b/Rakefile index 4e4a44a3e..12ac02d7b 100644 --- a/Rakefile +++ b/Rakefile @@ -5,8 +5,6 @@ require "rubygems" require "net/http" require "active_record" require "active_support/core_ext/kernel/reporting" -require "delayed_job" -require "delayed_job_active_record" require "sinatra/activerecord/rake" ActiveRecord::Tasks::DatabaseTasks.db_dir = "db" @@ -22,41 +20,11 @@ task :fetch_feeds do FetchFeeds.new(Feed.all).fetch_all end -desc "Lazily fetch all feeds." -task :lazy_fetch do - if ENV["APP_URL"] - uri = URI(ENV["APP_URL"]) - Net::HTTP.get_response(uri) - end - - FeedRepository.list.each do |feed| - Delayed::Job.enqueue FetchFeedJob.new(feed.id) - end -end - desc "Fetch single feed" task :fetch_feed, :id do |_t, args| FetchFeed.new(Feed.find(args[:id])).fetch end -desc "Clear the delayed_job queue." -task :clear_jobs do - Delayed::Job.delete_all -end - -desc "Work the delayed_job queue." -task :work_jobs do - Delayed::Job.delete_all - - worker_retry = Integer(ENV["WORKER_RETRY"] || 3) - worker_retry.times do - Delayed::Worker.new( - min_priority: ENV["MIN_PRIORITY"], - max_priority: ENV["MAX_PRIORITY"] - ).start - end -end - desc "Change your password" task :change_password do ChangePassword.new.change_password diff --git a/app.json b/app.json index 4288d8c97..f8d184fd1 100644 --- a/app.json +++ b/app.json @@ -1,4 +1,5 @@ { + "name": "Stringer", "description": "A self-hosted, anti-social RSS reader.", "logo": "https://raw.githubusercontent.com/stringer-rss/stringer/main/screenshots/logo.png", diff --git a/app.rb b/app.rb index 9f0d3fcb4..55168a1e3 100644 --- a/app.rb +++ b/app.rb @@ -7,10 +7,11 @@ require "i18n" require "will_paginate" require "will_paginate/active_record" +require "rack/protection" require "sprockets" require "sprockets-helpers" require "securerandom" - +require 'sassc-rails' require_relative "app/helpers/authentication_helpers" require_relative "app/repositories/user_repository" require_relative "config/asset_pipeline" @@ -34,7 +35,9 @@ class Stringer < Sinatra::Base set :root, File.dirname(__FILE__) enable :sessions - set :session_secret, ENV["SECRET_TOKEN"] || SecureRandom.hex(32) + set :session_secret, ENV["SECRET_TOKEN"] || "secret!" + use Rack::Protection + enable :logging enable :method_override diff --git a/app/controllers/debug_controller.rb b/app/controllers/debug_controller.rb index f38594201..35dd670a5 100644 --- a/app/controllers/debug_controller.rb +++ b/app/controllers/debug_controller.rb @@ -3,8 +3,6 @@ class Stringer < Sinatra::Base get "/debug" do erb :debug, locals: { - queued_jobs_count: Delayed::Job.count, - pending_migrations: MigrationStatus.new.pending_migrations } end diff --git a/app/controllers/feeds_controller.rb b/app/controllers/feeds_controller.rb index 0cbb0edb7..ef23cfe1f 100644 --- a/app/controllers/feeds_controller.rb +++ b/app/controllers/feeds_controller.rb @@ -40,8 +40,6 @@ class Stringer < Sinatra::Base feed = AddNewFeed.add(@feed_url) if feed && feed.valid? - FetchFeeds.enqueue([feed]) - flash[:success] = t("feeds.add.flash.added_successfully") redirect to("/") elsif feed diff --git a/app/controllers/first_run_controller.rb b/app/controllers/first_run_controller.rb index 6be12f769..51607852d 100644 --- a/app/controllers/first_run_controller.rb +++ b/app/controllers/first_run_controller.rb @@ -28,7 +28,6 @@ class Stringer < Sinatra::Base end get "/tutorial" do - FetchFeeds.enqueue(Feed.all) CompleteSetup.complete(current_user) @sample_stories = StoryRepository.samples diff --git a/app/models/story.rb b/app/models/story.rb index ae346ef2a..ffc46c18a 100644 --- a/app/models/story.rb +++ b/app/models/story.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require_relative "./application_record" require_relative "./feed" diff --git a/app/tasks/fetch_feed.rb b/app/tasks/fetch_feed.rb index c683823e3..9ba42b113 100644 --- a/app/tasks/fetch_feed.rb +++ b/app/tasks/fetch_feed.rb @@ -1,3 +1,4 @@ +# frozen_string_literal: true require "feedjira" require "httparty" @@ -33,7 +34,12 @@ def fetch def fetch_raw_feed response = @client.get(@feed.url).to_s - @parser.parse(response) + begin + @parser.parse(response) + rescue => e + @logger&.error "Error fetching feed: #{e}, server response: #{response[...100]}" + raise + end end def feed_not_modified diff --git a/app/tasks/fetch_feeds.rb b/app/tasks/fetch_feeds.rb index c1c1aab66..23b02ea91 100644 --- a/app/tasks/fetch_feeds.rb +++ b/app/tasks/fetch_feeds.rb @@ -2,7 +2,6 @@ require_relative "fetch_feed" require "active_support/core_ext/kernel/reporting" -require "delayed_job_active_record" class FetchFeeds def initialize(feeds, pool = nil) @@ -26,14 +25,4 @@ def fetch_all @pool.shutdown end - - def prepare_to_delay - @feeds_ids = @feeds.map(&:id) - @feeds = [] - self - end - - def self.enqueue(feeds) - new(feeds).prepare_to_delay.delay.fetch_all - end end diff --git a/app/views/debug.erb b/app/views/debug.erb index 8d010de79..147e4f940 100644 --- a/app/views/debug.erb +++ b/app/views/debug.erb @@ -5,19 +5,5 @@
<%= RUBY_VERSION %>
User Agent
<%= request.user_agent %>
-
Queued Jobs
-
<%= queued_jobs_count %>
-
Pending Migrations
-
- <% if pending_migrations.present? %> - - <% else %> - None - <% end %> -
diff --git a/app/views/feeds/index.erb b/app/views/feeds/index.erb index 0dad10031..002f5b0d5 100644 --- a/app/views/feeds/index.erb +++ b/app/views/feeds/index.erb @@ -20,4 +20,4 @@ $(document).ready(function () { $(".status").tooltip(); }); - \ No newline at end of file + diff --git a/config.ru b/config.ru index a45d484f3..54f36b213 100644 --- a/config.ru +++ b/config.ru @@ -1,9 +1,8 @@ require "rubygems" require "bundler" - -require "active_support/core_ext/kernel/reporting" Bundler.require + require "./fever_api" map "/fever" do run FeverAPI::Endpoint diff --git a/config/database.yml b/config/database.yml index f18477a44..87f34b020 100644 --- a/config/database.yml +++ b/config/database.yml @@ -1,8 +1,6 @@ development: - adapter: postgresql + adapter: sqlite3 database: stringer_dev - encoding: unicode - pool: 5 test: adapter: postgresql @@ -11,6 +9,7 @@ test: pool: 5 production: - url: <%= ENV["DATABASE_URL"] %> + adapter: postgresql encoding: unicode pool: 5 + url: <%= ENV["DATABASE_URL"] %> diff --git a/config/locales/en.yml b/config/locales/en.yml index 8eb38af8d..1dd699d69 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -71,7 +71,7 @@ en: import: Import logout: Logout support: Support - title: stringer | your rss buddy + title: AlkaliNews partials: action_bar: add_feed: Add a feed diff --git a/config/puma.rb b/config/puma.rb index 0e079d63c..47bea32e5 100644 --- a/config/puma.rb +++ b/config/puma.rb @@ -2,22 +2,18 @@ threads_count = Integer(ENV.fetch("MAX_THREADS", 2)) threads threads_count, threads_count -port ENV.fetch("PORT", 3000) +port 8080 environment ENV.fetch("RACK_ENV", "development") worker_timeout Integer(ENV.fetch("PUMA_WORKER_TIMEOUT", 25)) worker_shutdown_timeout Integer(ENV.fetch("PUMA_WORKER_SHUTDOWN_TIMEOUT", 25)) preload_app! -@delayed_job_pid = nil - before_fork do # the following is highly recommended for Rails + "preload_app true" # as there's no need for the master process to hold a connection ActiveRecord::Base.connection.disconnect! if defined?(ActiveRecord::Base) - @delayed_job_pid ||= spawn("bundle exec rake work_jobs") unless ENV["WORKER_EMBEDDED"] == "false" - sleep 1 end @@ -28,7 +24,3 @@ ActiveRecord::Base.establish_connection(config) end end - -on_worker_shutdown do - Process.kill("QUIT", @delayed_job_pid) if !ENV["RACK_ENV"] || ENV["RACK_ENV"] == "development" -end diff --git a/db/migrate/20130409010818_create_feeds.rb b/db/migrate/20130409010818_create_feeds.rb index 628400da3..144345bd0 100644 --- a/db/migrate/20130409010818_create_feeds.rb +++ b/db/migrate/20130409010818_create_feeds.rb @@ -1,4 +1,4 @@ -class CreateFeeds < ActiveRecord::Migration[4.2] +class CreateFeeds < ActiveRecord::Migration[5.1] def change create_table :feeds do |t| t.string :name diff --git a/db/migrate/20130409010826_create_stories.rb b/db/migrate/20130409010826_create_stories.rb index 8df287917..6450aa653 100644 --- a/db/migrate/20130409010826_create_stories.rb +++ b/db/migrate/20130409010826_create_stories.rb @@ -1,4 +1,4 @@ -class CreateStories < ActiveRecord::Migration[4.2] +class CreateStories < ActiveRecord::Migration[5.1] def change create_table :stories do |t| t.string :title diff --git a/db/migrate/20130412185253_add_new_fields_to_stories.rb b/db/migrate/20130412185253_add_new_fields_to_stories.rb index 00beff1cc..84c31d214 100644 --- a/db/migrate/20130412185253_add_new_fields_to_stories.rb +++ b/db/migrate/20130412185253_add_new_fields_to_stories.rb @@ -1,4 +1,4 @@ -class AddNewFieldsToStories < ActiveRecord::Migration[4.2] +class AddNewFieldsToStories < ActiveRecord::Migration[5.1] def change add_column :stories, :published, :timestamp add_column :stories, :is_read, :boolean diff --git a/db/migrate/20130418221144_add_user_model.rb b/db/migrate/20130418221144_add_user_model.rb index 5adc4fc3b..9f9ce5ad0 100644 --- a/db/migrate/20130418221144_add_user_model.rb +++ b/db/migrate/20130418221144_add_user_model.rb @@ -1,4 +1,4 @@ -class AddUserModel < ActiveRecord::Migration[4.2] +class AddUserModel < ActiveRecord::Migration[5.1] def change create_table :users do |t| t.string :email diff --git a/db/migrate/20130423001740_drop_email_from_user.rb b/db/migrate/20130423001740_drop_email_from_user.rb index f40b8a62c..f41e2ecd6 100644 --- a/db/migrate/20130423001740_drop_email_from_user.rb +++ b/db/migrate/20130423001740_drop_email_from_user.rb @@ -1,4 +1,4 @@ -class DropEmailFromUser < ActiveRecord::Migration[4.2] +class DropEmailFromUser < ActiveRecord::Migration[5.1] def up remove_column :users, :email end diff --git a/db/migrate/20130423180446_remove_author_from_stories.rb b/db/migrate/20130423180446_remove_author_from_stories.rb index 47b630d0f..2d0107940 100644 --- a/db/migrate/20130423180446_remove_author_from_stories.rb +++ b/db/migrate/20130423180446_remove_author_from_stories.rb @@ -1,4 +1,4 @@ -class RemoveAuthorFromStories < ActiveRecord::Migration[4.2] +class RemoveAuthorFromStories < ActiveRecord::Migration[5.1] def up remove_column :stories, :author end diff --git a/db/migrate/20130425211008_add_setup_complete_to_user.rb b/db/migrate/20130425211008_add_setup_complete_to_user.rb index d98aa7a1c..629284d9b 100644 --- a/db/migrate/20130425211008_add_setup_complete_to_user.rb +++ b/db/migrate/20130425211008_add_setup_complete_to_user.rb @@ -1,4 +1,4 @@ -class AddSetupCompleteToUser < ActiveRecord::Migration[4.2] +class AddSetupCompleteToUser < ActiveRecord::Migration[5.1] def change add_column :users, :setup_complete, :boolean end diff --git a/db/migrate/20130425222157_add_delayed_job.rb b/db/migrate/20130425222157_add_delayed_job.rb index 60736b629..7e5e83582 100644 --- a/db/migrate/20130425222157_add_delayed_job.rb +++ b/db/migrate/20130425222157_add_delayed_job.rb @@ -1,4 +1,4 @@ -class AddDelayedJob < ActiveRecord::Migration[4.2] +class AddDelayedJob < ActiveRecord::Migration[5.1] def self.up create_table :delayed_jobs, force: true do |table| # Allows some jobs to jump to the front of the queue diff --git a/db/migrate/20130429232127_add_status_to_feeds.rb b/db/migrate/20130429232127_add_status_to_feeds.rb index 9bb84a23f..c804dc1a7 100644 --- a/db/migrate/20130429232127_add_status_to_feeds.rb +++ b/db/migrate/20130429232127_add_status_to_feeds.rb @@ -1,4 +1,4 @@ -class AddStatusToFeeds < ActiveRecord::Migration[4.2] +class AddStatusToFeeds < ActiveRecord::Migration[5.1] def change add_column :feeds, :status, :int end diff --git a/db/migrate/20130504005816_text_url.rb b/db/migrate/20130504005816_text_url.rb index 5f3c1ea7a..8de4fd5f3 100644 --- a/db/migrate/20130504005816_text_url.rb +++ b/db/migrate/20130504005816_text_url.rb @@ -1,4 +1,4 @@ -class TextUrl < ActiveRecord::Migration[4.2] +class TextUrl < ActiveRecord::Migration[5.1] def up change_column :feeds, :url, :text end diff --git a/db/migrate/20130504022615_change_story_permalink_column.rb b/db/migrate/20130504022615_change_story_permalink_column.rb index db8d8b924..59d21fb55 100644 --- a/db/migrate/20130504022615_change_story_permalink_column.rb +++ b/db/migrate/20130504022615_change_story_permalink_column.rb @@ -1,4 +1,4 @@ -class ChangeStoryPermalinkColumn < ActiveRecord::Migration[4.2] +class ChangeStoryPermalinkColumn < ActiveRecord::Migration[5.1] def up change_column :stories, :permalink, :text end diff --git a/db/migrate/20130509131045_add_unique_constraints.rb b/db/migrate/20130509131045_add_unique_constraints.rb index f04989164..db0e3a7de 100644 --- a/db/migrate/20130509131045_add_unique_constraints.rb +++ b/db/migrate/20130509131045_add_unique_constraints.rb @@ -1,4 +1,4 @@ -class AddUniqueConstraints < ActiveRecord::Migration[4.2] +class AddUniqueConstraints < ActiveRecord::Migration[5.1] def change add_index :stories, [:permalink, :feed_id], unique: true add_index :feeds, :url, unique: true diff --git a/db/migrate/20130513025939_add_keep_unread_to_stories.rb b/db/migrate/20130513025939_add_keep_unread_to_stories.rb index 76742a0de..4244512ff 100644 --- a/db/migrate/20130513025939_add_keep_unread_to_stories.rb +++ b/db/migrate/20130513025939_add_keep_unread_to_stories.rb @@ -1,4 +1,4 @@ -class AddKeepUnreadToStories < ActiveRecord::Migration[4.2] +class AddKeepUnreadToStories < ActiveRecord::Migration[5.1] def change add_column :stories, :keep_unread, :boolean, default: false end diff --git a/db/migrate/20130513044029_add_is_starred_status_for_stories.rb b/db/migrate/20130513044029_add_is_starred_status_for_stories.rb index 8333225a0..777d44602 100644 --- a/db/migrate/20130513044029_add_is_starred_status_for_stories.rb +++ b/db/migrate/20130513044029_add_is_starred_status_for_stories.rb @@ -1,4 +1,4 @@ -class AddIsStarredStatusForStories < ActiveRecord::Migration[4.2] +class AddIsStarredStatusForStories < ActiveRecord::Migration[5.1] def change add_column :stories, :is_starred, :boolean, default: false end diff --git a/db/migrate/20130522014405_add_api_key_to_user.rb b/db/migrate/20130522014405_add_api_key_to_user.rb index b52aae725..a40f73a5d 100644 --- a/db/migrate/20130522014405_add_api_key_to_user.rb +++ b/db/migrate/20130522014405_add_api_key_to_user.rb @@ -1,4 +1,4 @@ -class AddApiKeyToUser < ActiveRecord::Migration[4.2] +class AddApiKeyToUser < ActiveRecord::Migration[5.1] def change add_column :users, :api_key, :string end diff --git a/db/migrate/20130730120312_add_entry_id_to_stories.rb b/db/migrate/20130730120312_add_entry_id_to_stories.rb index f160a6d9d..af5e77ca5 100644 --- a/db/migrate/20130730120312_add_entry_id_to_stories.rb +++ b/db/migrate/20130730120312_add_entry_id_to_stories.rb @@ -1,4 +1,4 @@ -class AddEntryIdToStories < ActiveRecord::Migration[4.2] +class AddEntryIdToStories < ActiveRecord::Migration[5.1] def change add_column :stories, :entry_id, :string end diff --git a/db/migrate/20130805113712_update_stories_unique_constraints.rb b/db/migrate/20130805113712_update_stories_unique_constraints.rb index 53ec20b71..d20dce176 100644 --- a/db/migrate/20130805113712_update_stories_unique_constraints.rb +++ b/db/migrate/20130805113712_update_stories_unique_constraints.rb @@ -1,4 +1,4 @@ -class UpdateStoriesUniqueConstraints < ActiveRecord::Migration[4.2] +class UpdateStoriesUniqueConstraints < ActiveRecord::Migration[5.1] def up remove_index :stories, [:permalink, :feed_id] add_index :stories, [:entry_id, :feed_id], unique: true, length: { permalink: 767 } diff --git a/db/migrate/20130821020313_update_nil_entry_ids.rb b/db/migrate/20130821020313_update_nil_entry_ids.rb index 2b5624910..b8441f4e1 100644 --- a/db/migrate/20130821020313_update_nil_entry_ids.rb +++ b/db/migrate/20130821020313_update_nil_entry_ids.rb @@ -1,4 +1,4 @@ -class UpdateNilEntryIds < ActiveRecord::Migration[4.2] +class UpdateNilEntryIds < ActiveRecord::Migration[5.1] def up Story.where(entry_id: nil).each do |story| story.entry_id = story.permalink || story.id diff --git a/db/migrate/20130905204142_use_text_datatype_for_title_and_entry_id.rb b/db/migrate/20130905204142_use_text_datatype_for_title_and_entry_id.rb index 0d847e58c..bb2e217de 100644 --- a/db/migrate/20130905204142_use_text_datatype_for_title_and_entry_id.rb +++ b/db/migrate/20130905204142_use_text_datatype_for_title_and_entry_id.rb @@ -1,4 +1,4 @@ -class UseTextDatatypeForTitleAndEntryId < ActiveRecord::Migration[4.2] +class UseTextDatatypeForTitleAndEntryId < ActiveRecord::Migration[5.1] def up change_column :stories, :title, :text change_column :stories, :entry_id, :text diff --git a/db/migrate/20140413100725_add_groups_table_and_foreign_keys_to_feeds.rb b/db/migrate/20140413100725_add_groups_table_and_foreign_keys_to_feeds.rb index 149e3f77b..f23d2c007 100644 --- a/db/migrate/20140413100725_add_groups_table_and_foreign_keys_to_feeds.rb +++ b/db/migrate/20140413100725_add_groups_table_and_foreign_keys_to_feeds.rb @@ -1,4 +1,4 @@ -class AddGroupsTableAndForeignKeysToFeeds < ActiveRecord::Migration[4.2] +class AddGroupsTableAndForeignKeysToFeeds < ActiveRecord::Migration[5.1] def up create_table :groups do |t| t.string :name, null: false diff --git a/db/migrate/20140421224454_fix_invalid_unicode.rb b/db/migrate/20140421224454_fix_invalid_unicode.rb index 902e311d2..01ee2822b 100644 --- a/db/migrate/20140421224454_fix_invalid_unicode.rb +++ b/db/migrate/20140421224454_fix_invalid_unicode.rb @@ -1,4 +1,4 @@ -class FixInvalidUnicode < ActiveRecord::Migration[4.2] +class FixInvalidUnicode < ActiveRecord::Migration[5.1] def up Story.find_each do |story| valid_body = story.body.delete("\u2028").delete("\u2029") diff --git a/db/migrate/20141102103617_fix_invalid_titles_with_unicode_line_endings.rb b/db/migrate/20141102103617_fix_invalid_titles_with_unicode_line_endings.rb index 5d0fa90d4..ed912fce8 100644 --- a/db/migrate/20141102103617_fix_invalid_titles_with_unicode_line_endings.rb +++ b/db/migrate/20141102103617_fix_invalid_titles_with_unicode_line_endings.rb @@ -1,4 +1,4 @@ -class FixInvalidTitlesWithUnicodeLineEndings < ActiveRecord::Migration[4.2] +class FixInvalidTitlesWithUnicodeLineEndings < ActiveRecord::Migration[5.1] def up Story.find_each do |story| unless story.title.nil? diff --git a/db/migrate/20221126005221_drop_delayed_job.rb b/db/migrate/20221126005221_drop_delayed_job.rb new file mode 100644 index 000000000..41bdeaf8c --- /dev/null +++ b/db/migrate/20221126005221_drop_delayed_job.rb @@ -0,0 +1,5 @@ +class DropDelayedJob < ActiveRecord::Migration[7.0] + def change + drop_table :delayed_jobs + end +end diff --git a/db/schema.rb b/db/schema.rb index f4d5a1075..f4dbf8e05 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,32 +10,13 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2014_11_02_103617) do - - # These are extensions that must be enabled in order to support this database - enable_extension "plpgsql" - - create_table "delayed_jobs", force: :cascade do |t| - t.integer "priority", default: 0 - t.integer "attempts", default: 0 - t.text "handler" - t.text "last_error" - t.datetime "run_at" - t.datetime "locked_at" - t.datetime "failed_at" - t.string "locked_by" - t.string "queue" - t.datetime "created_at" - t.datetime "updated_at" - t.index ["priority", "run_at"], name: "delayed_jobs_priority" - end - +ActiveRecord::Schema[7.0].define(version: 2022_11_26_005221) do create_table "feeds", force: :cascade do |t| t.string "name" t.text "url" - t.datetime "last_fetched" - t.datetime "created_at" - t.datetime "updated_at" + t.datetime "last_fetched", precision: nil + t.datetime "created_at", precision: nil, null: false + t.datetime "updated_at", precision: nil, null: false t.integer "status" t.integer "group_id" t.index ["url"], name: "index_feeds_on_url", unique: true @@ -43,8 +24,8 @@ create_table "groups", force: :cascade do |t| t.string "name", null: false - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", precision: nil, null: false + t.datetime "updated_at", precision: nil, null: false end create_table "stories", force: :cascade do |t| @@ -52,20 +33,21 @@ t.text "permalink" t.text "body" t.integer "feed_id" - t.datetime "created_at" - t.datetime "updated_at" - t.datetime "published" + t.datetime "created_at", precision: nil, null: false + t.datetime "updated_at", precision: nil, null: false + t.datetime "published", precision: nil t.boolean "is_read" t.boolean "keep_unread", default: false t.boolean "is_starred", default: false t.text "entry_id" t.index ["entry_id", "feed_id"], name: "index_stories_on_entry_id_and_feed_id", unique: true + t.index ["feed_id"], name: "index_stories_on_feed_id" end create_table "users", force: :cascade do |t| t.string "password_digest" - t.datetime "created_at" - t.datetime "updated_at" + t.datetime "created_at", precision: nil, null: false + t.datetime "updated_at", precision: nil, null: false t.boolean "setup_complete" t.string "api_key" end diff --git a/docker-compose.yml b/docker-compose.yml index 8293e4752..cbdaa50d7 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,24 +1,39 @@ version: '2' services: postgres: - image: postgres:9.5-alpine + image: postgres:15-alpine restart: always volumes: - - ~/stringer:/var/lib/postgresql/data + - stringer:/var/lib/postgresql/data environment: - - POSTGRES_PASSWORD=super_secret_password + - POSTGRES_PASSWORD=${POSTGRES_PASSWORD} - POSTGRES_USER=db_user - POSTGRES_DB=stringer web: - image: mockdeep/stringer + image: alksol/stringer build: . depends_on: - postgres restart: always - ports: - - 80:8080 + labels: + - "traefik.http.routers.stringer.rule=Host(`news.alkaline.solutions`)" + - "traefik.http.routers.stringer.tls.certResolver=default" + - "traefik.http.routers.stringer2.rule=Host(`news.alkaline.solutions`)" + - "traefik.http.routers.stringer2.middlewares=require-ssl@file" + - "traefik.enable=true" + expose: + - 8080 environment: - - SECRET_TOKEN=YOUR_SECRET_TOKEN - - PORT=8080 - - DATABASE_URL=postgres://db_user:super_secret_password@postgres:5432/stringer + - SECRET_TOKEN=${SECRET_TOKEN} + - DATABASE_URL=postgres://db_user:${POSTGRES_PASSWORD}@postgres:5432/stringer + - RACK_ENV=${RACK_ENV} + - WORKER_RETRY=${WORKER_RETRY} + - WEB_CONCURRENCY=${WEB_CONCURRENCY} + - MAX_THREADS=${MAX_THREADS} +volumes: + stringer: +networks: + default: + external: + name: stringer diff --git a/spec/controllers/debug_controller_spec.rb b/spec/controllers/debug_controller_spec.rb index 3c435ef9a..82630af67 100644 --- a/spec/controllers/debug_controller_spec.rb +++ b/spec/controllers/debug_controller_spec.rb @@ -6,9 +6,6 @@ describe "DebugController" do describe "GET /debug" do before do - delayed_job = double "Delayed::Job" - allow(delayed_job).to receive(:count).and_return(42) - stub_const("Delayed::Job", delayed_job) migration_status_instance = double "migration_status_instance" allow(migration_status_instance).to receive(:pending_migrations).and_return ["Migration B - 2", "Migration C - 3"] @@ -31,20 +28,6 @@ expect(page).to have_tag("dd", text: /test/) end - it "displays the delayed job count" do - get "/debug" - - page = last_response.body - expect(page).to have_tag("dd", text: /42/) - end - - it "displays pending migrations" do - get "/debug" - - page = last_response.body - expect(page).to have_tag("li", text: /Migration B - 2/) - expect(page).to have_tag("li", text: /Migration C - 3/) - end end describe "#heroku" do diff --git a/spec/controllers/feeds_controller_spec.rb b/spec/controllers/feeds_controller_spec.rb index 90c8c3d3d..8f9de9549 100644 --- a/spec/controllers/feeds_controller_spec.rb +++ b/spec/controllers/feeds_controller_spec.rb @@ -85,7 +85,6 @@ it "adds the feed and queues it to be fetched" do expect(AddNewFeed).to receive(:add).with(feed_url).and_return(valid_feed) - expect(FetchFeeds).to receive(:enqueue).with([valid_feed]) post "/feeds", feed_url: feed_url diff --git a/spec/controllers/first_run_controller_spec.rb b/spec/controllers/first_run_controller_spec.rb index 878cc3037..d97f9adca 100644 --- a/spec/controllers/first_run_controller_spec.rb +++ b/spec/controllers/first_run_controller_spec.rb @@ -57,7 +57,6 @@ it "displays the tutorial and completes setup" do expect(CompleteSetup).to receive(:complete).with(user).once - expect(FetchFeeds).to receive(:enqueue).with(feeds).once get "/setup/tutorial" diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 8c57bddd5..cf648c436 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,11 +1,8 @@ -ENV["RACK_ENV"] = "test" - require "capybara" require "capybara/server" require "rspec" require "rspec-html-matchers" require "rack/test" -require "pry" require "faker" require "ostruct" require "date" diff --git a/spec/support/active_record.rb b/spec/support/active_record.rb index 724b89aa0..985fdcba4 100644 --- a/spec/support/active_record.rb +++ b/spec/support/active_record.rb @@ -1,14 +1,15 @@ require "active_record" -db_config = YAML.safe_load(File.read("config/database.yml")) -ActiveRecord::Base.establish_connection(db_config["test"]) +db_config = YAML.safe_load(File.read("config/database.yml"), aliases: true) +rack_env = ENV["RACK_ENV"] || "test" +ActiveRecord::Base.establish_connection(db_config[rack_env]) ActiveRecord::Base.logger = Logger.new("log/test.log") def need_to_migrate? - ActiveRecord::Base.connection.migration_context.needs_migration? + ActiveRecord::MigrationContext.new(["db/migrate"], ActiveRecord::SchemaMigration).needs_migration? end -ActiveRecord::MigrationContext.new("db/migrate").migrate if need_to_migrate? +ActiveRecord::MigrationContext.new(["db/migrate"], ActiveRecord::SchemaMigration).migrate if need_to_migrate? RSpec.configure do |config| config.around do |example| diff --git a/spec/tasks/fetch_feeds_spec.rb b/spec/tasks/fetch_feeds_spec.rb index 9a7ce4ffd..368099d8d 100644 --- a/spec/tasks/fetch_feeds_spec.rb +++ b/spec/tasks/fetch_feeds_spec.rb @@ -20,29 +20,4 @@ FetchFeeds.new(feeds, pool).fetch_all end end - - describe "#prepare_to_delay" do - it "serializes the instance for backgrounding" do - feeds = [create_feed, create_feed] - feeds_ids = feeds.map(&:id) - fetch_feeds = FetchFeeds.new(feeds) - - fetch_feeds.prepare_to_delay - - expect(fetch_feeds.instance_variable_get(:@feeds)).to be_empty - expect(fetch_feeds.instance_variable_get(:@feeds_ids)).to eq(feeds_ids) - end - end - - describe ".enqueue" do - it "enqueues a fetch_all job" do - feeds = [create_feed, create_feed] - feeds_ids = feeds.map(&:id) - - expect { FetchFeeds.enqueue(feeds) }.to change(Delayed::Job, :count).by(1) - - job_object = Delayed::Job.last.payload_object.object - expect(job_object.instance_variable_get(:@feeds_ids)).to eq(feeds_ids) - end - end end diff --git a/spec/utils/i18n_support_spec.rb b/spec/utils/i18n_support_spec.rb index edca8a19f..0f2e18c0d 100644 --- a/spec/utils/i18n_support_spec.rb +++ b/spec/utils/i18n_support_spec.rb @@ -21,7 +21,7 @@ it "should load default locale" do expect(I18n.locale.to_s).to eq "en" - expect(I18n.t("layout.title")).to eq "stringer | your rss buddy" + expect(I18n.t("layout.title")).to eq "AlkaliNews" end end