Merge pull request 'Upgrade Rails to 7.1, update dependencies, require Ruby 3.x' (#160) from chore/update_dependencies into master
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #160 Reviewed-by: slvrbckt <slvrbckt@noreply.kosmos.org>
@ -17,7 +17,7 @@ steps:
|
|||||||
branch:
|
branch:
|
||||||
- master
|
- master
|
||||||
- name: rspec
|
- name: rspec
|
||||||
image: gitea.kosmos.org/kosmos/akkounts-ci:0.1.0
|
image: gitea.kosmos.org/kosmos/akkounts-ci:0.9.1
|
||||||
environment:
|
environment:
|
||||||
RAILS_ENV: test
|
RAILS_ENV: test
|
||||||
REDIS_URL: redis://redis:6379/0
|
REDIS_URL: redis://redis:6379/0
|
||||||
@ -28,6 +28,8 @@ steps:
|
|||||||
- bundle config set cache_path 'vendor/cache'
|
- bundle config set cache_path 'vendor/cache'
|
||||||
- bundle config set with 'development test'
|
- bundle config set with 'development test'
|
||||||
- bundle install --jobs=3 --retry=3
|
- bundle install --jobs=3 --retry=3
|
||||||
|
- bundle exec rails db:create
|
||||||
|
- bundle exec rails db:migrate
|
||||||
- yarn install
|
- yarn install
|
||||||
- rake css:build
|
- rake css:build
|
||||||
- bundle exec rspec
|
- bundle exec rspec
|
||||||
|
@ -1 +1 @@
|
|||||||
2.7.2
|
3.3.0
|
||||||
|
14
Dockerfile
@ -1,10 +1,18 @@
|
|||||||
# syntax=docker/dockerfile:1
|
# syntax=docker/dockerfile:1
|
||||||
FROM ruby:2.7.6
|
FROM debian:bullseye-slim as base
|
||||||
|
|
||||||
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
|
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
|
||||||
|
|
||||||
RUN apt-get update -qq && apt-get install -y --no-install-recommends curl \
|
# TODO Remove when upstream Ruby works properly on Apple silicon
|
||||||
ldap-utils tini libvips
|
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 curl -fsSL https://deb.nodesource.com/setup_lts.x | bash -
|
||||||
RUN apt-get update && apt-get install -y nodejs
|
RUN apt-get update && apt-get install -y nodejs
|
||||||
|
|
||||||
|
17
Gemfile
@ -2,7 +2,7 @@ source 'https://rubygems.org'
|
|||||||
git_source(:github) { |repo| "https://github.com/#{repo}.git" }
|
git_source(:github) { |repo| "https://github.com/#{repo}.git" }
|
||||||
|
|
||||||
# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
|
# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
|
||||||
gem 'rails', '~> 7.0.2'
|
gem 'rails', '~> 7.1'
|
||||||
# Use Puma as the app server
|
# Use Puma as the app server
|
||||||
gem 'puma', '~> 4.1'
|
gem 'puma', '~> 4.1'
|
||||||
# View components
|
# View components
|
||||||
@ -22,7 +22,7 @@ gem 'jbuilder', '~> 2.7'
|
|||||||
# Use Redis adapter to run Action Cable in production
|
# Use Redis adapter to run Action Cable in production
|
||||||
# gem 'redis', '~> 4.0'
|
# gem 'redis', '~> 4.0'
|
||||||
# Use Active Model has_secure_password
|
# Use Active Model has_secure_password
|
||||||
gem 'bcrypt', '~> 3.1.7'
|
gem 'bcrypt', '~> 3.1'
|
||||||
|
|
||||||
# Configuration
|
# Configuration
|
||||||
gem 'dotenv-rails'
|
gem 'dotenv-rails'
|
||||||
@ -61,20 +61,19 @@ gem "sentry-rails"
|
|||||||
# Services
|
# Services
|
||||||
gem 'discourse_api'
|
gem 'discourse_api'
|
||||||
gem "lnurl"
|
gem "lnurl"
|
||||||
gem 'manifique', git: 'https://gitea.kosmos.org/5apps/manifique.git', branch: 'master'
|
gem 'manifique'
|
||||||
gem 'nostr', git: 'https://gitea.kosmos.org/kosmos/nostr-gem.git', ref: 'd59f31a'
|
gem 'nostr'
|
||||||
|
|
||||||
group :development, :test do
|
group :development, :test do
|
||||||
# Use sqlite3 as the database for Active Record
|
# Use sqlite3 as the database for Active Record
|
||||||
gem 'sqlite3', '~> 1.4'
|
gem 'sqlite3', '~> 1.7.2'
|
||||||
gem 'rspec-rails'
|
gem 'rspec-rails'
|
||||||
gem 'rails-controller-testing'
|
gem 'rails-controller-testing'
|
||||||
gem "byebug", "~> 11.1"
|
|
||||||
end
|
end
|
||||||
|
|
||||||
group :development do
|
group :development do
|
||||||
# Access an interactive console on exception pages or by calling 'console' anywhere in the code.
|
# 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 'listen', '~> 3.2'
|
||||||
gem 'letter_opener'
|
gem 'letter_opener'
|
||||||
gem 'letter_opener_web'
|
gem 'letter_opener_web'
|
||||||
@ -90,8 +89,8 @@ group :test do
|
|||||||
end
|
end
|
||||||
|
|
||||||
group :production do
|
group :production do
|
||||||
# Use postgresql as the database for Active Record
|
gem 'pg', '~> 1.5'
|
||||||
gem 'pg', '~> 1.2.3'
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
|
# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
|
||||||
gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]
|
gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]
|
||||||
|
369
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
|
GEM
|
||||||
remote: https://rubygems.org/
|
remote: https://rubygems.org/
|
||||||
specs:
|
specs:
|
||||||
actioncable (7.0.8)
|
actioncable (7.1.3)
|
||||||
actionpack (= 7.0.8)
|
actionpack (= 7.1.3)
|
||||||
activesupport (= 7.0.8)
|
activesupport (= 7.1.3)
|
||||||
nio4r (~> 2.0)
|
nio4r (~> 2.0)
|
||||||
websocket-driver (>= 0.6.1)
|
websocket-driver (>= 0.6.1)
|
||||||
actionmailbox (7.0.8)
|
zeitwerk (~> 2.6)
|
||||||
actionpack (= 7.0.8)
|
actionmailbox (7.1.3)
|
||||||
activejob (= 7.0.8)
|
actionpack (= 7.1.3)
|
||||||
activerecord (= 7.0.8)
|
activejob (= 7.1.3)
|
||||||
activestorage (= 7.0.8)
|
activerecord (= 7.1.3)
|
||||||
activesupport (= 7.0.8)
|
activestorage (= 7.1.3)
|
||||||
|
activesupport (= 7.1.3)
|
||||||
mail (>= 2.7.1)
|
mail (>= 2.7.1)
|
||||||
net-imap
|
net-imap
|
||||||
net-pop
|
net-pop
|
||||||
net-smtp
|
net-smtp
|
||||||
actionmailer (7.0.8)
|
actionmailer (7.1.3)
|
||||||
actionpack (= 7.0.8)
|
actionpack (= 7.1.3)
|
||||||
actionview (= 7.0.8)
|
actionview (= 7.1.3)
|
||||||
activejob (= 7.0.8)
|
activejob (= 7.1.3)
|
||||||
activesupport (= 7.0.8)
|
activesupport (= 7.1.3)
|
||||||
mail (~> 2.5, >= 2.5.4)
|
mail (~> 2.5, >= 2.5.4)
|
||||||
net-imap
|
net-imap
|
||||||
net-pop
|
net-pop
|
||||||
net-smtp
|
net-smtp
|
||||||
rails-dom-testing (~> 2.0)
|
rails-dom-testing (~> 2.2)
|
||||||
actionpack (7.0.8)
|
actionpack (7.1.3)
|
||||||
actionview (= 7.0.8)
|
actionview (= 7.1.3)
|
||||||
activesupport (= 7.0.8)
|
activesupport (= 7.1.3)
|
||||||
rack (~> 2.0, >= 2.2.4)
|
nokogiri (>= 1.8.5)
|
||||||
|
racc
|
||||||
|
rack (>= 2.2.4)
|
||||||
|
rack-session (>= 1.0.1)
|
||||||
rack-test (>= 0.6.3)
|
rack-test (>= 0.6.3)
|
||||||
rails-dom-testing (~> 2.0)
|
rails-dom-testing (~> 2.2)
|
||||||
rails-html-sanitizer (~> 1.0, >= 1.2.0)
|
rails-html-sanitizer (~> 1.6)
|
||||||
actiontext (7.0.8)
|
actiontext (7.1.3)
|
||||||
actionpack (= 7.0.8)
|
actionpack (= 7.1.3)
|
||||||
activerecord (= 7.0.8)
|
activerecord (= 7.1.3)
|
||||||
activestorage (= 7.0.8)
|
activestorage (= 7.1.3)
|
||||||
activesupport (= 7.0.8)
|
activesupport (= 7.1.3)
|
||||||
globalid (>= 0.6.0)
|
globalid (>= 0.6.0)
|
||||||
nokogiri (>= 1.8.5)
|
nokogiri (>= 1.8.5)
|
||||||
actionview (7.0.8)
|
actionview (7.1.3)
|
||||||
activesupport (= 7.0.8)
|
activesupport (= 7.1.3)
|
||||||
builder (~> 3.1)
|
builder (~> 3.1)
|
||||||
erubi (~> 1.4)
|
erubi (~> 1.11)
|
||||||
rails-dom-testing (~> 2.0)
|
rails-dom-testing (~> 2.2)
|
||||||
rails-html-sanitizer (~> 1.1, >= 1.2.0)
|
rails-html-sanitizer (~> 1.6)
|
||||||
activejob (7.0.8)
|
activejob (7.1.3)
|
||||||
activesupport (= 7.0.8)
|
activesupport (= 7.1.3)
|
||||||
globalid (>= 0.3.6)
|
globalid (>= 0.3.6)
|
||||||
activemodel (7.0.8)
|
activemodel (7.1.3)
|
||||||
activesupport (= 7.0.8)
|
activesupport (= 7.1.3)
|
||||||
activerecord (7.0.8)
|
activerecord (7.1.3)
|
||||||
activemodel (= 7.0.8)
|
activemodel (= 7.1.3)
|
||||||
activesupport (= 7.0.8)
|
activesupport (= 7.1.3)
|
||||||
activestorage (7.0.8)
|
timeout (>= 0.4.0)
|
||||||
actionpack (= 7.0.8)
|
activestorage (7.1.3)
|
||||||
activejob (= 7.0.8)
|
actionpack (= 7.1.3)
|
||||||
activerecord (= 7.0.8)
|
activejob (= 7.1.3)
|
||||||
activesupport (= 7.0.8)
|
activerecord (= 7.1.3)
|
||||||
|
activesupport (= 7.1.3)
|
||||||
marcel (~> 1.0)
|
marcel (~> 1.0)
|
||||||
mini_mime (>= 1.1.0)
|
activesupport (7.1.3)
|
||||||
activesupport (7.0.8)
|
base64
|
||||||
|
bigdecimal
|
||||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||||
|
connection_pool (>= 2.2.5)
|
||||||
|
drb
|
||||||
i18n (>= 1.6, < 2)
|
i18n (>= 1.6, < 2)
|
||||||
minitest (>= 5.1)
|
minitest (>= 5.1)
|
||||||
|
mutex_m
|
||||||
tzinfo (~> 2.0)
|
tzinfo (~> 2.0)
|
||||||
addressable (2.8.5)
|
addressable (2.8.6)
|
||||||
public_suffix (>= 2.0.2, < 6.0)
|
public_suffix (>= 2.0.2, < 6.0)
|
||||||
ast (2.4.2)
|
ast (2.4.2)
|
||||||
aws-eventstream (1.2.0)
|
aws-eventstream (1.3.0)
|
||||||
aws-partitions (1.839.0)
|
aws-partitions (1.886.0)
|
||||||
aws-sdk-core (3.185.1)
|
aws-sdk-core (3.191.0)
|
||||||
aws-eventstream (~> 1, >= 1.0.2)
|
aws-eventstream (~> 1, >= 1.3.0)
|
||||||
aws-partitions (~> 1, >= 1.651.0)
|
aws-partitions (~> 1, >= 1.651.0)
|
||||||
aws-sigv4 (~> 1.5)
|
aws-sigv4 (~> 1.8)
|
||||||
jmespath (~> 1, >= 1.6.1)
|
jmespath (~> 1, >= 1.6.1)
|
||||||
aws-sdk-kms (1.72.0)
|
aws-sdk-kms (1.77.0)
|
||||||
aws-sdk-core (~> 3, >= 3.184.0)
|
aws-sdk-core (~> 3, >= 3.191.0)
|
||||||
aws-sigv4 (~> 1.1)
|
aws-sigv4 (~> 1.1)
|
||||||
aws-sdk-s3 (1.136.0)
|
aws-sdk-s3 (1.143.0)
|
||||||
aws-sdk-core (~> 3, >= 3.181.0)
|
aws-sdk-core (~> 3, >= 3.191.0)
|
||||||
aws-sdk-kms (~> 1)
|
aws-sdk-kms (~> 1)
|
||||||
aws-sigv4 (~> 1.6)
|
aws-sigv4 (~> 1.8)
|
||||||
aws-sigv4 (1.6.0)
|
aws-sigv4 (1.8.0)
|
||||||
aws-eventstream (~> 1, >= 1.0.2)
|
aws-eventstream (~> 1, >= 1.0.2)
|
||||||
backport (1.2.0)
|
backport (1.2.0)
|
||||||
base64 (0.1.1)
|
base64 (0.2.0)
|
||||||
bcrypt (3.1.19)
|
bcrypt (3.1.20)
|
||||||
bech32 (1.4.2)
|
bech32 (1.4.2)
|
||||||
thor (>= 1.1.0)
|
thor (>= 1.1.0)
|
||||||
benchmark (0.2.1)
|
benchmark (0.3.0)
|
||||||
|
bigdecimal (3.1.6)
|
||||||
bindex (0.8.1)
|
bindex (0.8.1)
|
||||||
bip-schnorr (0.7.0)
|
bip-schnorr (0.7.0)
|
||||||
ecdsa_ext (~> 0.5.0)
|
ecdsa_ext (~> 0.5.0)
|
||||||
brow (0.4.1)
|
|
||||||
builder (3.2.4)
|
builder (3.2.4)
|
||||||
byebug (11.1.3)
|
capybara (3.40.0)
|
||||||
capybara (3.39.2)
|
|
||||||
addressable
|
addressable
|
||||||
matrix
|
matrix
|
||||||
mini_mime (>= 0.1.3)
|
mini_mime (>= 0.1.3)
|
||||||
nokogiri (~> 1.8)
|
nokogiri (~> 1.11)
|
||||||
rack (>= 1.6.0)
|
rack (>= 1.6.0)
|
||||||
rack-test (>= 0.6.3)
|
rack-test (>= 0.6.3)
|
||||||
regexp_parser (>= 1.5, < 3.0)
|
regexp_parser (>= 1.5, < 3.0)
|
||||||
xpath (~> 3.2)
|
xpath (~> 3.2)
|
||||||
chunky_png (1.4.0)
|
chunky_png (1.4.0)
|
||||||
concurrent-ruby (1.2.2)
|
concurrent-ruby (1.2.3)
|
||||||
connection_pool (2.4.1)
|
connection_pool (2.4.1)
|
||||||
crack (0.4.5)
|
crack (0.4.6)
|
||||||
|
bigdecimal
|
||||||
rexml
|
rexml
|
||||||
crass (1.0.6)
|
crass (1.0.6)
|
||||||
cssbundling-rails (1.3.3)
|
cssbundling-rails (1.4.0)
|
||||||
railties (>= 6.0.0)
|
railties (>= 6.0.0)
|
||||||
database_cleaner (2.0.2)
|
database_cleaner (2.0.2)
|
||||||
database_cleaner-active_record (>= 2, < 3)
|
database_cleaner-active_record (>= 2, < 3)
|
||||||
@ -143,7 +129,7 @@ GEM
|
|||||||
activerecord (>= 5.a)
|
activerecord (>= 5.a)
|
||||||
database_cleaner-core (~> 2.0.0)
|
database_cleaner-core (~> 2.0.0)
|
||||||
database_cleaner-core (2.0.1)
|
database_cleaner-core (2.0.1)
|
||||||
date (3.3.3)
|
date (3.3.4)
|
||||||
devise (4.9.3)
|
devise (4.9.3)
|
||||||
bcrypt (~> 3.0)
|
bcrypt (~> 3.0)
|
||||||
orm_adapter (~> 0.1)
|
orm_adapter (~> 0.1)
|
||||||
@ -153,7 +139,7 @@ GEM
|
|||||||
devise_ldap_authenticatable (0.8.7)
|
devise_ldap_authenticatable (0.8.7)
|
||||||
devise (>= 3.4.1)
|
devise (>= 3.4.1)
|
||||||
net-ldap (>= 0.16.0)
|
net-ldap (>= 0.16.0)
|
||||||
diff-lcs (1.5.0)
|
diff-lcs (1.5.1)
|
||||||
discourse_api (2.0.1)
|
discourse_api (2.0.1)
|
||||||
faraday (~> 2.7)
|
faraday (~> 2.7)
|
||||||
faraday-follow_redirects
|
faraday-follow_redirects
|
||||||
@ -165,6 +151,8 @@ GEM
|
|||||||
railties (>= 3.2)
|
railties (>= 3.2)
|
||||||
down (5.4.1)
|
down (5.4.1)
|
||||||
addressable (~> 2.8)
|
addressable (~> 2.8)
|
||||||
|
drb (2.2.0)
|
||||||
|
ruby2_keywords
|
||||||
e2mmap (0.1.0)
|
e2mmap (0.1.0)
|
||||||
ecdsa (1.2.0)
|
ecdsa (1.2.0)
|
||||||
ecdsa_ext (0.5.0)
|
ecdsa_ext (0.5.0)
|
||||||
@ -174,58 +162,61 @@ GEM
|
|||||||
tzinfo
|
tzinfo
|
||||||
event_emitter (0.2.6)
|
event_emitter (0.2.6)
|
||||||
eventmachine (1.2.7)
|
eventmachine (1.2.7)
|
||||||
factory_bot (6.2.1)
|
factory_bot (6.4.6)
|
||||||
activesupport (>= 5.0.0)
|
activesupport (>= 5.0.0)
|
||||||
factory_bot_rails (6.2.0)
|
factory_bot_rails (6.4.3)
|
||||||
factory_bot (~> 6.2.0)
|
factory_bot (~> 6.4)
|
||||||
railties (>= 5.0.0)
|
railties (>= 5.0.0)
|
||||||
faker (3.2.1)
|
faker (3.2.3)
|
||||||
i18n (>= 1.8.11, < 2)
|
i18n (>= 1.8.11, < 2)
|
||||||
faraday (2.7.11)
|
faraday (2.9.0)
|
||||||
base64
|
faraday-net_http (>= 2.0, < 3.2)
|
||||||
faraday-net_http (>= 2.0, < 3.1)
|
|
||||||
ruby2_keywords (>= 0.0.4)
|
|
||||||
faraday-follow_redirects (0.3.0)
|
faraday-follow_redirects (0.3.0)
|
||||||
faraday (>= 1, < 3)
|
faraday (>= 1, < 3)
|
||||||
faraday-multipart (1.0.4)
|
faraday-multipart (1.0.4)
|
||||||
multipart-post (~> 2)
|
multipart-post (~> 2)
|
||||||
faraday-net_http (3.0.2)
|
faraday-net_http (3.1.0)
|
||||||
|
net-http
|
||||||
faye-websocket (0.11.3)
|
faye-websocket (0.11.3)
|
||||||
eventmachine (>= 0.12.0)
|
eventmachine (>= 0.12.0)
|
||||||
websocket-driver (>= 0.5.1)
|
websocket-driver (>= 0.5.1)
|
||||||
ffi (1.16.3)
|
ffi (1.16.3)
|
||||||
flipper (1.0.0)
|
flipper (1.2.2)
|
||||||
brow (~> 0.4.1)
|
|
||||||
concurrent-ruby (< 2)
|
concurrent-ruby (< 2)
|
||||||
flipper-active_record (1.0.0)
|
flipper-active_record (1.2.2)
|
||||||
activerecord (>= 4.2, < 8)
|
activerecord (>= 4.2, < 8)
|
||||||
flipper (~> 1.0.0)
|
flipper (~> 1.2.2)
|
||||||
flipper-ui (1.0.0)
|
flipper-ui (1.2.2)
|
||||||
erubi (>= 1.0.0, < 2.0.0)
|
erubi (>= 1.0.0, < 2.0.0)
|
||||||
flipper (~> 1.0.0)
|
flipper (~> 1.2.2)
|
||||||
rack (>= 1.4, < 4)
|
rack (>= 1.4, < 4)
|
||||||
rack-protection (>= 1.5.3, <= 4.0.0)
|
rack-protection (>= 1.5.3, <= 4.0.0)
|
||||||
sanitize (< 7)
|
sanitize (< 7)
|
||||||
fugit (1.8.1)
|
fugit (1.9.0)
|
||||||
et-orbi (~> 1, >= 1.2.7)
|
et-orbi (~> 1, >= 1.2.7)
|
||||||
raabro (~> 1.4)
|
raabro (~> 1.4)
|
||||||
globalid (1.2.1)
|
globalid (1.2.1)
|
||||||
activesupport (>= 6.1)
|
activesupport (>= 6.1)
|
||||||
hashdiff (1.0.1)
|
hashdiff (1.1.0)
|
||||||
i18n (1.14.1)
|
i18n (1.14.1)
|
||||||
concurrent-ruby (~> 1.0)
|
concurrent-ruby (~> 1.0)
|
||||||
image_processing (1.12.2)
|
image_processing (1.12.2)
|
||||||
mini_magick (>= 4.9.5, < 5)
|
mini_magick (>= 4.9.5, < 5)
|
||||||
ruby-vips (>= 2.0.17, < 3)
|
ruby-vips (>= 2.0.17, < 3)
|
||||||
importmap-rails (1.2.1)
|
importmap-rails (2.0.1)
|
||||||
actionpack (>= 6.0.0)
|
actionpack (>= 6.0.0)
|
||||||
|
activesupport (>= 6.0.0)
|
||||||
railties (>= 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)
|
jaro_winkler (1.5.6)
|
||||||
jbuilder (2.11.5)
|
jbuilder (2.11.5)
|
||||||
actionview (>= 5.0.0)
|
actionview (>= 5.0.0)
|
||||||
activesupport (>= 5.0.0)
|
activesupport (>= 5.0.0)
|
||||||
jmespath (1.6.2)
|
jmespath (1.6.2)
|
||||||
json (2.6.3)
|
json (2.7.1)
|
||||||
kramdown (2.4.0)
|
kramdown (2.4.0)
|
||||||
rexml
|
rexml
|
||||||
kramdown-parser-gfm (1.1.0)
|
kramdown-parser-gfm (1.1.0)
|
||||||
@ -245,8 +236,8 @@ GEM
|
|||||||
rb-inotify (~> 0.9, >= 0.9.10)
|
rb-inotify (~> 0.9, >= 0.9.10)
|
||||||
lnurl (1.1.0)
|
lnurl (1.1.0)
|
||||||
bech32 (~> 1.1)
|
bech32 (~> 1.1)
|
||||||
lockbox (1.3.0)
|
lockbox (1.3.2)
|
||||||
loofah (2.21.4)
|
loofah (2.22.0)
|
||||||
crass (~> 1.0.2)
|
crass (~> 1.0.2)
|
||||||
nokogiri (>= 1.12.0)
|
nokogiri (>= 1.12.0)
|
||||||
mail (2.8.1)
|
mail (2.8.1)
|
||||||
@ -254,63 +245,85 @@ GEM
|
|||||||
net-imap
|
net-imap
|
||||||
net-pop
|
net-pop
|
||||||
net-smtp
|
net-smtp
|
||||||
|
manifique (1.0.1)
|
||||||
|
faraday (~> 2.9.0)
|
||||||
|
faraday-follow_redirects (= 0.3.0)
|
||||||
|
nokogiri (~> 1.16.0)
|
||||||
marcel (1.0.2)
|
marcel (1.0.2)
|
||||||
matrix (0.4.2)
|
matrix (0.4.2)
|
||||||
method_source (1.0.0)
|
method_source (1.0.0)
|
||||||
mini_magick (4.12.0)
|
mini_magick (4.12.0)
|
||||||
mini_mime (1.1.5)
|
mini_mime (1.1.5)
|
||||||
mini_portile2 (2.8.5)
|
mini_portile2 (2.8.5)
|
||||||
minitest (5.20.0)
|
minitest (5.21.2)
|
||||||
multipart-post (2.3.0)
|
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
|
date
|
||||||
net-protocol
|
net-protocol
|
||||||
net-ldap (0.18.0)
|
net-ldap (0.19.0)
|
||||||
net-pop (0.1.2)
|
net-pop (0.1.2)
|
||||||
net-protocol
|
net-protocol
|
||||||
net-protocol (0.2.1)
|
net-protocol (0.2.2)
|
||||||
timeout
|
timeout
|
||||||
net-smtp (0.4.0)
|
net-smtp (0.4.0.1)
|
||||||
net-protocol
|
net-protocol
|
||||||
nio4r (2.5.9)
|
nio4r (2.7.0)
|
||||||
nokogiri (1.15.4)
|
nokogiri (1.16.0)
|
||||||
mini_portile2 (~> 2.8.2)
|
mini_portile2 (~> 2.8.2)
|
||||||
racc (~> 1.4)
|
racc (~> 1.4)
|
||||||
nokogiri (1.15.4-arm64-darwin)
|
nokogiri (1.16.0-arm64-darwin)
|
||||||
racc (~> 1.4)
|
racc (~> 1.4)
|
||||||
nokogiri (1.15.4-x86_64-linux)
|
nokogiri (1.16.0-x86_64-linux)
|
||||||
racc (~> 1.4)
|
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)
|
orm_adapter (0.5.0)
|
||||||
pagy (6.1.0)
|
pagy (6.4.3)
|
||||||
parallel (1.23.0)
|
parallel (1.24.0)
|
||||||
parser (3.2.2.4)
|
parser (3.3.0.5)
|
||||||
ast (~> 2.4.1)
|
ast (~> 2.4.1)
|
||||||
racc
|
racc
|
||||||
pg (1.2.3)
|
pg (1.5.4)
|
||||||
public_suffix (5.0.3)
|
psych (5.1.2)
|
||||||
|
stringio
|
||||||
|
public_suffix (5.0.4)
|
||||||
puma (4.3.12)
|
puma (4.3.12)
|
||||||
nio4r (~> 2.0)
|
nio4r (~> 2.0)
|
||||||
raabro (1.4.0)
|
raabro (1.4.0)
|
||||||
racc (1.7.1)
|
racc (1.7.3)
|
||||||
rack (2.2.8)
|
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 (~> 2.2, >= 2.2.4)
|
||||||
|
rack-session (1.0.2)
|
||||||
|
rack (< 3)
|
||||||
rack-test (2.1.0)
|
rack-test (2.1.0)
|
||||||
rack (>= 1.3)
|
rack (>= 1.3)
|
||||||
rails (7.0.8)
|
rackup (1.0.0)
|
||||||
actioncable (= 7.0.8)
|
rack (< 3)
|
||||||
actionmailbox (= 7.0.8)
|
webrick
|
||||||
actionmailer (= 7.0.8)
|
rails (7.1.3)
|
||||||
actionpack (= 7.0.8)
|
actioncable (= 7.1.3)
|
||||||
actiontext (= 7.0.8)
|
actionmailbox (= 7.1.3)
|
||||||
actionview (= 7.0.8)
|
actionmailer (= 7.1.3)
|
||||||
activejob (= 7.0.8)
|
actionpack (= 7.1.3)
|
||||||
activemodel (= 7.0.8)
|
actiontext (= 7.1.3)
|
||||||
activerecord (= 7.0.8)
|
actionview (= 7.1.3)
|
||||||
activestorage (= 7.0.8)
|
activejob (= 7.1.3)
|
||||||
activesupport (= 7.0.8)
|
activemodel (= 7.1.3)
|
||||||
|
activerecord (= 7.1.3)
|
||||||
|
activestorage (= 7.1.3)
|
||||||
|
activesupport (= 7.1.3)
|
||||||
bundler (>= 1.15.0)
|
bundler (>= 1.15.0)
|
||||||
railties (= 7.0.8)
|
railties (= 7.1.3)
|
||||||
rails-controller-testing (1.0.5)
|
rails-controller-testing (1.0.5)
|
||||||
actionpack (>= 5.0.1.rc1)
|
actionpack (>= 5.0.1.rc1)
|
||||||
actionview (>= 5.0.1.rc1)
|
actionview (>= 5.0.1.rc1)
|
||||||
@ -325,21 +338,26 @@ GEM
|
|||||||
rails-settings-cached (2.8.3)
|
rails-settings-cached (2.8.3)
|
||||||
activerecord (>= 5.0.0)
|
activerecord (>= 5.0.0)
|
||||||
railties (>= 5.0.0)
|
railties (>= 5.0.0)
|
||||||
railties (7.0.8)
|
railties (7.1.3)
|
||||||
actionpack (= 7.0.8)
|
actionpack (= 7.1.3)
|
||||||
activesupport (= 7.0.8)
|
activesupport (= 7.1.3)
|
||||||
method_source
|
irb
|
||||||
|
rackup (>= 1.0.0)
|
||||||
rake (>= 12.2)
|
rake (>= 12.2)
|
||||||
thor (~> 1.0)
|
thor (~> 1.0, >= 1.2.2)
|
||||||
zeitwerk (~> 2.5)
|
zeitwerk (~> 2.6)
|
||||||
rainbow (3.1.1)
|
rainbow (3.1.1)
|
||||||
rake (13.0.6)
|
rake (13.1.0)
|
||||||
rb-fsevent (0.11.2)
|
rb-fsevent (0.11.2)
|
||||||
rb-inotify (0.10.1)
|
rb-inotify (0.10.1)
|
||||||
ffi (~> 1.0)
|
ffi (~> 1.0)
|
||||||
rbs (2.8.4)
|
rbs (2.8.4)
|
||||||
|
rdoc (6.6.2)
|
||||||
|
psych (>= 4.0.0)
|
||||||
redis (4.8.1)
|
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)
|
responders (3.1.1)
|
||||||
actionpack (>= 5.2)
|
actionpack (>= 5.2)
|
||||||
railties (>= 5.2)
|
railties (>= 5.2)
|
||||||
@ -358,7 +376,7 @@ GEM
|
|||||||
rspec-mocks (3.12.6)
|
rspec-mocks (3.12.6)
|
||||||
diff-lcs (>= 1.2.0, < 2.0)
|
diff-lcs (>= 1.2.0, < 2.0)
|
||||||
rspec-support (~> 3.12.0)
|
rspec-support (~> 3.12.0)
|
||||||
rspec-rails (6.0.3)
|
rspec-rails (6.1.1)
|
||||||
actionpack (>= 6.1)
|
actionpack (>= 6.1)
|
||||||
activesupport (>= 6.1)
|
activesupport (>= 6.1)
|
||||||
railties (>= 6.1)
|
railties (>= 6.1)
|
||||||
@ -367,19 +385,18 @@ GEM
|
|||||||
rspec-mocks (~> 3.12)
|
rspec-mocks (~> 3.12)
|
||||||
rspec-support (~> 3.12)
|
rspec-support (~> 3.12)
|
||||||
rspec-support (3.12.1)
|
rspec-support (3.12.1)
|
||||||
rubocop (1.57.1)
|
rubocop (1.60.2)
|
||||||
base64 (~> 0.1.1)
|
|
||||||
json (~> 2.3)
|
json (~> 2.3)
|
||||||
language_server-protocol (>= 3.17.0)
|
language_server-protocol (>= 3.17.0)
|
||||||
parallel (~> 1.10)
|
parallel (~> 1.10)
|
||||||
parser (>= 3.2.2.4)
|
parser (>= 3.3.0.2)
|
||||||
rainbow (>= 2.2.2, < 4.0)
|
rainbow (>= 2.2.2, < 4.0)
|
||||||
regexp_parser (>= 1.8, < 3.0)
|
regexp_parser (>= 1.8, < 3.0)
|
||||||
rexml (>= 3.2.5, < 4.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)
|
ruby-progressbar (~> 1.7)
|
||||||
unicode-display_width (>= 2.4.0, < 3.0)
|
unicode-display_width (>= 2.4.0, < 3.0)
|
||||||
rubocop-ast (1.29.0)
|
rubocop-ast (1.30.0)
|
||||||
parser (>= 3.2.1.0)
|
parser (>= 3.2.1.0)
|
||||||
ruby-progressbar (1.13.0)
|
ruby-progressbar (1.13.0)
|
||||||
ruby-vips (2.2.0)
|
ruby-vips (2.2.0)
|
||||||
@ -390,10 +407,10 @@ GEM
|
|||||||
sanitize (6.1.0)
|
sanitize (6.1.0)
|
||||||
crass (~> 1.0.2)
|
crass (~> 1.0.2)
|
||||||
nokogiri (>= 1.12.0)
|
nokogiri (>= 1.12.0)
|
||||||
sentry-rails (5.12.0)
|
sentry-rails (5.16.1)
|
||||||
railties (>= 5.0)
|
railties (>= 5.0)
|
||||||
sentry-ruby (~> 5.12.0)
|
sentry-ruby (~> 5.16.1)
|
||||||
sentry-ruby (5.12.0)
|
sentry-ruby (5.16.1)
|
||||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||||
sidekiq (6.5.12)
|
sidekiq (6.5.12)
|
||||||
connection_pool (>= 2.2.5, < 3)
|
connection_pool (>= 2.2.5, < 3)
|
||||||
@ -403,7 +420,7 @@ GEM
|
|||||||
rufus-scheduler (~> 3.2)
|
rufus-scheduler (~> 3.2)
|
||||||
sidekiq (>= 6, < 8)
|
sidekiq (>= 6, < 8)
|
||||||
tilt (>= 1.4.0)
|
tilt (>= 1.4.0)
|
||||||
solargraph (0.49.0)
|
solargraph (0.50.0)
|
||||||
backport (~> 1.2)
|
backport (~> 1.2)
|
||||||
benchmark
|
benchmark
|
||||||
bundler (~> 2.0)
|
bundler (~> 2.0)
|
||||||
@ -426,15 +443,16 @@ GEM
|
|||||||
actionpack (>= 5.2)
|
actionpack (>= 5.2)
|
||||||
activesupport (>= 5.2)
|
activesupport (>= 5.2)
|
||||||
sprockets (>= 3.0.0)
|
sprockets (>= 3.0.0)
|
||||||
sqlite3 (1.6.7)
|
sqlite3 (1.7.2)
|
||||||
mini_portile2 (~> 2.8.0)
|
mini_portile2 (~> 2.8.0)
|
||||||
sqlite3 (1.6.7-arm64-darwin)
|
sqlite3 (1.7.2-arm64-darwin)
|
||||||
sqlite3 (1.6.7-x86_64-linux)
|
sqlite3 (1.7.2-x86_64-linux)
|
||||||
stimulus-rails (1.3.0)
|
stimulus-rails (1.3.3)
|
||||||
railties (>= 6.0.0)
|
railties (>= 6.0.0)
|
||||||
|
stringio (3.1.0)
|
||||||
thor (1.3.0)
|
thor (1.3.0)
|
||||||
tilt (2.3.0)
|
tilt (2.3.0)
|
||||||
timeout (0.4.0)
|
timeout (0.4.1)
|
||||||
turbo-rails (1.5.0)
|
turbo-rails (1.5.0)
|
||||||
actionpack (>= 6.0.0)
|
actionpack (>= 6.0.0)
|
||||||
activejob (>= 6.0.0)
|
activejob (>= 6.0.0)
|
||||||
@ -442,7 +460,8 @@ GEM
|
|||||||
tzinfo (2.0.6)
|
tzinfo (2.0.6)
|
||||||
concurrent-ruby (~> 1.0)
|
concurrent-ruby (~> 1.0)
|
||||||
unicode-display_width (2.5.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)
|
activesupport (>= 5.2.0, < 8.0)
|
||||||
concurrent-ruby (~> 1.0)
|
concurrent-ruby (~> 1.0)
|
||||||
method_source (~> 1.0)
|
method_source (~> 1.0)
|
||||||
@ -457,6 +476,7 @@ GEM
|
|||||||
addressable (>= 2.8.0)
|
addressable (>= 2.8.0)
|
||||||
crack (>= 0.3.2)
|
crack (>= 0.3.2)
|
||||||
hashdiff (>= 0.4.0, < 2.0.0)
|
hashdiff (>= 0.4.0, < 2.0.0)
|
||||||
|
webrick (1.8.1)
|
||||||
websocket-driver (0.7.6)
|
websocket-driver (0.7.6)
|
||||||
websocket-extensions (>= 0.1.0)
|
websocket-extensions (>= 0.1.0)
|
||||||
websocket-extensions (0.1.5)
|
websocket-extensions (0.1.5)
|
||||||
@ -472,8 +492,7 @@ PLATFORMS
|
|||||||
|
|
||||||
DEPENDENCIES
|
DEPENDENCIES
|
||||||
aws-sdk-s3
|
aws-sdk-s3
|
||||||
bcrypt (~> 3.1.7)
|
bcrypt (~> 3.1)
|
||||||
byebug (~> 11.1)
|
|
||||||
capybara
|
capybara
|
||||||
cssbundling-rails
|
cssbundling-rails
|
||||||
database_cleaner
|
database_cleaner
|
||||||
@ -496,13 +515,13 @@ DEPENDENCIES
|
|||||||
listen (~> 3.2)
|
listen (~> 3.2)
|
||||||
lnurl
|
lnurl
|
||||||
lockbox
|
lockbox
|
||||||
manifique!
|
manifique
|
||||||
net-ldap
|
net-ldap
|
||||||
nostr!
|
nostr
|
||||||
pagy (~> 6.0, >= 6.0.2)
|
pagy (~> 6.0, >= 6.0.2)
|
||||||
pg (~> 1.2.3)
|
pg (~> 1.5)
|
||||||
puma (~> 4.1)
|
puma (~> 4.1)
|
||||||
rails (~> 7.0.2)
|
rails (~> 7.1)
|
||||||
rails-controller-testing
|
rails-controller-testing
|
||||||
rails-settings-cached (~> 2.8.3)
|
rails-settings-cached (~> 2.8.3)
|
||||||
rqrcode (~> 2.0)
|
rqrcode (~> 2.0)
|
||||||
@ -513,14 +532,14 @@ DEPENDENCIES
|
|||||||
sidekiq-scheduler
|
sidekiq-scheduler
|
||||||
solargraph
|
solargraph
|
||||||
sprockets-rails
|
sprockets-rails
|
||||||
sqlite3 (~> 1.4)
|
sqlite3 (~> 1.7.2)
|
||||||
stimulus-rails
|
stimulus-rails
|
||||||
turbo-rails
|
turbo-rails
|
||||||
tzinfo-data
|
tzinfo-data
|
||||||
view_component
|
view_component
|
||||||
warden
|
warden
|
||||||
web-console (>= 3.3.0)
|
web-console (~> 4.2)
|
||||||
webmock
|
webmock
|
||||||
|
|
||||||
BUNDLED WITH
|
BUNDLED WITH
|
||||||
2.3.7
|
2.5.5
|
||||||
|
@ -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 %>
|
21
app/components/app_catalog/web_app_icon_component.rb
Normal file
@ -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
|
@ -2,13 +2,21 @@
|
|||||||
<div class="relative inline-block">
|
<div class="relative inline-block">
|
||||||
<div role="button" tabindex="0" data-dropdown-target="button"
|
<div role="button" tabindex="0" data-dropdown-target="button"
|
||||||
class="inline-block select-none">
|
class="inline-block select-none">
|
||||||
|
<% if @size == :large %>
|
||||||
<span class="appearance-none flex items-center inline-block">
|
<span class="appearance-none flex items-center inline-block">
|
||||||
<span class="p-2 bg-gray-50 hover:bg-gray-100 rounded-full">
|
<span class="p-2 bg-gray-50 hover:bg-gray-100 rounded-full">
|
||||||
<%= render partial: "icons/kebab-menu", locals: {
|
<%= render partial: "icons/#{@icon_name}",
|
||||||
custom_class: "inline text-gray-500 h-6 w-6"
|
locals: { custom_class: "inline text-gray-500 h-6 w-6" } %>
|
||||||
} %>
|
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
|
<% elsif @size == :small %>
|
||||||
|
<span class="appearance-none flex items-center inline-block">
|
||||||
|
<span class="text-gray-500 hover:text-blue-600">
|
||||||
|
<%= render partial: "icons/#{@icon_name}",
|
||||||
|
locals: { custom_class: "inline h-4 w-4" } %>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
<div data-dropdown-target="menu"
|
<div data-dropdown-target="menu"
|
||||||
data-transition-enter="transition ease-out duration-200"
|
data-transition-enter="transition ease-out duration-200"
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class DropdownComponent < ViewComponent::Base
|
class DropdownComponent < ViewComponent::Base
|
||||||
|
def initialize(size: :large, icon_name: "kebap-menu")
|
||||||
|
@size = size.to_sym
|
||||||
|
@icon_name = icon_name
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
@ -5,7 +5,9 @@
|
|||||||
} : nil do %>
|
} : nil do %>
|
||||||
<div class="flex flex-col">
|
<div class="flex flex-col">
|
||||||
<label class="font-bold mb-1"><%= @title %></label>
|
<label class="font-bold mb-1"><%= @title %></label>
|
||||||
|
<% if @description.present? %>
|
||||||
<p class="text-gray-500"><%= @descripton %></p>
|
<p class="text-gray-500"><%= @descripton %></p>
|
||||||
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
<div class="relative ml-4 inline-flex flex-shrink-0">
|
<div class="relative ml-4 inline-flex flex-shrink-0">
|
||||||
<%= render FormElements::ToggleComponent.new(
|
<%= render FormElements::ToggleComponent.new(
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
module FormElements
|
module FormElements
|
||||||
class FieldsetToggleComponent < ViewComponent::Base
|
class FieldsetToggleComponent < ViewComponent::Base
|
||||||
def initialize(tag: "li", form: nil, attribute: nil, field_name: nil,
|
def initialize(tag: "li", form: nil, attribute: nil, field_name: nil,
|
||||||
enabled: false, input_enabled: true, title:, description:)
|
enabled: false, input_enabled: true, title:, description: nil)
|
||||||
@tag = tag
|
@tag = tag
|
||||||
@form = form
|
@form = form
|
||||||
@attribute = attribute
|
@attribute = attribute
|
||||||
|
@ -18,9 +18,11 @@
|
|||||||
<div class="m-1 bg-white rounded shadow">
|
<div class="m-1 bg-white rounded shadow">
|
||||||
<div class="p-8">
|
<div class="p-8">
|
||||||
<%= content %>
|
<%= content %>
|
||||||
|
<% if @show_close_button %>
|
||||||
<div class="flex justify-end items-center flex-wrap mt-6">
|
<div class="flex justify-end items-center flex-wrap mt-6">
|
||||||
<button class="btn-md btn-blue" data-action="click->modal#close:prevent">Close</button>
|
<button class="btn-md btn-blue" data-action="click->modal#close:prevent">Close</button>
|
||||||
</div>
|
</div>
|
||||||
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,2 +1,5 @@
|
|||||||
class ModalComponent < ViewComponent::Base
|
class ModalComponent < ViewComponent::Base
|
||||||
|
def initialize(show_close_button: true)
|
||||||
|
@show_close_button = show_close_button
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
@ -1,16 +1,10 @@
|
|||||||
<div class="flex items-center gap-4">
|
<div class="flex items-center gap-4">
|
||||||
<div class="h-16 w-16 flex-none">
|
<div class="h-16 w-16 flex-none">
|
||||||
<% if @web_app.icon.attached? %>
|
<%= render AppCatalog::WebAppIconComponent.new(web_app: @web_app) %>
|
||||||
<%= image_tag s3_image_url(@web_app.icon), class: "h-full w-full" %>
|
|
||||||
<% elsif @web_app.apple_touch_icon.attached? %>
|
|
||||||
<%= image_tag s3_image_url(@web_app.apple_touch_icon), class: "h-full w-full" %>
|
|
||||||
<% else %>
|
|
||||||
<%= render partial: "icons/remotestorage", locals: { custom_class: "h-full w-full p-0.5 text-gray-200" } %>
|
|
||||||
<% end %>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-grow">
|
<div class="flex-grow">
|
||||||
<h4 class="mb-1 text-lg font-bold">
|
<h4 class="mb-1 text-lg font-bold">
|
||||||
<%= @web_app.name %>
|
<%= @web_app&.name || @auth.app_name %>
|
||||||
</h4>
|
</h4>
|
||||||
<p class="text-sm text-gray-500">
|
<p class="text-sm text-gray-500">
|
||||||
<%= @auth.client_id %>
|
<%= @auth.client_id %>
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
class Admin::UsersController < Admin::BaseController
|
class Admin::UsersController < Admin::BaseController
|
||||||
before_action :set_user, only: [:show]
|
before_action :set_user, except: [:index]
|
||||||
before_action :set_current_section
|
before_action :set_current_section
|
||||||
|
|
||||||
|
# GET /admin/users
|
||||||
def index
|
def index
|
||||||
ldap = LdapService.new
|
ldap = LdapService.new
|
||||||
@ou = params[:ou] || Setting.primary_domain
|
@ou = Setting.primary_domain
|
||||||
@orgs = ldap.fetch_organizations
|
|
||||||
@pagy, @users = pagy(User.where(ou: @ou).order(cn: :asc))
|
@pagy, @users = pagy(User.where(ou: @ou).order(cn: :asc))
|
||||||
|
|
||||||
@stats = {
|
@stats = {
|
||||||
@ -14,6 +14,7 @@ class Admin::UsersController < Admin::BaseController
|
|||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# GET /admin/users/:username
|
||||||
def show
|
def show
|
||||||
if Setting.lndhub_admin_enabled?
|
if Setting.lndhub_admin_enabled?
|
||||||
@lndhub_user = @user.lndhub_user
|
@lndhub_user = @user.lndhub_user
|
||||||
@ -21,14 +22,38 @@ class Admin::UsersController < Admin::BaseController
|
|||||||
|
|
||||||
@services_enabled = @user.services_enabled
|
@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
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def set_user
|
def set_user
|
||||||
address = params[:address].split("@")
|
@user = User.find_by(cn: params[:username], ou: Setting.primary_domain)
|
||||||
@user = User.where(cn: address.first, ou: address.last).first
|
http_status :not_found unless @user
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_current_section
|
def set_current_section
|
||||||
|
@ -24,11 +24,11 @@ class SettingsController < ApplicationController
|
|||||||
|
|
||||||
if @user.save
|
if @user.save
|
||||||
if @user.display_name && (@user.display_name != @user.ldap_entry[:display_name])
|
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
|
end
|
||||||
|
|
||||||
if @user.avatar_new.present?
|
if @user.avatar_new.present?
|
||||||
LdapManager::UpdateAvatar.call(@user.dn, @user.avatar_new)
|
LdapManager::UpdateAvatar.call(dn: @user.dn, file: @user.avatar_new)
|
||||||
end
|
end
|
||||||
|
|
||||||
redirect_to setting_path(@settings_section), flash: {
|
redirect_to setting_path(@settings_section), flash: {
|
||||||
@ -64,10 +64,10 @@ class SettingsController < ApplicationController
|
|||||||
@user.current_password = nil
|
@user.current_password = nil
|
||||||
session[:new_email_password] = generate_email_password
|
session[:new_email_password] = generate_email_password
|
||||||
hashed_password = hash_email_password(session[:new_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
|
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
|
end
|
||||||
|
|
||||||
redirect_to new_password_services_email_path
|
redirect_to new_password_services_email_path
|
||||||
@ -88,8 +88,8 @@ class SettingsController < ApplicationController
|
|||||||
|
|
||||||
def set_nostr_pubkey
|
def set_nostr_pubkey
|
||||||
signed_event = nostr_event_params[:signed_event].to_h.symbolize_keys
|
signed_event = nostr_event_params[:signed_event].to_h.symbolize_keys
|
||||||
is_valid_id = NostrManager::ValidateId.call(signed_event)
|
is_valid_id = NostrManager::ValidateId.call(event: signed_event)
|
||||||
is_valid_sig = NostrManager::VerifySignature.call(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]})"
|
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
|
unless is_valid_id && is_valid_sig && is_correct_content
|
||||||
|
@ -96,13 +96,13 @@ class SignupController < ApplicationController
|
|||||||
session[:new_user] = nil
|
session[:new_user] = nil
|
||||||
session[:validation_error] = nil
|
session[:validation_error] = nil
|
||||||
|
|
||||||
CreateAccount.call(
|
CreateAccount.call(account: {
|
||||||
username: @user.cn,
|
username: @user.cn,
|
||||||
domain: Setting.primary_domain,
|
domain: Setting.primary_domain,
|
||||||
email: @user.email,
|
email: @user.email,
|
||||||
password: @user.password,
|
password: @user.password,
|
||||||
invitation: @invitation
|
invitation: @invitation
|
||||||
)
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_context
|
def set_context
|
||||||
|
@ -17,4 +17,10 @@ class NotificationMailer < ApplicationMailer
|
|||||||
@subject = "New app connected to your storage"
|
@subject = "New app connected to your storage"
|
||||||
mail to: @user.email, subject: @subject
|
mail to: @user.email, subject: @subject
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def new_invitations_available
|
||||||
|
@user = params[:user]
|
||||||
|
@subject = "New invitations added to your account"
|
||||||
|
mail to: @user.email, subject: @subject
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
class AppCatalog::WebApp < ApplicationRecord
|
class AppCatalog::WebApp < ApplicationRecord
|
||||||
store :metadata, coder: JSON
|
store :metadata, coder: JSON
|
||||||
|
|
||||||
has_many :remote_storage_authorizations
|
has_many :remote_storage_authorizations, dependent: :destroy
|
||||||
|
|
||||||
has_one_attached :icon
|
has_one_attached :icon
|
||||||
has_one_attached :apple_touch_icon
|
has_one_attached :apple_touch_icon
|
||||||
@ -11,6 +11,6 @@ class AppCatalog::WebApp < ApplicationRecord
|
|||||||
if: Proc.new { |a| a.url.present? }
|
if: Proc.new { |a| a.url.present? }
|
||||||
|
|
||||||
def update_metadata
|
def update_metadata
|
||||||
AppCatalogManager::UpdateMetadata.call(self)
|
AppCatalogManager::UpdateMetadata.call(app: self)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -15,6 +15,9 @@ class Setting < RailsSettings::Base
|
|||||||
field :redis_url, type: :string,
|
field :redis_url, type: :string,
|
||||||
default: ENV["REDIS_URL"] || "redis://localhost:6379/0"
|
default: ENV["REDIS_URL"] || "redis://localhost:6379/0"
|
||||||
|
|
||||||
|
field :s3_enabled, type: :boolean,
|
||||||
|
default: ENV["S3_ENABLED"] && ENV["S3_ENABLED"].to_s != "false"
|
||||||
|
|
||||||
#
|
#
|
||||||
# Registrations
|
# Registrations
|
||||||
#
|
#
|
||||||
|
@ -7,7 +7,7 @@ class User < ApplicationRecord
|
|||||||
attr_accessor :avatar_new
|
attr_accessor :avatar_new
|
||||||
attr_accessor :current_password
|
attr_accessor :current_password
|
||||||
|
|
||||||
serialize :preferences, UserPreferences
|
serialize :preferences, coder: UserPreferences
|
||||||
|
|
||||||
#
|
#
|
||||||
# Relations
|
# Relations
|
||||||
@ -92,7 +92,7 @@ class User < ApplicationRecord
|
|||||||
def devise_after_confirmation
|
def devise_after_confirmation
|
||||||
if ldap_entry[:mail] != self.email
|
if ldap_entry[:mail] != self.email
|
||||||
# E-Mail update confirmed
|
# E-Mail update confirmed
|
||||||
LdapManager::UpdateEmail.call(self.dn, self.email)
|
LdapManager::UpdateEmail.call(dn: self.dn, address: self.email)
|
||||||
else
|
else
|
||||||
# E-Mail from signup confirmed (i.e. account activation)
|
# E-Mail from signup confirmed (i.e. account activation)
|
||||||
|
|
||||||
@ -164,7 +164,7 @@ class User < ApplicationRecord
|
|||||||
end
|
end
|
||||||
|
|
||||||
def avatar
|
def avatar
|
||||||
@avatar_base64 ||= LdapManager::FetchAvatar.call(cn: cn, ou: ou)
|
@avatar_base64 ||= LdapManager::FetchAvatar.call(cn: cn)
|
||||||
end
|
end
|
||||||
|
|
||||||
def services_enabled
|
def services_enabled
|
||||||
|
@ -3,7 +3,7 @@ require "down"
|
|||||||
|
|
||||||
module AppCatalogManager
|
module AppCatalogManager
|
||||||
class UpdateMetadata < AppCatalogManagerService
|
class UpdateMetadata < AppCatalogManagerService
|
||||||
def initialize(app)
|
def initialize(app:)
|
||||||
@app = app
|
@app = app
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -18,6 +18,10 @@ module AppCatalogManager
|
|||||||
@app.metadata[prop] = metadata.send(prop) if prop
|
@app.metadata[prop] = metadata.send(prop) if prop
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@app.save!
|
||||||
|
|
||||||
|
# TODO move icon downloads to separate, async job
|
||||||
|
|
||||||
if icon = metadata.select_icon(sizes: "256x256") ||
|
if icon = metadata.select_icon(sizes: "256x256") ||
|
||||||
icon = metadata.select_icon(sizes: "192x192")
|
icon = metadata.select_icon(sizes: "192x192")
|
||||||
attach_remote_image(:icon, icon)
|
attach_remote_image(:icon, icon)
|
||||||
@ -27,8 +31,6 @@ module AppCatalogManager
|
|||||||
if apple_touch_icon = metadata.select_icon(purpose: "apple-touch-icon")
|
if apple_touch_icon = metadata.select_icon(purpose: "apple-touch-icon")
|
||||||
attach_remote_image(:apple_touch_icon, apple_touch_icon)
|
attach_remote_image(:apple_touch_icon, apple_touch_icon)
|
||||||
end
|
end
|
||||||
|
|
||||||
@app.save!
|
|
||||||
rescue Manifique::Error => e
|
rescue Manifique::Error => e
|
||||||
msg = "Fetching web app manifest failed for #{e.url}: #{e.type}"
|
msg = "Fetching web app manifest failed for #{e.url}: #{e.type}"
|
||||||
Rails.logger.warn(msg)
|
Rails.logger.warn(msg)
|
||||||
@ -42,14 +44,19 @@ module AppCatalogManager
|
|||||||
else
|
else
|
||||||
download_url = "#{@app.url}/#{icon["src"].gsub(/^\//,'')}"
|
download_url = "#{@app.url}/#{icon["src"].gsub(/^\//,'')}"
|
||||||
end
|
end
|
||||||
filename = "#{attachment_name}.png"
|
filename = "#{attachment_name}-#{Time.now.to_i}.png"
|
||||||
key = "web_apps/#{@app.id}/icons/#{attachment_name}.png"
|
key = "web_apps/#{@app.id}/icons/#{filename}"
|
||||||
|
|
||||||
begin
|
begin
|
||||||
tempfile = Down.download(download_url)
|
tempfile = Down.download(download_url)
|
||||||
@app.send(attachment_name).attach(key: key, io: tempfile, filename: filename)
|
@app.send(attachment_name).attach(key: key, io: tempfile, filename: filename)
|
||||||
rescue Down::NotFound
|
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
|
end
|
||||||
end
|
end
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
class ApplicationService
|
class ApplicationService
|
||||||
# This enables executing a service's `#call` method directly via
|
# This enables executing a service's `#call` method directly via
|
||||||
# `MyService.call(args)`, without creating a class instance it first.
|
# `MyService.call(args)`, without creating a class instance it first.
|
||||||
def self.call(*args, &block)
|
def self.call(**args, &block)
|
||||||
new(*args, &block).call
|
new(**args, &block).call
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
class CreateAccount < ApplicationService
|
class CreateAccount < ApplicationService
|
||||||
def initialize(args)
|
def initialize(account:)
|
||||||
@username = args[:username]
|
@username = account[:username]
|
||||||
@domain = args[:ou] || Setting.primary_domain
|
@domain = account[:ou] || Setting.primary_domain
|
||||||
@email = args[:email]
|
@email = account[:email]
|
||||||
@password = args[:password]
|
@password = account[:password]
|
||||||
@invitation = args[:invitation]
|
@invitation = account[:invitation]
|
||||||
@confirmed = args[:confirmed]
|
@confirmed = account[:confirmed]
|
||||||
end
|
end
|
||||||
|
|
||||||
def call
|
def call
|
||||||
|
17
app/services/create_invitations.rb
Normal file
@ -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
|
@ -1,12 +1,11 @@
|
|||||||
module LdapManager
|
module LdapManager
|
||||||
class FetchAvatar < LdapManagerService
|
class FetchAvatar < LdapManagerService
|
||||||
def initialize(cn:, ou: nil)
|
def initialize(cn:)
|
||||||
@cn = cn
|
@cn = cn
|
||||||
@ou = ou
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def call
|
def call
|
||||||
treebase = @ou ? "ou=#{@ou},cn=users,#{suffix}" : ldap_config["base"]
|
treebase = ldap_config["base"]
|
||||||
attributes = %w{ jpegPhoto }
|
attributes = %w{ jpegPhoto }
|
||||||
filter = Net::LDAP::Filter.eq("cn", @cn)
|
filter = Net::LDAP::Filter.eq("cn", @cn)
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@ require "image_processing/vips"
|
|||||||
|
|
||||||
module LdapManager
|
module LdapManager
|
||||||
class UpdateAvatar < LdapManagerService
|
class UpdateAvatar < LdapManagerService
|
||||||
def initialize(dn, file)
|
def initialize(dn:, file:)
|
||||||
@dn = dn
|
@dn = dn
|
||||||
@img_data = process(file)
|
@img_data = process(file)
|
||||||
end
|
end
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
module LdapManager
|
module LdapManager
|
||||||
class UpdateDisplayName < LdapManagerService
|
class UpdateDisplayName < LdapManagerService
|
||||||
def initialize(dn, display_name)
|
def initialize(dn:, display_name:)
|
||||||
@dn = dn
|
@dn = dn
|
||||||
@display_name = display_name
|
@display_name = display_name
|
||||||
end
|
end
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
module LdapManager
|
module LdapManager
|
||||||
class UpdateEmail < LdapManagerService
|
class UpdateEmail < LdapManagerService
|
||||||
def initialize(dn, address)
|
def initialize(dn:, address:)
|
||||||
@dn = dn
|
@dn = dn
|
||||||
@address = address
|
@address = address
|
||||||
end
|
end
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
module LdapManager
|
module LdapManager
|
||||||
class UpdateEmailMaildrop < LdapManagerService
|
class UpdateEmailMaildrop < LdapManagerService
|
||||||
def initialize(dn, address)
|
def initialize(dn:, address:)
|
||||||
@dn = dn
|
@dn = dn
|
||||||
@address = address
|
@address = address
|
||||||
end
|
end
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
module LdapManager
|
module LdapManager
|
||||||
class UpdateEmailPassword < LdapManagerService
|
class UpdateEmailPassword < LdapManagerService
|
||||||
def initialize(dn, password_hash)
|
def initialize(dn:, password_hash:)
|
||||||
@dn = dn
|
@dn = dn
|
||||||
@password_hash = password_hash
|
@password_hash = password_hash
|
||||||
end
|
end
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
module NostrManager
|
module NostrManager
|
||||||
class ValidateId < NostrManagerService
|
class ValidateId < NostrManagerService
|
||||||
def initialize(event)
|
def initialize(event:)
|
||||||
@event = Nostr::Event.new(**event)
|
@event = Nostr::Event.new(**event)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
module NostrManager
|
module NostrManager
|
||||||
class VerifySignature < NostrManagerService
|
class VerifySignature < NostrManagerService
|
||||||
def initialize(event)
|
def initialize(event:)
|
||||||
@event = Nostr::Event.new(**event)
|
@event = Nostr::Event.new(**event)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
21
app/views/admin/users/_create_invitations.html.erb
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<h3>Add new invitations to <%= @user.cn %>'s account</h3>
|
||||||
|
<%= form_with(url: invitations_admin_user_path, method: :post) do |form| %>
|
||||||
|
<ul role="list">
|
||||||
|
<%= render FormElements::FieldsetComponent.new(
|
||||||
|
positioning: :horizontal,
|
||||||
|
title: "Amount"
|
||||||
|
) do %>
|
||||||
|
<%= form.select :amount, options_for_select([
|
||||||
|
["3", "3"], ["5", "5"], ["10", "10"], ["20", "20"]
|
||||||
|
]) %>
|
||||||
|
<% end %>
|
||||||
|
<%= render FormElements::FieldsetToggleComponent.new(
|
||||||
|
field_name: "notify_user",
|
||||||
|
enabled: true,
|
||||||
|
title: "Notify user via email"
|
||||||
|
) %>
|
||||||
|
</ul>
|
||||||
|
<p class="pt-6 border-t border-gray-200 text-right">
|
||||||
|
<%= form.submit 'Add', class: "btn-md btn-blue w-full" %>
|
||||||
|
</p>
|
||||||
|
<% end %>
|
@ -1,4 +1,4 @@
|
|||||||
<%= render HeaderComponent.new(title: "Users: #{@ou}") %>
|
<%= render HeaderComponent.new(title: "Users") %>
|
||||||
|
|
||||||
<%= render MainSimpleComponent.new do %>
|
<%= render MainSimpleComponent.new do %>
|
||||||
<section>
|
<section>
|
||||||
@ -16,19 +16,6 @@
|
|||||||
<% end %>
|
<% end %>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<% if @orgs.length > 1 %>
|
|
||||||
<section>
|
|
||||||
<h3 class="hidden">Domains</h3>
|
|
||||||
<ul>
|
|
||||||
<% @orgs.each do |org| %>
|
|
||||||
<li class="inline-block">
|
|
||||||
<%= link_to org[:ou], admin_users_path(ou: org[:ou]), class: "ks-text-link" %>
|
|
||||||
</li>
|
|
||||||
<% end %>
|
|
||||||
</ul>
|
|
||||||
</section>
|
|
||||||
<% end %>
|
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
<table class="divided mb-8">
|
<table class="divided mb-8">
|
||||||
<thead>
|
<thead>
|
||||||
@ -36,13 +23,12 @@
|
|||||||
<th>UID</th>
|
<th>UID</th>
|
||||||
<th>Status</th>
|
<th>Status</th>
|
||||||
<th>Roles</th>
|
<th>Roles</th>
|
||||||
<!-- <th>Password</th> -->
|
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<% @users.each do |user| %>
|
<% @users.each do |user| %>
|
||||||
<tr>
|
<tr>
|
||||||
<td><%= link_to(user.cn, admin_user_path(user.address), class: 'ks-text-link') %></td>
|
<td><%= link_to(user.cn, admin_user_path(user.cn), class: 'ks-text-link') %></td>
|
||||||
<td><%= user.confirmed_at.nil? ? badge("pending", :yellow) : "" %></td>
|
<td><%= user.confirmed_at.nil? ? badge("pending", :yellow) : "" %></td>
|
||||||
<td><%= user.is_admin? ? badge("admin", :red) : "" %></td>
|
<td><%= user.is_admin? ? badge("admin", :red) : "" %></td>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<%= render HeaderComponent.new(title: "User: #{@user.address}") %>
|
<%= render HeaderComponent.new(title: "User: #{@user.cn}") %>
|
||||||
|
|
||||||
<%= render MainSimpleComponent.new do %>
|
<%= render MainSimpleComponent.new do %>
|
||||||
<div class="mb-12 sm:flex sm:flex-row sm:gap-x-8">
|
<div class="mb-12 sm:flex sm:flex-row sm:gap-x-8">
|
||||||
@ -42,8 +42,34 @@
|
|||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Invitations available</th>
|
<th>Invitations available</th>
|
||||||
<td>
|
<td data-controller="modal" data-action="keydown.esc->modal#close">
|
||||||
|
<div class="flex justify-between">
|
||||||
|
<span>
|
||||||
<%= @user.invitations.count %>
|
<%= @user.invitations.count %>
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
<button id="add-invitations" data-action="click->modal#open">
|
||||||
|
<%= render partial: "icons/plus-circle", locals: {
|
||||||
|
custom_class: "text-green-600 hover:text-green-500 -mt-2 -mb-1 h-6 w-6 inline-block"
|
||||||
|
} %>
|
||||||
|
</button>
|
||||||
|
<% 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 %>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<%= render ModalComponent.new(show_close_button: false) do %>
|
||||||
|
<%= render partial: "admin/users/create_invitations",
|
||||||
|
locals: { user: @user } %>
|
||||||
|
<% end %>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
@ -1 +1 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-plus-circle"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="8" x2="12" y2="16"></line><line x1="8" y1="12" x2="16" y2="12"></line></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-plus-circle <%= custom_class %>"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="8" x2="12" y2="16"></line><line x1="8" y1="12" x2="16" y2="12"></line></svg>
|
||||||
|
Before Width: | Height: | Size: 351 B After Width: | Height: | Size: 372 B |
@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<svg width="24" height="24" class="<%= custom_class %>" clip-rule="evenodd" fill-rule="evenodd" image-rendering="optimizeQuality" shape-rendering="geometricPrecision" text-rendering="geometricPrecision" version="1.1" viewBox="0 0 250 249.9" xml:space="preserve" xmlns="http://www.w3.org/2000/svg">
|
<svg width="24" height="24" class="icon-remotestorage <%= custom_class %>" clip-rule="evenodd" fill-rule="evenodd" image-rendering="optimizeQuality" shape-rendering="geometricPrecision" text-rendering="geometricPrecision" version="1.1" viewBox="0 0 250 249.9" xml:space="preserve" xmlns="http://www.w3.org/2000/svg">
|
||||||
<g transform="translate(-66.822 -.16484)">
|
<g transform="translate(-66.822 -.16484)">
|
||||||
<polygon id="polygon1" fill="currentColor" transform="matrix(.29308 0 0 .29308 83.528 -.028385)" points="228 181 370 100 511 181 652 263 370 425 87 263 87 263 0 213 0 213 0 311 0 378 0 427 0 476 86 525 185 582 370 689 554 582 653 525 653 590 653 592 370 754 0 542 0 640 185 747 370 853 554 747 739 640 739 525 739 476 739 427 739 378 653 427 370 589 86 427 86 361 185 418 370 524 554 418 653 361 739 311 739 213 554 107 370 0 185 107 58 180 144 230"/>
|
<polygon id="polygon1" fill="currentColor" transform="matrix(.29308 0 0 .29308 83.528 -.028385)" points="228 181 370 100 511 181 652 263 370 425 87 263 87 263 0 213 0 213 0 311 0 378 0 427 0 476 86 525 185 582 370 689 554 582 653 525 653 590 653 592 370 754 0 542 0 640 185 747 370 853 554 747 739 640 739 525 739 476 739 427 739 378 653 427 370 589 86 427 86 361 185 418 370 524 554 418 653 361 739 311 739 213 554 107 370 0 185 107 58 180 144 230"/>
|
||||||
</g>
|
</g>
|
||||||
|
Before Width: | Height: | Size: 848 B After Width: | Height: | Size: 867 B |
@ -1 +1 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-x-circle"><circle cx="12" cy="12" r="10"></circle><line x1="15" y1="9" x2="9" y2="15"></line><line x1="9" y1="9" x2="15" y2="15"></line></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-x-circle <%= custom_class %>"><circle cx="12" cy="12" r="10"></circle><line x1="15" y1="9" x2="9" y2="15"></line><line x1="9" y1="9" x2="15" y2="15"></line></svg>
|
||||||
|
Before Width: | Height: | Size: 346 B After Width: | Height: | Size: 367 B |
@ -77,7 +77,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="pt-4 pb-3 border-t border-gray-200/10">
|
<div class="pt-4 pb-3 border-t border-gray-200/10">
|
||||||
<div class="px-5 text-base font-normal text-white">
|
<div class="px-5 text-base font-normal text-white">
|
||||||
<%= current_user.address %></strong>
|
<%= current_user.address %>
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-3 px-2 space-y-1">
|
<div class="mt-3 px-2 space-y-1">
|
||||||
<%= link_to "Log out", destroy_user_session_path,
|
<%= link_to "Log out", destroy_user_session_path,
|
||||||
|
@ -0,0 +1,11 @@
|
|||||||
|
Hi <%= @user.display_name.presence || @user.cn %>,
|
||||||
|
|
||||||
|
New invitations have just been added to your Kosmos account, so you can invite more people to our cooperative services:
|
||||||
|
|
||||||
|
<%= invitations_url %>
|
||||||
|
|
||||||
|
Have a nice day!
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Tip: if you want to invite someone you're meeting in person, log into your account panel on a mobile device and let people scan the invitation QR code from theirs.
|
@ -1,4 +1,10 @@
|
|||||||
# syntax=docker/dockerfile:1
|
# syntax=docker/dockerfile:1
|
||||||
FROM guildeducation/rails:2.7.2-14.20.0
|
FROM ruby:3.3.0
|
||||||
|
|
||||||
RUN apt-get update -qq && apt-get install -y --no-install-recommends ldap-utils libvips
|
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
|
||||||
|
|
||||||
|
RUN apt-get update -qq && 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
|
||||||
|
RUN npm install -g yarn
|
||||||
|
@ -42,7 +42,5 @@ module Akkounts
|
|||||||
|
|
||||||
config.active_job.queue_adapter = :sidekiq
|
config.active_job.queue_adapter = :sidekiq
|
||||||
config.action_mailer.deliver_later_queue_name = nil # use "default" queue
|
config.action_mailer.deliver_later_queue_name = nil # use "default" queue
|
||||||
|
|
||||||
config.active_record.legacy_connection_handling = false
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -69,9 +69,9 @@ Rails.application.configure do
|
|||||||
config.action_mailer.default_url_options = { host: "localhost:3000", protocol: "http" }
|
config.action_mailer.default_url_options = { host: "localhost:3000", protocol: "http" }
|
||||||
|
|
||||||
# Allow requests from any IP
|
# Allow requests from any IP
|
||||||
config.web_console.whiny_requests = false
|
config.web_console.permissions = '0.0.0.0/0'
|
||||||
|
|
||||||
if ENV["S3_ENABLED"]
|
if ENV["S3_ENABLED"] && ENV["S3_ENABLED"].to_s != "false"
|
||||||
config.active_storage.service = :s3
|
config.active_storage.service = :s3
|
||||||
else
|
else
|
||||||
config.active_storage.service = :local
|
config.active_storage.service = :local
|
||||||
|
@ -110,7 +110,7 @@ Rails.application.configure do
|
|||||||
# Set this to true and configure the email server for immediate delivery to raise delivery errors.
|
# Set this to true and configure the email server for immediate delivery to raise delivery errors.
|
||||||
config.action_mailer.raise_delivery_errors = true
|
config.action_mailer.raise_delivery_errors = true
|
||||||
|
|
||||||
if ENV["S3_ENABLED"]
|
if ENV["S3_ENABLED"] && ENV["S3_ENABLED"].to_s != "false"
|
||||||
config.active_storage.service = :s3
|
config.active_storage.service = :s3
|
||||||
else
|
else
|
||||||
config.active_storage.service = :local
|
config.active_storage.service = :local
|
||||||
|
@ -26,7 +26,7 @@ Rails.application.configure do
|
|||||||
config.cache_store = :null_store
|
config.cache_store = :null_store
|
||||||
|
|
||||||
# Raise exceptions instead of rendering exception templates.
|
# Raise exceptions instead of rendering exception templates.
|
||||||
config.action_dispatch.show_exceptions = false
|
config.action_dispatch.show_exceptions = :none
|
||||||
|
|
||||||
# Disable request forgery protection in test environment.
|
# Disable request forgery protection in test environment.
|
||||||
config.action_controller.allow_forgery_protection = false
|
config.action_controller.allow_forgery_protection = false
|
||||||
|
@ -325,3 +325,10 @@ Devise.setup do |config|
|
|||||||
# changed. Defaults to true, so a user is signed in automatically after changing a password.
|
# changed. Defaults to true, so a user is signed in automatically after changing a password.
|
||||||
# config.sign_in_after_change_password = true
|
# config.sign_in_after_change_password = true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# https://github.com/heartcombo/devise/issues/5644
|
||||||
|
class Devise::SecretKeyFinder
|
||||||
|
def find
|
||||||
|
@application.secret_key_base
|
||||||
|
end
|
||||||
|
end
|
||||||
|
@ -73,9 +73,19 @@ Rails.application.routes.draw do
|
|||||||
namespace :admin do
|
namespace :admin do
|
||||||
root to: 'dashboard#index'
|
root to: 'dashboard#index'
|
||||||
|
|
||||||
resources 'users', param: 'address', only: ['index', 'show'], constraints: { address: /.*/ }
|
resources 'users', param: 'username', only: ['index', 'show'] do
|
||||||
|
member do
|
||||||
|
post 'invitations', to: 'users#create_invitations'
|
||||||
|
delete 'invitations', to: 'users#delete_invitations'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# post 'users/:username/invitations', to: 'users#create_invitations'
|
||||||
|
|
||||||
get 'invitations', to: 'invitations#index'
|
get 'invitations', to: 'invitations#index'
|
||||||
|
|
||||||
resources :donations
|
resources :donations
|
||||||
|
|
||||||
get 'lightning', to: 'lightning#index'
|
get 'lightning', to: 'lightning#index'
|
||||||
|
|
||||||
namespace :app_catalog do
|
namespace :app_catalog do
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
local:
|
local:
|
||||||
service: Disk
|
service: Disk
|
||||||
root: <%= Rails.root.join("storage") %>
|
root: <%= ENV["ACTIVE_STORAGE_PATH"] || Rails.root.join("storage") %>
|
||||||
|
|
||||||
test:
|
test:
|
||||||
service: Disk
|
service: Disk
|
||||||
root: <%= Rails.root.join("tmp/storage") %>
|
root: <%= Rails.root.join("tmp/storage") %>
|
||||||
|
|
||||||
<% if ENV["S3_ENABLED"] %>
|
<% if ENV["S3_ENABLED"] && ENV["S3_ENABLED"].to_s != "false" %>
|
||||||
s3:
|
s3:
|
||||||
service: S3
|
service: S3
|
||||||
endpoint: <%= ENV["S3_ENDPOINT"] %>
|
endpoint: <%= ENV["S3_ENDPOINT"] %>
|
||||||
|
@ -0,0 +1,18 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class ChangeFlipperGatesValueToText < ActiveRecord::Migration[7.1]
|
||||||
|
def up
|
||||||
|
# Ensure this incremental update migration is idempotent
|
||||||
|
return unless connection.column_exists? :flipper_gates, :value, :string
|
||||||
|
|
||||||
|
if index_exists? :flipper_gates, [:feature_key, :key, :value]
|
||||||
|
remove_index :flipper_gates, [:feature_key, :key, :value]
|
||||||
|
end
|
||||||
|
change_column :flipper_gates, :value, :text
|
||||||
|
add_index :flipper_gates, [:feature_key, :key, :value], unique: true, length: { value: 255 }
|
||||||
|
end
|
||||||
|
|
||||||
|
def down
|
||||||
|
change_column :flipper_gates, :value, :string
|
||||||
|
end
|
||||||
|
end
|
10
db/schema.rb
@ -10,12 +10,12 @@
|
|||||||
#
|
#
|
||||||
# It's strongly recommended that you check this file into your version control system.
|
# It's strongly recommended that you check this file into your version control system.
|
||||||
|
|
||||||
ActiveRecord::Schema[7.0].define(version: 2023_10_24_104909) do
|
ActiveRecord::Schema[7.1].define(version: 2024_02_07_080515) do
|
||||||
create_table "active_storage_attachments", force: :cascade do |t|
|
create_table "active_storage_attachments", force: :cascade do |t|
|
||||||
t.string "name", null: false
|
t.string "name", null: false
|
||||||
t.string "record_type", null: false
|
t.string "record_type", null: false
|
||||||
t.bigint "record_id", null: false
|
t.integer "record_id", null: false
|
||||||
t.bigint "blob_id", null: false
|
t.integer "blob_id", null: false
|
||||||
t.datetime "created_at", null: false
|
t.datetime "created_at", null: false
|
||||||
t.index ["blob_id"], name: "index_active_storage_attachments_on_blob_id"
|
t.index ["blob_id"], name: "index_active_storage_attachments_on_blob_id"
|
||||||
t.index ["record_type", "record_id", "name", "blob_id"], name: "index_active_storage_attachments_uniqueness", unique: true
|
t.index ["record_type", "record_id", "name", "blob_id"], name: "index_active_storage_attachments_uniqueness", unique: true
|
||||||
@ -34,7 +34,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_10_24_104909) do
|
|||||||
end
|
end
|
||||||
|
|
||||||
create_table "active_storage_variant_records", force: :cascade do |t|
|
create_table "active_storage_variant_records", force: :cascade do |t|
|
||||||
t.bigint "blob_id", null: false
|
t.integer "blob_id", null: false
|
||||||
t.string "variation_digest", null: false
|
t.string "variation_digest", null: false
|
||||||
t.index ["blob_id", "variation_digest"], name: "index_active_storage_variant_records_uniqueness", unique: true
|
t.index ["blob_id", "variation_digest"], name: "index_active_storage_variant_records_uniqueness", unique: true
|
||||||
end
|
end
|
||||||
@ -69,7 +69,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_10_24_104909) do
|
|||||||
create_table "flipper_gates", force: :cascade do |t|
|
create_table "flipper_gates", force: :cascade do |t|
|
||||||
t.string "feature_key", null: false
|
t.string "feature_key", null: false
|
||||||
t.string "key", null: false
|
t.string "key", null: false
|
||||||
t.string "value"
|
t.text "value"
|
||||||
t.datetime "created_at", null: false
|
t.datetime "created_at", null: false
|
||||||
t.datetime "updated_at", null: false
|
t.datetime "updated_at", null: false
|
||||||
t.index ["feature_key", "key", "value"], name: "index_flipper_gates_on_feature_key_and_key_and_value", unique: true
|
t.index ["feature_key", "key", "value"], name: "index_flipper_gates_on_feature_key_and_key_and_value", unique: true
|
||||||
|
@ -3,10 +3,10 @@ require 'sidekiq/testing'
|
|||||||
ldap = LdapService.new
|
ldap = LdapService.new
|
||||||
|
|
||||||
Sidekiq::Testing.inline! do
|
Sidekiq::Testing.inline! do
|
||||||
CreateAccount.call(
|
CreateAccount.call(account: {
|
||||||
username: "admin", domain: "kosmos.org", email: "admin@example.com",
|
username: "admin", domain: "kosmos.org", email: "admin@example.com",
|
||||||
password: "admin is admin", confirmed: true
|
password: "admin is admin", confirmed: true
|
||||||
)
|
})
|
||||||
|
|
||||||
ldap.add_attribute "cn=admin,ou=kosmos.org,cn=users,dc=kosmos,dc=org", :admin, "true"
|
ldap.add_attribute "cn=admin,ou=kosmos.org,cn=users,dc=kosmos,dc=org", :admin, "true"
|
||||||
|
|
||||||
@ -15,9 +15,9 @@ Sidekiq::Testing.inline! do
|
|||||||
email = Faker::Internet.unique.email
|
email = Faker::Internet.unique.email
|
||||||
next if username.length < 3
|
next if username.length < 3
|
||||||
|
|
||||||
CreateAccount.call(
|
CreateAccount.call(account: {
|
||||||
username: username, domain: "kosmos.org", email: email,
|
username: username, domain: "kosmos.org", email: email,
|
||||||
password: "user is user", confirmed: true
|
password: "user is user", confirmed: true
|
||||||
)
|
})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -2,7 +2,7 @@ services:
|
|||||||
ldap:
|
ldap:
|
||||||
image: 4teamwork/389ds:latest
|
image: 4teamwork/389ds:latest
|
||||||
volumes:
|
volumes:
|
||||||
- ./tmp/389ds:/data
|
- 389ds-data:/data
|
||||||
networks:
|
networks:
|
||||||
- external_network
|
- external_network
|
||||||
- internal_network
|
- internal_network
|
||||||
@ -20,7 +20,7 @@ services:
|
|||||||
healthcheck:
|
healthcheck:
|
||||||
test: ['CMD', 'redis-cli', 'ping']
|
test: ['CMD', 'redis-cli', 'ping']
|
||||||
volumes:
|
volumes:
|
||||||
- ./tmp/redis:/data
|
- redis-data:/data
|
||||||
|
|
||||||
web:
|
web:
|
||||||
build: .
|
build: .
|
||||||
@ -42,8 +42,10 @@ services:
|
|||||||
LDAP_ADMIN_PASSWORD: passthebutter
|
LDAP_ADMIN_PASSWORD: passthebutter
|
||||||
LDAP_USE_TLS: "false"
|
LDAP_USE_TLS: "false"
|
||||||
REDIS_URL: redis://redis:6379/0
|
REDIS_URL: redis://redis:6379/0
|
||||||
|
ACTIVE_STORAGE_PATH: "/akkounts/tmp/attachments"
|
||||||
RS_REDIS_URL: redis://redis:6379/1
|
RS_REDIS_URL: redis://redis:6379/1
|
||||||
RS_STORAGE_URL: "http://localhost:4567"
|
RS_STORAGE_URL: "http://localhost:4567"
|
||||||
|
S3_ENABLED: false
|
||||||
depends_on:
|
depends_on:
|
||||||
- ldap
|
- ldap
|
||||||
- redis
|
- redis
|
||||||
@ -67,6 +69,7 @@ services:
|
|||||||
REDIS_URL: redis://redis:6379/0
|
REDIS_URL: redis://redis:6379/0
|
||||||
RS_REDIS_URL: redis://redis:6379/1
|
RS_REDIS_URL: redis://redis:6379/1
|
||||||
RS_STORAGE_URL: "http://localhost:4567"
|
RS_STORAGE_URL: "http://localhost:4567"
|
||||||
|
S3_ENABLED: false
|
||||||
depends_on:
|
depends_on:
|
||||||
- ldap
|
- ldap
|
||||||
- redis
|
- redis
|
||||||
@ -81,7 +84,7 @@ services:
|
|||||||
- "9000:9000"
|
- "9000:9000"
|
||||||
- "9001:9001"
|
- "9001:9001"
|
||||||
volumes:
|
volumes:
|
||||||
- ./tmp/minio:/data
|
- minio-data:/data
|
||||||
|
|
||||||
liquor-cabinet:
|
liquor-cabinet:
|
||||||
image: gitea.kosmos.org/5apps/liquor-cabinet:2.0.0-beta.2
|
image: gitea.kosmos.org/5apps/liquor-cabinet:2.0.0-beta.2
|
||||||
@ -116,3 +119,11 @@ networks:
|
|||||||
external_network:
|
external_network:
|
||||||
internal_network:
|
internal_network:
|
||||||
internal: true
|
internal: true
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
389ds-data:
|
||||||
|
driver: local
|
||||||
|
minio-data:
|
||||||
|
driver: local
|
||||||
|
redis-data:
|
||||||
|
driver: local
|
||||||
|
11
spec/components/app_catalog/web_app_icon_component_spec.rb
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
require "rails_helper"
|
||||||
|
|
||||||
|
RSpec.describe AppCatalog::WebAppIconComponent, type: :component do
|
||||||
|
describe "No web app given" do
|
||||||
|
it "renders the default icon" do
|
||||||
|
expect(
|
||||||
|
render_inline(described_class.new(web_app: nil)) {}.to_html
|
||||||
|
).to include("icon-remotestorage")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
55
spec/features/admin/users_spec.rb
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
require "rails_helper"
|
||||||
|
|
||||||
|
RSpec.describe "Admin: User management", type: :feature do
|
||||||
|
let(:admin) { create :user }
|
||||||
|
let(:user) { create :user, id: 2, cn: "alfred", email: "alfred@example.com" }
|
||||||
|
|
||||||
|
before do
|
||||||
|
user.save!
|
||||||
|
|
||||||
|
allow(Devise::LDAP::Adapter).to receive(:get_ldap_param)
|
||||||
|
.with(admin.cn, :admin).and_return(["true"])
|
||||||
|
allow(Devise::LDAP::Adapter).to receive(:get_ldap_param)
|
||||||
|
.with(user.cn, :admin).and_return(nil)
|
||||||
|
allow_any_instance_of(User).to receive(:ldap_entry)
|
||||||
|
.and_return({ uid: user.cn, mail: user.email, display_name: "Freddy" })
|
||||||
|
allow_any_instance_of(LdapManager::FetchAvatar).to receive(:call)
|
||||||
|
.and_return(nil)
|
||||||
|
|
||||||
|
login_as admin, :scope => :user
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "User details page" do
|
||||||
|
before do
|
||||||
|
visit admin_user_path("alfred")
|
||||||
|
end
|
||||||
|
|
||||||
|
it "shows the user info" do
|
||||||
|
within "h1" do
|
||||||
|
expect(page).to have_content("User: alfred")
|
||||||
|
end
|
||||||
|
expect(page).to have_content("alfred@example.com")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
scenario 'Add invitations to account' do
|
||||||
|
visit admin_user_path("alfred")
|
||||||
|
find("#add-invitations").click
|
||||||
|
|
||||||
|
select "5", :from => "amount"
|
||||||
|
uncheck "notify_user"
|
||||||
|
click_button "Add"
|
||||||
|
|
||||||
|
expect(user.invitations.count).to eq(5)
|
||||||
|
end
|
||||||
|
|
||||||
|
scenario 'Remove invitations from account' do
|
||||||
|
3.times { Invitation.create(user: user) }
|
||||||
|
expect(user.invitations.count).to eq(3)
|
||||||
|
|
||||||
|
visit admin_user_path("alfred")
|
||||||
|
find("#remove-invitations").click
|
||||||
|
|
||||||
|
expect(user.invitations.count).to eq(0)
|
||||||
|
end
|
||||||
|
end
|
@ -50,7 +50,7 @@ RSpec.describe 'E-Mail settings', type: :feature do
|
|||||||
|
|
||||||
expect(LdapManager::UpdateEmailPassword).to receive(:call).and_return(true)
|
expect(LdapManager::UpdateEmailPassword).to receive(:call).and_return(true)
|
||||||
expect(LdapManager::UpdateEmailMaildrop).to receive(:call)
|
expect(LdapManager::UpdateEmailMaildrop).to receive(:call)
|
||||||
.with(user.dn, user.address).and_return(true)
|
.with(dn: user.dn, address: user.address).and_return(true)
|
||||||
|
|
||||||
visit setting_path(:email)
|
visit setting_path(:email)
|
||||||
fill_in 'Current account password', with: "valid password"
|
fill_in 'Current account password', with: "valid password"
|
||||||
|
@ -29,7 +29,7 @@ RSpec.describe 'Profile settings', type: :feature do
|
|||||||
|
|
||||||
scenario 'works with valid input' do
|
scenario 'works with valid input' do
|
||||||
expect(LdapManager::UpdateDisplayName).to receive(:call)
|
expect(LdapManager::UpdateDisplayName).to receive(:call)
|
||||||
.with(user.dn, "Marky Mark").and_return(true)
|
.with(dn: user.dn, display_name: "Marky Mark").and_return(true)
|
||||||
|
|
||||||
visit setting_path(:profile)
|
visit setting_path(:profile)
|
||||||
fill_in 'Display name', with: "Marky Mark"
|
fill_in 'Display name', with: "Marky Mark"
|
||||||
|
@ -53,7 +53,7 @@ RSpec.describe "Signup", type: :feature do
|
|||||||
expect(page).to have_content("Choose a password")
|
expect(page).to have_content("Choose a password")
|
||||||
|
|
||||||
expect(CreateAccount).to receive(:call)
|
expect(CreateAccount).to receive(:call)
|
||||||
.with({
|
.with(account: {
|
||||||
username: "tony", domain: "kosmos.org",
|
username: "tony", domain: "kosmos.org",
|
||||||
email: "tony@example.com", password: "a-valid-password",
|
email: "tony@example.com", password: "a-valid-password",
|
||||||
invitation: Invitation.last
|
invitation: Invitation.last
|
||||||
@ -97,7 +97,7 @@ RSpec.describe "Signup", type: :feature do
|
|||||||
expect(page).to have_content("Password is too short")
|
expect(page).to have_content("Password is too short")
|
||||||
|
|
||||||
expect(CreateAccount).to receive(:call)
|
expect(CreateAccount).to receive(:call)
|
||||||
.with({
|
.with(account: {
|
||||||
username: "tony", domain: "kosmos.org",
|
username: "tony", domain: "kosmos.org",
|
||||||
email: "tony@example.com", password: "a-valid-password",
|
email: "tony@example.com", password: "a-valid-password",
|
||||||
invitation: Invitation.last
|
invitation: Invitation.last
|
||||||
|
@ -190,7 +190,7 @@ RSpec.describe User, type: :model do
|
|||||||
|
|
||||||
it "updates the LDAP 'mail' attribute" do
|
it "updates the LDAP 'mail' attribute" do
|
||||||
expect(LdapManager::UpdateEmail).to receive(:call)
|
expect(LdapManager::UpdateEmail).to receive(:call)
|
||||||
.with("cn=willherschel,ou=kosmos.org,cn=users,dc=kosmos,dc=org", "will@hrsch.el")
|
.with(dn: "cn=willherschel,ou=kosmos.org,cn=users,dc=kosmos,dc=org", address: "will@hrsch.el")
|
||||||
user.send :devise_after_confirmation
|
user.send :devise_after_confirmation
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -39,7 +39,7 @@ rescue ActiveRecord::PendingMigrationError => e
|
|||||||
end
|
end
|
||||||
RSpec.configure do |config|
|
RSpec.configure do |config|
|
||||||
# Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
|
# Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
|
||||||
config.fixture_path = "#{::Rails.root}/spec/fixtures"
|
config.fixture_paths = ["#{::Rails.root}/spec/fixtures"]
|
||||||
|
|
||||||
# If you're not using ActiveRecord, or you'd prefer not to run each of your
|
# If you're not using ActiveRecord, or you'd prefer not to run each of your
|
||||||
# examples within a transaction, remove the following line or assign false
|
# examples within a transaction, remove the following line or assign false
|
||||||
|
@ -2,11 +2,11 @@ require 'rails_helper'
|
|||||||
|
|
||||||
RSpec.describe CreateAccount, type: :model do
|
RSpec.describe CreateAccount, type: :model do
|
||||||
describe "#create_user_in_database" do
|
describe "#create_user_in_database" do
|
||||||
let(:service) { CreateAccount.new(
|
let(:service) { CreateAccount.new(account: {
|
||||||
username: 'isaacnewton',
|
username: 'isaacnewton',
|
||||||
email: 'isaacnewton@example.com',
|
email: 'isaacnewton@example.com',
|
||||||
password: 'bright-ideas-in-autumn'
|
password: 'bright-ideas-in-autumn'
|
||||||
)}
|
})}
|
||||||
|
|
||||||
it "creates a new user record in the akkounts database" do
|
it "creates a new user record in the akkounts database" do
|
||||||
expect(User.count).to eq(0)
|
expect(User.count).to eq(0)
|
||||||
@ -19,12 +19,12 @@ RSpec.describe CreateAccount, type: :model do
|
|||||||
|
|
||||||
describe "#update_invitation" do
|
describe "#update_invitation" do
|
||||||
let(:invitation) { create :invitation }
|
let(:invitation) { create :invitation }
|
||||||
let(:service) { CreateAccount.new(
|
let(:service) { CreateAccount.new(account: {
|
||||||
username: 'isaacnewton',
|
username: 'isaacnewton',
|
||||||
email: 'isaacnewton@example.com',
|
email: 'isaacnewton@example.com',
|
||||||
password: 'bright-ideas-in-autumn',
|
password: 'bright-ideas-in-autumn',
|
||||||
invitation: invitation
|
invitation: invitation
|
||||||
)}
|
})}
|
||||||
|
|
||||||
before(:each) do
|
before(:each) do
|
||||||
service.send(:update_invitation, 23)
|
service.send(:update_invitation, 23)
|
||||||
@ -42,11 +42,11 @@ RSpec.describe CreateAccount, type: :model do
|
|||||||
describe "#add_ldap_document" do
|
describe "#add_ldap_document" do
|
||||||
include ActiveJob::TestHelper
|
include ActiveJob::TestHelper
|
||||||
|
|
||||||
let(:service) { CreateAccount.new(
|
let(:service) { CreateAccount.new(account: {
|
||||||
username: 'halfinney',
|
username: 'halfinney',
|
||||||
email: 'halfinney@example.com',
|
email: 'halfinney@example.com',
|
||||||
password: 'remember-remember-the-5th-of-november'
|
password: 'remember-remember-the-5th-of-november'
|
||||||
)}
|
})}
|
||||||
|
|
||||||
it "enqueues a job to create the LDAP user document" do
|
it "enqueues a job to create the LDAP user document" do
|
||||||
service.send(:add_ldap_document)
|
service.send(:add_ldap_document)
|
||||||
@ -68,10 +68,10 @@ RSpec.describe CreateAccount, type: :model do
|
|||||||
describe "#create_lndhub_account" do
|
describe "#create_lndhub_account" do
|
||||||
include ActiveJob::TestHelper
|
include ActiveJob::TestHelper
|
||||||
|
|
||||||
let(:service) { CreateAccount.new(
|
let(:service) { CreateAccount.new(account: {
|
||||||
username: 'halfinney', email: 'halfinney@example.com',
|
username: 'halfinney', email: 'halfinney@example.com',
|
||||||
password: 'bright-ideas-in-winter'
|
password: 'bright-ideas-in-winter'
|
||||||
)}
|
})}
|
||||||
let(:new_user) { create :user, cn: "halfinney", ou: "kosmos.org" }
|
let(:new_user) { create :user, cn: "halfinney", ou: "kosmos.org" }
|
||||||
|
|
||||||
it "enqueues a job to create an LndHub account" do
|
it "enqueues a job to create an LndHub account" do
|
||||||
|
40
spec/services/create_invitations_spec.rb
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
RSpec.describe CreateInvitations, type: :model do
|
||||||
|
include ActiveJob::TestHelper
|
||||||
|
|
||||||
|
let(:user) { create :user }
|
||||||
|
|
||||||
|
describe "#call" do
|
||||||
|
before do
|
||||||
|
CreateInvitations.call(user: user, amount: 5)
|
||||||
|
end
|
||||||
|
|
||||||
|
after(:each) { clear_enqueued_jobs }
|
||||||
|
|
||||||
|
it "creates the right amount of invitations for the given user" do
|
||||||
|
expect(user.invitations.count).to eq(5)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "sends an email notification to the user" do
|
||||||
|
expect(enqueued_jobs.size).to eq(1)
|
||||||
|
expect(enqueued_jobs.first["job_class"]).to eq("ActionMailer::MailDeliveryJob")
|
||||||
|
args = enqueued_jobs.first['arguments']
|
||||||
|
expect(args[0]).to eq("NotificationMailer")
|
||||||
|
expect(args[1]).to eq("new_invitations_available")
|
||||||
|
expect(args[3]["params"]["user"]["_aj_globalid"]).to eq("gid://akkounts/User/1")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#call with notification disabled" do
|
||||||
|
before do
|
||||||
|
CreateInvitations.call(user: user, amount: 3, notify: false)
|
||||||
|
end
|
||||||
|
|
||||||
|
after(:each) { clear_enqueued_jobs }
|
||||||
|
|
||||||
|
it "does not send an email notification to the user" do
|
||||||
|
expect(enqueued_jobs.size).to eq(0)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|