diff --git a/.drone.yml b/.drone.yml index e17d39a..90d510d 100644 --- a/.drone.yml +++ b/.drone.yml @@ -17,7 +17,7 @@ steps: branch: - master - name: rspec - image: gitea.kosmos.org/kosmos/akkounts-ci:0.1.0 + image: gitea.kosmos.org/kosmos/akkounts-ci:0.9.1 environment: RAILS_ENV: test REDIS_URL: redis://redis:6379/0 @@ -28,6 +28,8 @@ steps: - bundle config set cache_path 'vendor/cache' - bundle config set with 'development test' - bundle install --jobs=3 --retry=3 + - bundle exec rails db:create + - bundle exec rails db:migrate - yarn install - rake css:build - bundle exec rspec diff --git a/.ruby-version b/.ruby-version index 37c2961..15a2799 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -2.7.2 +3.3.0 diff --git a/Dockerfile b/Dockerfile index 612eace..a6f8081 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,10 +1,18 @@ # syntax=docker/dockerfile:1 -FROM ruby:2.7.6 +FROM debian:bullseye-slim as base SHELL ["/bin/bash", "-o", "pipefail", "-c"] -RUN apt-get update -qq && apt-get install -y --no-install-recommends curl \ - ldap-utils tini libvips +# TODO Remove when upstream Ruby works properly on Apple silicon +RUN apt update && apt install -y build-essential wget autoconf libpq-dev pkg-config +RUN wget https://github.com/postmodern/ruby-install/releases/download/v0.9.3/ruby-install-0.9.3.tar.gz \ + && tar -xzvf ruby-install-0.9.3.tar.gz \ + && cd ruby-install-0.9.3/ \ + && make install +RUN ruby-install -p https://github.com/ruby/ruby/pull/9371.diff ruby 3.3.0 +ENV PATH="/opt/rubies/ruby-3.3.0/bin:${PATH}" + +RUN apt-get install -y --no-install-recommends curl ldap-utils tini libvips RUN curl -fsSL https://deb.nodesource.com/setup_lts.x | bash - RUN apt-get update && apt-get install -y nodejs diff --git a/Gemfile b/Gemfile index ee14a24..f8e375e 100644 --- a/Gemfile +++ b/Gemfile @@ -2,7 +2,7 @@ source 'https://rubygems.org' git_source(:github) { |repo| "https://github.com/#{repo}.git" } # Bundle edge Rails instead: gem 'rails', github: 'rails/rails' -gem 'rails', '~> 7.0.2' +gem 'rails', '~> 7.1' # Use Puma as the app server gem 'puma', '~> 4.1' # View components @@ -22,7 +22,7 @@ gem 'jbuilder', '~> 2.7' # Use Redis adapter to run Action Cable in production # gem 'redis', '~> 4.0' # Use Active Model has_secure_password -gem 'bcrypt', '~> 3.1.7' +gem 'bcrypt', '~> 3.1' # Configuration gem 'dotenv-rails' @@ -61,20 +61,19 @@ gem "sentry-rails" # Services gem 'discourse_api' gem "lnurl" -gem 'manifique', git: 'https://gitea.kosmos.org/5apps/manifique.git', branch: 'master' -gem 'nostr', git: 'https://gitea.kosmos.org/kosmos/nostr-gem.git', ref: 'd59f31a' +gem 'manifique' +gem 'nostr' group :development, :test do # Use sqlite3 as the database for Active Record - gem 'sqlite3', '~> 1.4' + gem 'sqlite3', '~> 1.7.2' gem 'rspec-rails' gem 'rails-controller-testing' - gem "byebug", "~> 11.1" end group :development do # Access an interactive console on exception pages or by calling 'console' anywhere in the code. - gem 'web-console', '>= 3.3.0' + gem 'web-console', '~> 4.2' gem 'listen', '~> 3.2' gem 'letter_opener' gem 'letter_opener_web' @@ -90,8 +89,8 @@ group :test do end group :production do - # Use postgresql as the database for Active Record - gem 'pg', '~> 1.2.3' + gem 'pg', '~> 1.5' end + # Windows does not include zoneinfo files, so bundle the tzinfo-data gem gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby] diff --git a/Gemfile.lock b/Gemfile.lock index c04783a..ff21ad9 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,141 +1,127 @@ -GIT - remote: https://gitea.kosmos.org/5apps/manifique.git - revision: 8d79113438ee7c3e4288f840a135622519cffd5c - branch: master - specs: - manifique (0.1.0) - faraday (~> 2.7.11) - faraday-follow_redirects (= 0.3.0) - nokogiri (~> 1.15.4) - -GIT - remote: https://gitea.kosmos.org/kosmos/nostr-gem.git - revision: d59f31a3c63c7642fe2d3eb50b785da54fdabfab - ref: d59f31a - specs: - nostr (0.5.0) - bech32 (~> 1.4) - bip-schnorr (~> 0.6) - ecdsa (~> 1.2) - event_emitter (~> 0.2) - faye-websocket (~> 0.11) - json (~> 2.6) - GEM remote: https://rubygems.org/ specs: - actioncable (7.0.8) - actionpack (= 7.0.8) - activesupport (= 7.0.8) + actioncable (7.1.3) + actionpack (= 7.1.3) + activesupport (= 7.1.3) nio4r (~> 2.0) websocket-driver (>= 0.6.1) - actionmailbox (7.0.8) - actionpack (= 7.0.8) - activejob (= 7.0.8) - activerecord (= 7.0.8) - activestorage (= 7.0.8) - activesupport (= 7.0.8) + zeitwerk (~> 2.6) + actionmailbox (7.1.3) + actionpack (= 7.1.3) + activejob (= 7.1.3) + activerecord (= 7.1.3) + activestorage (= 7.1.3) + activesupport (= 7.1.3) mail (>= 2.7.1) net-imap net-pop net-smtp - actionmailer (7.0.8) - actionpack (= 7.0.8) - actionview (= 7.0.8) - activejob (= 7.0.8) - activesupport (= 7.0.8) + actionmailer (7.1.3) + actionpack (= 7.1.3) + actionview (= 7.1.3) + activejob (= 7.1.3) + activesupport (= 7.1.3) mail (~> 2.5, >= 2.5.4) net-imap net-pop net-smtp - rails-dom-testing (~> 2.0) - actionpack (7.0.8) - actionview (= 7.0.8) - activesupport (= 7.0.8) - rack (~> 2.0, >= 2.2.4) + rails-dom-testing (~> 2.2) + actionpack (7.1.3) + actionview (= 7.1.3) + activesupport (= 7.1.3) + nokogiri (>= 1.8.5) + racc + rack (>= 2.2.4) + rack-session (>= 1.0.1) rack-test (>= 0.6.3) - rails-dom-testing (~> 2.0) - rails-html-sanitizer (~> 1.0, >= 1.2.0) - actiontext (7.0.8) - actionpack (= 7.0.8) - activerecord (= 7.0.8) - activestorage (= 7.0.8) - activesupport (= 7.0.8) + rails-dom-testing (~> 2.2) + rails-html-sanitizer (~> 1.6) + actiontext (7.1.3) + actionpack (= 7.1.3) + activerecord (= 7.1.3) + activestorage (= 7.1.3) + activesupport (= 7.1.3) globalid (>= 0.6.0) nokogiri (>= 1.8.5) - actionview (7.0.8) - activesupport (= 7.0.8) + actionview (7.1.3) + activesupport (= 7.1.3) builder (~> 3.1) - erubi (~> 1.4) - rails-dom-testing (~> 2.0) - rails-html-sanitizer (~> 1.1, >= 1.2.0) - activejob (7.0.8) - activesupport (= 7.0.8) + erubi (~> 1.11) + rails-dom-testing (~> 2.2) + rails-html-sanitizer (~> 1.6) + activejob (7.1.3) + activesupport (= 7.1.3) globalid (>= 0.3.6) - activemodel (7.0.8) - activesupport (= 7.0.8) - activerecord (7.0.8) - activemodel (= 7.0.8) - activesupport (= 7.0.8) - activestorage (7.0.8) - actionpack (= 7.0.8) - activejob (= 7.0.8) - activerecord (= 7.0.8) - activesupport (= 7.0.8) + activemodel (7.1.3) + activesupport (= 7.1.3) + activerecord (7.1.3) + activemodel (= 7.1.3) + activesupport (= 7.1.3) + timeout (>= 0.4.0) + activestorage (7.1.3) + actionpack (= 7.1.3) + activejob (= 7.1.3) + activerecord (= 7.1.3) + activesupport (= 7.1.3) marcel (~> 1.0) - mini_mime (>= 1.1.0) - activesupport (7.0.8) + activesupport (7.1.3) + base64 + bigdecimal concurrent-ruby (~> 1.0, >= 1.0.2) + connection_pool (>= 2.2.5) + drb i18n (>= 1.6, < 2) minitest (>= 5.1) + mutex_m tzinfo (~> 2.0) - addressable (2.8.5) + addressable (2.8.6) public_suffix (>= 2.0.2, < 6.0) ast (2.4.2) - aws-eventstream (1.2.0) - aws-partitions (1.839.0) - aws-sdk-core (3.185.1) - aws-eventstream (~> 1, >= 1.0.2) + aws-eventstream (1.3.0) + aws-partitions (1.886.0) + aws-sdk-core (3.191.0) + aws-eventstream (~> 1, >= 1.3.0) aws-partitions (~> 1, >= 1.651.0) - aws-sigv4 (~> 1.5) + aws-sigv4 (~> 1.8) jmespath (~> 1, >= 1.6.1) - aws-sdk-kms (1.72.0) - aws-sdk-core (~> 3, >= 3.184.0) + aws-sdk-kms (1.77.0) + aws-sdk-core (~> 3, >= 3.191.0) aws-sigv4 (~> 1.1) - aws-sdk-s3 (1.136.0) - aws-sdk-core (~> 3, >= 3.181.0) + aws-sdk-s3 (1.143.0) + aws-sdk-core (~> 3, >= 3.191.0) aws-sdk-kms (~> 1) - aws-sigv4 (~> 1.6) - aws-sigv4 (1.6.0) + aws-sigv4 (~> 1.8) + aws-sigv4 (1.8.0) aws-eventstream (~> 1, >= 1.0.2) backport (1.2.0) - base64 (0.1.1) - bcrypt (3.1.19) + base64 (0.2.0) + bcrypt (3.1.20) bech32 (1.4.2) thor (>= 1.1.0) - benchmark (0.2.1) + benchmark (0.3.0) + bigdecimal (3.1.6) bindex (0.8.1) bip-schnorr (0.7.0) ecdsa_ext (~> 0.5.0) - brow (0.4.1) builder (3.2.4) - byebug (11.1.3) - capybara (3.39.2) + capybara (3.40.0) addressable matrix mini_mime (>= 0.1.3) - nokogiri (~> 1.8) + nokogiri (~> 1.11) rack (>= 1.6.0) rack-test (>= 0.6.3) regexp_parser (>= 1.5, < 3.0) xpath (~> 3.2) chunky_png (1.4.0) - concurrent-ruby (1.2.2) + concurrent-ruby (1.2.3) connection_pool (2.4.1) - crack (0.4.5) + crack (0.4.6) + bigdecimal rexml crass (1.0.6) - cssbundling-rails (1.3.3) + cssbundling-rails (1.4.0) railties (>= 6.0.0) database_cleaner (2.0.2) database_cleaner-active_record (>= 2, < 3) @@ -143,7 +129,7 @@ GEM activerecord (>= 5.a) database_cleaner-core (~> 2.0.0) database_cleaner-core (2.0.1) - date (3.3.3) + date (3.3.4) devise (4.9.3) bcrypt (~> 3.0) orm_adapter (~> 0.1) @@ -153,7 +139,7 @@ GEM devise_ldap_authenticatable (0.8.7) devise (>= 3.4.1) net-ldap (>= 0.16.0) - diff-lcs (1.5.0) + diff-lcs (1.5.1) discourse_api (2.0.1) faraday (~> 2.7) faraday-follow_redirects @@ -165,6 +151,8 @@ GEM railties (>= 3.2) down (5.4.1) addressable (~> 2.8) + drb (2.2.0) + ruby2_keywords e2mmap (0.1.0) ecdsa (1.2.0) ecdsa_ext (0.5.0) @@ -174,58 +162,61 @@ GEM tzinfo event_emitter (0.2.6) eventmachine (1.2.7) - factory_bot (6.2.1) + factory_bot (6.4.6) activesupport (>= 5.0.0) - factory_bot_rails (6.2.0) - factory_bot (~> 6.2.0) + factory_bot_rails (6.4.3) + factory_bot (~> 6.4) railties (>= 5.0.0) - faker (3.2.1) + faker (3.2.3) i18n (>= 1.8.11, < 2) - faraday (2.7.11) - base64 - faraday-net_http (>= 2.0, < 3.1) - ruby2_keywords (>= 0.0.4) + faraday (2.9.0) + faraday-net_http (>= 2.0, < 3.2) faraday-follow_redirects (0.3.0) faraday (>= 1, < 3) faraday-multipart (1.0.4) multipart-post (~> 2) - faraday-net_http (3.0.2) + faraday-net_http (3.1.0) + net-http faye-websocket (0.11.3) eventmachine (>= 0.12.0) websocket-driver (>= 0.5.1) ffi (1.16.3) - flipper (1.0.0) - brow (~> 0.4.1) + flipper (1.2.2) concurrent-ruby (< 2) - flipper-active_record (1.0.0) + flipper-active_record (1.2.2) activerecord (>= 4.2, < 8) - flipper (~> 1.0.0) - flipper-ui (1.0.0) + flipper (~> 1.2.2) + flipper-ui (1.2.2) erubi (>= 1.0.0, < 2.0.0) - flipper (~> 1.0.0) + flipper (~> 1.2.2) rack (>= 1.4, < 4) rack-protection (>= 1.5.3, <= 4.0.0) sanitize (< 7) - fugit (1.8.1) + fugit (1.9.0) et-orbi (~> 1, >= 1.2.7) raabro (~> 1.4) globalid (1.2.1) activesupport (>= 6.1) - hashdiff (1.0.1) + hashdiff (1.1.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) - importmap-rails (1.2.1) + importmap-rails (2.0.1) actionpack (>= 6.0.0) + activesupport (>= 6.0.0) railties (>= 6.0.0) + io-console (0.7.2) + irb (1.11.1) + rdoc + reline (>= 0.4.2) jaro_winkler (1.5.6) jbuilder (2.11.5) actionview (>= 5.0.0) activesupport (>= 5.0.0) jmespath (1.6.2) - json (2.6.3) + json (2.7.1) kramdown (2.4.0) rexml kramdown-parser-gfm (1.1.0) @@ -245,8 +236,8 @@ GEM rb-inotify (~> 0.9, >= 0.9.10) lnurl (1.1.0) bech32 (~> 1.1) - lockbox (1.3.0) - loofah (2.21.4) + lockbox (1.3.2) + loofah (2.22.0) crass (~> 1.0.2) nokogiri (>= 1.12.0) mail (2.8.1) @@ -254,63 +245,85 @@ GEM net-imap net-pop net-smtp + manifique (1.0.1) + faraday (~> 2.9.0) + faraday-follow_redirects (= 0.3.0) + nokogiri (~> 1.16.0) marcel (1.0.2) matrix (0.4.2) method_source (1.0.0) mini_magick (4.12.0) mini_mime (1.1.5) mini_portile2 (2.8.5) - minitest (5.20.0) + minitest (5.21.2) multipart-post (2.3.0) - net-imap (0.3.7) + mutex_m (0.2.0) + net-http (0.4.1) + uri + net-imap (0.4.9.1) date net-protocol - net-ldap (0.18.0) + net-ldap (0.19.0) net-pop (0.1.2) net-protocol - net-protocol (0.2.1) + net-protocol (0.2.2) timeout - net-smtp (0.4.0) + net-smtp (0.4.0.1) net-protocol - nio4r (2.5.9) - nokogiri (1.15.4) + nio4r (2.7.0) + nokogiri (1.16.0) mini_portile2 (~> 2.8.2) racc (~> 1.4) - nokogiri (1.15.4-arm64-darwin) + nokogiri (1.16.0-arm64-darwin) racc (~> 1.4) - nokogiri (1.15.4-x86_64-linux) + nokogiri (1.16.0-x86_64-linux) racc (~> 1.4) + nostr (0.5.0) + bech32 (~> 1.4) + bip-schnorr (~> 0.6) + ecdsa (~> 1.2) + event_emitter (~> 0.2) + faye-websocket (~> 0.11) + json (~> 2.6) orm_adapter (0.5.0) - pagy (6.1.0) - parallel (1.23.0) - parser (3.2.2.4) + pagy (6.4.3) + parallel (1.24.0) + parser (3.3.0.5) ast (~> 2.4.1) racc - pg (1.2.3) - public_suffix (5.0.3) + pg (1.5.4) + psych (5.1.2) + stringio + public_suffix (5.0.4) puma (4.3.12) nio4r (~> 2.0) raabro (1.4.0) - racc (1.7.1) + racc (1.7.3) rack (2.2.8) - rack-protection (3.1.0) + rack-protection (3.2.0) + base64 (>= 0.1.0) rack (~> 2.2, >= 2.2.4) + rack-session (1.0.2) + rack (< 3) rack-test (2.1.0) rack (>= 1.3) - rails (7.0.8) - actioncable (= 7.0.8) - actionmailbox (= 7.0.8) - actionmailer (= 7.0.8) - actionpack (= 7.0.8) - actiontext (= 7.0.8) - actionview (= 7.0.8) - activejob (= 7.0.8) - activemodel (= 7.0.8) - activerecord (= 7.0.8) - activestorage (= 7.0.8) - activesupport (= 7.0.8) + rackup (1.0.0) + rack (< 3) + webrick + rails (7.1.3) + actioncable (= 7.1.3) + actionmailbox (= 7.1.3) + actionmailer (= 7.1.3) + actionpack (= 7.1.3) + actiontext (= 7.1.3) + actionview (= 7.1.3) + activejob (= 7.1.3) + activemodel (= 7.1.3) + activerecord (= 7.1.3) + activestorage (= 7.1.3) + activesupport (= 7.1.3) bundler (>= 1.15.0) - railties (= 7.0.8) + railties (= 7.1.3) rails-controller-testing (1.0.5) actionpack (>= 5.0.1.rc1) actionview (>= 5.0.1.rc1) @@ -325,21 +338,26 @@ GEM rails-settings-cached (2.8.3) activerecord (>= 5.0.0) railties (>= 5.0.0) - railties (7.0.8) - actionpack (= 7.0.8) - activesupport (= 7.0.8) - method_source + railties (7.1.3) + actionpack (= 7.1.3) + activesupport (= 7.1.3) + irb + rackup (>= 1.0.0) rake (>= 12.2) - thor (~> 1.0) - zeitwerk (~> 2.5) + thor (~> 1.0, >= 1.2.2) + zeitwerk (~> 2.6) rainbow (3.1.1) - rake (13.0.6) + rake (13.1.0) rb-fsevent (0.11.2) rb-inotify (0.10.1) ffi (~> 1.0) rbs (2.8.4) + rdoc (6.6.2) + psych (>= 4.0.0) redis (4.8.1) - regexp_parser (2.8.2) + regexp_parser (2.9.0) + reline (0.4.2) + io-console (~> 0.5) responders (3.1.1) actionpack (>= 5.2) railties (>= 5.2) @@ -358,7 +376,7 @@ GEM rspec-mocks (3.12.6) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.12.0) - rspec-rails (6.0.3) + rspec-rails (6.1.1) actionpack (>= 6.1) activesupport (>= 6.1) railties (>= 6.1) @@ -367,19 +385,18 @@ GEM rspec-mocks (~> 3.12) rspec-support (~> 3.12) rspec-support (3.12.1) - rubocop (1.57.1) - base64 (~> 0.1.1) + rubocop (1.60.2) 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) + rubocop-ast (>= 1.30.0, < 2.0) ruby-progressbar (~> 1.7) unicode-display_width (>= 2.4.0, < 3.0) - rubocop-ast (1.29.0) + rubocop-ast (1.30.0) parser (>= 3.2.1.0) ruby-progressbar (1.13.0) ruby-vips (2.2.0) @@ -390,10 +407,10 @@ GEM sanitize (6.1.0) crass (~> 1.0.2) nokogiri (>= 1.12.0) - sentry-rails (5.12.0) + sentry-rails (5.16.1) railties (>= 5.0) - sentry-ruby (~> 5.12.0) - sentry-ruby (5.12.0) + sentry-ruby (~> 5.16.1) + sentry-ruby (5.16.1) concurrent-ruby (~> 1.0, >= 1.0.2) sidekiq (6.5.12) connection_pool (>= 2.2.5, < 3) @@ -403,7 +420,7 @@ GEM rufus-scheduler (~> 3.2) sidekiq (>= 6, < 8) tilt (>= 1.4.0) - solargraph (0.49.0) + solargraph (0.50.0) backport (~> 1.2) benchmark bundler (~> 2.0) @@ -426,15 +443,16 @@ GEM actionpack (>= 5.2) activesupport (>= 5.2) sprockets (>= 3.0.0) - sqlite3 (1.6.7) + sqlite3 (1.7.2) mini_portile2 (~> 2.8.0) - sqlite3 (1.6.7-arm64-darwin) - sqlite3 (1.6.7-x86_64-linux) - stimulus-rails (1.3.0) + sqlite3 (1.7.2-arm64-darwin) + sqlite3 (1.7.2-x86_64-linux) + stimulus-rails (1.3.3) railties (>= 6.0.0) + stringio (3.1.0) thor (1.3.0) tilt (2.3.0) - timeout (0.4.0) + timeout (0.4.1) turbo-rails (1.5.0) actionpack (>= 6.0.0) activejob (>= 6.0.0) @@ -442,7 +460,8 @@ GEM tzinfo (2.0.6) concurrent-ruby (~> 1.0) unicode-display_width (2.5.0) - view_component (3.6.0) + uri (0.13.0) + view_component (3.10.0) activesupport (>= 5.2.0, < 8.0) concurrent-ruby (~> 1.0) method_source (~> 1.0) @@ -457,6 +476,7 @@ GEM addressable (>= 2.8.0) crack (>= 0.3.2) hashdiff (>= 0.4.0, < 2.0.0) + webrick (1.8.1) websocket-driver (0.7.6) websocket-extensions (>= 0.1.0) websocket-extensions (0.1.5) @@ -472,8 +492,7 @@ PLATFORMS DEPENDENCIES aws-sdk-s3 - bcrypt (~> 3.1.7) - byebug (~> 11.1) + bcrypt (~> 3.1) capybara cssbundling-rails database_cleaner @@ -496,13 +515,13 @@ DEPENDENCIES listen (~> 3.2) lnurl lockbox - manifique! + manifique net-ldap - nostr! + nostr pagy (~> 6.0, >= 6.0.2) - pg (~> 1.2.3) + pg (~> 1.5) puma (~> 4.1) - rails (~> 7.0.2) + rails (~> 7.1) rails-controller-testing rails-settings-cached (~> 2.8.3) rqrcode (~> 2.0) @@ -513,14 +532,14 @@ DEPENDENCIES sidekiq-scheduler solargraph sprockets-rails - sqlite3 (~> 1.4) + sqlite3 (~> 1.7.2) stimulus-rails turbo-rails tzinfo-data view_component warden - web-console (>= 3.3.0) + web-console (~> 4.2) webmock BUNDLED WITH - 2.3.7 + 2.5.5 diff --git a/app/components/app_catalog/web_app_icon_component.html.erb b/app/components/app_catalog/web_app_icon_component.html.erb new file mode 100644 index 0000000..3f9c5f3 --- /dev/null +++ b/app/components/app_catalog/web_app_icon_component.html.erb @@ -0,0 +1,5 @@ +<% if @image_url %> + <%= image_tag @image_url, class: "h-full w-full" %> +<% else %> + <%= render partial: "icons/remotestorage", locals: { custom_class: "h-full w-full p-0.5 text-gray-200" } %> +<% end %> diff --git a/app/components/app_catalog/web_app_icon_component.rb b/app/components/app_catalog/web_app_icon_component.rb new file mode 100644 index 0000000..8421d4f --- /dev/null +++ b/app/components/app_catalog/web_app_icon_component.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +module AppCatalog + class WebAppIconComponent < ViewComponent::Base + def initialize(web_app:) + if web_app&.icon&.attached? + @image_url = image_url_for(web_app.icon) + elsif web_app&.apple_touch_icon&.attached? + @image_url = image_url_for(web_app.apple_touch_icon) + end + end + + def image_url_for(attachment) + if Setting.s3_enabled? + s3_image_url(attachment) + else + Rails.application.routes.url_helpers.rails_blob_path(attachment, only_path: true) + end + end + end +end diff --git a/app/components/dropdown_component.html.erb b/app/components/dropdown_component.html.erb index 3ea3bce..eb04538 100644 --- a/app/components/dropdown_component.html.erb +++ b/app/components/dropdown_component.html.erb @@ -2,13 +2,21 @@
<%= @descripton %>
+ <% end %><%= @auth.client_id %> diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb index 3cb5735..6b6f510 100644 --- a/app/controllers/admin/users_controller.rb +++ b/app/controllers/admin/users_controller.rb @@ -1,11 +1,11 @@ class Admin::UsersController < Admin::BaseController - before_action :set_user, only: [:show] + before_action :set_user, except: [:index] before_action :set_current_section + # GET /admin/users def index ldap = LdapService.new - @ou = params[:ou] || Setting.primary_domain - @orgs = ldap.fetch_organizations + @ou = Setting.primary_domain @pagy, @users = pagy(User.where(ou: @ou).order(cn: :asc)) @stats = { @@ -14,6 +14,7 @@ class Admin::UsersController < Admin::BaseController } end + # GET /admin/users/:username def show if Setting.lndhub_admin_enabled? @lndhub_user = @user.lndhub_user @@ -21,14 +22,38 @@ class Admin::UsersController < Admin::BaseController @services_enabled = @user.services_enabled - @avatar = LdapManager::FetchAvatar.call(cn: @user.cn, ou: @user.ou) + @avatar = LdapManager::FetchAvatar.call(cn: @user.cn) + end + + # POST /admin/users/:username/invitations + def create_invitations + amount = params[:amount].to_i + notify_user = ActiveRecord::Type::Boolean.new.cast(params[:notify_user]) + + CreateInvitations.call(user: @user, amount: amount, notify: notify_user) + + redirect_to admin_user_path(@user.cn), flash: { + success: "Added #{amount} invitations to #{@user.cn}'s account" + } + end + + # DELETE /admin/users/:username/invitations + def delete_invitations + invitations = @user.invitations.unused + amount = invitations.count + + invitations.destroy_all + + redirect_to admin_user_path(@user.cn), flash: { + success: "Removed #{amount} invitations from #{@user.cn}'s account" + } end private def set_user - address = params[:address].split("@") - @user = User.where(cn: address.first, ou: address.last).first + @user = User.find_by(cn: params[:username], ou: Setting.primary_domain) + http_status :not_found unless @user end def set_current_section diff --git a/app/controllers/settings_controller.rb b/app/controllers/settings_controller.rb index f1fa357..b054740 100644 --- a/app/controllers/settings_controller.rb +++ b/app/controllers/settings_controller.rb @@ -24,11 +24,11 @@ class SettingsController < ApplicationController if @user.save if @user.display_name && (@user.display_name != @user.ldap_entry[:display_name]) - LdapManager::UpdateDisplayName.call(@user.dn, @user.display_name) + LdapManager::UpdateDisplayName.call(dn: @user.dn, display_name: @user.display_name) end if @user.avatar_new.present? - LdapManager::UpdateAvatar.call(@user.dn, @user.avatar_new) + LdapManager::UpdateAvatar.call(dn: @user.dn, file: @user.avatar_new) end redirect_to setting_path(@settings_section), flash: { @@ -64,10 +64,10 @@ class SettingsController < ApplicationController @user.current_password = nil session[:new_email_password] = generate_email_password hashed_password = hash_email_password(session[:new_email_password]) - LdapManager::UpdateEmailPassword.call(@user.dn, hashed_password) + LdapManager::UpdateEmailPassword.call(dn: @user.dn, password_hash: hashed_password) if @user.ldap_entry[:email_maildrop] != @user.address - LdapManager::UpdateEmailMaildrop.call(@user.dn, @user.address) + LdapManager::UpdateEmailMaildrop.call(dn: @user.dn, address: @user.address) end redirect_to new_password_services_email_path @@ -88,8 +88,8 @@ class SettingsController < ApplicationController def set_nostr_pubkey signed_event = nostr_event_params[:signed_event].to_h.symbolize_keys - is_valid_id = NostrManager::ValidateId.call(signed_event) - is_valid_sig = NostrManager::VerifySignature.call(signed_event) + is_valid_id = NostrManager::ValidateId.call(event: signed_event) + is_valid_sig = NostrManager::VerifySignature.call(event: signed_event) is_correct_content = signed_event[:content] == "Connect my public key to #{current_user.address} (confirmation #{session[:shared_secret]})" unless is_valid_id && is_valid_sig && is_correct_content diff --git a/app/controllers/signup_controller.rb b/app/controllers/signup_controller.rb index c21820b..236db54 100644 --- a/app/controllers/signup_controller.rb +++ b/app/controllers/signup_controller.rb @@ -96,13 +96,13 @@ class SignupController < ApplicationController session[:new_user] = nil session[:validation_error] = nil - CreateAccount.call( + CreateAccount.call(account: { username: @user.cn, domain: Setting.primary_domain, email: @user.email, password: @user.password, invitation: @invitation - ) + }) end def set_context diff --git a/app/mailers/notification_mailer.rb b/app/mailers/notification_mailer.rb index 81132b9..004f94f 100644 --- a/app/mailers/notification_mailer.rb +++ b/app/mailers/notification_mailer.rb @@ -17,4 +17,10 @@ class NotificationMailer < ApplicationMailer @subject = "New app connected to your storage" mail to: @user.email, subject: @subject end + + def new_invitations_available + @user = params[:user] + @subject = "New invitations added to your account" + mail to: @user.email, subject: @subject + end end diff --git a/app/models/app_catalog/web_app.rb b/app/models/app_catalog/web_app.rb index b42bea7..388e610 100644 --- a/app/models/app_catalog/web_app.rb +++ b/app/models/app_catalog/web_app.rb @@ -1,7 +1,7 @@ class AppCatalog::WebApp < ApplicationRecord store :metadata, coder: JSON - has_many :remote_storage_authorizations + has_many :remote_storage_authorizations, dependent: :destroy has_one_attached :icon has_one_attached :apple_touch_icon @@ -11,6 +11,6 @@ class AppCatalog::WebApp < ApplicationRecord if: Proc.new { |a| a.url.present? } def update_metadata - AppCatalogManager::UpdateMetadata.call(self) + AppCatalogManager::UpdateMetadata.call(app: self) end end diff --git a/app/models/setting.rb b/app/models/setting.rb index 27acdcc..daa4b29 100644 --- a/app/models/setting.rb +++ b/app/models/setting.rb @@ -15,6 +15,9 @@ class Setting < RailsSettings::Base field :redis_url, type: :string, default: ENV["REDIS_URL"] || "redis://localhost:6379/0" + field :s3_enabled, type: :boolean, + default: ENV["S3_ENABLED"] && ENV["S3_ENABLED"].to_s != "false" + # # Registrations # diff --git a/app/models/user.rb b/app/models/user.rb index 27dfbfd..9620bfe 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -7,7 +7,7 @@ class User < ApplicationRecord attr_accessor :avatar_new attr_accessor :current_password - serialize :preferences, UserPreferences + serialize :preferences, coder: UserPreferences # # Relations @@ -92,7 +92,7 @@ class User < ApplicationRecord def devise_after_confirmation if ldap_entry[:mail] != self.email # E-Mail update confirmed - LdapManager::UpdateEmail.call(self.dn, self.email) + LdapManager::UpdateEmail.call(dn: self.dn, address: self.email) else # E-Mail from signup confirmed (i.e. account activation) @@ -164,7 +164,7 @@ class User < ApplicationRecord end def avatar - @avatar_base64 ||= LdapManager::FetchAvatar.call(cn: cn, ou: ou) + @avatar_base64 ||= LdapManager::FetchAvatar.call(cn: cn) end def services_enabled diff --git a/app/services/app_catalog_manager/update_metadata.rb b/app/services/app_catalog_manager/update_metadata.rb index d78c5d8..a506013 100644 --- a/app/services/app_catalog_manager/update_metadata.rb +++ b/app/services/app_catalog_manager/update_metadata.rb @@ -3,7 +3,7 @@ require "down" module AppCatalogManager class UpdateMetadata < AppCatalogManagerService - def initialize(app) + def initialize(app:) @app = app end @@ -18,6 +18,10 @@ module AppCatalogManager @app.metadata[prop] = metadata.send(prop) if prop end + @app.save! + + # TODO move icon downloads to separate, async job + if icon = metadata.select_icon(sizes: "256x256") || icon = metadata.select_icon(sizes: "192x192") attach_remote_image(:icon, icon) @@ -27,8 +31,6 @@ module AppCatalogManager if apple_touch_icon = metadata.select_icon(purpose: "apple-touch-icon") attach_remote_image(:apple_touch_icon, apple_touch_icon) end - - @app.save! rescue Manifique::Error => e msg = "Fetching web app manifest failed for #{e.url}: #{e.type}" Rails.logger.warn(msg) @@ -42,14 +44,19 @@ module AppCatalogManager else download_url = "#{@app.url}/#{icon["src"].gsub(/^\//,'')}" end - filename = "#{attachment_name}.png" - key = "web_apps/#{@app.id}/icons/#{attachment_name}.png" + filename = "#{attachment_name}-#{Time.now.to_i}.png" + key = "web_apps/#{@app.id}/icons/#{filename}" begin tempfile = Down.download(download_url) @app.send(attachment_name).attach(key: key, io: tempfile, filename: filename) rescue Down::NotFound - Rails.logger.warn "Icon download failed: NotFound error for #{download_url}" + msg = "Download of \"#{attachment_name}\" failed: NotFound error for #{download_url}" + Rails.logger.warn(msg) + Sentry.capture_message(msg) + rescue => e + Rails.logger.warn "Saving attachment \"#{attachment_name}\" failed: \"#{e.message}\"" + Sentry.capture_exception(e) if Setting.sentry_enabled? end end end diff --git a/app/services/application_service.rb b/app/services/application_service.rb index 401fe02..2d0394b 100644 --- a/app/services/application_service.rb +++ b/app/services/application_service.rb @@ -1,7 +1,7 @@ class ApplicationService # This enables executing a service's `#call` method directly via # `MyService.call(args)`, without creating a class instance it first. - def self.call(*args, &block) - new(*args, &block).call + def self.call(**args, &block) + new(**args, &block).call end end diff --git a/app/services/create_account.rb b/app/services/create_account.rb index 8c732cf..4e31200 100644 --- a/app/services/create_account.rb +++ b/app/services/create_account.rb @@ -1,11 +1,11 @@ class CreateAccount < ApplicationService - def initialize(args) - @username = args[:username] - @domain = args[:ou] || Setting.primary_domain - @email = args[:email] - @password = args[:password] - @invitation = args[:invitation] - @confirmed = args[:confirmed] + def initialize(account:) + @username = account[:username] + @domain = account[:ou] || Setting.primary_domain + @email = account[:email] + @password = account[:password] + @invitation = account[:invitation] + @confirmed = account[:confirmed] end def call diff --git a/app/services/create_invitations.rb b/app/services/create_invitations.rb new file mode 100644 index 0000000..3003b1a --- /dev/null +++ b/app/services/create_invitations.rb @@ -0,0 +1,17 @@ +class CreateInvitations < ApplicationService + def initialize(user:, amount:, notify: true) + @user = user + @amount = amount + @notify = notify + end + + def call + @amount.times do + Invitation.create(user: @user) + end + + if @notify + NotificationMailer.with(user: @user).new_invitations_available.deliver_later + end + end +end diff --git a/app/services/ldap_manager/fetch_avatar.rb b/app/services/ldap_manager/fetch_avatar.rb index 9d8f2b6..c2643f7 100644 --- a/app/services/ldap_manager/fetch_avatar.rb +++ b/app/services/ldap_manager/fetch_avatar.rb @@ -1,12 +1,11 @@ module LdapManager class FetchAvatar < LdapManagerService - def initialize(cn:, ou: nil) + def initialize(cn:) @cn = cn - @ou = ou end def call - treebase = @ou ? "ou=#{@ou},cn=users,#{suffix}" : ldap_config["base"] + treebase = ldap_config["base"] attributes = %w{ jpegPhoto } filter = Net::LDAP::Filter.eq("cn", @cn) diff --git a/app/services/ldap_manager/update_avatar.rb b/app/services/ldap_manager/update_avatar.rb index f26b92c..f3c8975 100644 --- a/app/services/ldap_manager/update_avatar.rb +++ b/app/services/ldap_manager/update_avatar.rb @@ -2,7 +2,7 @@ require "image_processing/vips" module LdapManager class UpdateAvatar < LdapManagerService - def initialize(dn, file) + def initialize(dn:, file:) @dn = dn @img_data = process(file) end diff --git a/app/services/ldap_manager/update_display_name.rb b/app/services/ldap_manager/update_display_name.rb index 2d3e90f..85418d3 100644 --- a/app/services/ldap_manager/update_display_name.rb +++ b/app/services/ldap_manager/update_display_name.rb @@ -1,6 +1,6 @@ module LdapManager class UpdateDisplayName < LdapManagerService - def initialize(dn, display_name) + def initialize(dn:, display_name:) @dn = dn @display_name = display_name end diff --git a/app/services/ldap_manager/update_email.rb b/app/services/ldap_manager/update_email.rb index 80f40e4..9df3f17 100644 --- a/app/services/ldap_manager/update_email.rb +++ b/app/services/ldap_manager/update_email.rb @@ -1,6 +1,6 @@ module LdapManager class UpdateEmail < LdapManagerService - def initialize(dn, address) + def initialize(dn:, address:) @dn = dn @address = address end diff --git a/app/services/ldap_manager/update_email_maildrop.rb b/app/services/ldap_manager/update_email_maildrop.rb index d26cf16..1945308 100644 --- a/app/services/ldap_manager/update_email_maildrop.rb +++ b/app/services/ldap_manager/update_email_maildrop.rb @@ -1,6 +1,6 @@ module LdapManager class UpdateEmailMaildrop < LdapManagerService - def initialize(dn, address) + def initialize(dn:, address:) @dn = dn @address = address end diff --git a/app/services/ldap_manager/update_email_password.rb b/app/services/ldap_manager/update_email_password.rb index e9cde6a..7ef327a 100644 --- a/app/services/ldap_manager/update_email_password.rb +++ b/app/services/ldap_manager/update_email_password.rb @@ -1,6 +1,6 @@ module LdapManager class UpdateEmailPassword < LdapManagerService - def initialize(dn, password_hash) + def initialize(dn:, password_hash:) @dn = dn @password_hash = password_hash end diff --git a/app/services/nostr_manager/validate_id.rb b/app/services/nostr_manager/validate_id.rb index d184b75..e838441 100644 --- a/app/services/nostr_manager/validate_id.rb +++ b/app/services/nostr_manager/validate_id.rb @@ -1,6 +1,6 @@ module NostrManager class ValidateId < NostrManagerService - def initialize(event) + def initialize(event:) @event = Nostr::Event.new(**event) end diff --git a/app/services/nostr_manager/verify_signature.rb b/app/services/nostr_manager/verify_signature.rb index 63b0489..9c62234 100644 --- a/app/services/nostr_manager/verify_signature.rb +++ b/app/services/nostr_manager/verify_signature.rb @@ -1,6 +1,6 @@ module NostrManager class VerifySignature < NostrManagerService - def initialize(event) + def initialize(event:) @event = Nostr::Event.new(**event) end diff --git a/app/views/admin/users/_create_invitations.html.erb b/app/views/admin/users/_create_invitations.html.erb new file mode 100644 index 0000000..dfffa36 --- /dev/null +++ b/app/views/admin/users/_create_invitations.html.erb @@ -0,0 +1,21 @@ +
+ <%= form.submit 'Add', class: "btn-md btn-blue w-full" %> +
+<% end %> diff --git a/app/views/admin/users/index.html.erb b/app/views/admin/users/index.html.erb index e0eb90d..830057a 100644 --- a/app/views/admin/users/index.html.erb +++ b/app/views/admin/users/index.html.erb @@ -1,4 +1,4 @@ -<%= render HeaderComponent.new(title: "Users: #{@ou}") %> +<%= render HeaderComponent.new(title: "Users") %> <%= render MainSimpleComponent.new do %>UID | Status | Roles | - <% @users.each do |user| %>|
---|---|---|---|
<%= link_to(user.cn, admin_user_path(user.address), class: 'ks-text-link') %> | +<%= link_to(user.cn, admin_user_path(user.cn), class: 'ks-text-link') %> | <%= user.confirmed_at.nil? ? badge("pending", :yellow) : "" %> | <%= user.is_admin? ? badge("admin", :red) : "" %> |
Invitations available | -- <%= @user.invitations.count %> + |
+
+
+ <%= @user.invitations.count %>
+
+
+
+ <% if @user.invitations.unused.count > 0 %>
+ <%= link_to invitations_admin_user_path(@user.cn),
+ id: "remove-invitations", data: {
+ turbo_method: :delete,
+ turbo_confirm: "Delete all of #{@user.cn}'s available invitations?"
+ } do %>
+ <%= render partial: "icons/x-circle", locals: {
+ custom_class: "text-red-600 hover:text-red-500 -mt-2 -mb-1 h-6 w-6 inline-block"
+ } %>
+ <% end %>
+ <% end %>
+
+
+ <%= render ModalComponent.new(show_close_button: false) do %>
+ <%= render partial: "admin/users/create_invitations",
+ locals: { user: @user } %>
+ <% end %>
|
|