From 49de4007abddbcefb08a8824ece33925d1daba6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A2u=20Cao?= Date: Wed, 8 Mar 2023 13:44:18 +0700 Subject: [PATCH] Settings page for adding verified nostr pubkeys --- Gemfile | 7 +- Gemfile.lock | 316 ++++++++++-------- app/assets/stylesheets/components/base.css | 4 + app/assets/stylesheets/components/buttons.css | 5 + app/assets/stylesheets/components/forms.css | 4 + app/controllers/settings_controller.rb | 43 ++- .../settings/nostr_pubkey_controller.js | 63 ++++ app/models/user.rb | 9 +- app/services/nostr_manager/validate_id.rb | 11 + .../nostr_manager/verify_signature.rb | 17 + app/services/nostr_manager_service.rb | 4 + app/views/icons/_science.html.erb | 1 + app/views/settings/_experiments.html.erb | 62 ++++ app/views/shared/_sidenav_settings.html.erb | 6 + config/routes.rb | 1 + db/schema.rb | 21 +- spec/features/settings/experiments_spec.rb | 22 ++ spec/requests/settings_spec.rb | 104 ++++++ vendor/javascript/.keep | 0 vendor/javascript/bech32.js | 2 + yarn.lock | 152 ++++----- 21 files changed, 621 insertions(+), 233 deletions(-) create mode 100644 app/javascript/controllers/settings/nostr_pubkey_controller.js create mode 100644 app/services/nostr_manager/validate_id.rb create mode 100644 app/services/nostr_manager/verify_signature.rb create mode 100644 app/services/nostr_manager_service.rb create mode 100644 app/views/icons/_science.html.erb create mode 100644 app/views/settings/_experiments.html.erb create mode 100644 spec/features/settings/experiments_spec.rb create mode 100644 spec/requests/settings_spec.rb create mode 100644 vendor/javascript/.keep create mode 100644 vendor/javascript/bech32.js diff --git a/Gemfile b/Gemfile index dad66b1..81aceee 100644 --- a/Gemfile +++ b/Gemfile @@ -51,13 +51,14 @@ gem 'faraday' gem 'sidekiq', '< 7' gem 'sidekiq-scheduler' -# Service integrations -gem 'discourse_api' - # Monitoring gem "sentry-ruby" gem "sentry-rails" +# Services +gem 'discourse_api' +gem 'nostr', git: 'https://gitea.kosmos.org/kosmos/nostr-gem.git', branch: 'feature/ruby_2.7_compat' + group :development, :test do # Use sqlite3 as the database for Active Record gem 'sqlite3', '~> 1.4' diff --git a/Gemfile.lock b/Gemfile.lock index 6b33fa8..75236f6 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,81 +1,98 @@ +GIT + remote: https://gitea.kosmos.org/kosmos/nostr-gem.git + revision: 596529d9eb50d13b3f385245636698fccf37b442 + branch: feature/ruby_2.7_compat + specs: + nostr (0.4.0) + bech32 (~> 1.3) + bip-schnorr (~> 0.4) + ecdsa (~> 1.2) + event_emitter (~> 0.2) + faye-websocket (~> 0.11) + json (~> 2.6) + GEM remote: https://rubygems.org/ specs: - actioncable (7.0.4) - actionpack (= 7.0.4) - activesupport (= 7.0.4) + actioncable (7.0.5) + actionpack (= 7.0.5) + activesupport (= 7.0.5) nio4r (~> 2.0) websocket-driver (>= 0.6.1) - actionmailbox (7.0.4) - actionpack (= 7.0.4) - activejob (= 7.0.4) - activerecord (= 7.0.4) - activestorage (= 7.0.4) - activesupport (= 7.0.4) + actionmailbox (7.0.5) + actionpack (= 7.0.5) + activejob (= 7.0.5) + activerecord (= 7.0.5) + activestorage (= 7.0.5) + activesupport (= 7.0.5) mail (>= 2.7.1) net-imap net-pop net-smtp - actionmailer (7.0.4) - actionpack (= 7.0.4) - actionview (= 7.0.4) - activejob (= 7.0.4) - activesupport (= 7.0.4) + actionmailer (7.0.5) + actionpack (= 7.0.5) + actionview (= 7.0.5) + activejob (= 7.0.5) + activesupport (= 7.0.5) mail (~> 2.5, >= 2.5.4) net-imap net-pop net-smtp rails-dom-testing (~> 2.0) - actionpack (7.0.4) - actionview (= 7.0.4) - activesupport (= 7.0.4) - rack (~> 2.0, >= 2.2.0) + actionpack (7.0.5) + actionview (= 7.0.5) + activesupport (= 7.0.5) + rack (~> 2.0, >= 2.2.4) rack-test (>= 0.6.3) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.0, >= 1.2.0) - actiontext (7.0.4) - actionpack (= 7.0.4) - activerecord (= 7.0.4) - activestorage (= 7.0.4) - activesupport (= 7.0.4) + actiontext (7.0.5) + actionpack (= 7.0.5) + activerecord (= 7.0.5) + activestorage (= 7.0.5) + activesupport (= 7.0.5) globalid (>= 0.6.0) nokogiri (>= 1.8.5) - actionview (7.0.4) - activesupport (= 7.0.4) + actionview (7.0.5) + activesupport (= 7.0.5) builder (~> 3.1) erubi (~> 1.4) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.1, >= 1.2.0) - activejob (7.0.4) - activesupport (= 7.0.4) + activejob (7.0.5) + activesupport (= 7.0.5) globalid (>= 0.3.6) - activemodel (7.0.4) - activesupport (= 7.0.4) - activerecord (7.0.4) - activemodel (= 7.0.4) - activesupport (= 7.0.4) - activestorage (7.0.4) - actionpack (= 7.0.4) - activejob (= 7.0.4) - activerecord (= 7.0.4) - activesupport (= 7.0.4) + activemodel (7.0.5) + activesupport (= 7.0.5) + activerecord (7.0.5) + activemodel (= 7.0.5) + activesupport (= 7.0.5) + activestorage (7.0.5) + actionpack (= 7.0.5) + activejob (= 7.0.5) + activerecord (= 7.0.5) + activesupport (= 7.0.5) marcel (~> 1.0) mini_mime (>= 1.1.0) - activesupport (7.0.4) + activesupport (7.0.5) concurrent-ruby (~> 1.0, >= 1.0.2) i18n (>= 1.6, < 2) minitest (>= 5.1) tzinfo (~> 2.0) - addressable (2.8.1) + addressable (2.8.4) public_suffix (>= 2.0.2, < 6.0) ast (2.4.2) backport (1.2.0) bcrypt (3.1.18) + bech32 (1.3.0) + thor (>= 1.1.0) benchmark (0.2.1) bindex (0.8.1) + bip-schnorr (0.6.0) + ecdsa_ext (~> 0.5.0) builder (3.2.4) byebug (11.1.3) - capybara (3.38.0) + capybara (3.39.2) addressable matrix mini_mime (>= 0.1.3) @@ -85,20 +102,21 @@ GEM regexp_parser (>= 1.5, < 3.0) xpath (~> 3.2) chunky_png (1.4.0) - concurrent-ruby (1.1.10) - connection_pool (2.3.0) + concurrent-ruby (1.2.2) + connection_pool (2.4.1) crack (0.4.5) rexml crass (1.0.6) - cssbundling-rails (1.1.1) + cssbundling-rails (1.1.2) railties (>= 6.0.0) - database_cleaner (2.0.1) - database_cleaner-active_record (~> 2.0.0) - database_cleaner-active_record (2.0.1) + database_cleaner (2.0.2) + database_cleaner-active_record (>= 2, < 3) + database_cleaner-active_record (2.1.0) activerecord (>= 5.a) database_cleaner-core (~> 2.0.0) database_cleaner-core (2.0.1) - devise (4.9.0) + date (3.3.3) + devise (4.9.2) bcrypt (~> 3.0) orm_adapter (~> 0.1) railties (>= 4.1.0) @@ -108,7 +126,7 @@ GEM devise (>= 3.4.1) net-ldap (>= 0.16.0) diff-lcs (1.5.0) - discourse_api (2.0.0) + discourse_api (2.0.1) faraday (~> 2.7) faraday-follow_redirects faraday-multipart @@ -118,17 +136,22 @@ GEM dotenv (= 2.8.1) railties (>= 3.2) e2mmap (0.1.0) - erubi (1.11.0) + ecdsa (1.2.0) + ecdsa_ext (0.5.0) + ecdsa (~> 1.2.0) + erubi (1.12.0) et-orbi (1.2.7) tzinfo + event_emitter (0.2.6) + eventmachine (1.2.7) factory_bot (6.2.1) activesupport (>= 5.0.0) factory_bot_rails (6.2.0) factory_bot (~> 6.2.0) railties (>= 5.0.0) - faker (3.0.0) + faker (3.2.0) i18n (>= 1.8.11, < 2) - faraday (2.7.1) + faraday (2.7.6) faraday-net_http (>= 2.0, < 3.1) ruby2_keywords (>= 0.0.4) faraday-follow_redirects (0.3.0) @@ -136,6 +159,9 @@ GEM faraday-multipart (1.0.4) multipart-post (~> 2) faraday-net_http (3.0.2) + faye-websocket (0.11.2) + eventmachine (>= 0.12.0) + websocket-driver (>= 0.5.1) ffi (1.15.5) flipper (0.28.0) concurrent-ruby (< 2) @@ -148,18 +174,18 @@ GEM rack (>= 1.4, < 3) rack-protection (>= 1.5.3, <= 4.0.0) sanitize (< 7) - fugit (1.7.2) + fugit (1.8.1) et-orbi (~> 1, >= 1.2.7) raabro (~> 1.4) - globalid (1.0.0) + globalid (1.1.0) activesupport (>= 5.0) hashdiff (1.0.1) - i18n (1.12.0) + i18n (1.14.1) concurrent-ruby (~> 1.0) - importmap-rails (1.1.5) + importmap-rails (1.1.6) actionpack (>= 6.0.0) railties (>= 6.0.0) - jaro_winkler (1.5.4) + jaro_winkler (1.5.6) jbuilder (2.11.5) actionview (>= 5.0.0) activesupport (>= 5.0.0) @@ -168,8 +194,8 @@ GEM rexml kramdown-parser-gfm (1.1.0) kramdown (~> 2.0) - launchy (2.5.0) - addressable (~> 2.7) + launchy (2.5.2) + addressable (~> 2.8) letter_opener (1.8.1) launchy (>= 2.2, < 3) letter_opener_web (2.0.0) @@ -177,78 +203,80 @@ GEM letter_opener (~> 1.7) railties (>= 5.2) rexml - listen (3.7.1) + listen (3.8.0) rb-fsevent (~> 0.10, >= 0.10.3) rb-inotify (~> 0.9, >= 0.9.10) - lockbox (1.1.0) - loofah (2.19.0) + lockbox (1.2.0) + loofah (2.21.3) crass (~> 1.0.2) - nokogiri (>= 1.5.9) - mail (2.7.1) + nokogiri (>= 1.12.0) + mail (2.8.1) mini_mime (>= 0.1.1) + net-imap + net-pop + net-smtp marcel (1.0.2) matrix (0.4.2) method_source (1.0.0) mini_mime (1.1.2) - mini_portile2 (2.8.0) - minitest (5.16.3) + minitest (5.18.0) multipart-post (2.3.0) - net-imap (0.3.1) + net-imap (0.3.6) + date net-protocol - net-ldap (0.17.1) + net-ldap (0.18.0) net-pop (0.1.2) net-protocol - net-protocol (0.1.3) + net-protocol (0.2.1) timeout net-smtp (0.3.3) net-protocol - nio4r (2.5.8) - nokogiri (1.13.9) - mini_portile2 (~> 2.8.0) - racc (~> 1.4) - nokogiri (1.13.9-x86_64-linux) + nio4r (2.5.9) + nokogiri (1.15.2-x86_64-linux) racc (~> 1.4) orm_adapter (0.5.0) - pagy (6.0.2) - parallel (1.22.1) - parser (3.2.1.1) + pagy (6.0.4) + parallel (1.23.0) + parser (3.2.2.3) ast (~> 2.4.1) + racc pg (1.2.3) - public_suffix (5.0.0) + public_suffix (5.0.1) puma (4.3.12) nio4r (~> 2.0) raabro (1.4.0) - racc (1.6.0) - rack (2.2.4) + racc (1.7.1) + rack (2.2.7) rack-protection (3.0.6) rack - rack-test (2.0.2) + rack-test (2.1.0) rack (>= 1.3) - rails (7.0.4) - actioncable (= 7.0.4) - actionmailbox (= 7.0.4) - actionmailer (= 7.0.4) - actionpack (= 7.0.4) - actiontext (= 7.0.4) - actionview (= 7.0.4) - activejob (= 7.0.4) - activemodel (= 7.0.4) - activerecord (= 7.0.4) - activestorage (= 7.0.4) - activesupport (= 7.0.4) + rails (7.0.5) + actioncable (= 7.0.5) + actionmailbox (= 7.0.5) + actionmailer (= 7.0.5) + actionpack (= 7.0.5) + actiontext (= 7.0.5) + actionview (= 7.0.5) + activejob (= 7.0.5) + activemodel (= 7.0.5) + activerecord (= 7.0.5) + activestorage (= 7.0.5) + activesupport (= 7.0.5) bundler (>= 1.15.0) - railties (= 7.0.4) + railties (= 7.0.5) rails-dom-testing (2.0.3) activesupport (>= 4.2.0) nokogiri (>= 1.6) - rails-html-sanitizer (1.4.3) - loofah (~> 2.3) + rails-html-sanitizer (1.6.0) + loofah (~> 2.21) + nokogiri (~> 1.14) rails-settings-cached (2.8.3) activerecord (>= 5.0.0) railties (>= 5.0.0) - railties (7.0.4) - actionpack (= 7.0.4) - activesupport (= 7.0.4) + railties (7.0.5) + actionpack (= 7.0.5) + activesupport (= 7.0.5) method_source rake (>= 12.2) thor (~> 1.0) @@ -258,110 +286,106 @@ GEM rb-fsevent (0.11.2) rb-inotify (0.10.1) ffi (~> 1.0) - redis (5.0.5) - redis-client (>= 0.9.0) - redis-client (0.11.2) - connection_pool - regexp_parser (2.6.1) + rbs (2.8.4) + redis (4.8.1) + regexp_parser (2.8.1) responders (3.1.0) actionpack (>= 5.2) railties (>= 5.2) reverse_markdown (2.1.1) nokogiri rexml (3.2.5) - rqrcode (2.1.2) + rqrcode (2.2.0) chunky_png (~> 1.0) rqrcode_core (~> 1.0) rqrcode_core (1.2.0) - rspec-core (3.12.0) + rspec-core (3.12.2) rspec-support (~> 3.12.0) - rspec-expectations (3.12.0) + rspec-expectations (3.12.3) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.12.0) - rspec-mocks (3.12.0) + rspec-mocks (3.12.5) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.12.0) - rspec-rails (6.0.1) + rspec-rails (6.0.3) actionpack (>= 6.1) activesupport (>= 6.1) railties (>= 6.1) - rspec-core (~> 3.11) - rspec-expectations (~> 3.11) - rspec-mocks (~> 3.11) - rspec-support (~> 3.11) + rspec-core (~> 3.12) + rspec-expectations (~> 3.12) + rspec-mocks (~> 3.12) + rspec-support (~> 3.12) rspec-support (3.12.0) - rubocop (1.48.1) + rubocop (1.52.1) json (~> 2.3) parallel (~> 1.10) - parser (>= 3.2.0.0) + parser (>= 3.2.2.3) rainbow (>= 2.2.2, < 4.0) regexp_parser (>= 1.8, < 3.0) rexml (>= 3.2.5, < 4.0) - rubocop-ast (>= 1.26.0, < 2.0) + rubocop-ast (>= 1.28.0, < 2.0) ruby-progressbar (~> 1.7) unicode-display_width (>= 2.4.0, < 3.0) - rubocop-ast (1.28.0) + rubocop-ast (1.29.0) parser (>= 3.2.1.0) ruby-progressbar (1.13.0) ruby2_keywords (0.0.5) - rufus-scheduler (3.8.2) + rufus-scheduler (3.9.1) fugit (~> 1.1, >= 1.1.6) sanitize (6.0.1) crass (~> 1.0.2) nokogiri (>= 1.12.0) - sentry-rails (5.8.0) + sentry-rails (5.9.0) railties (>= 5.0) - sentry-ruby (~> 5.8.0) - sentry-ruby (5.8.0) + sentry-ruby (~> 5.9.0) + sentry-ruby (5.9.0) concurrent-ruby (~> 1.0, >= 1.0.2) - sidekiq (6.5.5) - connection_pool (>= 2.2.2) + sidekiq (6.5.9) + connection_pool (>= 2.2.5, < 3) rack (~> 2.0) - redis (>= 4.5.0) - sidekiq-scheduler (4.0.3) - redis (>= 4.2.0) + redis (>= 4.5.0, < 5) + sidekiq-scheduler (5.0.3) rufus-scheduler (~> 3.2) - sidekiq (>= 4, < 7) + sidekiq (>= 6, < 8) tilt (>= 1.4.0) - solargraph (0.48.0) + solargraph (0.49.0) backport (~> 1.2) benchmark - bundler (>= 1.17.2) + bundler (~> 2.0) diff-lcs (~> 1.4) e2mmap jaro_winkler (~> 1.5) kramdown (~> 2.3) kramdown-parser-gfm (~> 1.1) parser (~> 3.0) - reverse_markdown (>= 1.0.5, < 3) - rubocop (>= 0.52) + rbs (~> 2.0) + reverse_markdown (~> 2.0) + rubocop (~> 1.38) thor (~> 1.0) tilt (~> 2.0) yard (~> 0.9, >= 0.9.24) - sprockets (4.1.1) + sprockets (4.2.0) concurrent-ruby (~> 1.0) - rack (> 1, < 3) + rack (>= 2.2.4, < 4) sprockets-rails (3.4.2) actionpack (>= 5.2) activesupport (>= 5.2) sprockets (>= 3.0.0) - sqlite3 (1.5.4) - mini_portile2 (~> 2.8.0) - sqlite3 (1.5.4-x86_64-linux) + sqlite3 (1.6.3-x86_64-linux) stimulus-rails (1.2.1) railties (>= 6.0.0) - thor (1.2.1) - tilt (2.0.11) - timeout (0.3.0) - turbo-rails (1.3.2) + thor (1.2.2) + tilt (2.2.0) + timeout (0.3.2) + turbo-rails (1.4.0) actionpack (>= 6.0.0) activejob (>= 6.0.0) railties (>= 6.0.0) - tzinfo (2.0.5) + tzinfo (2.0.6) concurrent-ruby (~> 1.0) unicode-display_width (2.4.2) - view_component (2.78.0) - activesupport (>= 5.0.0, < 8.0) + view_component (3.2.0) + activesupport (>= 5.2.0, < 8.0) concurrent-ruby (~> 1.0) method_source (~> 1.0) warden (1.2.9) @@ -375,18 +399,15 @@ GEM addressable (>= 2.8.0) crack (>= 0.3.2) hashdiff (>= 0.4.0, < 2.0.0) - webrick (1.7.0) websocket-driver (0.7.5) websocket-extensions (>= 0.1.0) websocket-extensions (0.1.5) xpath (3.2.0) nokogiri (~> 1.8) - yard (0.9.28) - webrick (~> 1.7.0) - zeitwerk (2.6.6) + yard (0.9.34) + zeitwerk (2.6.8) PLATFORMS - ruby x86_64-linux DEPENDENCIES @@ -411,6 +432,7 @@ DEPENDENCIES listen (~> 3.2) lockbox net-ldap + nostr! pagy (~> 6.0, >= 6.0.2) pg (~> 1.2.3) puma (~> 4.1) diff --git a/app/assets/stylesheets/components/base.css b/app/assets/stylesheets/components/base.css index b4fa491..6824786 100644 --- a/app/assets/stylesheets/components/base.css +++ b/app/assets/stylesheets/components/base.css @@ -24,6 +24,10 @@ @apply text-xl mb-6; } + h4 { + @apply font-bold mb-4 leading-6; + } + main section { @apply pt-8 sm:pt-12; } diff --git a/app/assets/stylesheets/components/buttons.css b/app/assets/stylesheets/components/buttons.css index c50d071..c1269ce 100644 --- a/app/assets/stylesheets/components/buttons.css +++ b/app/assets/stylesheets/components/buttons.css @@ -32,4 +32,9 @@ @apply bg-red-600 hover:bg-red-700 text-white focus:ring-red-500 focus:ring-opacity-75; } + + .btn:disabled { + @apply bg-gray-100 hover:bg-gray-200 text-gray-400 + focus:ring-gray-300 focus:ring-opacity-75; + } } diff --git a/app/assets/stylesheets/components/forms.css b/app/assets/stylesheets/components/forms.css index 46f1df9..5883569 100644 --- a/app/assets/stylesheets/components/forms.css +++ b/app/assets/stylesheets/components/forms.css @@ -15,6 +15,10 @@ @apply border-b-red-600; } + .field_with_errors { + @apply inline-block; + } + .error-msg { @apply text-red-700; } diff --git a/app/controllers/settings_controller.rb b/app/controllers/settings_controller.rb index 622b623..7a42a3e 100644 --- a/app/controllers/settings_controller.rb +++ b/app/controllers/settings_controller.rb @@ -1,3 +1,5 @@ +require 'securerandom' + class SettingsController < ApplicationController before_action :authenticate_user! before_action :set_main_nav_section @@ -9,6 +11,9 @@ class SettingsController < ApplicationController end def show + if @settings_section == "experiments" + session[:shared_secret] ||= SecureRandom.base64(12) + end end def update @@ -53,6 +58,36 @@ class SettingsController < ApplicationController redirect_to check_your_email_path, notice: msg end + 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_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 + flash[:alert] = "Public key could not be verified" + http_status :unprocessable_entity and return + end + + pubkey_taken = User.all_except(current_user).where( + ou: current_user.ou, nostr_pubkey: signed_event[:pubkey] + ).any? + + if pubkey_taken + flash[:alert] = "Public key already in use for a different account" + http_status :unprocessable_entity and return + end + + current_user.update! nostr_pubkey: signed_event[:pubkey] + session[:shared_secret] = nil + + flash[:success] = "Public key verification successful" + http_status :ok + rescue + flash[:alert] = "Public key could not be verified" + http_status :unprocessable_entity and return + end + private def set_main_nav_section @@ -61,7 +96,7 @@ class SettingsController < ApplicationController def set_settings_section @settings_section = params[:section] - allowed_sections = [:profile, :account, :lightning, :xmpp] + allowed_sections = [:profile, :account, :lightning, :xmpp, :experiments] unless allowed_sections.include?(@settings_section.to_sym) redirect_to setting_path(:profile) @@ -82,4 +117,10 @@ class SettingsController < ApplicationController def email_params params.require(:user).permit(:email, :current_password) end + + def nostr_event_params + params.permit(signed_event: [ + :id, :pubkey, :created_at, :kind, :tags, :content, :sig + ]) + end end diff --git a/app/javascript/controllers/settings/nostr_pubkey_controller.js b/app/javascript/controllers/settings/nostr_pubkey_controller.js new file mode 100644 index 0000000..4660326 --- /dev/null +++ b/app/javascript/controllers/settings/nostr_pubkey_controller.js @@ -0,0 +1,63 @@ +import { Controller } from "@hotwired/stimulus" +import { bech32 } from "bech32" + +function hexToBytes (hex) { + let bytes = [] + for (let c = 0; c < hex.length; c += 2) { + bytes.push(parseInt(hex.substr(c, 2), 16)) + } + return bytes +} + +// Connects to data-controller="settings--nostr-pubkey" +export default class extends Controller { + static targets = [ "noExtension", "setPubkey", "pubkeyBech32Input" ] + static values = { userAddress: String, pubkeyHex: String, sharedSecret: String } + + connect () { + if (this.hasPubkeyHexValue && this.pubkeyHexValue.length > 0) { + this.pubkeyBech32InputTarget.value = this.pubkeyBech32 + } + + if (window.nostr) { + this.setPubkeyTarget.disabled = false + } else { + this.noExtensionTarget.classList.remove("hidden") + } + } + + async setPubkey () { + this.setPubkeyTarget.disabled = true + + try { + const signedEvent = await window.nostr.signEvent({ + created_at: Math.floor(Date.now() / 1000), + kind: 1, + tags: [], + content: `Connect my public key to ${this.userAddressValue} (confirmation ${this.sharedSecretValue})` + }) + + const res = await fetch("/settings/set_nostr_pubkey", { + method: "POST", credentials: "include", headers: { + "Accept": "application/json", 'Content-Type': 'application/json', + "X-CSRF-Token": this.csrfToken + }, body: JSON.stringify({ signed_event: signedEvent }) + }); + + window.location.reload() + } catch (error) { + console.warn('Unable to verify pubkey:', error.message) + this.setPubkeyTarget.disabled = false + } + } + + get pubkeyBech32 () { + const words = bech32.toWords(hexToBytes(this.pubkeyHexValue)) + return bech32.encode('npub', words) + } + + get csrfToken () { + const element = document.head.querySelector('meta[name="csrf-token"]') + return element.getAttribute("content") + } +} diff --git a/app/models/user.rb b/app/models/user.rb index 855fd0d..137589a 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -18,7 +18,7 @@ class User < ApplicationRecord has_many :accounts, through: :lndhub_user - validates_uniqueness_of :cn + validates_uniqueness_of :cn, scope: :ou validates_length_of :cn, minimum: 3 validates_format_of :cn, with: /\A([a-z0-9\-])*\z/, if: Proc.new{ |u| u.cn.present? }, @@ -36,8 +36,11 @@ class User < ApplicationRecord validates_length_of :display_name, minimum: 3, maximum: 35, allow_blank: true, if: -> { defined?(@display_name) } - scope :confirmed, -> { where.not(confirmed_at: nil) } - scope :pending, -> { where(confirmed_at: nil) } + validates_uniqueness_of :nostr_pubkey, scope: :ou, allow_blank: true + + scope :confirmed, -> { where.not(confirmed_at: nil) } + scope :pending, -> { where(confirmed_at: nil) } + scope :all_except, -> (user) { where.not(id: user) } has_encrypted :ln_login, :ln_password diff --git a/app/services/nostr_manager/validate_id.rb b/app/services/nostr_manager/validate_id.rb new file mode 100644 index 0000000..d184b75 --- /dev/null +++ b/app/services/nostr_manager/validate_id.rb @@ -0,0 +1,11 @@ +module NostrManager + class ValidateId < NostrManagerService + def initialize(event) + @event = Nostr::Event.new(**event) + end + + def call + @event.id == Digest::SHA256.hexdigest(JSON.generate(@event.serialize)) + end + end +end diff --git a/app/services/nostr_manager/verify_signature.rb b/app/services/nostr_manager/verify_signature.rb new file mode 100644 index 0000000..63b0489 --- /dev/null +++ b/app/services/nostr_manager/verify_signature.rb @@ -0,0 +1,17 @@ +module NostrManager + class VerifySignature < NostrManagerService + def initialize(event) + @event = Nostr::Event.new(**event) + end + + def call + Schnorr.check_sig!( + [@event.id].pack('H*'), + [@event.pubkey].pack('H*'), + [@event.sig].pack('H*') + ) + rescue Schnorr::InvalidSignatureError + false + end + end +end diff --git a/app/services/nostr_manager_service.rb b/app/services/nostr_manager_service.rb new file mode 100644 index 0000000..3376226 --- /dev/null +++ b/app/services/nostr_manager_service.rb @@ -0,0 +1,4 @@ +require "nostr" + +class NostrManagerService < ApplicationService +end diff --git a/app/views/icons/_science.html.erb b/app/views/icons/_science.html.erb new file mode 100644 index 0000000..623f6c0 --- /dev/null +++ b/app/views/icons/_science.html.erb @@ -0,0 +1 @@ + diff --git a/app/views/settings/_experiments.html.erb b/app/views/settings/_experiments.html.erb new file mode 100644 index 0000000..4318db5 --- /dev/null +++ b/app/views/settings/_experiments.html.erb @@ -0,0 +1,62 @@ +
+

