diff --git a/.ruby-version b/.ruby-version index be94e6f..8cf6caf 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -3.2.2 +3.4.1 \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index cb8b560..c1c92c7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ -FROM ruby:3.2.2-alpine3.18 AS ruby-builder +FROM ruby:3.4.1-alpine3.21 AS ruby-builder -RUN apk --no-cache add build-base cmake git glib-dev postgresql14-dev +RUN apk --no-cache add build-base cmake git glib-dev postgresql17-dev COPY Gemfile Gemfile.lock ./ RUN gem i foreman && BUNDLE_IGNORE_CONFIG=true bundle install -j$(nproc) \ @@ -8,17 +8,16 @@ RUN gem i foreman && BUNDLE_IGNORE_CONFIG=true bundle install -j$(nproc) \ && find /usr/local/bundle/gems/ -name "*.c" -delete \ && find /usr/local/bundle/gems/ -name "*.o" -delete -FROM node:18-alpine3.18 AS node-builder +FROM node:20-alpine3.21 AS node-builder RUN apk --no-cache add git WORKDIR /app COPY package.json yarn.lock ./ -RUN corepack enable && corepack prepare --activate && yarn install -RUN corepack install -g pnpm +RUN corepack enable && corepack prepare --activate && yarn install --frozen-lockfile -FROM ruby:3.2.2-alpine3.18 +FROM ruby:3.4.1-alpine3.21 RUN apk --no-cache add ffmpeg vips gifsicle \ - postgresql14-client \ + postgresql17-client \ git git-lfs jemalloc tzdata \ openssh-keygen diff --git a/Gemfile b/Gemfile index c26ee9b..b9a7267 100644 --- a/Gemfile +++ b/Gemfile @@ -2,9 +2,9 @@ source "https://rubygems.org" -gem "dotenv-rails", require: "dotenv/rails-now" +gem "dotenv-rails", require: "dotenv/load" -ruby "3.2.2" +ruby "3.4.1" # Bundle edge Rails instead: gem "rails", github: "rails/rails", branch: "main" gem "rails", "~> 7.1.1" @@ -27,12 +27,6 @@ gem "stimulus-rails" # Build JSON APIs with ease [https://github.com/rails/jbuilder] gem "jbuilder" -# Use Redis adapter to run Action Cable in production -# gem "redis", ">= 4.0.1" - -# Use Kredis to get higher-level data types in Redis [https://github.com/rails/kredis] -# gem "kredis" - # Use Active Model has_secure_password [https://guides.rubyonrails.org/active_model_basics.html#securepassword] # gem "bcrypt", "~> 3.1.7" @@ -58,20 +52,14 @@ group :development do # gem "rack-mini-profiler" # Speed up commands on slow machines / big apps [https://github.com/rails/spring] - # gem "spring" -end - -group :test do - # Use system testing [https://guides.rubyonrails.org/testing.html#system-testing] - gem "capybara" - gem "selenium-webdriver" + gem "spring" end gem "github_webhook", "~> 1.4" -gem "httparty", "~> 0.21.0" +gem "httparty", "~> 0.22.0" gem "redis", "~> 5.0" gem "rubocop", "~> 1.57" -gem "rubocop-erb", "~> 0.3.0" +gem "rubocop-erb", "~> 0.5.5" gem "rubocop-rails", "~> 2.22" gem "filesize", "~> 0.2.0" @@ -90,7 +78,7 @@ gem "simple_form", "~> 5.3" gem "responders", "~> 3.1" -gem "pagy", "~> 8.3" +gem "pagy", "~> 8.6.3" gem "retriable", "~> 3.1" @@ -103,3 +91,7 @@ gem "opensearch-ruby", "~> 3.3" gem "good_job", "~> 3.99" gem "faraday", "~> 2.9" + +gem "discordrb-webhooks", "~> 3.5" + +gem "next_rails", "~> 1.4" diff --git a/Gemfile.lock b/Gemfile.lock index fcbc04f..3ad189a 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,146 +1,148 @@ GEM remote: https://rubygems.org/ specs: - actioncable (7.1.1) - actionpack (= 7.1.1) - activesupport (= 7.1.1) + actioncable (7.1.5.1) + actionpack (= 7.1.5.1) + activesupport (= 7.1.5.1) nio4r (~> 2.0) websocket-driver (>= 0.6.1) zeitwerk (~> 2.6) - actionmailbox (7.1.1) - actionpack (= 7.1.1) - activejob (= 7.1.1) - activerecord (= 7.1.1) - activestorage (= 7.1.1) - activesupport (= 7.1.1) + actionmailbox (7.1.5.1) + actionpack (= 7.1.5.1) + activejob (= 7.1.5.1) + activerecord (= 7.1.5.1) + activestorage (= 7.1.5.1) + activesupport (= 7.1.5.1) mail (>= 2.7.1) net-imap net-pop net-smtp - actionmailer (7.1.1) - actionpack (= 7.1.1) - actionview (= 7.1.1) - activejob (= 7.1.1) - activesupport (= 7.1.1) + actionmailer (7.1.5.1) + actionpack (= 7.1.5.1) + actionview (= 7.1.5.1) + activejob (= 7.1.5.1) + activesupport (= 7.1.5.1) mail (~> 2.5, >= 2.5.4) net-imap net-pop net-smtp rails-dom-testing (~> 2.2) - actionpack (7.1.1) - actionview (= 7.1.1) - activesupport (= 7.1.1) + actionpack (7.1.5.1) + actionview (= 7.1.5.1) + activesupport (= 7.1.5.1) nokogiri (>= 1.8.5) + racc rack (>= 2.2.4) rack-session (>= 1.0.1) rack-test (>= 0.6.3) rails-dom-testing (~> 2.2) rails-html-sanitizer (~> 1.6) - actiontext (7.1.1) - actionpack (= 7.1.1) - activerecord (= 7.1.1) - activestorage (= 7.1.1) - activesupport (= 7.1.1) + actiontext (7.1.5.1) + actionpack (= 7.1.5.1) + activerecord (= 7.1.5.1) + activestorage (= 7.1.5.1) + activesupport (= 7.1.5.1) globalid (>= 0.6.0) nokogiri (>= 1.8.5) - actionview (7.1.1) - activesupport (= 7.1.1) + actionview (7.1.5.1) + activesupport (= 7.1.5.1) builder (~> 3.1) erubi (~> 1.11) rails-dom-testing (~> 2.2) rails-html-sanitizer (~> 1.6) - activejob (7.1.1) - activesupport (= 7.1.1) + activejob (7.1.5.1) + activesupport (= 7.1.5.1) globalid (>= 0.3.6) - activemodel (7.1.1) - activesupport (= 7.1.1) - activerecord (7.1.1) - activemodel (= 7.1.1) - activesupport (= 7.1.1) + activemodel (7.1.5.1) + activesupport (= 7.1.5.1) + activerecord (7.1.5.1) + activemodel (= 7.1.5.1) + activesupport (= 7.1.5.1) timeout (>= 0.4.0) - activestorage (7.1.1) - actionpack (= 7.1.1) - activejob (= 7.1.1) - activerecord (= 7.1.1) - activesupport (= 7.1.1) + activestorage (7.1.5.1) + actionpack (= 7.1.5.1) + activejob (= 7.1.5.1) + activerecord (= 7.1.5.1) + activesupport (= 7.1.5.1) marcel (~> 1.0) - activesupport (7.1.1) + activesupport (7.1.5.1) base64 + benchmark (>= 0.3) bigdecimal concurrent-ruby (~> 1.0, >= 1.0.2) connection_pool (>= 2.2.5) drb i18n (>= 1.6, < 2) + logger (>= 1.4.2) minitest (>= 5.1) mutex_m + securerandom (>= 0.3) tzinfo (~> 2.0) - addressable (2.8.5) - public_suffix (>= 2.0.2, < 6.0) + addressable (2.8.7) + public_suffix (>= 2.0.2, < 7.0) ast (2.4.2) aws-eventstream (1.3.0) - aws-partitions (1.924.0) - aws-sdk-core (3.194.1) + aws-partitions (1.1035.0) + aws-sdk-core (3.215.0) aws-eventstream (~> 1, >= 1.3.0) - aws-partitions (~> 1, >= 1.651.0) - aws-sigv4 (~> 1.8) + aws-partitions (~> 1, >= 1.992.0) + aws-sigv4 (~> 1.9) jmespath (~> 1, >= 1.6.1) - aws-sdk-kms (1.80.0) - aws-sdk-core (~> 3, >= 3.193.0) - aws-sigv4 (~> 1.1) - aws-sdk-s3 (1.149.0) - aws-sdk-core (~> 3, >= 3.194.0) + aws-sdk-kms (1.96.0) + aws-sdk-core (~> 3, >= 3.210.0) + aws-sigv4 (~> 1.5) + aws-sdk-s3 (1.177.0) + aws-sdk-core (~> 3, >= 3.210.0) aws-sdk-kms (~> 1) - aws-sigv4 (~> 1.8) - aws-sigv4 (1.8.0) + aws-sigv4 (~> 1.5) + aws-sigv4 (1.11.0) aws-eventstream (~> 1, >= 1.0.2) base64 (0.2.0) - better_html (2.0.2) + benchmark (0.4.0) + better_html (2.1.1) actionview (>= 6.0) activesupport (>= 6.0) ast (~> 2.0) erubi (~> 1.4) parser (>= 2.4) smart_properties - bigdecimal (3.1.4) + bigdecimal (3.1.9) bindex (0.8.1) - bootsnap (1.17.0) + bootsnap (1.18.4) msgpack (~> 1.2) - builder (3.2.4) - capybara (3.39.2) - addressable - matrix - mini_mime (>= 0.1.3) - nokogiri (~> 1.8) - rack (>= 1.6.0) - rack-test (>= 0.6.3) - regexp_parser (>= 1.5, < 3.0) - xpath (~> 3.2) - concurrent-ruby (1.2.2) - connection_pool (2.4.1) + builder (3.3.0) + concurrent-ruby (1.3.4) + connection_pool (2.5.0) crass (1.0.6) - date (3.3.4) - debug (1.8.0) - irb (>= 1.5.0) - reline (>= 0.3.1) - dotenv (2.8.1) - dotenv-rails (2.8.1) - dotenv (= 2.8.1) - railties (>= 3.2) - drb (2.2.0) - ruby2_keywords + csv (3.3.2) + date (3.4.1) + debug (1.10.0) + irb (~> 1.10) + reline (>= 0.3.8) + discordrb-webhooks (3.5.0) + rest-client (>= 2.0.0) + domain_name (0.6.20240107) + dotenv (3.1.7) + dotenv-rails (3.1.7) + dotenv (= 3.1.7) + railties (>= 6.1) + drb (2.2.1) ed25519 (1.3.0) - erubi (1.12.0) + erubi (1.13.1) et-orbi (1.2.11) tzinfo - exifr (1.4.0) - faraday (2.9.0) - faraday-net_http (>= 2.0, < 3.2) - faraday-net_http (3.1.0) - net-http - ffi (1.16.3) + exifr (1.4.1) + faraday (2.12.2) + faraday-net_http (>= 2.0, < 3.5) + json + logger + faraday-net_http (3.4.0) + net-http (>= 0.5.0) + ffi (1.17.1-x86_64-linux-gnu) + ffi (1.17.1-x86_64-linux-musl) filesize (0.2.0) fspath (3.1.2) - fugit (1.11.0) + fugit (1.11.1) et-orbi (~> 1, >= 1.2.11) raabro (~> 1.4) github_webhook (1.4.2) @@ -156,32 +158,37 @@ GEM fugit (>= 1.1) railties (>= 6.0.0) thor (>= 0.14.1) - httparty (0.21.0) + http-accept (1.7.0) + http-cookie (1.0.8) + domain_name (~> 0.5) + httparty (0.22.0) + csv mini_mime (>= 1.0.0) multi_xml (>= 0.5.2) - i18n (1.14.1) + i18n (1.14.6) concurrent-ruby (~> 1.0) - image_optim (0.31.3) + image_optim (0.31.4) exifr (~> 1.2, >= 1.2.2) fspath (~> 3.0) image_size (>= 1.5, < 4) in_threads (~> 1.3) progress (~> 3.0, >= 3.0.1) - image_size (3.3.0) + image_size (3.4.0) in_threads (1.6.0) - io-console (0.6.0) - irb (1.8.3) - rdoc - reline (>= 0.3.8) - jbuilder (2.11.5) + io-console (0.8.0) + irb (1.14.3) + rdoc (>= 4.0.0) + reline (>= 0.4.2) + jbuilder (2.13.0) actionview (>= 5.0.0) activesupport (>= 5.0.0) jmespath (1.6.2) - jsbundling-rails (1.2.1) + jsbundling-rails (1.3.1) railties (>= 6.0.0) - json (2.6.3) + json (2.9.1) language_server-protocol (3.17.0.3) - loofah (2.21.4) + logger (1.6.5) + loofah (2.24.0) crass (~> 1.0.2) nokogiri (>= 1.12.0) mail (2.8.1) @@ -189,184 +196,195 @@ GEM net-imap net-pop net-smtp - marcel (1.0.2) - matrix (0.4.2) + marcel (1.0.4) + mime-types (3.6.0) + logger + mime-types-data (~> 3.2015) + mime-types-data (3.2025.0107) mini_mime (1.1.5) - minitest (5.20.0) - msgpack (1.7.2) + minitest (5.25.4) + msgpack (1.7.5) multi_json (1.15.0) - multi_xml (0.6.0) - mutex_m (0.2.0) - net-http (0.4.1) + multi_xml (0.7.1) + bigdecimal (~> 3.1) + mutex_m (0.3.0) + net-http (0.6.0) uri - net-imap (0.4.4) + net-imap (0.5.5) date net-protocol net-pop (0.1.2) net-protocol net-protocol (0.2.2) timeout - net-smtp (0.4.0) - net-protocol - nio4r (2.5.9) - nokogiri (1.15.4-x86_64-linux) + net-smtp (0.5.0) + netrc (0.11.0) + next_rails (1.4.2) + rainbow (>= 3) + nio4r (2.7.4) + nokogiri (1.18.1-x86_64-linux-gnu) racc (~> 1.4) - opensearch-ruby (3.3.0) + nokogiri (1.18.1-x86_64-linux-musl) + racc (~> 1.4) + opensearch-ruby (3.4.0) faraday (>= 1.0, < 3) multi_json (>= 1.0) - pagy (8.3.0) - parallel (1.23.0) - parser (3.2.2.4) + pagy (8.6.3) + parallel (1.26.3) + parser (3.3.6.0) ast (~> 2.4.1) racc - pg (1.5.4) + pg (1.5.9) progress (3.6.0) - psych (5.1.1.1) + psych (5.2.2) + date stringio - public_suffix (5.0.3) - puma (6.4.0) + public_suffix (6.0.1) + puma (6.5.0) nio4r (~> 2.0) raabro (1.4.0) - racc (1.7.3) - rack (3.0.8) - rack-session (2.0.0) + racc (1.8.1) + rack (3.1.8) + rack-session (2.1.0) + base64 (>= 0.1.0) rack (>= 3.0.0) - rack-test (2.1.0) + rack-test (2.2.0) rack (>= 1.3) - rackup (2.1.0) + rackup (2.2.1) rack (>= 3) - webrick (~> 1.8) - rails (7.1.1) - actioncable (= 7.1.1) - actionmailbox (= 7.1.1) - actionmailer (= 7.1.1) - actionpack (= 7.1.1) - actiontext (= 7.1.1) - actionview (= 7.1.1) - activejob (= 7.1.1) - activemodel (= 7.1.1) - activerecord (= 7.1.1) - activestorage (= 7.1.1) - activesupport (= 7.1.1) + rails (7.1.5.1) + actioncable (= 7.1.5.1) + actionmailbox (= 7.1.5.1) + actionmailer (= 7.1.5.1) + actionpack (= 7.1.5.1) + actiontext (= 7.1.5.1) + actionview (= 7.1.5.1) + activejob (= 7.1.5.1) + activemodel (= 7.1.5.1) + activerecord (= 7.1.5.1) + activestorage (= 7.1.5.1) + activesupport (= 7.1.5.1) bundler (>= 1.15.0) - railties (= 7.1.1) + railties (= 7.1.5.1) rails-dom-testing (2.2.0) activesupport (>= 5.0.0) minitest nokogiri (>= 1.6) - rails-html-sanitizer (1.6.0) + rails-html-sanitizer (1.6.2) loofah (~> 2.21) - nokogiri (~> 1.14) - railties (7.1.1) - actionpack (= 7.1.1) - activesupport (= 7.1.1) + nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0) + railties (7.1.5.1) + actionpack (= 7.1.5.1) + activesupport (= 7.1.5.1) irb rackup (>= 1.0.0) rake (>= 12.2) thor (~> 1.0, >= 1.2.2) zeitwerk (~> 2.6) rainbow (3.1.1) - rake (13.1.0) - rdoc (6.6.0) + rake (13.2.1) + rdoc (6.10.0) psych (>= 4.0.0) - redis (5.0.8) - redis-client (>= 0.17.0) - redis-client (0.18.0) + redis (5.3.0) + redis-client (>= 0.22.0) + redis-client (0.23.1) connection_pool - regexp_parser (2.8.2) - reline (0.4.0) + regexp_parser (2.10.0) + reline (0.6.0) io-console (~> 0.5) - request_store (1.5.1) + request_store (1.7.0) rack (>= 1.4) responders (3.1.1) actionpack (>= 5.2) railties (>= 5.2) + rest-client (2.1.0) + http-accept (>= 1.7.0, < 2.0) + http-cookie (>= 1.0.2, < 2.0) + mime-types (>= 1.16, < 4.0) + netrc (~> 0.8) retriable (3.1.2) - rexml (3.2.6) - rubocop (1.57.2) + rubocop (1.70.0) json (~> 2.3) language_server-protocol (>= 3.17.0) parallel (~> 1.10) - parser (>= 3.2.2.4) + parser (>= 3.3.0.2) rainbow (>= 2.2.2, < 4.0) - regexp_parser (>= 1.8, < 3.0) - rexml (>= 3.2.5, < 4.0) - rubocop-ast (>= 1.28.1, < 2.0) + regexp_parser (>= 2.9.3, < 3.0) + rubocop-ast (>= 1.36.2, < 2.0) ruby-progressbar (~> 1.7) - unicode-display_width (>= 2.4.0, < 3.0) - rubocop-ast (1.30.0) - parser (>= 3.2.1.0) - rubocop-erb (0.3.0) + unicode-display_width (>= 2.4.0, < 4.0) + rubocop-ast (1.37.0) + parser (>= 3.3.1.0) + rubocop-erb (0.5.5) better_html rubocop (~> 1.45) - rubocop-rails (2.22.1) + rubocop-rails (2.28.0) activesupport (>= 4.2.0) rack (>= 1.1) - rubocop (>= 1.33.0, < 2.0) + rubocop (>= 1.52.0, < 2.0) + rubocop-ast (>= 1.31.1, < 2.0) ruby-progressbar (1.13.0) - ruby-vips (2.2.0) + ruby-vips (2.2.2) ffi (~> 1.12) - ruby2_keywords (0.0.5) - rubyzip (2.3.2) - selenium-webdriver (4.15.0) - rexml (~> 3.2, >= 3.2.5) - rubyzip (>= 1.2.2, < 3.0) - websocket (~> 1.0) - simple_form (5.3.0) + logger + securerandom (0.4.1) + simple_form (5.3.1) actionpack (>= 5.2) activemodel (>= 5.2) smart_properties (1.17.0) + spring (4.2.1) sprockets (4.2.1) concurrent-ruby (~> 1.0) rack (>= 2.2.4, < 4) - sprockets-rails (3.4.2) - actionpack (>= 5.2) - activesupport (>= 5.2) + sprockets-rails (3.5.2) + actionpack (>= 6.1) + activesupport (>= 6.1) sprockets (>= 3.0.0) - stimulus-rails (1.3.0) + stimulus-rails (1.3.4) railties (>= 6.0.0) - stringio (3.0.8) - thor (1.3.0) - timeout (0.4.1) + stringio (3.1.2) + thor (1.3.2) + timeout (0.4.3) tzinfo (2.0.6) concurrent-ruby (~> 1.0) - unicode-display_width (2.5.0) - uri (0.13.0) + unicode-display_width (3.1.3) + unicode-emoji (~> 4.0, >= 4.0.4) + unicode-emoji (4.0.4) + uri (1.0.2) web-console (4.2.1) actionview (>= 6.0.0) activemodel (>= 6.0.0) bindex (>= 0.4.0) railties (>= 6.0.0) - webrick (1.8.1) - websocket (1.2.10) - websocket-driver (0.7.6) + websocket-driver (0.7.7) + base64 websocket-extensions (>= 0.1.0) websocket-extensions (0.1.5) - xpath (3.2.0) - nokogiri (~> 1.8) - zeitwerk (2.6.12) + zeitwerk (2.7.1) PLATFORMS x86_64-linux + x86_64-linux-musl DEPENDENCIES addressable (~> 2.8) aws-sdk-s3 (~> 1.149) bootsnap - capybara debug + discordrb-webhooks (~> 3.5) dotenv-rails ed25519 (~> 1.3) faraday (~> 2.9) filesize (~> 0.2.0) github_webhook (~> 1.4) good_job (~> 3.99) - httparty (~> 0.21.0) + httparty (~> 0.22.0) image_optim (~> 0.31.3) jbuilder jsbundling-rails + next_rails (~> 1.4) opensearch-ruby (~> 3.3) - pagy (~> 8.3) + pagy (~> 8.6.3) pg (~> 1.1) puma (>= 5.0) rails (~> 7.1.1) @@ -375,11 +393,11 @@ DEPENDENCIES responders (~> 3.1) retriable (~> 3.1) rubocop (~> 1.57) - rubocop-erb (~> 0.3.0) + rubocop-erb (~> 0.5.5) rubocop-rails (~> 2.22) ruby-vips (~> 2.2) - selenium-webdriver simple_form (~> 5.3) + spring sprockets-rails stimulus-rails timeout (~> 0.4.1) @@ -387,7 +405,7 @@ DEPENDENCIES web-console RUBY VERSION - ruby 3.2.2p53 + ruby 3.4.1p0 BUNDLED WITH - 2.5.10 + 2.6.2 diff --git a/app/controllers/yiff_rest/api_v2_controller.rb b/app/controllers/yiff_rest/api_v2_controller.rb index 4f7211f..eb1d733 100644 --- a/app/controllers/yiff_rest/api_v2_controller.rb +++ b/app/controllers/yiff_rest/api_v2_controller.rb @@ -188,10 +188,11 @@ module YiffRest elsif !category_info.find { |c| c.db == category }.sfw color = 0xDC143C end - Websites.config.yiffyapi_usage_webhook.execute(embeds: [ - { - title: "V2 API Request", - description: <<~DESC.strip, + + Websites.config.yiffyapi_usage_webhook.execute do |builder| + builder.add_embed do |embed| + embed.title = "V2 API Request" + embed.description = <<~DESC Host: **#{request.host}** Path: **#{request.path}** Category: `#{category}` @@ -201,10 +202,10 @@ module YiffRest IP: **#{request.remote_ip}** #{bulk} DESC - color: color, - timestamp: Time.now.iso8601, - }, - ]) + embed.color = color + embed.timestamp = Time.now + end + end end def allowed_readonly_actions diff --git a/app/jobs/e621_thumbnail_job.rb b/app/jobs/e621_thumbnail_job.rb index 0f008ef..427fdb3 100644 --- a/app/jobs/e621_thumbnail_job.rb +++ b/app/jobs/e621_thumbnail_job.rb @@ -47,28 +47,18 @@ class E621ThumbnailJob < ApplicationJob entry.update(expires_at: 5.minutes.from_now) E621ThumbnailErrorCleanupJob.set(wait: 5.minutes).perform_later(entry) end - thumb = {} - if entry.complete? - thumb = { - thumbnail: { - url: entry.url, - }, - } + Websites.config.e621_thumbnails_webhook.execute do |builder| + builder.add_embed do |embed| + embed.title = title + embed.description = <<~DESC + Key: ##{entry.api_key_id} (`#{entry.api_key.application_name}`) + Post: [##{entry.post_id}](https://e621.net/posts/#{entry.post_id}) + #{body} + DESC + embed.color = error ? 0xDC143C : 0x008000 + embed.timestamp = Time.now + embed.thumbnail = Discordrb::Webhooks::EmbedThumbnail.new(url: entry.url) if entry.complete? + end end - Websites.config.e621_thumbnails_webhook.execute({ - embeds: [ - { - title: title, - description: <<~DESC, - Key: ##{entry.api_key_id} (`#{entry.api_key.application_name}`) - Post: [##{entry.post_id}](https://e621.net/posts/#{entry.post_id}) - #{body} - DESC - color: error ? 0xDC143C : 0x008000, - timestamp: Time.now.iso8601, - **thumb, - }, - ], - }) end end diff --git a/app/logical/discord_constants.rb b/app/logical/discord_constants.rb index 3767552..bcfb732 100644 --- a/app/logical/discord_constants.rb +++ b/app/logical/discord_constants.rb @@ -10,7 +10,7 @@ module DiscordConstants end def include?(flag) - (@flag & flag) == @flag + @flag.allbits?(flag) end STAFF = new(1 << 0, "Discord Employee") diff --git a/app/logical/discord_interactions/interaction_options.rb b/app/logical/discord_interactions/interaction_options.rb index 977e234..ca8e737 100644 --- a/app/logical/discord_interactions/interaction_options.rb +++ b/app/logical/discord_interactions/interaction_options.rb @@ -126,7 +126,7 @@ module DiscordInteractions end def get_subcommand(required: false) - opt = options.find { |o| o["type"] == DiscordConstants::ApplicationCommandOptionTypes::SUB_COMMAND || o["type"] == DiscordConstants::ApplicationCommandOptionTypes::SUB_COMMAND_GROUP } + opt = options.find { |o| [DiscordConstants::ApplicationCommandOptionTypes::SUB_COMMAND, DiscordConstants::ApplicationCommandOptionTypes::SUB_COMMAND_GROUP].include?(o["type"]) } if opt.try(:[], "options") if opt["options"].length == 1 && opt["type"] == DiscordConstants::ApplicationCommandOptionTypes::SUB_COMMAND_GROUP sub = opt["options"].find { |o| o["type"] == DiscordConstants::ApplicationCommandOptionTypes::SUB_COMMAND } diff --git a/app/logical/rate_limiter.rb b/app/logical/rate_limiter.rb index 7422dc4..071f5a0 100644 --- a/app/logical/rate_limiter.rb +++ b/app/logical/rate_limiter.rb @@ -95,30 +95,29 @@ class RateLimiter route_ra = expires_route.nil? ? short_window / 1000 : expires_route / 1000 headers["Retry-After"] = route_ra.to_s - Websites.config.yiffyapi_ratelimit_webhook.execute({ - embeds: [ - { - title: "Rate Limit Exceeded", - description: <<~DESC.strip, - **Host:** **#{domain}** - Path: **#{path}** - Auth: **#{apikey.is_anon? ? 'no' : "Yes (`#{apikey.key}`)"}** - User Agent: `#{request.user_agent}` - IP: `#{request.remote_ip}` - Global: <:redTick:865401803256627221> - Info: - \u25fd Limit: **#{short_limit}** - \u25fd Remaining: **#{remaining}** - \u25fd Reset: **#{reset}** - \u25fd Reset After: **#{reset_after}** - \u25fd Bucket: **#{bucket}** - \u25fd Decoded Bucket: **#{Base64.urlsafe_decode64(bucket)}** - DESC - color: 0xDC143C, - timestamp: Time.now.iso8601, - }, - ], - }) + Websites.config.yiffyapi_ratelimit_webhook.execute do |builder| + builder.add_embed do |embed| + embed.title = "Rate Limit Exceeded" + embed.description = <<~DESC + **Host:** **#{domain}** + Path: **#{path}** + Auth: **#{apikey.is_anon? ? 'no' : "Yes (`#{apikey.key}`)"}** + User Agent: `#{request.user_agent}` + IP: `#{request.remote_ip}` + Global: <:redTick:865401803256627221> + Info: + \u25fd Limit: **#{short_limit}** + \u25fd Remaining: **#{remaining}** + \u25fd Reset: **#{reset}** + \u25fd Reset After: **#{reset_after}** + \u25fd Bucket: **#{bucket}** + \u25fd Decoded Bucket: **#{Base64.urlsafe_decode64(bucket)}** + DESC + embed.color = 0xDC143C + embed.timestamp = Time.now + end + end + return [:RATELIMIT_SHORT, { limit: short_limit, remaining: remaining, @@ -135,28 +134,27 @@ class RateLimiter global_ra = expires_global.nil? ? long_window / 1000 : expires_global / 1000 headers["Retry-After"] = global_ra.to_s - Websites.config.yiffyapi_ratelimit_webhook.execute({ - embeds: [ - { - title: "Rate Limit Exceeded", - description: <<~DESC.strip, - **Host:** **#{domain}** - Path: **#{path}** - Auth: **#{apikey.is_anon? ? 'no' : "Yes (`#{apikey.key}`)"}** - User Agent: `#{request.user_agent}` - IP: `#{request.remote_ip}` - Global: <:greenTick:865401802920951819> - Info: - \u25fd Limit: **#{long_limit}** - \u25fd Remaining: **#{global_remaining}** - \u25fd Reset: **#{global_reset}** - \u25fd Reset After: **#{global_reset_after}** - DESC - color: 0xDC143C, - timestamp: Time.now.iso8601, - }, - ], - }) + Websites.config.yiffyapi_ratelimit_webhook.execute do |builder| + builder.add_embed do |embed| + embed.title = "Rate Limit Exceeded" + embed.description = <<~DESC + **Host:** **#{domain}** + Path: **#{path}** + Auth: **#{apikey.is_anon? ? 'no' : "Yes (`#{apikey.key}`)"}** + User Agent: `#{request.user_agent}` + IP: `#{request.remote_ip}` + Global: <:greenTick:865401802920951819> + Info: + \u25fd Limit: **#{long_limit}** + \u25fd Remaining: **#{global_remaining}** + \u25fd Reset: **#{global_reset}** + \u25fd Reset After: **#{global_reset_after}** + DESC + embed.color = 0xDC143C + embed.timestamp = Time.now + end + end + return [:RATELIMIT_LONG, { limit: long_limit, remaining: global_remaining, diff --git a/app/logical/requests/discord_webhook.rb b/app/logical/requests/discord_webhook.rb index 3b07589..50c13a5 100644 --- a/app/logical/requests/discord_webhook.rb +++ b/app/logical/requests/discord_webhook.rb @@ -2,9 +2,6 @@ module Requests class DiscordWebhook - include HTTParty - base_uri "https://discord.com/api/webhooks" - attr_reader :id, :token def initialize(id:, token:) @@ -12,34 +9,14 @@ module Requests @token = token end - def execute(body) - r = self.class.post("/#{id}/#{token}?wait=false", { - body: body.to_json, - headers: { - "Content-Type" => "application/json", - }, - }) - Rails.logger.warn("Discord webhook failed: #{r.code} #{r.body}") unless r.success? - r + def client + Discordrb::Webhooks::Client.new(id: id, token: token) end - def edit(message_id, body) - r = self.class.patch("/#{id}/#{token}/#{message_id}?wait=false", { - body: body.to_json, - headers: { - "Content-Type" => "application/json", - }, - }) - Rails.logger.warn("Discord webhook edit failed: #{r.code} #{r.body}") unless r.success? - r - end + delegate :execute, :edit_message, :delete, to: :client - def delete - self.class.delete("/#{id}/#{token}") - end - - def self.is_deleted(response) - response.code == 404 && JSON.parse(response.body)["code"] == 10_015 + def self.is_deleted(error) + error.instance_of?(RestClient::NotFound) && JSON.parse(error.response.body)["code"] == 10_015 end end end diff --git a/app/models/api_image.rb b/app/models/api_image.rb index 7a281be..8c8cca3 100644 --- a/app/models/api_image.rb +++ b/app/models/api_image.rb @@ -318,50 +318,52 @@ class APIImage < ApplicationRecord GREEN_TICK = "<:GreenTick:1235058920762904576>" RED_TICK = "<:RedTick:1235058898549870724>" - def execute(content) + def execute(...) return unless Rails.env.production? - Websites.config.yiffyapi_image_logs_webhook.execute({ - embeds: [content], - }) + Discordrb::Webhooks::Client.new(url: Websites.config.yiffyapi_image_logs_webhook_url).execute(...) end def send_created - execute({ - title: "Image Uploaded", - description: <<~DESC, - ID: `#{md5}` - Category: `#{category}` - Artists: `#{artists.join(', ').presence || '[NONE]'}` - Sources: - #{sources.map { |s| "* #{s}" }.join("\n").presence || '[NONE]'} - Upload Type: #{original_url.present? ? "[url](#{original_url})" : 'file'} - Uploaded By: <@#{creator.id}> (`#{creator.name}`) - Created At: - Updated At: - DESC - color: GREEN, - timestamp: Time.now.iso8601, - }) + execute do |builder| + builder.add_embed do |embed| + embed.title = "Image Uploaded" + embed.description = <<~DESC + ID: `#{md5}` + Category: `#{category}` + Artists: `#{artists.join(', ').presence || '[NONE]'}` + Sources: + #{sources.map { |s| "* #{s}" }.join("\n").presence || '[NONE]'} + Upload Type: #{original_url.present? ? "[url](#{original_url})" : 'file'} + Uploaded By: <@#{creator.id}> (`#{creator.name}`) + Created At: + Updated At: + DESC + embed.color = GREEN + embed.timestamp = Time.now + end + end end def send_deleted - execute({ - title: "Image Deleted", - description: <<~DESC, - ID: `#{md5}` - Category: `#{category}` - Artists: `#{artists.join(', ').presence || '[NONE]'}}` - Sources: - #{sources.map { |s| "* #{s}" }.join("\n").presence || '[NONE]'}} - Upload Type: #{original_url.present? ? "[url](#{original_url})" : 'file'} - Uploaded By: <@#{creator.id}> (`#{creator.name}`) - Reason: #{deletion_reason || 'None Provided'} - Created At: - Updated At: - DESC - color: RED, - timestamp: Time.now.iso8601, - }) + execute do |builder| + builder.add_embed do |embed| + embed.title = "Image Deleted" + embed.description = <<~DESC + ID: `#{md5}` + Category: `#{category}` + Artists: `#{artists.join(', ').presence || '[NONE]'}}` + Sources: + #{sources.map { |s| "* #{s}" }.join("\n").presence || '[NONE]'}} + Upload Type: #{original_url.present? ? "[url](#{original_url})" : 'file'} + Uploaded By: <@#{creator.id}> (`#{creator.name}`) + Reason: #{deletion_reason || 'None Provided'} + Created At: + Updated At: + DESC + embed.color = RED + embed.timestamp = Time.now + end + end end def check_change(attr, changes) @@ -390,28 +392,32 @@ class APIImage < ApplicationRecord return if changes.empty? changes << "Blame: #{blame}" - execute({ - title: "Image Updated", - description: <<~DESC, - ID: `#{md5}` - #{changes.join("\n")} - DESC - color: YELLOW, - timestamp: Time.now.iso8601, - }) + execute do |builder| + builder.add_embed do |embed| + embed.title = "Image Updated" + embed.description = <<~DESC + ID: `#{md5}` + #{changes.join("\n")} + DESC + embed.color = YELLOW + embed.timestamp = Time.now + end + end end def send_converted - execute({ - title: "Image Converted", - description: <<~DESC, - ID: `#{md5}` - Blame: #{blame} - Converted to external image: #{external_api_image.url} - DESC - color: YELLOW, - timestamp: Time.now.iso8601, - }) + execute do |builder| + builder.add_embed do |embed| + embed.title = "Image Converted" + embed.description = <<~DESC + ID: `#{md5}` + Blame: #{blame} + Converted to external image: #{external_api_image.url} + DESC + embed.color = YELLOW + embed.timestamp = Time.now + end + end end def blame @@ -472,7 +478,7 @@ class APIImage < ApplicationRecord end def self.all_seen - Cache.fetch("img:all_seen", expires_in: 1.minutes) do + Cache.fetch("img:all_seen", expires_in: 1.minute) do keys = all.map { |img| "yiffy2:stats:image:#{img.md5}" } values = Cache.redis.mget(*keys) results = {} diff --git a/app/models/api_key.rb b/app/models/api_key.rb index a8eb365..5eb3c1c 100644 --- a/app/models/api_key.rb +++ b/app/models/api_key.rb @@ -227,48 +227,50 @@ class APIKey < ApplicationRecord GREEN_TICK = "<:GreenTick:1235058920762904576>" RED_TICK = "<:RedTick:1235058898549870724>" - def execute(content) - Websites.config.yiffyapi_apikey_logs_webhook.execute({ - embeds: [content], - }) + def execute(...) + Websites.config.yiffyapi_apikey_logs_webhook.execute(...) end def send_created - execute({ - title: "API Key ##{id} Created", - description: <<~DESC, - Key: `#{key}` - Application: `#{application_name}` - Usage: `#{usage}` - Active: #{active? ? GREEN_TICK : RED_TICK} - Disabled: #{disabled? ? "#{GREEN_TICK} (Reason: #{disabled_reason || 'None Provided'})" : RED_TICK} - Unlimited: #{unlimited? ? GREEN_TICK : RED_TICK} - Super: #{super? ? GREEN_TICK : RED_TICK} - Services: #{access_string} - Blame: #{blame} - DESC - color: GREEN, - timestamp: Time.now.iso8601, - }) + execute do |builder| + builder.add_embed do |embed| + embed.title = "API Key ##{id} Created" + embed.description = <<~DESC + Key: `#{key}` + Application: `#{application_name}` + Usage: `#{usage}` + Active: #{active? ? GREEN_TICK : RED_TICK} + Disabled: #{disabled? ? "#{GREEN_TICK} (Reason: #{disabled_reason || 'None Provided'})" : RED_TICK} + Unlimited: #{unlimited? ? GREEN_TICK : RED_TICK} + Super: #{super? ? GREEN_TICK : RED_TICK} + Services: #{access_string} + Blame: #{blame} + DESC + embed.color = GREEN + embed.timestamp = Time.now + end + end end def send_deleted - execute({ - title: "API Key ##{id} Deleted", - description: <<~DESC, - Key: `#{key}` - Application: `#{application_name}` - Usage: `#{usage}` - Active: #{active? ? GREEN_TICK : RED_TICK} - Disabled: #{disabled? ? "#{GREEN_TICK} (Reason: #{disabled_reason || 'None Provided'})" : RED_TICK} - Unlimited: #{unlimited? ? GREEN_TICK : RED_TICK} - Super: #{super? ? GREEN_TICK : RED_TICK} - Services: #{access_string} - Blame: #{blame} - DESC - color: RED, - timestamp: Time.now.iso8601, - }) + execute do |builder| + builder.add_embed do |embed| + embed.title = "API Key ##{id} Deleted" + embed.description = <<~DESC + Key: `#{key}` + Application: `#{application_name}` + Usage: `#{usage}` + Active: #{active? ? GREEN_TICK : RED_TICK} + Disabled: #{disabled? ? "#{GREEN_TICK} (Reason: #{disabled_reason || 'None Provided'})" : RED_TICK} + Unlimited: #{unlimited? ? GREEN_TICK : RED_TICK} + Super: #{super? ? GREEN_TICK : RED_TICK} + Services: #{access_string} + Blame: #{blame} + DESC + embed.color = RED + embed.timestamp = Time.now + end + end end def check_change(attr, changes) @@ -301,12 +303,14 @@ class APIKey < ApplicationRecord return if changes.empty? changes << "Blame: #{blame}" - execute({ - title: "API Key ##{id} Updated", - description: changes.join("\n"), - color: YELLOW, - timestamp: Time.now.iso8601, - }) + execute do |builder| + builder.add_embed do |embed| + embed.title = "API Key ##{id} Updated" + embed.description = changes.join("\n") + embed.color = YELLOW + embed.timestamp = Time.now + end + end end def blame diff --git a/app/models/application_record.rb b/app/models/application_record.rb index 01f5335..463f7f0 100644 --- a/app/models/application_record.rb +++ b/app/models/application_record.rb @@ -37,7 +37,7 @@ class ApplicationRecord < ActiveRecord::Base def belongs_to_creator(options = {}) field = options.delete(:field) || :creator class_eval do - belongs_to(field, **options.merge(class_name: "APIUser")) + belongs_to(field, **options, class_name: "APIUser") before_validation(on: :create) do |rec| rec.send("#{field}_id=", CurrentUser.id) if rec.send("#{field}_id").nil? rec.send("#{field}_ip_addr=", CurrentUser.ip_addr) if rec.respond_to?(:"#{field}_ip_addr=") && rec.send("#{field}_ip_addr").nil? diff --git a/app/models/e621_webhook.rb b/app/models/e621_webhook.rb index e263d83..70e12dc 100644 --- a/app/models/e621_webhook.rb +++ b/app/models/e621_webhook.rb @@ -61,16 +61,11 @@ class E621Webhook < ApplicationRecord Requests::DiscordWebhook.new(id: webhook_id, token: webhook_token) end - def embed(**data) - { - thumbnail: { - url: "https://status.e621.ws/icon.png", - }, - url: "https://status.e621.ws", - timestamp: Time.now.iso8601, - color: E621_COLOR, - **data, - } + def add_defaults(embed) + embed.thumbnail = Discordrb::Webhooks::EmbedThumbnail.new(url: "https://status.e621.ws/icon.png") + embed.url = "https://status.e621.ws" + embed.timestamp = Time.now + embed.color = E621_COLOR end def notification_webhook @@ -78,69 +73,88 @@ class E621Webhook < ApplicationRecord end def send_creation_message - execute({ - embeds: [ - embed(title: "E621 Status Check", description: "This webhook has been setup to receive status updates for e621's api#{creator_id.present? ? " by <@#{creator_id}>" : ''}."), - ], - }) + execute do |builder| + builder.add_embed do |embed| + add_defaults(embed) + embed.title = "E621 Status Check" + embed.description = "This webhook has been setup to receive status updates for e621's api#{creator_id.present? ? " by <@#{creator_id}>" : ''}." + end + end - notification_webhook.execute({ - embeds: [ - embed(title: "Status Check Webhook Added", description: "A status check has been added in the channel **#{channel_id}** of the guild **#{guild_id}**#{creator_id.present? ? " by <@#{creator_id}>" : ''}.", color: SUCCESS_COLOR), - ], - }) + notification_webhook.execute do |builder| + builder.add_embed do |embed| + add_defaults(embed) + embed.title = "Status Check Webhook Added" + embed.description = "A status check has been added in the channel **#{channel_id}** of the guild **#{guild_id}**#{creator_id.present? ? " by <@#{creator_id}>" : ''}." + embed.color = SUCCESS_COLOR + end + end end def send_deletion_message - notification_webhook.execute({ - embeds: [ - embed(title: "Status Check Webhook Removed", description: "A status check has been removed in the channel **#{channel_id}** of the guild **#{guild_id}**#{creator_id.present? ? " by <@#{creator_id}>" : ''}.", color: ERROR_COLOR), - ], - }) + notification_webhook.execute do |builder| + builder.add_embed do |embed| + add_defaults(embed) + embed.title = "Status Check Webhook Removed" + embed.description = "A status check has been removed in the channel **#{channel_id}** of the guild **#{guild_id}**#{creator_id.present? ? " by <@#{creator_id}>" : ''}." + embed.color = ERROR_COLOR + end + end end def send_too_many_guild_message - execute({ - embeds: [ - embed(title: "E621 Status Check", description: "You've already enabled #{MAX_PER_GUILD} status checks in this server. Please delete the other webhooks before adding a new check. This webhook will be automatically deleted.", color: ERROR_COLOR), - ], - }) + execute do |builder| + builder.add_embed do |embed| + add_defaults(embed) + embed.title = "E621 Status Check" + embed.description = "You've already enabled #{MAX_PER_GUILD} status checks in this server. Please delete the other webhooks before adding a new check. This webhook will be automatically deleted." + embed.color = ERROR_COLOR + end + end end def send_too_many_channel_message - execute({ - embeds: [ - embed(title: "E621 Status Check", description: "You already have a status check enabled in this channel. Delete the other webhook to use a new webhook. This webhook will be automatically deleted.", color: ERROR_COLOR), - ], - }) + execute do |builder| + builder.add_embed do |embed| + add_defaults(embed) + embed.title = "E621 Status Check" + embed.description = "You've already enabled #{MAX_PER_CHANNEL} status checks in this channel. Please delete the other webhooks before adding a new check. This webhook will be automatically deleted." + embed.color = ERROR_COLOR + end + end end def send_update(status) status_text = E621Status::STATUS_MESSAGES[status.status] - fields = [ - { name: "Status", value: "#{status.status} #{status_text.present? ? "(#{status_text})" : ''}", inline: true }, - { name: "State", value: E621Status::STATES[status.state] || (status.available ? "up" : "down"), inline: true }, - ] - # noinspection RubyMismatchedArgumentType - fields << { name: "Note", value: status.note, inline: false } if status.note.present? - color = status.available ? SUCCESS_COLOR : status.status == 403 ? WARNING_COLOR : ERROR_COLOR - execute({ - embeds: [ - embed(title: "E621 Status Update", description: "E621's api is **#{status.available ? 'available' : 'unavailable'}**.", color: color, - fields: fields, - timestamp: status.created_at.iso8601, - footer: { text: "Since" }), - ], - }) + execute do |builder| + builder.add_embed do |embed| + add_defaults(embed) + embed.title = "E621 Status Update" + embed.description = "E621's api is **#{status.available ? 'available' : 'unavailable'}**." + embed.color = status.available ? SUCCESS_COLOR : status.status == 403 ? WARNING_COLOR : ERROR_COLOR + embed.timestamp = status.created_at + embed.footer = Discordrb::Webhooks::EmbedFooter.new(text: "Since") + embed.add_field(name: "Status", value: "#{status.status} #{status_text.present? ? "(#{status_text})" : ''}", inline: true) + embed.add_field(name: "State", value: E621Status::STATES[status.state] || (status.available ? "up" : "down"), inline: true) + embed.add_field(name: "Note", value: status.note, inline: false) if status.note.present? + end + end end - def execute(body) - r = webhook&.execute(body) - if webhook.nil? || Requests::DiscordWebhook.is_deleted(r) + def execute(...) + if webhook.nil? @skip_delete_webhook = true destroy + return nil + end + + begin + webhook.execute(...) + rescue RestClient::NotFound + @skip_delete_webhook = true + destroy + nil end - r end end diff --git a/app/models/external_api_image.rb b/app/models/external_api_image.rb index fdd544c..e8ef4d9 100644 --- a/app/models/external_api_image.rb +++ b/app/models/external_api_image.rb @@ -16,7 +16,7 @@ class ExternalAPIImage < ApplicationRecord validates :site, inclusion: { in: SITES.map(&:value) } validate :not_deleted, on: :create - enum site: SITES.to_h { |s| [s.value, s.value] } + enum(:site, SITES.to_h { |s| [s.value, s.value] }) module DataMethods def url diff --git a/app/models/short_url.rb b/app/models/short_url.rb index c3813b9..e9318fe 100644 --- a/app/models/short_url.rb +++ b/app/models/short_url.rb @@ -64,40 +64,42 @@ class ShortUrl < ApplicationRecord YELLOW = 0xFFA500 RED = 0xFF0000 - def execute(content) - Websites.config.yiffyapi_apikey_shortener_webhook.execute({ - embeds: [content], - }) + def execute(...) + Websites.config.yiffyapi_apikey_shortener_webhook.execute(...) end def send_created - execute({ - title: "Short URL Created", - description: <<~DESC, - ID: **#{id}** - Code: **#{code}** - URL: #{url} - IP: **#{creator_ip_addr}** - User-Agent: **#{creator_ua}** - Blame: #{blame} - DESC - color: GREEN, - timestamp: Time.now.iso8601, - }) + execute do |builder| + builder.add_embed do |embed| + embed.title = "Short URL Created" + embed.description = <<~DESC + ID: **#{id}** + Code: **#{code}** + URL: #{url} + IP: **#{creator_ip_addr}** + User-Agent: **#{creator_ua}** + Blame: #{blame} + DESC + embed.color = GREEN + embed.timestamp = Time.now + end + end end def send_deleted - execute({ - title: "Short URL Deleted", - description: <<~DESC, - ID: **#{id}** - Code: **#{code}** - URL: #{url} - Blame: #{blame} - DESC - color: RED, - timestamp: Time.now.iso8601, - }) + execute do |builder| + builder.add_embed do |embed| + embed.title = "Short URL Deleted" + embed.description = <<~DESC + ID: **#{id}** + Code: **#{code}** + URL: #{url} + Blame: #{blame} + DESC + embed.color = RED + embed.timestamp = Time.now + end + end end def check_change(attr, changes) @@ -112,12 +114,14 @@ class ShortUrl < ApplicationRecord return if changes.empty? changes << "Blame: #{blame}" - execute({ - title: "Short URL Updated", - description: changes.join("\n"), - color: YELLOW, - timestamp: Time.now.iso8601, - }) + execute do |builder| + builder.add_embed do |embed| + embed.title = "Short URL Updated" + embed.description = changes.join("\n") + embed.color = YELLOW + embed.timestamp = Time.now + end + end end def blame diff --git a/config/application.rb b/config/application.rb index 32ace04..5859102 100644 --- a/config/application.rb +++ b/config/application.rb @@ -32,7 +32,7 @@ module Websites config.time_zone = "Central Time (US & Canada)" # config.eager_load_paths << Rails.root.join("extras") # - config.autoload_paths += Dir[Rails.root.join(Rails.root.join("config/routes/**/"))] + config.autoload_paths += Rails.root.glob(Rails.root.join("config/routes/**/")) config.action_controller.action_on_unpermitted_parameters = :raise config.action_dispatch.default_headers.clear diff --git a/db/migrate/20250112032852_add_external_api_images_generated_md5_column_and_site_index.rb b/db/migrate/20250112032852_add_external_api_images_generated_md5_column_and_site_index.rb index 118392b..95657ab 100644 --- a/db/migrate/20250112032852_add_external_api_images_generated_md5_column_and_site_index.rb +++ b/db/migrate/20250112032852_add_external_api_images_generated_md5_column_and_site_index.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class AddExternalAPIImagesGeneratedMd5ColumnAndSiteIndex < ActiveRecord::Migration[7.1] def change add_index(:external_api_images, :site) diff --git a/docker-compose.yml b/docker-compose.yml index 761e048..f0323a0 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -26,7 +26,7 @@ services: tty: true postgres: - image: postgres:14-alpine + image: postgres:17-alpine volumes: - db_data:/var/lib/postgresql/data restart: unless-stopped