Nostr

+

Public Key

+
+ +

"> + +

+ + <% unless current_user.nostr_pubkey.present? %> +

+ If you use any apps on the Nostr network, you can verify your public key + with us in order to enable Nostr-specific features for your account. +

+ <% end %> + + + +

+ +

+
+
diff --git a/app/views/shared/_sidenav_settings.html.erb b/app/views/shared/_sidenav_settings.html.erb index ce2a527..aa30f60 100644 --- a/app/views/shared/_sidenav_settings.html.erb +++ b/app/views/shared/_sidenav_settings.html.erb @@ -18,3 +18,9 @@ active: @settings_section.to_s == "lightning" ) %> <% end %> +<% if Setting.nostr_enabled %> +<%= render SidenavLinkComponent.new( + name: "Experiments", path: setting_path(:experiments), icon: "science", + active: @settings_section.to_s == "experiments" +) %> +<% end %> diff --git a/config/routes.rb b/config/routes.rb index 684f386..546d8ff 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -32,6 +32,7 @@ Rails.application.routes.draw do collection do post 'update_email' post 'reset_password' + post 'set_nostr_pubkey' end end diff --git a/db/schema.rb b/db/schema.rb index 28851e7..82260de 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -50,6 +50,20 @@ ActiveRecord::Schema[7.0].define(version: 2023_05_23_120753) do t.index ["user_id"], name: "index_invitations_on_user_id" end + create_table "remote_storage_authorizations", force: :cascade do |t| + t.integer "user_id", null: false + t.string "token" + t.text "permissions", default: "--- []\n" + t.string "client_id" + t.string "redirect_uri" + t.string "app_name" + t.datetime "expire_at" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["permissions"], name: "index_remote_storage_authorizations_on_permissions" + t.index ["user_id"], name: "index_remote_storage_authorizations_on_user_id" + end + create_table "settings", force: :cascade do |t| t.string "var", null: false t.text "value" @@ -70,15 +84,16 @@ ActiveRecord::Schema[7.0].define(version: 2023_05_23_120753) do t.datetime "confirmed_at", precision: nil t.datetime "confirmation_sent_at", precision: nil t.string "unconfirmed_email" - t.datetime "remember_created_at" - t.string "remember_token" - t.text "preferences" t.text "ln_login_ciphertext" t.text "ln_password_ciphertext" t.string "ln_account" t.string "nostr_pubkey" + t.datetime "remember_created_at" + t.string "remember_token" + t.text "preferences" t.index ["email"], name: "index_users_on_email", unique: true t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true end + add_foreign_key "remote_storage_authorizations", "users" end diff --git a/spec/features/settings/experiments_spec.rb b/spec/features/settings/experiments_spec.rb new file mode 100644 index 0000000..e87bd1f --- /dev/null +++ b/spec/features/settings/experiments_spec.rb @@ -0,0 +1,22 @@ +require 'rails_helper' + +RSpec.describe 'Experimental Settings', type: :feature do + let(:user) { create :user, cn: 'jimmy', ou: 'kosmos.org' } + + before do + login_as user, scope: :user + end + + describe 'Adding a nostr pubkey' do + scenario 'Without nostr browser extension available' do + visit setting_path(:experiments) + expect(page).to have_content("No browser extension found") + expect(page).to have_css('button[data-settings--nostr-pubkey-target=setPubkey]:disabled') + end + + # scenario 'Successfully saving a key' do + # Note: Needs a more complex JS testing setup (maybe poltergeist), not + # worth it for now + # end + end +end diff --git a/spec/requests/settings_spec.rb b/spec/requests/settings_spec.rb new file mode 100644 index 0000000..8bd04ab --- /dev/null +++ b/spec/requests/settings_spec.rb @@ -0,0 +1,104 @@ +require 'rails_helper' + +RSpec.describe "Settings", type: :request do + let(:user) { create :user, cn: 'mark', ou: 'kosmos.org' } + + before do + login_as user, :scope => :user + end + + describe "GET /settings/experiments" do + it "works" do + get setting_path(:experiments) + expect(response).to have_http_status(200) + end + end + + describe "POST /settings/set_nostr_pubkey" do + before do + session_stub = { shared_secret: "rMjWEmvcvtTlQkMd" } + allow_any_instance_of(SettingsController).to receive(:session).and_return(session_stub) + end + + context "With valid data" do + before do + post set_nostr_pubkey_settings_path, params: { + signed_event: { + id: "84f266bbd784551aaa9e35cb0aceb4ee59182a1dab9ab279d9e40dd56ecbbdd3", + pubkey: "07e188a1ff87ce171d517b8ed2bb7a31b1d3453a0db3b15379ec07b724d232f3", + created_at: 1678254161, + kind: 1, + content: "Connect my public key to mark@kosmos.org (confirmation rMjWEmvcvtTlQkMd)", + sig: "96796d420547d6e2c7be5de82a2ce7a48be99aac6415464a6081859ac1a9017305accc0228c630466a57d45ec1c3b456376eb538b76dfdaa2397e3258be02fdd" + } + }.to_json, headers: { + "CONTENT_TYPE" => "application/json", + "HTTP_ACCEPT" => "application/json" + } + end + + it "returns a success status" do + expect(response).to have_http_status(200) + end + + it "saves the pubkey" do + expect(user.nostr_pubkey).to eq("07e188a1ff87ce171d517b8ed2bb7a31b1d3453a0db3b15379ec07b724d232f3") + end + end + + context "With wrong username" do + before do + post set_nostr_pubkey_settings_path, params: { + signed_event: { + id: "2e1e20ee762d6a5b5b30835eda9ca03146e4baf82490e53fd75794c08de08ac0", + pubkey: "07e188a1ff87ce171d517b8ed2bb7a31b1d3453a0db3b15379ec07b724d232f3", + created_at: 1678255391, + kind: 1, + content: "Connect my public key to admin@kosmos.org (confirmation rMjWEmvcvtTlQkMd)", + sig: "2ace19c9db892ac6383848721a3e08b13d90d689fdeac60d9633a623d3f08eb7e0d468f1b3e928d1ea979477c2ec46ee6cdb2d053ef2e4ed3c0630a51d249029" + } + }.to_json, headers: { + "CONTENT_TYPE" => "application/json", + "HTTP_ACCEPT" => "application/json" + } + end + + it "returns a 422 status" do + expect(response).to have_http_status(422) + end + + it "does not save the pubkey" do + expect(user.nostr_pubkey).to be_nil + end + end + + context "With wrong shared secret" do + before do + session_stub = { shared_secret: "ho-chi-minh" } + allow_any_instance_of(SettingsController).to receive(:session).and_return(session_stub) + + post set_nostr_pubkey_settings_path, params: { + signed_event: { + id: "84f266bbd784551aaa9e35cb0aceb4ee59182a1dab9ab279d9e40dd56ecbbdd3", + pubkey: "07e188a1ff87ce171d517b8ed2bb7a31b1d3453a0db3b15379ec07b724d232f3", + created_at: 1678254161, + kind: 1, + content: "Connect my public key to mark@kosmos.org (confirmation rMjWEmvcvtTlQkMd)", + sig: "96796d420547d6e2c7be5de82a2ce7a48be99aac6415464a6081859ac1a9017305accc0228c630466a57d45ec1c3b456376eb538b76dfdaa2397e3258be02fdd" + } + }.to_json, headers: { + "CONTENT_TYPE" => "application/json", + "HTTP_ACCEPT" => "application/json" + } + end + + it "returns a 422 status" do + expect(response).to have_http_status(422) + end + + it "does not save the pubkey" do + expect(user.nostr_pubkey).to be_nil + end + end + end +end diff --git a/vendor/javascript/.keep b/vendor/javascript/.keep new file mode 100644 index 0000000..e69de29 diff --git a/vendor/javascript/bech32.js b/vendor/javascript/bech32.js new file mode 100644 index 0000000..2e6c3ea --- /dev/null +++ b/vendor/javascript/bech32.js @@ -0,0 +1,2 @@ +var e={};Object.defineProperty(e,"__esModule",{value:true});e.bech32m=e.bech32=void 0;const r="qpzry9x8gf2tvdw0s3jn54khce6mua7l";const t={};for(let e=0;e>25;return(33554431&e)<<5^996825010&-(r>>0&1)^642813549&-(r>>1&1)^513874426&-(r>>2&1)^1027748829&-(r>>3&1)^705979059&-(r>>4&1)}function prefixChk(e){let r=1;for(let t=0;t126)return"Invalid prefix ("+e+")";r=polymodStep(r)^o>>5}r=polymodStep(r);for(let t=0;t=t){c-=t;f.push(n>>c&s)}}if(o)c>0&&f.push(n<=r)return"Excess padding";if(n<n)throw new TypeError("Exceeds length limit");e=e.toLowerCase();let c=prefixChk(e);if("string"===typeof c)throw new Error(c);let s=e+"1";for(let e=0;e>5!==0)throw new Error("Non 5-bit word");c=polymodStep(c)^o;s+=r.charAt(o)}for(let e=0;e<6;++e)c=polymodStep(c);c^=o;for(let e=0;e<6;++e){const t=c>>5*(5-e)&31;s+=r.charAt(t)}return s}function __decode(e,r){r=r||90;if(e.length<8)return e+" too short";if(e.length>r)return"Exceeds length limit";const n=e.toLowerCase();const c=e.toUpperCase();if(e!==n&&e!==c)return"Mixed-case string "+e;e=n;const s=e.lastIndexOf("1");if(-1===s)return"No separator character for "+e;if(0===s)return"Missing prefix for "+e;const f=e.slice(0,s);const i=e.slice(s+1);if(i.length<6)return"Data too short";let d=prefixChk(f);if("string"===typeof d)return d;const l=[];for(let e=0;e=i.length||l.push(o)}return d!==o?"Invalid checksum for "+e:{prefix:f,words:l}}function decodeUnsafe(e,r){const t=__decode(e,r);if("object"===typeof t)return t}function decode(e,r){const t=__decode(e,r);if("object"===typeof t)return t;throw new Error(t)}return{decodeUnsafe:decodeUnsafe,decode:decode,encode:encode,toWords:toWords,fromWordsUnsafe:fromWordsUnsafe,fromWords:fromWords}}e.bech32=getLibraryFromEncoding("bech32");e.bech32m=getLibraryFromEncoding("bech32m");const o=e.__esModule,n=e.bech32m,c=e.bech32;export default e;export{o as __esModule,c as bech32,n as bech32m}; + diff --git a/yarn.lock b/yarn.lock index df131d2..e7395a2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4,7 +4,7 @@ "@csstools/postcss-cascade-layers@^1.1.1": version "1.1.1" - resolved "https://registry.yarnpkg.com/@csstools/postcss-cascade-layers/-/postcss-cascade-layers-1.1.1.tgz#8a997edf97d34071dd2e37ea6022447dd9e795ad" + resolved "https://registry.npmjs.org/@csstools/postcss-cascade-layers/-/postcss-cascade-layers-1.1.1.tgz" integrity sha512-+KdYrpKC5TgomQr2DlZF4lDEpHcoxnj5IGddYYfBWJAKfj1JtuHUIqMa+E1pJJ+z3kvDViWMqyqPlG4Ja7amQA== dependencies: "@csstools/selector-specificity" "^2.0.2" @@ -12,7 +12,7 @@ "@csstools/postcss-color-function@^1.1.1": version "1.1.1" - resolved "https://registry.yarnpkg.com/@csstools/postcss-color-function/-/postcss-color-function-1.1.1.tgz#2bd36ab34f82d0497cfacdc9b18d34b5e6f64b6b" + resolved "https://registry.npmjs.org/@csstools/postcss-color-function/-/postcss-color-function-1.1.1.tgz" integrity sha512-Bc0f62WmHdtRDjf5f3e2STwRAl89N2CLb+9iAwzrv4L2hncrbDwnQD9PCq0gtAt7pOI2leIV08HIBUd4jxD8cw== dependencies: "@csstools/postcss-progressive-custom-properties" "^1.1.0" @@ -20,21 +20,21 @@ "@csstools/postcss-font-format-keywords@^1.0.1": version "1.0.1" - resolved "https://registry.yarnpkg.com/@csstools/postcss-font-format-keywords/-/postcss-font-format-keywords-1.0.1.tgz#677b34e9e88ae997a67283311657973150e8b16a" + resolved "https://registry.npmjs.org/@csstools/postcss-font-format-keywords/-/postcss-font-format-keywords-1.0.1.tgz" integrity sha512-ZgrlzuUAjXIOc2JueK0X5sZDjCtgimVp/O5CEqTcs5ShWBa6smhWYbS0x5cVc/+rycTDbjjzoP0KTDnUneZGOg== dependencies: postcss-value-parser "^4.2.0" "@csstools/postcss-hwb-function@^1.0.2": version "1.0.2" - resolved "https://registry.yarnpkg.com/@csstools/postcss-hwb-function/-/postcss-hwb-function-1.0.2.tgz#ab54a9fce0ac102c754854769962f2422ae8aa8b" + resolved "https://registry.npmjs.org/@csstools/postcss-hwb-function/-/postcss-hwb-function-1.0.2.tgz" integrity sha512-YHdEru4o3Rsbjmu6vHy4UKOXZD+Rn2zmkAmLRfPet6+Jz4Ojw8cbWxe1n42VaXQhD3CQUXXTooIy8OkVbUcL+w== dependencies: postcss-value-parser "^4.2.0" "@csstools/postcss-ic-unit@^1.0.1": version "1.0.1" - resolved "https://registry.yarnpkg.com/@csstools/postcss-ic-unit/-/postcss-ic-unit-1.0.1.tgz#28237d812a124d1a16a5acc5c3832b040b303e58" + resolved "https://registry.npmjs.org/@csstools/postcss-ic-unit/-/postcss-ic-unit-1.0.1.tgz" integrity sha512-Ot1rcwRAaRHNKC9tAqoqNZhjdYBzKk1POgWfhN4uCOE47ebGcLRqXjKkApVDpjifL6u2/55ekkpnFcp+s/OZUw== dependencies: "@csstools/postcss-progressive-custom-properties" "^1.1.0" @@ -42,7 +42,7 @@ "@csstools/postcss-is-pseudo-class@^2.0.7": version "2.0.7" - resolved "https://registry.yarnpkg.com/@csstools/postcss-is-pseudo-class/-/postcss-is-pseudo-class-2.0.7.tgz#846ae6c0d5a1eaa878fce352c544f9c295509cd1" + resolved "https://registry.npmjs.org/@csstools/postcss-is-pseudo-class/-/postcss-is-pseudo-class-2.0.7.tgz" integrity sha512-7JPeVVZHd+jxYdULl87lvjgvWldYu+Bc62s9vD/ED6/QTGjy0jy0US/f6BG53sVMTBJ1lzKZFpYmofBN9eaRiA== dependencies: "@csstools/selector-specificity" "^2.0.0" @@ -50,21 +50,21 @@ "@csstools/postcss-nested-calc@^1.0.0": version "1.0.0" - resolved "https://registry.yarnpkg.com/@csstools/postcss-nested-calc/-/postcss-nested-calc-1.0.0.tgz#d7e9d1d0d3d15cf5ac891b16028af2a1044d0c26" + resolved "https://registry.npmjs.org/@csstools/postcss-nested-calc/-/postcss-nested-calc-1.0.0.tgz" integrity sha512-JCsQsw1wjYwv1bJmgjKSoZNvf7R6+wuHDAbi5f/7MbFhl2d/+v+TvBTU4BJH3G1X1H87dHl0mh6TfYogbT/dJQ== dependencies: postcss-value-parser "^4.2.0" "@csstools/postcss-normalize-display-values@^1.0.1": version "1.0.1" - resolved "https://registry.yarnpkg.com/@csstools/postcss-normalize-display-values/-/postcss-normalize-display-values-1.0.1.tgz#15da54a36e867b3ac5163ee12c1d7f82d4d612c3" + resolved "https://registry.npmjs.org/@csstools/postcss-normalize-display-values/-/postcss-normalize-display-values-1.0.1.tgz" integrity sha512-jcOanIbv55OFKQ3sYeFD/T0Ti7AMXc9nM1hZWu8m/2722gOTxFg7xYu4RDLJLeZmPUVQlGzo4jhzvTUq3x4ZUw== dependencies: postcss-value-parser "^4.2.0" "@csstools/postcss-oklab-function@^1.1.1": version "1.1.1" - resolved "https://registry.yarnpkg.com/@csstools/postcss-oklab-function/-/postcss-oklab-function-1.1.1.tgz#88cee0fbc8d6df27079ebd2fa016ee261eecf844" + resolved "https://registry.npmjs.org/@csstools/postcss-oklab-function/-/postcss-oklab-function-1.1.1.tgz" integrity sha512-nJpJgsdA3dA9y5pgyb/UfEzE7W5Ka7u0CX0/HIMVBNWzWemdcTH3XwANECU6anWv/ao4vVNLTMxhiPNZsTK6iA== dependencies: "@csstools/postcss-progressive-custom-properties" "^1.1.0" @@ -72,40 +72,40 @@ "@csstools/postcss-progressive-custom-properties@^1.1.0", "@csstools/postcss-progressive-custom-properties@^1.3.0": version "1.3.0" - resolved "https://registry.yarnpkg.com/@csstools/postcss-progressive-custom-properties/-/postcss-progressive-custom-properties-1.3.0.tgz#542292558384361776b45c85226b9a3a34f276fa" + resolved "https://registry.npmjs.org/@csstools/postcss-progressive-custom-properties/-/postcss-progressive-custom-properties-1.3.0.tgz" integrity sha512-ASA9W1aIy5ygskZYuWams4BzafD12ULvSypmaLJT2jvQ8G0M3I8PRQhC0h7mG0Z3LI05+agZjqSR9+K9yaQQjA== dependencies: postcss-value-parser "^4.2.0" "@csstools/postcss-stepped-value-functions@^1.0.1": version "1.0.1" - resolved "https://registry.yarnpkg.com/@csstools/postcss-stepped-value-functions/-/postcss-stepped-value-functions-1.0.1.tgz#f8772c3681cc2befed695e2b0b1d68e22f08c4f4" + resolved "https://registry.npmjs.org/@csstools/postcss-stepped-value-functions/-/postcss-stepped-value-functions-1.0.1.tgz" integrity sha512-dz0LNoo3ijpTOQqEJLY8nyaapl6umbmDcgj4AD0lgVQ572b2eqA1iGZYTTWhrcrHztWDDRAX2DGYyw2VBjvCvQ== dependencies: postcss-value-parser "^4.2.0" "@csstools/postcss-text-decoration-shorthand@^1.0.0": version "1.0.0" - resolved "https://registry.yarnpkg.com/@csstools/postcss-text-decoration-shorthand/-/postcss-text-decoration-shorthand-1.0.0.tgz#ea96cfbc87d921eca914d3ad29340d9bcc4c953f" + resolved "https://registry.npmjs.org/@csstools/postcss-text-decoration-shorthand/-/postcss-text-decoration-shorthand-1.0.0.tgz" integrity sha512-c1XwKJ2eMIWrzQenN0XbcfzckOLLJiczqy+YvfGmzoVXd7pT9FfObiSEfzs84bpE/VqfpEuAZ9tCRbZkZxxbdw== dependencies: postcss-value-parser "^4.2.0" "@csstools/postcss-trigonometric-functions@^1.0.2": version "1.0.2" - resolved "https://registry.yarnpkg.com/@csstools/postcss-trigonometric-functions/-/postcss-trigonometric-functions-1.0.2.tgz#94d3e4774c36d35dcdc88ce091336cb770d32756" + resolved "https://registry.npmjs.org/@csstools/postcss-trigonometric-functions/-/postcss-trigonometric-functions-1.0.2.tgz" integrity sha512-woKaLO///4bb+zZC2s80l+7cm07M7268MsyG3M0ActXXEFi6SuhvriQYcb58iiKGbjwwIU7n45iRLEHypB47Og== dependencies: postcss-value-parser "^4.2.0" "@csstools/postcss-unset-value@^1.0.2": version "1.0.2" - resolved "https://registry.yarnpkg.com/@csstools/postcss-unset-value/-/postcss-unset-value-1.0.2.tgz#c99bb70e2cdc7312948d1eb41df2412330b81f77" + resolved "https://registry.npmjs.org/@csstools/postcss-unset-value/-/postcss-unset-value-1.0.2.tgz" integrity sha512-c8J4roPBILnelAsdLr4XOAR/GsTm0GJi4XpcfvoWk3U6KiTCqiFYc63KhRMQQX35jYMp4Ao8Ij9+IZRgMfJp1g== "@csstools/selector-specificity@^2.0.0", "@csstools/selector-specificity@^2.0.2": version "2.0.2" - resolved "https://registry.yarnpkg.com/@csstools/selector-specificity/-/selector-specificity-2.0.2.tgz#1bfafe4b7ed0f3e4105837e056e0a89b108ebe36" + resolved "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-2.0.2.tgz" integrity sha512-IkpVW/ehM1hWKln4fCA3NzJU8KwD+kIOvPZA4cqxoJHtE21CCzjyp+Kxbu0i5I4tBNOlXPL9mjwnWlL0VEG4Fg== "@nodelib/fs.scandir@2.1.5": @@ -131,14 +131,14 @@ "@tailwindcss/forms@^0.5.3": version "0.5.3" - resolved "https://registry.yarnpkg.com/@tailwindcss/forms/-/forms-0.5.3.tgz#e4d7989686cbcaf416c53f1523df5225332a86e7" + resolved "https://registry.npmjs.org/@tailwindcss/forms/-/forms-0.5.3.tgz" integrity sha512-y5mb86JUoiUgBjY/o6FJSFZSEttfb3Q5gllE4xoKjAAD+vBrnIhE4dViwUuow3va8mpH4s9jyUbUbrRGoRdc2Q== dependencies: mini-svg-data-uri "^1.2.3" acorn-node@^1.8.2: version "1.8.2" - resolved "https://registry.yarnpkg.com/acorn-node/-/acorn-node-1.8.2.tgz#114c95d64539e53dede23de8b9d96df7c7ae2af8" + resolved "https://registry.npmjs.org/acorn-node/-/acorn-node-1.8.2.tgz" integrity sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A== dependencies: acorn "^7.0.0" @@ -165,12 +165,12 @@ anymatch@~3.1.2: arg@^5.0.2: version "5.0.2" - resolved "https://registry.yarnpkg.com/arg/-/arg-5.0.2.tgz#c81433cc427c92c4dcf4865142dbca6f15acd59c" + resolved "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz" integrity sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg== autoprefixer@^10.4.13: version "10.4.13" - resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.13.tgz#b5136b59930209a321e9fa3dca2e7c4d223e83a8" + resolved "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.13.tgz" integrity sha512-49vKpMqcZYsJjwotvt4+h/BCjJVnhGwcLpDt5xkcaOG3eLrG/HUYLagrihYsQ+qrIBgIzX1Rw7a6L8I/ZA1Atg== dependencies: browserslist "^4.21.4" @@ -194,7 +194,7 @@ braces@^3.0.1, braces@^3.0.2, braces@~3.0.2: browserslist@^4.21.4: version "4.21.4" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.4.tgz#e7496bbc67b9e39dd0f98565feccdcb0d4ff6987" + resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz" integrity sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw== dependencies: caniuse-lite "^1.0.30001400" @@ -209,12 +209,12 @@ camelcase-css@^2.0.1: caniuse-lite@^1.0.30001400, caniuse-lite@^1.0.30001426: version "1.0.30001435" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001435.tgz#502c93dbd2f493bee73a408fe98e98fb1dad10b2" + resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001435.tgz" integrity sha512-kdCkUTjR+v4YAJelyiDTqiu82BDr4W4CP5sgTA0ZBmqn30XfS2ZghPLMowik9TPhS+psWJiUNxsqLyurDbmutA== chokidar@^3.5.3: version "3.5.3" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" + resolved "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz" integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== dependencies: anymatch "~3.1.2" @@ -234,26 +234,26 @@ color-name@^1.1.4: css-blank-pseudo@^3.0.3: version "3.0.3" - resolved "https://registry.yarnpkg.com/css-blank-pseudo/-/css-blank-pseudo-3.0.3.tgz#36523b01c12a25d812df343a32c322d2a2324561" + resolved "https://registry.npmjs.org/css-blank-pseudo/-/css-blank-pseudo-3.0.3.tgz" integrity sha512-VS90XWtsHGqoM0t4KpH053c4ehxZ2E6HtGI7x68YFV0pTo/QmkV/YFA+NnlvK8guxZVNWGQhVNJGC39Q8XF4OQ== dependencies: postcss-selector-parser "^6.0.9" css-has-pseudo@^3.0.4: version "3.0.4" - resolved "https://registry.yarnpkg.com/css-has-pseudo/-/css-has-pseudo-3.0.4.tgz#57f6be91ca242d5c9020ee3e51bbb5b89fc7af73" + resolved "https://registry.npmjs.org/css-has-pseudo/-/css-has-pseudo-3.0.4.tgz" integrity sha512-Vse0xpR1K9MNlp2j5w1pgWIJtm1a8qS0JwS9goFYcImjlHEmywP9VUF05aGBXzGpDJF86QXk4L0ypBmwPhGArw== dependencies: postcss-selector-parser "^6.0.9" css-prefers-color-scheme@^6.0.3: version "6.0.3" - resolved "https://registry.yarnpkg.com/css-prefers-color-scheme/-/css-prefers-color-scheme-6.0.3.tgz#ca8a22e5992c10a5b9d315155e7caee625903349" + resolved "https://registry.npmjs.org/css-prefers-color-scheme/-/css-prefers-color-scheme-6.0.3.tgz" integrity sha512-4BqMbZksRkJQx2zAjrokiGMd07RqOa2IxIrrN10lyBe9xhn9DEvjUK79J6jkeiv9D9hQFXKb6g1jwU62jziJZA== cssdb@^7.1.0: version "7.2.0" - resolved "https://registry.yarnpkg.com/cssdb/-/cssdb-7.2.0.tgz#f44bd4abc430f0ff7f4c64b8a1fb857a753f77a8" + resolved "https://registry.npmjs.org/cssdb/-/cssdb-7.2.0.tgz" integrity sha512-JYlIsE7eKHSi0UNuCyo96YuIDFqvhGgHw4Ck6lsN+DP0Tp8M64UTDT2trGbkMDqnCoEjks7CkS0XcjU0rkvBdg== cssesc@^3.0.0: @@ -268,7 +268,7 @@ defined@^1.0.0: detective@^5.2.1: version "5.2.1" - resolved "https://registry.yarnpkg.com/detective/-/detective-5.2.1.tgz#6af01eeda11015acb0e73f933242b70f24f91034" + resolved "https://registry.npmjs.org/detective/-/detective-5.2.1.tgz" integrity sha512-v9XE1zRnz1wRtgurGu0Bs8uHKFSTdteYZNbIPFVhUZ39L/S79ppMpdmVOZAnoz1jfEFodc48n6MX483Xo3t1yw== dependencies: acorn-node "^1.8.2" @@ -287,7 +287,7 @@ dlv@^1.1.3: electron-to-chromium@^1.4.251: version "1.4.284" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz#61046d1e4cab3a25238f6bf7413795270f125592" + resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz" integrity sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA== escalade@^3.1.1: @@ -297,7 +297,7 @@ escalade@^3.1.1: fast-glob@^3.2.12: version "3.2.12" - resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.12.tgz#7f39ec99c2e6ab030337142da9e0c18f37afae80" + resolved "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz" integrity sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w== dependencies: "@nodelib/fs.stat" "^2.0.2" @@ -322,7 +322,7 @@ fill-range@^7.0.1: fraction.js@^4.2.0: version "4.2.0" - resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.2.0.tgz#448e5109a313a3527f5a3ab2119ec4cf0e0e2950" + resolved "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz" integrity sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA== fsevents@~2.3.2: @@ -372,7 +372,7 @@ is-core-module@^2.8.1: is-core-module@^2.9.0: version "2.11.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.11.0.tgz#ad4cb3e3863e814523c96f3f58d26cc570ff0144" + resolved "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz" integrity sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw== dependencies: has "^1.0.3" @@ -396,7 +396,7 @@ is-number@^7.0.0: lilconfig@^2.0.5, lilconfig@^2.0.6: version "2.0.6" - resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.0.6.tgz#32a384558bd58af3d4c6e077dd1ad1d397bc69d4" + resolved "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.6.tgz" integrity sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg== merge2@^1.3.0: @@ -414,7 +414,7 @@ micromatch@^4.0.4: micromatch@^4.0.5: version "4.0.5" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" + resolved "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz" integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== dependencies: braces "^3.0.2" @@ -427,17 +427,17 @@ mini-svg-data-uri@^1.2.3: minimist@^1.2.6: version "1.2.7" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.7.tgz#daa1c4d91f507390437c6a8bc01078e7000c4d18" + resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz" integrity sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g== nanoid@^3.3.4: version "3.3.4" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.4.tgz#730b67e3cd09e2deacf03c027c81c9d9dbc5e8ab" + resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz" integrity sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw== node-releases@^2.0.6: version "2.0.6" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.6.tgz#8a7088c63a55e493845683ebf3c828d8c51c5503" + resolved "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz" integrity sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg== normalize-path@^3.0.0, normalize-path@~3.0.0: @@ -452,7 +452,7 @@ normalize-range@^0.1.2: object-hash@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-3.0.0.tgz#73f97f753e7baffc0e2cc9d6e079079744ac82e9" + resolved "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz" integrity sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw== path-parse@^1.0.7: @@ -477,70 +477,70 @@ pify@^2.3.0: postcss-attribute-case-insensitive@^5.0.2: version "5.0.2" - resolved "https://registry.yarnpkg.com/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-5.0.2.tgz#03d761b24afc04c09e757e92ff53716ae8ea2741" + resolved "https://registry.npmjs.org/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-5.0.2.tgz" integrity sha512-XIidXV8fDr0kKt28vqki84fRK8VW8eTuIa4PChv2MqKuT6C9UjmSKzen6KaWhWEoYvwxFCa7n/tC1SZ3tyq4SQ== dependencies: postcss-selector-parser "^6.0.10" postcss-clamp@^4.1.0: version "4.1.0" - resolved "https://registry.yarnpkg.com/postcss-clamp/-/postcss-clamp-4.1.0.tgz#7263e95abadd8c2ba1bd911b0b5a5c9c93e02363" + resolved "https://registry.npmjs.org/postcss-clamp/-/postcss-clamp-4.1.0.tgz" integrity sha512-ry4b1Llo/9zz+PKC+030KUnPITTJAHeOwjfAyyB60eT0AorGLdzp52s31OsPRHRf8NchkgFoG2y6fCfn1IV1Ow== dependencies: postcss-value-parser "^4.2.0" postcss-color-functional-notation@^4.2.4: version "4.2.4" - resolved "https://registry.yarnpkg.com/postcss-color-functional-notation/-/postcss-color-functional-notation-4.2.4.tgz#21a909e8d7454d3612d1659e471ce4696f28caec" + resolved "https://registry.npmjs.org/postcss-color-functional-notation/-/postcss-color-functional-notation-4.2.4.tgz" integrity sha512-2yrTAUZUab9s6CpxkxC4rVgFEVaR6/2Pipvi6qcgvnYiVqZcbDHEoBDhrXzyb7Efh2CCfHQNtcqWcIruDTIUeg== dependencies: postcss-value-parser "^4.2.0" postcss-color-hex-alpha@^8.0.4: version "8.0.4" - resolved "https://registry.yarnpkg.com/postcss-color-hex-alpha/-/postcss-color-hex-alpha-8.0.4.tgz#c66e2980f2fbc1a63f5b079663340ce8b55f25a5" + resolved "https://registry.npmjs.org/postcss-color-hex-alpha/-/postcss-color-hex-alpha-8.0.4.tgz" integrity sha512-nLo2DCRC9eE4w2JmuKgVA3fGL3d01kGq752pVALF68qpGLmx2Qrk91QTKkdUqqp45T1K1XV8IhQpcu1hoAQflQ== dependencies: postcss-value-parser "^4.2.0" postcss-color-rebeccapurple@^7.1.1: version "7.1.1" - resolved "https://registry.yarnpkg.com/postcss-color-rebeccapurple/-/postcss-color-rebeccapurple-7.1.1.tgz#63fdab91d878ebc4dd4b7c02619a0c3d6a56ced0" + resolved "https://registry.npmjs.org/postcss-color-rebeccapurple/-/postcss-color-rebeccapurple-7.1.1.tgz" integrity sha512-pGxkuVEInwLHgkNxUc4sdg4g3py7zUeCQ9sMfwyHAT+Ezk8a4OaaVZ8lIY5+oNqA/BXXgLyXv0+5wHP68R79hg== dependencies: postcss-value-parser "^4.2.0" postcss-custom-media@^8.0.2: version "8.0.2" - resolved "https://registry.yarnpkg.com/postcss-custom-media/-/postcss-custom-media-8.0.2.tgz#c8f9637edf45fef761b014c024cee013f80529ea" + resolved "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-8.0.2.tgz" integrity sha512-7yi25vDAoHAkbhAzX9dHx2yc6ntS4jQvejrNcC+csQJAXjj15e7VcWfMgLqBNAbOvqi5uIa9huOVwdHbf+sKqg== dependencies: postcss-value-parser "^4.2.0" postcss-custom-properties@^12.1.10: version "12.1.11" - resolved "https://registry.yarnpkg.com/postcss-custom-properties/-/postcss-custom-properties-12.1.11.tgz#d14bb9b3989ac4d40aaa0e110b43be67ac7845cf" + resolved "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-12.1.11.tgz" integrity sha512-0IDJYhgU8xDv1KY6+VgUwuQkVtmYzRwu+dMjnmdMafXYv86SWqfxkc7qdDvWS38vsjaEtv8e0vGOUQrAiMBLpQ== dependencies: postcss-value-parser "^4.2.0" postcss-custom-selectors@^6.0.3: version "6.0.3" - resolved "https://registry.yarnpkg.com/postcss-custom-selectors/-/postcss-custom-selectors-6.0.3.tgz#1ab4684d65f30fed175520f82d223db0337239d9" + resolved "https://registry.npmjs.org/postcss-custom-selectors/-/postcss-custom-selectors-6.0.3.tgz" integrity sha512-fgVkmyiWDwmD3JbpCmB45SvvlCD6z9CG6Ie6Iere22W5aHea6oWa7EM2bpnv2Fj3I94L3VbtvX9KqwSi5aFzSg== dependencies: postcss-selector-parser "^6.0.4" postcss-dir-pseudo-class@^6.0.5: version "6.0.5" - resolved "https://registry.yarnpkg.com/postcss-dir-pseudo-class/-/postcss-dir-pseudo-class-6.0.5.tgz#2bf31de5de76added44e0a25ecf60ae9f7c7c26c" + resolved "https://registry.npmjs.org/postcss-dir-pseudo-class/-/postcss-dir-pseudo-class-6.0.5.tgz" integrity sha512-eqn4m70P031PF7ZQIvSgy9RSJ5uI2171O/OO/zcRNYpJbvaeKFUlar1aJ7rmgiQtbm0FSPsRewjpdS0Oew7MPA== dependencies: postcss-selector-parser "^6.0.10" postcss-double-position-gradients@^3.1.2: version "3.1.2" - resolved "https://registry.yarnpkg.com/postcss-double-position-gradients/-/postcss-double-position-gradients-3.1.2.tgz#b96318fdb477be95997e86edd29c6e3557a49b91" + resolved "https://registry.npmjs.org/postcss-double-position-gradients/-/postcss-double-position-gradients-3.1.2.tgz" integrity sha512-GX+FuE/uBR6eskOK+4vkXgT6pDkexLokPaz/AbJna9s5Kzp/yl488pKPjhy0obB475ovfT1Wv8ho7U/cHNaRgQ== dependencies: "@csstools/postcss-progressive-custom-properties" "^1.1.0" @@ -548,50 +548,50 @@ postcss-double-position-gradients@^3.1.2: postcss-env-function@^4.0.6: version "4.0.6" - resolved "https://registry.yarnpkg.com/postcss-env-function/-/postcss-env-function-4.0.6.tgz#7b2d24c812f540ed6eda4c81f6090416722a8e7a" + resolved "https://registry.npmjs.org/postcss-env-function/-/postcss-env-function-4.0.6.tgz" integrity sha512-kpA6FsLra+NqcFnL81TnsU+Z7orGtDTxcOhl6pwXeEq1yFPpRMkCDpHhrz8CFQDr/Wfm0jLiNQ1OsGGPjlqPwA== dependencies: postcss-value-parser "^4.2.0" postcss-flexbugs-fixes@^5.0.2: version "5.0.2" - resolved "https://registry.yarnpkg.com/postcss-flexbugs-fixes/-/postcss-flexbugs-fixes-5.0.2.tgz#2028e145313074fc9abe276cb7ca14e5401eb49d" + resolved "https://registry.npmjs.org/postcss-flexbugs-fixes/-/postcss-flexbugs-fixes-5.0.2.tgz" integrity sha512-18f9voByak7bTktR2QgDveglpn9DTbBWPUzSOe9g0N4WR/2eSt6Vrcbf0hmspvMI6YWGywz6B9f7jzpFNJJgnQ== postcss-focus-visible@^6.0.4: version "6.0.4" - resolved "https://registry.yarnpkg.com/postcss-focus-visible/-/postcss-focus-visible-6.0.4.tgz#50c9ea9afa0ee657fb75635fabad25e18d76bf9e" + resolved "https://registry.npmjs.org/postcss-focus-visible/-/postcss-focus-visible-6.0.4.tgz" integrity sha512-QcKuUU/dgNsstIK6HELFRT5Y3lbrMLEOwG+A4s5cA+fx3A3y/JTq3X9LaOj3OC3ALH0XqyrgQIgey/MIZ8Wczw== dependencies: postcss-selector-parser "^6.0.9" postcss-focus-within@^5.0.4: version "5.0.4" - resolved "https://registry.yarnpkg.com/postcss-focus-within/-/postcss-focus-within-5.0.4.tgz#5b1d2ec603195f3344b716c0b75f61e44e8d2e20" + resolved "https://registry.npmjs.org/postcss-focus-within/-/postcss-focus-within-5.0.4.tgz" integrity sha512-vvjDN++C0mu8jz4af5d52CB184ogg/sSxAFS+oUJQq2SuCe7T5U2iIsVJtsCp2d6R4j0jr5+q3rPkBVZkXD9fQ== dependencies: postcss-selector-parser "^6.0.9" postcss-font-variant@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/postcss-font-variant/-/postcss-font-variant-5.0.0.tgz#efd59b4b7ea8bb06127f2d031bfbb7f24d32fa66" + resolved "https://registry.npmjs.org/postcss-font-variant/-/postcss-font-variant-5.0.0.tgz" integrity sha512-1fmkBaCALD72CK2a9i468mA/+tr9/1cBxRRMXOUaZqO43oWPR5imcyPjXwuv7PXbCid4ndlP5zWhidQVVa3hmA== postcss-gap-properties@^3.0.5: version "3.0.5" - resolved "https://registry.yarnpkg.com/postcss-gap-properties/-/postcss-gap-properties-3.0.5.tgz#f7e3cddcf73ee19e94ccf7cb77773f9560aa2fff" + resolved "https://registry.npmjs.org/postcss-gap-properties/-/postcss-gap-properties-3.0.5.tgz" integrity sha512-IuE6gKSdoUNcvkGIqdtjtcMtZIFyXZhmFd5RUlg97iVEvp1BZKV5ngsAjCjrVy+14uhGBQl9tzmi1Qwq4kqVOg== postcss-image-set-function@^4.0.7: version "4.0.7" - resolved "https://registry.yarnpkg.com/postcss-image-set-function/-/postcss-image-set-function-4.0.7.tgz#08353bd756f1cbfb3b6e93182c7829879114481f" + resolved "https://registry.npmjs.org/postcss-image-set-function/-/postcss-image-set-function-4.0.7.tgz" integrity sha512-9T2r9rsvYzm5ndsBE8WgtrMlIT7VbtTfE7b3BQnudUqnBcBo7L758oc+o+pdj/dUV0l5wjwSdjeOH2DZtfv8qw== dependencies: postcss-value-parser "^4.2.0" postcss-import@^14.1.0: version "14.1.0" - resolved "https://registry.yarnpkg.com/postcss-import/-/postcss-import-14.1.0.tgz#a7333ffe32f0b8795303ee9e40215dac922781f0" + resolved "https://registry.npmjs.org/postcss-import/-/postcss-import-14.1.0.tgz" integrity sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw== dependencies: postcss-value-parser "^4.0.0" @@ -600,7 +600,7 @@ postcss-import@^14.1.0: postcss-import@^15.0.1: version "15.0.1" - resolved "https://registry.yarnpkg.com/postcss-import/-/postcss-import-15.0.1.tgz#5887da24440ef259324d65e08343437a43ff92b1" + resolved "https://registry.npmjs.org/postcss-import/-/postcss-import-15.0.1.tgz" integrity sha512-UGlvk8EgT7Gm/Ndf9xZHnzr8xm8P54N8CBWLtcY5alP+YxlEge/Rv78etQyevZs3qWTE9If13+Bo6zATBrPOpA== dependencies: postcss-value-parser "^4.0.0" @@ -609,7 +609,7 @@ postcss-import@^15.0.1: postcss-initial@^4.0.1: version "4.0.1" - resolved "https://registry.yarnpkg.com/postcss-initial/-/postcss-initial-4.0.1.tgz#529f735f72c5724a0fb30527df6fb7ac54d7de42" + resolved "https://registry.npmjs.org/postcss-initial/-/postcss-initial-4.0.1.tgz" integrity sha512-0ueD7rPqX8Pn1xJIjay0AZeIuDoF+V+VvMt/uOnn+4ezUKhZM/NokDeP6DwMNyIoYByuN/94IQnt5FEkaN59xQ== postcss-js@^4.0.0: @@ -621,7 +621,7 @@ postcss-js@^4.0.0: postcss-lab-function@^4.2.1: version "4.2.1" - resolved "https://registry.yarnpkg.com/postcss-lab-function/-/postcss-lab-function-4.2.1.tgz#6fe4c015102ff7cd27d1bd5385582f67ebdbdc98" + resolved "https://registry.npmjs.org/postcss-lab-function/-/postcss-lab-function-4.2.1.tgz" integrity sha512-xuXll4isR03CrQsmxyz92LJB2xX9n+pZJ5jE9JgcnmsCammLyKdlzrBin+25dy6wIjfhJpKBAN80gsTlCgRk2w== dependencies: "@csstools/postcss-progressive-custom-properties" "^1.1.0" @@ -629,7 +629,7 @@ postcss-lab-function@^4.2.1: postcss-load-config@^3.1.4: version "3.1.4" - resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-3.1.4.tgz#1ab2571faf84bb078877e1d07905eabe9ebda855" + resolved "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.4.tgz" integrity sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg== dependencies: lilconfig "^2.0.5" @@ -637,24 +637,24 @@ postcss-load-config@^3.1.4: postcss-logical@^5.0.4: version "5.0.4" - resolved "https://registry.yarnpkg.com/postcss-logical/-/postcss-logical-5.0.4.tgz#ec75b1ee54421acc04d5921576b7d8db6b0e6f73" + resolved "https://registry.npmjs.org/postcss-logical/-/postcss-logical-5.0.4.tgz" integrity sha512-RHXxplCeLh9VjinvMrZONq7im4wjWGlRJAqmAVLXyZaXwfDWP73/oq4NdIp+OZwhQUMj0zjqDfM5Fj7qby+B4g== postcss-media-minmax@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/postcss-media-minmax/-/postcss-media-minmax-5.0.0.tgz#7140bddec173e2d6d657edbd8554a55794e2a5b5" + resolved "https://registry.npmjs.org/postcss-media-minmax/-/postcss-media-minmax-5.0.0.tgz" integrity sha512-yDUvFf9QdFZTuCUg0g0uNSHVlJ5X1lSzDZjPSFaiCWvjgsvu8vEVxtahPrLMinIDEEGnx6cBe6iqdx5YWz08wQ== postcss-nested@6.0.0, postcss-nested@^6.0.0: version "6.0.0" - resolved "https://registry.yarnpkg.com/postcss-nested/-/postcss-nested-6.0.0.tgz#1572f1984736578f360cffc7eb7dca69e30d1735" + resolved "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.0.tgz" integrity sha512-0DkamqrPcmkBDsLn+vQDIrtkSbNkv5AD/M322ySo9kqFkCIYklym2xEmWkwo+Y3/qZo34tzEPNUw4y7yMCdv5w== dependencies: postcss-selector-parser "^6.0.10" postcss-nesting@^10.2.0: version "10.2.0" - resolved "https://registry.yarnpkg.com/postcss-nesting/-/postcss-nesting-10.2.0.tgz#0b12ce0db8edfd2d8ae0aaf86427370b898890be" + resolved "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-10.2.0.tgz" integrity sha512-EwMkYchxiDiKUhlJGzWsD9b2zvq/r2SSubcRrgP+jujMXFzqvANLt16lJANC+5uZ6hjI7lpRmI6O8JIl+8l1KA== dependencies: "@csstools/selector-specificity" "^2.0.0" @@ -662,31 +662,31 @@ postcss-nesting@^10.2.0: postcss-opacity-percentage@^1.1.2: version "1.1.2" - resolved "https://registry.yarnpkg.com/postcss-opacity-percentage/-/postcss-opacity-percentage-1.1.2.tgz#bd698bb3670a0a27f6d657cc16744b3ebf3b1145" + resolved "https://registry.npmjs.org/postcss-opacity-percentage/-/postcss-opacity-percentage-1.1.2.tgz" integrity sha512-lyUfF7miG+yewZ8EAk9XUBIlrHyUE6fijnesuz+Mj5zrIHIEw6KcIZSOk/elVMqzLvREmXB83Zi/5QpNRYd47w== postcss-overflow-shorthand@^3.0.4: version "3.0.4" - resolved "https://registry.yarnpkg.com/postcss-overflow-shorthand/-/postcss-overflow-shorthand-3.0.4.tgz#7ed6486fec44b76f0eab15aa4866cda5d55d893e" + resolved "https://registry.npmjs.org/postcss-overflow-shorthand/-/postcss-overflow-shorthand-3.0.4.tgz" integrity sha512-otYl/ylHK8Y9bcBnPLo3foYFLL6a6Ak+3EQBPOTR7luMYCOsiVTUk1iLvNf6tVPNGXcoL9Hoz37kpfriRIFb4A== dependencies: postcss-value-parser "^4.2.0" postcss-page-break@^3.0.4: version "3.0.4" - resolved "https://registry.yarnpkg.com/postcss-page-break/-/postcss-page-break-3.0.4.tgz#7fbf741c233621622b68d435babfb70dd8c1ee5f" + resolved "https://registry.npmjs.org/postcss-page-break/-/postcss-page-break-3.0.4.tgz" integrity sha512-1JGu8oCjVXLa9q9rFTo4MbeeA5FMe00/9C7lN4va606Rdb+HkxXtXsmEDrIraQ11fGz/WvKWa8gMuCKkrXpTsQ== postcss-place@^7.0.5: version "7.0.5" - resolved "https://registry.yarnpkg.com/postcss-place/-/postcss-place-7.0.5.tgz#95dbf85fd9656a3a6e60e832b5809914236986c4" + resolved "https://registry.npmjs.org/postcss-place/-/postcss-place-7.0.5.tgz" integrity sha512-wR8igaZROA6Z4pv0d+bvVrvGY4GVHihBCBQieXFY3kuSuMyOmEnnfFzHl/tQuqHZkfkIVBEbDvYcFfHmpSet9g== dependencies: postcss-value-parser "^4.2.0" postcss-preset-env@^7.8.3: version "7.8.3" - resolved "https://registry.yarnpkg.com/postcss-preset-env/-/postcss-preset-env-7.8.3.tgz#2a50f5e612c3149cc7af75634e202a5b2ad4f1e2" + resolved "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-7.8.3.tgz" integrity sha512-T1LgRm5uEVFSEF83vHZJV2z19lHg4yJuZ6gXZZkqVsqv63nlr6zabMH3l4Pc01FQCyfWVrh2GaUeCVy9Po+Aag== dependencies: "@csstools/postcss-cascade-layers" "^1.1.1" @@ -741,26 +741,26 @@ postcss-preset-env@^7.8.3: postcss-pseudo-class-any-link@^7.1.6: version "7.1.6" - resolved "https://registry.yarnpkg.com/postcss-pseudo-class-any-link/-/postcss-pseudo-class-any-link-7.1.6.tgz#2693b221902da772c278def85a4d9a64b6e617ab" + resolved "https://registry.npmjs.org/postcss-pseudo-class-any-link/-/postcss-pseudo-class-any-link-7.1.6.tgz" integrity sha512-9sCtZkO6f/5ML9WcTLcIyV1yz9D1rf0tWc+ulKcvV30s0iZKS/ONyETvoWsr6vnrmW+X+KmuK3gV/w5EWnT37w== dependencies: postcss-selector-parser "^6.0.10" postcss-replace-overflow-wrap@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-4.0.0.tgz#d2df6bed10b477bf9c52fab28c568b4b29ca4319" + resolved "https://registry.npmjs.org/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-4.0.0.tgz" integrity sha512-KmF7SBPphT4gPPcKZc7aDkweHiKEEO8cla/GjcBK+ckKxiZslIu3C4GCRW3DNfL0o7yW7kMQu9xlZ1kXRXLXtw== postcss-selector-not@^6.0.1: version "6.0.1" - resolved "https://registry.yarnpkg.com/postcss-selector-not/-/postcss-selector-not-6.0.1.tgz#8f0a709bf7d4b45222793fc34409be407537556d" + resolved "https://registry.npmjs.org/postcss-selector-not/-/postcss-selector-not-6.0.1.tgz" integrity sha512-1i9affjAe9xu/y9uqWH+tD4r6/hDaXJruk8xn2x1vzxC2U3J3LKO3zJW4CyxlNhA56pADJ/djpEwpH1RClI2rQ== dependencies: postcss-selector-parser "^6.0.10" postcss-selector-parser@^6.0.10: version "6.0.11" - resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.11.tgz#2e41dc39b7ad74046e1615185185cd0b17d0c8dc" + resolved "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.11.tgz" integrity sha512-zbARubNdogI9j7WY4nQJBiNqQf3sLS3wCP4WfOidu+p28LofJqDH1tcXypGrcmMHhDk2t9wGhCsYe/+szLTy1g== dependencies: cssesc "^3.0.0" @@ -781,7 +781,7 @@ postcss-value-parser@^4.0.0, postcss-value-parser@^4.2.0: postcss@^8.4.18, postcss@^8.4.19: version "8.4.19" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.19.tgz#61178e2add236b17351897c8bcc0b4c8ecab56fc" + resolved "https://registry.npmjs.org/postcss/-/postcss-8.4.19.tgz" integrity sha512-h+pbPsyhlYj6N2ozBmHhHrs9DzGmbaarbLvWipMRO7RLS+v4onj26MPFXA5OBYFxyqYhUJK456SwDcY9H2/zsA== dependencies: nanoid "^3.3.4" @@ -823,7 +823,7 @@ resolve@^1.1.7: resolve@^1.22.1: version "1.22.1" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" + resolved "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz" integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== dependencies: is-core-module "^2.9.0" @@ -854,7 +854,7 @@ supports-preserve-symlinks-flag@^1.0.0: tailwindcss@^3.2.4: version "3.2.4" - resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-3.2.4.tgz#afe3477e7a19f3ceafb48e4b083e292ce0dc0250" + resolved "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.2.4.tgz" integrity sha512-AhwtHCKMtR71JgeYDaswmZXhPcW9iuI9Sp2LvZPo9upDZ7231ZJ7eA9RaURbhpXGVlrjX4cFNlB4ieTetEb7hQ== dependencies: arg "^5.0.2" @@ -890,7 +890,7 @@ to-regex-range@^5.0.1: update-browserslist-db@^1.0.9: version "1.0.10" - resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz#0f54b876545726f17d00cd9a2561e6dade943ff3" + resolved "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz" integrity sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ== dependencies: escalade "^3.1.1"