Compare commits
2 Commits
06d2705c4c
...
feature/no
| Author | SHA1 | Date | |
|---|---|---|---|
|
011386fb8d
|
|||
|
4d77f5d38c
|
23
.env.example
23
.env.example
@@ -1,23 +1,6 @@
|
|||||||
# PRIMARY_DOMAIN=kosmos.org
|
# PRIMARY_DOMAIN=kosmos.org
|
||||||
# AKKOUNTS_DOMAIN=accounts.example.com
|
# AKKOUNTS_DOMAIN=accounts.example.com
|
||||||
|
|
||||||
# Generate this using `rails secret`
|
|
||||||
# SECRET_KEY_BASE=
|
|
||||||
|
|
||||||
# Generate these using `rails db:encryption:init`
|
|
||||||
# (Optional, needed for LndHub integration)
|
|
||||||
# ENCRYPTION_PRIMARY_KEY=
|
|
||||||
# ENCRYPTION_KEY_DERIVATION_SALT=
|
|
||||||
|
|
||||||
# The default backend is SQLite
|
|
||||||
# DB_ADAPTER=postgresql
|
|
||||||
# PG_HOST=localhost
|
|
||||||
# PG_PORT=5432
|
|
||||||
# PG_DATABASE=akkounts
|
|
||||||
# PG_DATABASE_QUEUE=akkounts_queue
|
|
||||||
# PG_USERNAME=akkounts
|
|
||||||
# PG_PASSWORD=
|
|
||||||
|
|
||||||
# SMTP_SERVER=smtp.example.com
|
# SMTP_SERVER=smtp.example.com
|
||||||
# SMTP_PORT=587
|
# SMTP_PORT=587
|
||||||
# SMTP_LOGIN=accounts
|
# SMTP_LOGIN=accounts
|
||||||
@@ -37,12 +20,8 @@
|
|||||||
|
|
||||||
# LDAP_HOST=localhost
|
# LDAP_HOST=localhost
|
||||||
# LDAP_PORT=389
|
# LDAP_PORT=389
|
||||||
# LDAP_USE_TLS=false
|
|
||||||
# LDAP_UID_ATTR=cn
|
|
||||||
# LDAP_BASE="ou=kosmos.org,cn=users,dc=kosmos,dc=org"
|
|
||||||
# LDAP_ADMIN_USER="cn=Directory Manager"
|
|
||||||
# LDAP_ADMIN_PASSWORD=passthebutter
|
# LDAP_ADMIN_PASSWORD=passthebutter
|
||||||
# LDAP_SUFFIX="dc=kosmos,dc=org"
|
# LDAP_SUFFIX='dc=kosmos,dc=org'
|
||||||
|
|
||||||
# REDIS_URL='redis://localhost:6379/1'
|
# REDIS_URL='redis://localhost:6379/1'
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
PRIMARY_DOMAIN=kosmos.org
|
PRIMARY_DOMAIN=kosmos.org
|
||||||
AKKOUNTS_DOMAIN=accounts.kosmos.org
|
AKKOUNTS_DOMAIN=accounts.kosmos.org
|
||||||
|
|
||||||
ENCRYPTION_PRIMARY_KEY=YhNLBgCFMAzw5dV3gISxnGrhNDMQwRdn
|
|
||||||
ENCRYPTION_KEY_DERIVATION_SALT=h28g16MRZ1sghF2jTCos1DiLZXUswinR
|
|
||||||
|
|
||||||
REDIS_URL='redis://localhost:6379/0'
|
REDIS_URL='redis://localhost:6379/0'
|
||||||
|
|
||||||
BTCPAY_PUBLIC_URL='https://btcpay.example.com'
|
BTCPAY_PUBLIC_URL='https://btcpay.example.com'
|
||||||
@@ -24,8 +21,7 @@ LNDHUB_PUBLIC_KEY='024cd3be18617f39cf645851e3ba63f51fc13f0bb09e3bb25e6fd4de55648
|
|||||||
NOSTR_PRIVATE_KEY='7c3ef7e448505f0615137af38569d01807d3b05b5005d5ecf8aaafcd40323cea'
|
NOSTR_PRIVATE_KEY='7c3ef7e448505f0615137af38569d01807d3b05b5005d5ecf8aaafcd40323cea'
|
||||||
NOSTR_PUBLIC_KEY='bdd76ce2934b2f591f9fad2ebe9da18f20d2921de527494ba00eeaa0a0efadcf'
|
NOSTR_PUBLIC_KEY='bdd76ce2934b2f591f9fad2ebe9da18f20d2921de527494ba00eeaa0a0efadcf'
|
||||||
|
|
||||||
RS_REDIS_URL='redis://localhost:6379/1'
|
|
||||||
RS_STORAGE_URL='https://storage.kosmos.org'
|
RS_STORAGE_URL='https://storage.kosmos.org'
|
||||||
RS_AKKOUNTS_DOMAIN=localhost
|
RS_REDIS_URL='redis://localhost:6379/1'
|
||||||
|
|
||||||
WEBHOOKS_ALLOWED_IPS='10.1.1.23'
|
WEBHOOKS_ALLOWED_IPS='10.1.1.23'
|
||||||
|
|||||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -37,7 +37,6 @@
|
|||||||
/yarn-error.log
|
/yarn-error.log
|
||||||
yarn-debug.log*
|
yarn-debug.log*
|
||||||
.yarn-integrity
|
.yarn-integrity
|
||||||
bun.lock
|
|
||||||
|
|
||||||
# Ignore local dotenv config file
|
# Ignore local dotenv config file
|
||||||
.env
|
.env
|
||||||
@@ -48,6 +47,3 @@ dump.rdb
|
|||||||
|
|
||||||
/app/assets/builds/*
|
/app/assets/builds/*
|
||||||
!/app/assets/builds/.keep
|
!/app/assets/builds/.keep
|
||||||
|
|
||||||
# Ignore generated ctags
|
|
||||||
*.tags
|
|
||||||
|
|||||||
22
Gemfile
22
Gemfile
@@ -2,13 +2,13 @@ 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', '~> 8.0'
|
gem 'rails', '~> 7.1'
|
||||||
# Use Puma as the app server
|
# Use Puma as the app server
|
||||||
gem 'puma', '~> 6.6'
|
gem 'puma', '~> 4.1'
|
||||||
# View components
|
# View components
|
||||||
gem "view_component"
|
gem "view_component"
|
||||||
# Asset bundler
|
# Separate dependency since Rails 7.0
|
||||||
gem 'propshaft'
|
gem 'sprockets-rails'
|
||||||
# Allows custom JS build tasks to integrate with the asset pipeline
|
# Allows custom JS build tasks to integrate with the asset pipeline
|
||||||
gem 'cssbundling-rails'
|
gem 'cssbundling-rails'
|
||||||
# Use JavaScript with ESM import maps [https://github.com/rails/importmap-rails]
|
# Use JavaScript with ESM import maps [https://github.com/rails/importmap-rails]
|
||||||
@@ -19,12 +19,17 @@ gem "turbo-rails"
|
|||||||
gem "stimulus-rails"
|
gem "stimulus-rails"
|
||||||
# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
|
# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
|
||||||
gem 'jbuilder', '~> 2.7'
|
gem 'jbuilder', '~> 2.7'
|
||||||
|
# Use Redis adapter to run Action Cable in production
|
||||||
|
# gem 'redis', '~> 4.0'
|
||||||
# Use Active Model has_secure_password
|
# Use Active Model has_secure_password
|
||||||
gem 'bcrypt', '~> 3.1'
|
gem 'bcrypt', '~> 3.1'
|
||||||
|
|
||||||
# Configuration
|
# Configuration
|
||||||
gem 'dotenv-rails'
|
gem 'dotenv-rails'
|
||||||
|
|
||||||
|
# Security
|
||||||
|
gem 'lockbox'
|
||||||
|
|
||||||
# Authentication
|
# Authentication
|
||||||
gem 'warden'
|
gem 'warden'
|
||||||
gem 'devise', '~> 4.9.0'
|
gem 'devise', '~> 4.9.0'
|
||||||
@@ -39,8 +44,6 @@ gem 'pagy', '~> 6.0', '>= 6.0.2'
|
|||||||
gem 'flipper'
|
gem 'flipper'
|
||||||
gem 'flipper-active_record'
|
gem 'flipper-active_record'
|
||||||
gem 'flipper-ui'
|
gem 'flipper-ui'
|
||||||
gem 'gpgme', '~> 2.0.24'
|
|
||||||
gem 'zbase32', '~> 0.1.1'
|
|
||||||
|
|
||||||
# HTTP requests
|
# HTTP requests
|
||||||
gem 'faraday'
|
gem 'faraday'
|
||||||
@@ -48,8 +51,8 @@ gem 'down'
|
|||||||
gem 'aws-sdk-s3', require: false
|
gem 'aws-sdk-s3', require: false
|
||||||
|
|
||||||
# Background/scheduled jobs
|
# Background/scheduled jobs
|
||||||
gem 'solid_queue'
|
gem 'sidekiq', '< 7'
|
||||||
gem "mission_control-jobs"
|
gem 'sidekiq-scheduler'
|
||||||
|
|
||||||
# Monitoring
|
# Monitoring
|
||||||
gem "sentry-ruby"
|
gem "sentry-ruby"
|
||||||
@@ -60,11 +63,10 @@ gem 'discourse_api'
|
|||||||
gem "lnurl"
|
gem "lnurl"
|
||||||
gem 'manifique', '~> 1.1.0'
|
gem 'manifique', '~> 1.1.0'
|
||||||
gem 'nostr', '~> 0.6.0'
|
gem 'nostr', '~> 0.6.0'
|
||||||
gem "redis", "~> 5.4"
|
|
||||||
|
|
||||||
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', '>= 2.1'
|
gem 'sqlite3', '~> 1.7.2'
|
||||||
gem 'rspec-rails'
|
gem 'rspec-rails'
|
||||||
gem 'rails-controller-testing'
|
gem 'rails-controller-testing'
|
||||||
end
|
end
|
||||||
|
|||||||
567
Gemfile.lock
567
Gemfile.lock
@@ -1,109 +1,110 @@
|
|||||||
GEM
|
GEM
|
||||||
remote: https://rubygems.org/
|
remote: https://rubygems.org/
|
||||||
specs:
|
specs:
|
||||||
actioncable (8.0.2)
|
actioncable (7.1.3)
|
||||||
actionpack (= 8.0.2)
|
actionpack (= 7.1.3)
|
||||||
activesupport (= 8.0.2)
|
activesupport (= 7.1.3)
|
||||||
nio4r (~> 2.0)
|
nio4r (~> 2.0)
|
||||||
websocket-driver (>= 0.6.1)
|
websocket-driver (>= 0.6.1)
|
||||||
zeitwerk (~> 2.6)
|
zeitwerk (~> 2.6)
|
||||||
actionmailbox (8.0.2)
|
actionmailbox (7.1.3)
|
||||||
actionpack (= 8.0.2)
|
actionpack (= 7.1.3)
|
||||||
activejob (= 8.0.2)
|
activejob (= 7.1.3)
|
||||||
activerecord (= 8.0.2)
|
activerecord (= 7.1.3)
|
||||||
activestorage (= 8.0.2)
|
activestorage (= 7.1.3)
|
||||||
activesupport (= 8.0.2)
|
activesupport (= 7.1.3)
|
||||||
mail (>= 2.8.0)
|
mail (>= 2.7.1)
|
||||||
actionmailer (8.0.2)
|
net-imap
|
||||||
actionpack (= 8.0.2)
|
net-pop
|
||||||
actionview (= 8.0.2)
|
net-smtp
|
||||||
activejob (= 8.0.2)
|
actionmailer (7.1.3)
|
||||||
activesupport (= 8.0.2)
|
actionpack (= 7.1.3)
|
||||||
mail (>= 2.8.0)
|
actionview (= 7.1.3)
|
||||||
|
activejob (= 7.1.3)
|
||||||
|
activesupport (= 7.1.3)
|
||||||
|
mail (~> 2.5, >= 2.5.4)
|
||||||
|
net-imap
|
||||||
|
net-pop
|
||||||
|
net-smtp
|
||||||
rails-dom-testing (~> 2.2)
|
rails-dom-testing (~> 2.2)
|
||||||
actionpack (8.0.2)
|
actionpack (7.1.3)
|
||||||
actionview (= 8.0.2)
|
actionview (= 7.1.3)
|
||||||
activesupport (= 8.0.2)
|
activesupport (= 7.1.3)
|
||||||
nokogiri (>= 1.8.5)
|
nokogiri (>= 1.8.5)
|
||||||
|
racc
|
||||||
rack (>= 2.2.4)
|
rack (>= 2.2.4)
|
||||||
rack-session (>= 1.0.1)
|
rack-session (>= 1.0.1)
|
||||||
rack-test (>= 0.6.3)
|
rack-test (>= 0.6.3)
|
||||||
rails-dom-testing (~> 2.2)
|
rails-dom-testing (~> 2.2)
|
||||||
rails-html-sanitizer (~> 1.6)
|
rails-html-sanitizer (~> 1.6)
|
||||||
useragent (~> 0.16)
|
actiontext (7.1.3)
|
||||||
actiontext (8.0.2)
|
actionpack (= 7.1.3)
|
||||||
actionpack (= 8.0.2)
|
activerecord (= 7.1.3)
|
||||||
activerecord (= 8.0.2)
|
activestorage (= 7.1.3)
|
||||||
activestorage (= 8.0.2)
|
activesupport (= 7.1.3)
|
||||||
activesupport (= 8.0.2)
|
|
||||||
globalid (>= 0.6.0)
|
globalid (>= 0.6.0)
|
||||||
nokogiri (>= 1.8.5)
|
nokogiri (>= 1.8.5)
|
||||||
actionview (8.0.2)
|
actionview (7.1.3)
|
||||||
activesupport (= 8.0.2)
|
activesupport (= 7.1.3)
|
||||||
builder (~> 3.1)
|
builder (~> 3.1)
|
||||||
erubi (~> 1.11)
|
erubi (~> 1.11)
|
||||||
rails-dom-testing (~> 2.2)
|
rails-dom-testing (~> 2.2)
|
||||||
rails-html-sanitizer (~> 1.6)
|
rails-html-sanitizer (~> 1.6)
|
||||||
activejob (8.0.2)
|
activejob (7.1.3)
|
||||||
activesupport (= 8.0.2)
|
activesupport (= 7.1.3)
|
||||||
globalid (>= 0.3.6)
|
globalid (>= 0.3.6)
|
||||||
activemodel (8.0.2)
|
activemodel (7.1.3)
|
||||||
activesupport (= 8.0.2)
|
activesupport (= 7.1.3)
|
||||||
activerecord (8.0.2)
|
activerecord (7.1.3)
|
||||||
activemodel (= 8.0.2)
|
activemodel (= 7.1.3)
|
||||||
activesupport (= 8.0.2)
|
activesupport (= 7.1.3)
|
||||||
timeout (>= 0.4.0)
|
timeout (>= 0.4.0)
|
||||||
activestorage (8.0.2)
|
activestorage (7.1.3)
|
||||||
actionpack (= 8.0.2)
|
actionpack (= 7.1.3)
|
||||||
activejob (= 8.0.2)
|
activejob (= 7.1.3)
|
||||||
activerecord (= 8.0.2)
|
activerecord (= 7.1.3)
|
||||||
activesupport (= 8.0.2)
|
activesupport (= 7.1.3)
|
||||||
marcel (~> 1.0)
|
marcel (~> 1.0)
|
||||||
activesupport (8.0.2)
|
activesupport (7.1.3)
|
||||||
base64
|
base64
|
||||||
benchmark (>= 0.3)
|
|
||||||
bigdecimal
|
bigdecimal
|
||||||
concurrent-ruby (~> 1.0, >= 1.3.1)
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||||
connection_pool (>= 2.2.5)
|
connection_pool (>= 2.2.5)
|
||||||
drb
|
drb
|
||||||
i18n (>= 1.6, < 2)
|
i18n (>= 1.6, < 2)
|
||||||
logger (>= 1.4.2)
|
|
||||||
minitest (>= 5.1)
|
minitest (>= 5.1)
|
||||||
securerandom (>= 0.3)
|
mutex_m
|
||||||
tzinfo (~> 2.0, >= 2.0.5)
|
tzinfo (~> 2.0)
|
||||||
uri (>= 0.13.1)
|
addressable (2.8.6)
|
||||||
addressable (2.8.7)
|
public_suffix (>= 2.0.2, < 6.0)
|
||||||
public_suffix (>= 2.0.2, < 7.0)
|
ast (2.4.2)
|
||||||
ast (2.4.3)
|
aws-eventstream (1.3.0)
|
||||||
aws-eventstream (1.3.2)
|
aws-partitions (1.886.0)
|
||||||
aws-partitions (1.1092.0)
|
aws-sdk-core (3.191.0)
|
||||||
aws-sdk-core (3.222.2)
|
|
||||||
aws-eventstream (~> 1, >= 1.3.0)
|
aws-eventstream (~> 1, >= 1.3.0)
|
||||||
aws-partitions (~> 1, >= 1.992.0)
|
aws-partitions (~> 1, >= 1.651.0)
|
||||||
aws-sigv4 (~> 1.9)
|
aws-sigv4 (~> 1.8)
|
||||||
base64
|
|
||||||
jmespath (~> 1, >= 1.6.1)
|
jmespath (~> 1, >= 1.6.1)
|
||||||
logger
|
aws-sdk-kms (1.77.0)
|
||||||
aws-sdk-kms (1.99.0)
|
aws-sdk-core (~> 3, >= 3.191.0)
|
||||||
aws-sdk-core (~> 3, >= 3.216.0)
|
aws-sigv4 (~> 1.1)
|
||||||
aws-sigv4 (~> 1.5)
|
aws-sdk-s3 (1.143.0)
|
||||||
aws-sdk-s3 (1.183.0)
|
aws-sdk-core (~> 3, >= 3.191.0)
|
||||||
aws-sdk-core (~> 3, >= 3.216.0)
|
|
||||||
aws-sdk-kms (~> 1)
|
aws-sdk-kms (~> 1)
|
||||||
aws-sigv4 (~> 1.5)
|
aws-sigv4 (~> 1.8)
|
||||||
aws-sigv4 (1.11.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.2.0)
|
base64 (0.2.0)
|
||||||
bcrypt (3.1.20)
|
bcrypt (3.1.20)
|
||||||
bech32 (1.5.0)
|
bech32 (1.4.2)
|
||||||
thor (>= 1.1.0)
|
thor (>= 1.1.0)
|
||||||
benchmark (0.4.0)
|
benchmark (0.3.0)
|
||||||
bigdecimal (3.1.9)
|
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)
|
||||||
builder (3.3.0)
|
builder (3.2.4)
|
||||||
capybara (3.40.0)
|
capybara (3.40.0)
|
||||||
addressable
|
addressable
|
||||||
matrix
|
matrix
|
||||||
@@ -113,25 +114,23 @@ GEM
|
|||||||
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)
|
||||||
childprocess (5.1.0)
|
|
||||||
logger (~> 1.5)
|
|
||||||
chunky_png (1.4.0)
|
chunky_png (1.4.0)
|
||||||
concurrent-ruby (1.3.4)
|
concurrent-ruby (1.2.3)
|
||||||
connection_pool (2.5.2)
|
connection_pool (2.4.1)
|
||||||
crack (1.0.0)
|
crack (0.4.6)
|
||||||
bigdecimal
|
bigdecimal
|
||||||
rexml
|
rexml
|
||||||
crass (1.0.6)
|
crass (1.0.6)
|
||||||
cssbundling-rails (1.4.3)
|
cssbundling-rails (1.4.0)
|
||||||
railties (>= 6.0.0)
|
railties (>= 6.0.0)
|
||||||
database_cleaner (2.1.0)
|
database_cleaner (2.0.2)
|
||||||
database_cleaner-active_record (>= 2, < 3)
|
database_cleaner-active_record (>= 2, < 3)
|
||||||
database_cleaner-active_record (2.2.0)
|
database_cleaner-active_record (2.1.0)
|
||||||
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.4.1)
|
date (3.3.4)
|
||||||
devise (4.9.4)
|
devise (4.9.3)
|
||||||
bcrypt (~> 3.0)
|
bcrypt (~> 3.0)
|
||||||
orm_adapter (~> 0.1)
|
orm_adapter (~> 0.1)
|
||||||
railties (>= 4.1.0)
|
railties (>= 4.1.0)
|
||||||
@@ -140,112 +139,105 @@ 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.6.1)
|
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
|
||||||
faraday-multipart
|
faraday-multipart
|
||||||
rack (>= 1.6)
|
rack (>= 1.6)
|
||||||
dotenv (3.1.8)
|
dotenv (2.8.1)
|
||||||
dotenv-rails (3.1.8)
|
dotenv-rails (2.8.1)
|
||||||
dotenv (= 3.1.8)
|
dotenv (= 2.8.1)
|
||||||
railties (>= 6.1)
|
railties (>= 3.2)
|
||||||
down (5.4.2)
|
down (5.4.1)
|
||||||
addressable (~> 2.8)
|
addressable (~> 2.8)
|
||||||
drb (2.2.1)
|
drb (2.2.0)
|
||||||
|
ruby2_keywords
|
||||||
|
e2mmap (0.1.0)
|
||||||
ecdsa (1.2.0)
|
ecdsa (1.2.0)
|
||||||
ecdsa_ext (0.5.1)
|
ecdsa_ext (0.5.1)
|
||||||
ecdsa (~> 1.2.0)
|
ecdsa (~> 1.2.0)
|
||||||
erubi (1.13.1)
|
erubi (1.12.0)
|
||||||
et-orbi (1.2.11)
|
et-orbi (1.2.7)
|
||||||
tzinfo
|
tzinfo
|
||||||
event_emitter (0.2.6)
|
event_emitter (0.2.6)
|
||||||
eventmachine (1.2.7)
|
eventmachine (1.2.7)
|
||||||
factory_bot (6.5.1)
|
factory_bot (6.4.6)
|
||||||
activesupport (>= 6.1.0)
|
activesupport (>= 5.0.0)
|
||||||
factory_bot_rails (6.4.4)
|
factory_bot_rails (6.4.3)
|
||||||
factory_bot (~> 6.5)
|
factory_bot (~> 6.4)
|
||||||
railties (>= 5.0.0)
|
railties (>= 5.0.0)
|
||||||
faker (3.5.1)
|
faker (3.2.3)
|
||||||
i18n (>= 1.8.11, < 2)
|
i18n (>= 1.8.11, < 2)
|
||||||
faraday (2.9.2)
|
faraday (2.9.0)
|
||||||
faraday-net_http (>= 2.0, < 3.2)
|
faraday-net_http (>= 2.0, < 3.2)
|
||||||
faraday-follow_redirects (0.3.0)
|
faraday-follow_redirects (0.3.0)
|
||||||
faraday (>= 1, < 3)
|
faraday (>= 1, < 3)
|
||||||
faraday-multipart (1.1.0)
|
faraday-multipart (1.0.4)
|
||||||
multipart-post (~> 2.0)
|
multipart-post (~> 2)
|
||||||
faraday-net_http (3.1.1)
|
faraday-net_http (3.1.0)
|
||||||
net-http
|
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.17.2)
|
ffi (1.16.3)
|
||||||
ffi (1.17.2-arm64-darwin)
|
flipper (1.2.2)
|
||||||
ffi (1.17.2-x86_64-linux-gnu)
|
|
||||||
flipper (1.3.4)
|
|
||||||
concurrent-ruby (< 2)
|
concurrent-ruby (< 2)
|
||||||
flipper-active_record (1.3.4)
|
flipper-active_record (1.2.2)
|
||||||
activerecord (>= 4.2, < 9)
|
activerecord (>= 4.2, < 8)
|
||||||
flipper (~> 1.3.4)
|
flipper (~> 1.2.2)
|
||||||
flipper-ui (1.3.4)
|
flipper-ui (1.2.2)
|
||||||
erubi (>= 1.0.0, < 2.0.0)
|
erubi (>= 1.0.0, < 2.0.0)
|
||||||
flipper (~> 1.3.4)
|
flipper (~> 1.2.2)
|
||||||
rack (>= 1.4, < 4)
|
rack (>= 1.4, < 4)
|
||||||
rack-protection (>= 1.5.3, < 5.0.0)
|
rack-protection (>= 1.5.3, <= 4.0.0)
|
||||||
rack-session (>= 1.0.2, < 3.0.0)
|
sanitize (< 7)
|
||||||
sanitize (< 8)
|
fugit (1.9.0)
|
||||||
fugit (1.11.1)
|
et-orbi (~> 1, >= 1.2.7)
|
||||||
et-orbi (~> 1, >= 1.2.11)
|
|
||||||
raabro (~> 1.4)
|
raabro (~> 1.4)
|
||||||
globalid (1.2.1)
|
globalid (1.2.1)
|
||||||
activesupport (>= 6.1)
|
activesupport (>= 6.1)
|
||||||
gpgme (2.0.24)
|
hashdiff (1.1.0)
|
||||||
mini_portile2 (~> 2.7)
|
i18n (1.14.1)
|
||||||
hashdiff (1.1.2)
|
|
||||||
i18n (1.14.7)
|
|
||||||
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 (2.1.0)
|
importmap-rails (2.0.1)
|
||||||
actionpack (>= 6.0.0)
|
actionpack (>= 6.0.0)
|
||||||
activesupport (>= 6.0.0)
|
activesupport (>= 6.0.0)
|
||||||
railties (>= 6.0.0)
|
railties (>= 6.0.0)
|
||||||
io-console (0.8.0)
|
io-console (0.7.2)
|
||||||
irb (1.15.2)
|
irb (1.11.1)
|
||||||
pp (>= 0.6.0)
|
rdoc
|
||||||
rdoc (>= 4.0.0)
|
|
||||||
reline (>= 0.4.2)
|
reline (>= 0.4.2)
|
||||||
jaro_winkler (1.6.0)
|
jaro_winkler (1.5.6)
|
||||||
jbuilder (2.13.0)
|
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.11.3)
|
json (2.7.1)
|
||||||
kramdown (2.5.1)
|
kramdown (2.4.0)
|
||||||
rexml (>= 3.3.9)
|
rexml
|
||||||
kramdown-parser-gfm (1.1.0)
|
kramdown-parser-gfm (1.1.0)
|
||||||
kramdown (~> 2.0)
|
kramdown (~> 2.0)
|
||||||
language_server-protocol (3.17.0.4)
|
language_server-protocol (3.17.0.3)
|
||||||
launchy (3.1.1)
|
launchy (2.5.2)
|
||||||
addressable (~> 2.8)
|
addressable (~> 2.8)
|
||||||
childprocess (~> 5.0)
|
letter_opener (1.8.1)
|
||||||
logger (~> 1.6)
|
launchy (>= 2.2, < 3)
|
||||||
letter_opener (1.10.0)
|
letter_opener_web (2.0.0)
|
||||||
launchy (>= 2.2, < 4)
|
actionmailer (>= 5.2)
|
||||||
letter_opener_web (3.0.0)
|
letter_opener (~> 1.7)
|
||||||
actionmailer (>= 6.1)
|
railties (>= 5.2)
|
||||||
letter_opener (~> 1.9)
|
|
||||||
railties (>= 6.1)
|
|
||||||
rexml
|
rexml
|
||||||
lint_roller (1.1.0)
|
listen (3.8.0)
|
||||||
listen (3.9.0)
|
|
||||||
rb-fsevent (~> 0.10, >= 0.10.3)
|
rb-fsevent (~> 0.10, >= 0.10.3)
|
||||||
rb-inotify (~> 0.9, >= 0.9.10)
|
rb-inotify (~> 0.9, >= 0.9.10)
|
||||||
lnurl (1.1.1)
|
lnurl (1.1.0)
|
||||||
bech32 (~> 1.1)
|
bech32 (~> 1.1)
|
||||||
logger (1.7.0)
|
lockbox (1.3.2)
|
||||||
loofah (2.24.0)
|
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)
|
||||||
@@ -257,27 +249,18 @@ GEM
|
|||||||
faraday (~> 2.9.0)
|
faraday (~> 2.9.0)
|
||||||
faraday-follow_redirects (= 0.3.0)
|
faraday-follow_redirects (= 0.3.0)
|
||||||
nokogiri (~> 1.16.0)
|
nokogiri (~> 1.16.0)
|
||||||
marcel (1.0.4)
|
marcel (1.0.2)
|
||||||
matrix (0.4.2)
|
matrix (0.4.2)
|
||||||
method_source (1.1.0)
|
method_source (1.0.0)
|
||||||
mini_magick (4.13.2)
|
mini_magick (4.12.0)
|
||||||
mini_mime (1.1.5)
|
mini_mime (1.1.5)
|
||||||
mini_portile2 (2.8.8)
|
mini_portile2 (2.8.5)
|
||||||
minitest (5.25.5)
|
minitest (5.21.2)
|
||||||
mission_control-jobs (1.0.2)
|
multipart-post (2.3.0)
|
||||||
actioncable (>= 7.1)
|
mutex_m (0.2.0)
|
||||||
actionpack (>= 7.1)
|
net-http (0.4.1)
|
||||||
activejob (>= 7.1)
|
|
||||||
activerecord (>= 7.1)
|
|
||||||
importmap-rails (>= 1.2.1)
|
|
||||||
irb (~> 1.13)
|
|
||||||
railties (>= 7.1)
|
|
||||||
stimulus-rails
|
|
||||||
turbo-rails
|
|
||||||
multipart-post (2.4.1)
|
|
||||||
net-http (0.6.0)
|
|
||||||
uri
|
uri
|
||||||
net-imap (0.5.7)
|
net-imap (0.4.9.1)
|
||||||
date
|
date
|
||||||
net-protocol
|
net-protocol
|
||||||
net-ldap (0.19.0)
|
net-ldap (0.19.0)
|
||||||
@@ -285,15 +268,15 @@ GEM
|
|||||||
net-protocol
|
net-protocol
|
||||||
net-protocol (0.2.2)
|
net-protocol (0.2.2)
|
||||||
timeout
|
timeout
|
||||||
net-smtp (0.5.1)
|
net-smtp (0.4.0.1)
|
||||||
net-protocol
|
net-protocol
|
||||||
nio4r (2.7.4)
|
nio4r (2.7.0)
|
||||||
nokogiri (1.16.8)
|
nokogiri (1.16.0)
|
||||||
mini_portile2 (~> 2.8.2)
|
mini_portile2 (~> 2.8.2)
|
||||||
racc (~> 1.4)
|
racc (~> 1.4)
|
||||||
nokogiri (1.16.8-arm64-darwin)
|
nokogiri (1.16.0-arm64-darwin)
|
||||||
racc (~> 1.4)
|
racc (~> 1.4)
|
||||||
nokogiri (1.16.8-x86_64-linux)
|
nokogiri (1.16.0-x86_64-linux)
|
||||||
racc (~> 1.4)
|
racc (~> 1.4)
|
||||||
nostr (0.6.0)
|
nostr (0.6.0)
|
||||||
bech32 (~> 1.4)
|
bech32 (~> 1.4)
|
||||||
@@ -302,57 +285,45 @@ GEM
|
|||||||
event_emitter (~> 0.2)
|
event_emitter (~> 0.2)
|
||||||
faye-websocket (~> 0.11)
|
faye-websocket (~> 0.11)
|
||||||
json (~> 2.6)
|
json (~> 2.6)
|
||||||
observer (0.1.2)
|
|
||||||
orm_adapter (0.5.0)
|
orm_adapter (0.5.0)
|
||||||
ostruct (0.6.1)
|
pagy (6.4.3)
|
||||||
pagy (6.5.0)
|
parallel (1.24.0)
|
||||||
parallel (1.27.0)
|
parser (3.3.0.5)
|
||||||
parser (3.3.8.0)
|
|
||||||
ast (~> 2.4.1)
|
ast (~> 2.4.1)
|
||||||
racc
|
racc
|
||||||
pg (1.5.9)
|
pg (1.5.4)
|
||||||
pp (0.6.2)
|
psych (5.1.2)
|
||||||
prettyprint
|
|
||||||
prettyprint (0.2.0)
|
|
||||||
prism (1.4.0)
|
|
||||||
propshaft (1.1.0)
|
|
||||||
actionpack (>= 7.0.0)
|
|
||||||
activesupport (>= 7.0.0)
|
|
||||||
rack
|
|
||||||
railties (>= 7.0.0)
|
|
||||||
psych (5.2.3)
|
|
||||||
date
|
|
||||||
stringio
|
stringio
|
||||||
public_suffix (6.0.1)
|
public_suffix (5.0.4)
|
||||||
puma (6.6.0)
|
puma (4.3.12)
|
||||||
nio4r (~> 2.0)
|
nio4r (~> 2.0)
|
||||||
raabro (1.4.0)
|
raabro (1.4.0)
|
||||||
racc (1.8.1)
|
racc (1.7.3)
|
||||||
rack (2.2.13)
|
rack (2.2.8)
|
||||||
rack-protection (3.2.0)
|
rack-protection (3.2.0)
|
||||||
base64 (>= 0.1.0)
|
base64 (>= 0.1.0)
|
||||||
rack (~> 2.2, >= 2.2.4)
|
rack (~> 2.2, >= 2.2.4)
|
||||||
rack-session (1.0.2)
|
rack-session (1.0.2)
|
||||||
rack (< 3)
|
rack (< 3)
|
||||||
rack-test (2.2.0)
|
rack-test (2.1.0)
|
||||||
rack (>= 1.3)
|
rack (>= 1.3)
|
||||||
rackup (1.0.1)
|
rackup (1.0.0)
|
||||||
rack (< 3)
|
rack (< 3)
|
||||||
webrick
|
webrick
|
||||||
rails (8.0.2)
|
rails (7.1.3)
|
||||||
actioncable (= 8.0.2)
|
actioncable (= 7.1.3)
|
||||||
actionmailbox (= 8.0.2)
|
actionmailbox (= 7.1.3)
|
||||||
actionmailer (= 8.0.2)
|
actionmailer (= 7.1.3)
|
||||||
actionpack (= 8.0.2)
|
actionpack (= 7.1.3)
|
||||||
actiontext (= 8.0.2)
|
actiontext (= 7.1.3)
|
||||||
actionview (= 8.0.2)
|
actionview (= 7.1.3)
|
||||||
activejob (= 8.0.2)
|
activejob (= 7.1.3)
|
||||||
activemodel (= 8.0.2)
|
activemodel (= 7.1.3)
|
||||||
activerecord (= 8.0.2)
|
activerecord (= 7.1.3)
|
||||||
activestorage (= 8.0.2)
|
activestorage (= 7.1.3)
|
||||||
activesupport (= 8.0.2)
|
activesupport (= 7.1.3)
|
||||||
bundler (>= 1.15.0)
|
bundler (>= 1.15.0)
|
||||||
railties (= 8.0.2)
|
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)
|
||||||
@@ -361,140 +332,138 @@ GEM
|
|||||||
activesupport (>= 5.0.0)
|
activesupport (>= 5.0.0)
|
||||||
minitest
|
minitest
|
||||||
nokogiri (>= 1.6)
|
nokogiri (>= 1.6)
|
||||||
rails-html-sanitizer (1.6.2)
|
rails-html-sanitizer (1.6.0)
|
||||||
loofah (~> 2.21)
|
loofah (~> 2.21)
|
||||||
nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0)
|
nokogiri (~> 1.14)
|
||||||
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 (8.0.2)
|
railties (7.1.3)
|
||||||
actionpack (= 8.0.2)
|
actionpack (= 7.1.3)
|
||||||
activesupport (= 8.0.2)
|
activesupport (= 7.1.3)
|
||||||
irb (~> 1.13)
|
irb
|
||||||
rackup (>= 1.0.0)
|
rackup (>= 1.0.0)
|
||||||
rake (>= 12.2)
|
rake (>= 12.2)
|
||||||
thor (~> 1.0, >= 1.2.2)
|
thor (~> 1.0, >= 1.2.2)
|
||||||
zeitwerk (~> 2.6)
|
zeitwerk (~> 2.6)
|
||||||
rainbow (3.1.1)
|
rainbow (3.1.1)
|
||||||
rake (13.2.1)
|
rake (13.1.0)
|
||||||
rb-fsevent (0.11.2)
|
rb-fsevent (0.11.2)
|
||||||
rb-inotify (0.11.1)
|
rb-inotify (0.10.1)
|
||||||
ffi (~> 1.0)
|
ffi (~> 1.0)
|
||||||
rbs (3.9.2)
|
rbs (2.8.4)
|
||||||
logger
|
rdoc (6.6.2)
|
||||||
rdoc (6.13.1)
|
|
||||||
psych (>= 4.0.0)
|
psych (>= 4.0.0)
|
||||||
redis (5.4.0)
|
redis (4.8.1)
|
||||||
redis-client (>= 0.22.0)
|
regexp_parser (2.9.0)
|
||||||
redis-client (0.24.0)
|
reline (0.4.2)
|
||||||
connection_pool
|
|
||||||
regexp_parser (2.10.0)
|
|
||||||
reline (0.6.1)
|
|
||||||
io-console (~> 0.5)
|
io-console (~> 0.5)
|
||||||
responders (3.1.1)
|
responders (3.1.1)
|
||||||
actionpack (>= 5.2)
|
actionpack (>= 5.2)
|
||||||
railties (>= 5.2)
|
railties (>= 5.2)
|
||||||
reverse_markdown (3.0.0)
|
reverse_markdown (2.1.1)
|
||||||
nokogiri
|
nokogiri
|
||||||
rexml (3.4.1)
|
rexml (3.2.6)
|
||||||
rqrcode (2.2.0)
|
rqrcode (2.2.0)
|
||||||
chunky_png (~> 1.0)
|
chunky_png (~> 1.0)
|
||||||
rqrcode_core (~> 1.0)
|
rqrcode_core (~> 1.0)
|
||||||
rqrcode_core (1.2.0)
|
rqrcode_core (1.2.0)
|
||||||
rspec-core (3.13.3)
|
rspec-core (3.12.2)
|
||||||
rspec-support (~> 3.13.0)
|
rspec-support (~> 3.12.0)
|
||||||
rspec-expectations (3.13.3)
|
rspec-expectations (3.12.3)
|
||||||
diff-lcs (>= 1.2.0, < 2.0)
|
diff-lcs (>= 1.2.0, < 2.0)
|
||||||
rspec-support (~> 3.13.0)
|
rspec-support (~> 3.12.0)
|
||||||
rspec-mocks (3.13.2)
|
rspec-mocks (3.12.6)
|
||||||
diff-lcs (>= 1.2.0, < 2.0)
|
diff-lcs (>= 1.2.0, < 2.0)
|
||||||
rspec-support (~> 3.13.0)
|
rspec-support (~> 3.12.0)
|
||||||
rspec-rails (7.1.1)
|
rspec-rails (6.1.1)
|
||||||
actionpack (>= 7.0)
|
actionpack (>= 6.1)
|
||||||
activesupport (>= 7.0)
|
activesupport (>= 6.1)
|
||||||
railties (>= 7.0)
|
railties (>= 6.1)
|
||||||
rspec-core (~> 3.13)
|
rspec-core (~> 3.12)
|
||||||
rspec-expectations (~> 3.13)
|
rspec-expectations (~> 3.12)
|
||||||
rspec-mocks (~> 3.13)
|
rspec-mocks (~> 3.12)
|
||||||
rspec-support (~> 3.13)
|
rspec-support (~> 3.12)
|
||||||
rspec-support (3.13.2)
|
rspec-support (3.12.1)
|
||||||
rubocop (1.75.3)
|
rubocop (1.60.2)
|
||||||
json (~> 2.3)
|
json (~> 2.3)
|
||||||
language_server-protocol (~> 3.17.0.2)
|
language_server-protocol (>= 3.17.0)
|
||||||
lint_roller (~> 1.1.0)
|
|
||||||
parallel (~> 1.10)
|
parallel (~> 1.10)
|
||||||
parser (>= 3.3.0.2)
|
parser (>= 3.3.0.2)
|
||||||
rainbow (>= 2.2.2, < 4.0)
|
rainbow (>= 2.2.2, < 4.0)
|
||||||
regexp_parser (>= 2.9.3, < 3.0)
|
regexp_parser (>= 1.8, < 3.0)
|
||||||
rubocop-ast (>= 1.44.0, < 2.0)
|
rexml (>= 3.2.5, < 4.0)
|
||||||
|
rubocop-ast (>= 1.30.0, < 2.0)
|
||||||
ruby-progressbar (~> 1.7)
|
ruby-progressbar (~> 1.7)
|
||||||
unicode-display_width (>= 2.4.0, < 4.0)
|
unicode-display_width (>= 2.4.0, < 3.0)
|
||||||
rubocop-ast (1.44.1)
|
rubocop-ast (1.30.0)
|
||||||
parser (>= 3.3.7.2)
|
parser (>= 3.2.1.0)
|
||||||
prism (~> 1.4)
|
|
||||||
ruby-progressbar (1.13.0)
|
ruby-progressbar (1.13.0)
|
||||||
ruby-vips (2.2.3)
|
ruby-vips (2.2.0)
|
||||||
ffi (~> 1.12)
|
ffi (~> 1.12)
|
||||||
logger
|
ruby2_keywords (0.0.5)
|
||||||
sanitize (7.0.0)
|
rufus-scheduler (3.9.1)
|
||||||
|
fugit (~> 1.1, >= 1.1.6)
|
||||||
|
sanitize (6.1.0)
|
||||||
crass (~> 1.0.2)
|
crass (~> 1.0.2)
|
||||||
nokogiri (>= 1.16.8)
|
nokogiri (>= 1.12.0)
|
||||||
securerandom (0.4.1)
|
sentry-rails (5.16.1)
|
||||||
sentry-rails (5.23.0)
|
|
||||||
railties (>= 5.0)
|
railties (>= 5.0)
|
||||||
sentry-ruby (~> 5.23.0)
|
sentry-ruby (~> 5.16.1)
|
||||||
sentry-ruby (5.23.0)
|
sentry-ruby (5.16.1)
|
||||||
bigdecimal
|
|
||||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||||
solargraph (0.54.2)
|
sidekiq (6.5.12)
|
||||||
|
connection_pool (>= 2.2.5, < 3)
|
||||||
|
rack (~> 2.0)
|
||||||
|
redis (>= 4.5.0, < 5)
|
||||||
|
sidekiq-scheduler (5.0.3)
|
||||||
|
rufus-scheduler (~> 3.2)
|
||||||
|
sidekiq (>= 6, < 8)
|
||||||
|
tilt (>= 1.4.0)
|
||||||
|
solargraph (0.50.0)
|
||||||
backport (~> 1.2)
|
backport (~> 1.2)
|
||||||
benchmark (~> 0.4)
|
benchmark
|
||||||
bundler (~> 2.0)
|
bundler (~> 2.0)
|
||||||
diff-lcs (~> 1.4)
|
diff-lcs (~> 1.4)
|
||||||
jaro_winkler (~> 1.6)
|
e2mmap
|
||||||
|
jaro_winkler (~> 1.5)
|
||||||
kramdown (~> 2.3)
|
kramdown (~> 2.3)
|
||||||
kramdown-parser-gfm (~> 1.1)
|
kramdown-parser-gfm (~> 1.1)
|
||||||
logger (~> 1.6)
|
|
||||||
observer (~> 0.1)
|
|
||||||
ostruct (~> 0.6)
|
|
||||||
parser (~> 3.0)
|
parser (~> 3.0)
|
||||||
rbs (~> 3.3)
|
rbs (~> 2.0)
|
||||||
reverse_markdown (~> 3.0)
|
reverse_markdown (~> 2.0)
|
||||||
rubocop (~> 1.38)
|
rubocop (~> 1.38)
|
||||||
thor (~> 1.0)
|
thor (~> 1.0)
|
||||||
tilt (~> 2.0)
|
tilt (~> 2.0)
|
||||||
yard (~> 0.9, >= 0.9.24)
|
yard (~> 0.9, >= 0.9.24)
|
||||||
yard-solargraph (~> 0.1)
|
sprockets (4.2.1)
|
||||||
solid_queue (1.1.5)
|
concurrent-ruby (~> 1.0)
|
||||||
activejob (>= 7.1)
|
rack (>= 2.2.4, < 4)
|
||||||
activerecord (>= 7.1)
|
sprockets-rails (3.4.2)
|
||||||
concurrent-ruby (>= 1.3.1)
|
actionpack (>= 5.2)
|
||||||
fugit (~> 1.11.0)
|
activesupport (>= 5.2)
|
||||||
railties (>= 7.1)
|
sprockets (>= 3.0.0)
|
||||||
thor (~> 1.3.1)
|
sqlite3 (1.7.2)
|
||||||
sqlite3 (2.6.0)
|
|
||||||
mini_portile2 (~> 2.8.0)
|
mini_portile2 (~> 2.8.0)
|
||||||
sqlite3 (2.6.0-arm64-darwin)
|
sqlite3 (1.7.2-arm64-darwin)
|
||||||
sqlite3 (2.6.0-x86_64-linux-gnu)
|
sqlite3 (1.7.2-x86_64-linux)
|
||||||
stimulus-rails (1.3.4)
|
stimulus-rails (1.3.3)
|
||||||
|
railties (>= 6.0.0)
|
||||||
|
stringio (3.1.0)
|
||||||
|
thor (1.3.0)
|
||||||
|
tilt (2.3.0)
|
||||||
|
timeout (0.4.1)
|
||||||
|
turbo-rails (1.5.0)
|
||||||
|
actionpack (>= 6.0.0)
|
||||||
|
activejob (>= 6.0.0)
|
||||||
railties (>= 6.0.0)
|
railties (>= 6.0.0)
|
||||||
stringio (3.1.7)
|
|
||||||
thor (1.3.2)
|
|
||||||
tilt (2.6.0)
|
|
||||||
timeout (0.4.3)
|
|
||||||
turbo-rails (2.0.13)
|
|
||||||
actionpack (>= 7.1.0)
|
|
||||||
railties (>= 7.1.0)
|
|
||||||
tzinfo (2.0.6)
|
tzinfo (2.0.6)
|
||||||
concurrent-ruby (~> 1.0)
|
concurrent-ruby (~> 1.0)
|
||||||
unicode-display_width (3.1.4)
|
unicode-display_width (2.5.0)
|
||||||
unicode-emoji (~> 4.0, >= 4.0.4)
|
uri (0.13.0)
|
||||||
unicode-emoji (4.0.4)
|
view_component (3.10.0)
|
||||||
uri (1.0.3)
|
activesupport (>= 5.2.0, < 8.0)
|
||||||
useragent (0.16.11)
|
concurrent-ruby (~> 1.0)
|
||||||
view_component (3.22.0)
|
|
||||||
activesupport (>= 5.2.0, < 8.1)
|
|
||||||
concurrent-ruby (= 1.3.4)
|
|
||||||
method_source (~> 1.0)
|
method_source (~> 1.0)
|
||||||
warden (1.2.9)
|
warden (1.2.9)
|
||||||
rack (>= 2.0.9)
|
rack (>= 2.0.9)
|
||||||
@@ -503,22 +472,18 @@ GEM
|
|||||||
activemodel (>= 6.0.0)
|
activemodel (>= 6.0.0)
|
||||||
bindex (>= 0.4.0)
|
bindex (>= 0.4.0)
|
||||||
railties (>= 6.0.0)
|
railties (>= 6.0.0)
|
||||||
webmock (3.25.1)
|
webmock (3.19.1)
|
||||||
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.9.1)
|
webrick (1.8.1)
|
||||||
websocket-driver (0.7.7)
|
websocket-driver (0.7.6)
|
||||||
base64
|
|
||||||
websocket-extensions (>= 0.1.0)
|
websocket-extensions (>= 0.1.0)
|
||||||
websocket-extensions (0.1.5)
|
websocket-extensions (0.1.5)
|
||||||
xpath (3.2.0)
|
xpath (3.2.0)
|
||||||
nokogiri (~> 1.8)
|
nokogiri (~> 1.8)
|
||||||
yard (0.9.37)
|
yard (0.9.34)
|
||||||
yard-solargraph (0.1.0)
|
zeitwerk (2.6.12)
|
||||||
yard (~> 0.9)
|
|
||||||
zbase32 (0.1.1)
|
|
||||||
zeitwerk (2.7.2)
|
|
||||||
|
|
||||||
PLATFORMS
|
PLATFORMS
|
||||||
arm64-darwin-22
|
arm64-darwin-22
|
||||||
@@ -542,7 +507,6 @@ DEPENDENCIES
|
|||||||
flipper
|
flipper
|
||||||
flipper-active_record
|
flipper-active_record
|
||||||
flipper-ui
|
flipper-ui
|
||||||
gpgme (~> 2.0.24)
|
|
||||||
image_processing (~> 1.12.2)
|
image_processing (~> 1.12.2)
|
||||||
importmap-rails
|
importmap-rails
|
||||||
jbuilder (~> 2.7)
|
jbuilder (~> 2.7)
|
||||||
@@ -550,25 +514,25 @@ DEPENDENCIES
|
|||||||
letter_opener_web
|
letter_opener_web
|
||||||
listen (~> 3.2)
|
listen (~> 3.2)
|
||||||
lnurl
|
lnurl
|
||||||
|
lockbox
|
||||||
manifique (~> 1.1.0)
|
manifique (~> 1.1.0)
|
||||||
mission_control-jobs
|
|
||||||
net-ldap
|
net-ldap
|
||||||
nostr (~> 0.6.0)
|
nostr (~> 0.6.0)
|
||||||
pagy (~> 6.0, >= 6.0.2)
|
pagy (~> 6.0, >= 6.0.2)
|
||||||
pg (~> 1.5)
|
pg (~> 1.5)
|
||||||
propshaft
|
puma (~> 4.1)
|
||||||
puma (~> 6.6)
|
rails (~> 7.1)
|
||||||
rails (~> 8.0)
|
|
||||||
rails-controller-testing
|
rails-controller-testing
|
||||||
rails-settings-cached (~> 2.8.3)
|
rails-settings-cached (~> 2.8.3)
|
||||||
redis (~> 5.4)
|
|
||||||
rqrcode (~> 2.0)
|
rqrcode (~> 2.0)
|
||||||
rspec-rails
|
rspec-rails
|
||||||
sentry-rails
|
sentry-rails
|
||||||
sentry-ruby
|
sentry-ruby
|
||||||
|
sidekiq (< 7)
|
||||||
|
sidekiq-scheduler
|
||||||
solargraph
|
solargraph
|
||||||
solid_queue
|
sprockets-rails
|
||||||
sqlite3 (>= 2.1)
|
sqlite3 (~> 1.7.2)
|
||||||
stimulus-rails
|
stimulus-rails
|
||||||
turbo-rails
|
turbo-rails
|
||||||
tzinfo-data
|
tzinfo-data
|
||||||
@@ -576,7 +540,6 @@ DEPENDENCIES
|
|||||||
warden
|
warden
|
||||||
web-console (~> 4.2)
|
web-console (~> 4.2)
|
||||||
webmock
|
webmock
|
||||||
zbase32 (~> 0.1.1)
|
|
||||||
|
|
||||||
BUNDLED WITH
|
BUNDLED WITH
|
||||||
2.5.5
|
2.5.5
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ Running the test suite:
|
|||||||
Running the test suite with Docker Compose requires overriding the Rails
|
Running the test suite with Docker Compose requires overriding the Rails
|
||||||
environment:
|
environment:
|
||||||
|
|
||||||
docker-compose exec -e "RAILS_ENV=test" web rspec
|
docker-compose run -e "RAILS_ENV=test" web rspec
|
||||||
|
|
||||||
### Docker Compose
|
### Docker Compose
|
||||||
|
|
||||||
|
|||||||
4
app/assets/config/manifest.js
Normal file
4
app/assets/config/manifest.js
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
//= link_tree ../images
|
||||||
|
//= link_tree ../../javascript .js
|
||||||
|
//= link_tree ../builds
|
||||||
|
//= link_tree ../../../vendor/javascript .js
|
||||||
@@ -9,5 +9,13 @@ module AppCatalog
|
|||||||
@image_url = image_url_for(web_app.apple_touch_icon)
|
@image_url = image_url_for(web_app.apple_touch_icon)
|
||||||
end
|
end
|
||||||
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
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ class Admin::LightningController < Admin::BaseController
|
|||||||
def index
|
def index
|
||||||
@current_section = :lightning
|
@current_section = :lightning
|
||||||
|
|
||||||
@users = User.pluck(:cn, :ou, :lndhub_username)
|
@users = User.pluck(:cn, :ou, :ln_account)
|
||||||
@accounts = LndhubAccount.with_balances.order(balance: :desc).to_a
|
@accounts = LndhubAccount.with_balances.order(balance: :desc).to_a
|
||||||
|
|
||||||
@ln = {}
|
@ln = {}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ class Admin::UsersController < Admin::BaseController
|
|||||||
|
|
||||||
@services_enabled = @user.services_enabled
|
@services_enabled = @user.services_enabled
|
||||||
|
|
||||||
@ldap_avatar = LdapManager::FetchAvatar.call(cn: @user.cn)
|
@avatar = LdapManager::FetchAvatar.call(cn: @user.cn)
|
||||||
end
|
end
|
||||||
|
|
||||||
# POST /admin/users/:username/invitations
|
# POST /admin/users/:username/invitations
|
||||||
@@ -30,7 +30,7 @@ class Admin::UsersController < Admin::BaseController
|
|||||||
amount = params[:amount].to_i
|
amount = params[:amount].to_i
|
||||||
notify_user = ActiveRecord::Type::Boolean.new.cast(params[:notify_user])
|
notify_user = ActiveRecord::Type::Boolean.new.cast(params[:notify_user])
|
||||||
|
|
||||||
UserManager::CreateInvitations.call(user: @user, amount: amount, notify: notify_user)
|
CreateInvitations.call(user: @user, amount: amount, notify: notify_user)
|
||||||
|
|
||||||
redirect_to admin_user_path(@user.cn), flash: {
|
redirect_to admin_user_path(@user.cn), flash: {
|
||||||
success: "Added #{amount} invitations to #{@user.cn}'s account"
|
success: "Added #{amount} invitations to #{@user.cn}'s account"
|
||||||
|
|||||||
@@ -1,27 +0,0 @@
|
|||||||
class AvatarsController < ApplicationController
|
|
||||||
def show
|
|
||||||
if user = User.find_by(cn: params[:username])
|
|
||||||
http_status :not_found and return unless user.avatar.attached?
|
|
||||||
|
|
||||||
sha256_hash = params[:hash]
|
|
||||||
format = params[:format]&.to_sym || :png
|
|
||||||
# size = params[:size]&.to_sym || :original
|
|
||||||
|
|
||||||
unless user.avatar.filename.to_s == "#{sha256_hash}.#{format}"
|
|
||||||
http_status :not_found and return
|
|
||||||
end
|
|
||||||
|
|
||||||
# TODO See note for avatar_variant in user model
|
|
||||||
# blob = if size == :original
|
|
||||||
# user.avatar.blob
|
|
||||||
# else
|
|
||||||
# user.avatar_variant(size: size)&.blob
|
|
||||||
# end
|
|
||||||
|
|
||||||
data = user.avatar.blob.download
|
|
||||||
send_data data, type: "image/#{format}", disposition: "inline"
|
|
||||||
else
|
|
||||||
http_status :not_found
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -8,9 +8,6 @@ class Discourse::SsoController < ApplicationController
|
|||||||
sso.email = current_user.email
|
sso.email = current_user.email
|
||||||
sso.username = current_user.cn
|
sso.username = current_user.cn
|
||||||
sso.name = current_user.display_name
|
sso.name = current_user.display_name
|
||||||
if current_user.avatar.attached?
|
|
||||||
sso.avatar_url = helpers.image_url_for(current_user.avatar)
|
|
||||||
end
|
|
||||||
sso.admin = current_user.is_admin?
|
sso.admin = current_user.is_admin?
|
||||||
sso.sso_secret = secret
|
sso.sso_secret = secret
|
||||||
|
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ class LnurlpayController < ApplicationController
|
|||||||
pubkey: Setting.lndhub_public_key,
|
pubkey: Setting.lndhub_public_key,
|
||||||
customData: [{
|
customData: [{
|
||||||
customKey: "696969",
|
customKey: "696969",
|
||||||
customValue: @user.lndhub_username
|
customValue: @user.ln_account
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ class Services::LightningController < ApplicationController
|
|||||||
before_action :lndhub_fetch_balance
|
before_action :lndhub_fetch_balance
|
||||||
|
|
||||||
def index
|
def index
|
||||||
@wallet_setup_url = "lndhub://#{current_user.lndhub_username}:#{current_user.lndhub_password}@#{ENV['LNDHUB_PUBLIC_URL']}"
|
@wallet_setup_url = "lndhub://#{current_user.ln_account}:#{current_user.ln_password}@#{ENV['LNDHUB_PUBLIC_URL']}"
|
||||||
end
|
end
|
||||||
|
|
||||||
def transactions
|
def transactions
|
||||||
|
|||||||
@@ -21,12 +21,10 @@ class SettingsController < ApplicationController
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# PUT /settings/:section
|
|
||||||
def update
|
def update
|
||||||
@user.preferences.merge!(user_params[:preferences] || {})
|
@user.preferences.merge!(user_params[:preferences] || {})
|
||||||
@user.display_name = user_params[:display_name]
|
@user.display_name = user_params[:display_name]
|
||||||
@user.avatar_new = user_params[:avatar_new]
|
@user.avatar_new = user_params[:avatar]
|
||||||
@user.pgp_pubkey = user_params[:pgp_pubkey]
|
|
||||||
|
|
||||||
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])
|
||||||
@@ -34,16 +32,7 @@ class SettingsController < ApplicationController
|
|||||||
end
|
end
|
||||||
|
|
||||||
if @user.avatar_new.present?
|
if @user.avatar_new.present?
|
||||||
if store_user_avatar
|
LdapManager::UpdateAvatar.call(dn: @user.dn, file: @user.avatar_new)
|
||||||
LdapManager::UpdateAvatar.call(user: @user)
|
|
||||||
else
|
|
||||||
@validation_errors = @user.errors
|
|
||||||
render :show, status: :unprocessable_entity and return
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if @user.pgp_pubkey && (@user.pgp_pubkey != @user.ldap_entry[:pgp_key])
|
|
||||||
UserManager::UpdatePgpKey.call(user: @user)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
redirect_to setting_path(@settings_section), flash: {
|
redirect_to setting_path(@settings_section), flash: {
|
||||||
@@ -55,7 +44,6 @@ class SettingsController < ApplicationController
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# POST /settings/update_email
|
|
||||||
def update_email
|
def update_email
|
||||||
if @user.valid_ldap_authentication?(security_params[:current_password])
|
if @user.valid_ldap_authentication?(security_params[:current_password])
|
||||||
if @user.update email: email_params[:email]
|
if @user.update email: email_params[:email]
|
||||||
@@ -73,7 +61,6 @@ class SettingsController < ApplicationController
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# POST /settings/reset_email_password
|
|
||||||
def reset_email_password
|
def reset_email_password
|
||||||
@user.current_password = security_params[:current_password]
|
@user.current_password = security_params[:current_password]
|
||||||
|
|
||||||
@@ -96,7 +83,6 @@ class SettingsController < ApplicationController
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# POST /settings/reset_password
|
|
||||||
def reset_password
|
def reset_password
|
||||||
current_user.send_reset_password_instructions
|
current_user.send_reset_password_instructions
|
||||||
sign_out current_user
|
sign_out current_user
|
||||||
@@ -104,7 +90,6 @@ class SettingsController < ApplicationController
|
|||||||
redirect_to check_your_email_path, notice: msg
|
redirect_to check_your_email_path, notice: msg
|
||||||
end
|
end
|
||||||
|
|
||||||
# POST /settings/set_nostr_pubkey
|
|
||||||
def set_nostr_pubkey
|
def set_nostr_pubkey
|
||||||
signed_event = Nostr::Event.new(**nostr_event_from_params)
|
signed_event = Nostr::Event.new(**nostr_event_from_params)
|
||||||
|
|
||||||
@@ -167,8 +152,7 @@ class SettingsController < ApplicationController
|
|||||||
|
|
||||||
def user_params
|
def user_params
|
||||||
params.require(:user).permit(
|
params.require(:user).permit(
|
||||||
:display_name, :avatar_new, :pgp_pubkey,
|
:display_name, :avatar, preferences: UserPreferences.pref_keys
|
||||||
preferences: UserPreferences.pref_keys
|
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -189,40 +173,4 @@ class SettingsController < ApplicationController
|
|||||||
salt = BCrypt::Engine.generate_salt
|
salt = BCrypt::Engine.generate_salt
|
||||||
BCrypt::Engine.hash_secret(password, salt)
|
BCrypt::Engine.hash_secret(password, salt)
|
||||||
end
|
end
|
||||||
|
|
||||||
def store_user_avatar
|
|
||||||
io = @user.avatar_new.tempfile
|
|
||||||
img_data = process_avatar(io)
|
|
||||||
tempfile = Tempfile.create
|
|
||||||
tempfile.binmode
|
|
||||||
tempfile.write(img_data)
|
|
||||||
tempfile.rewind
|
|
||||||
|
|
||||||
hash = Digest::SHA256.hexdigest(img_data)
|
|
||||||
ext = @user.avatar_new.content_type == "image/png" ? "png" : "jpg"
|
|
||||||
filename = "#{hash}.#{ext}"
|
|
||||||
|
|
||||||
if filename == @user.avatar.filename.to_s
|
|
||||||
@user.errors.add(:avatar, "must be a new file/picture")
|
|
||||||
false
|
|
||||||
else
|
|
||||||
key = "users/#{@user.cn}/avatars/#{filename}"
|
|
||||||
@user.avatar.attach io: tempfile, key: key, filename: filename
|
|
||||||
@user.save
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def process_avatar(io)
|
|
||||||
processed = ImageProcessing::Vips
|
|
||||||
.source(io)
|
|
||||||
.resize_to_fill(400, 400)
|
|
||||||
.saver(strip: true)
|
|
||||||
.call
|
|
||||||
io.rewind
|
|
||||||
processed.read
|
|
||||||
rescue Vips::Error => e
|
|
||||||
Sentry.capture_exception(e) if Setting.sentry_enabled?
|
|
||||||
Rails.logger.error { "Image processing failed for avatar: #{e.message}" }
|
|
||||||
nil
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ class SignupController < ApplicationController
|
|||||||
session[:new_user] = nil
|
session[:new_user] = nil
|
||||||
session[:validation_error] = nil
|
session[:validation_error] = nil
|
||||||
|
|
||||||
UserManager::CreateAccount.call(account: {
|
CreateAccount.call(account: {
|
||||||
username: @user.cn,
|
username: @user.cn,
|
||||||
domain: Setting.primary_domain,
|
domain: Setting.primary_domain,
|
||||||
email: @user.email,
|
email: @user.email,
|
||||||
|
|||||||
@@ -1,35 +0,0 @@
|
|||||||
class WebKeyDirectoryController < WellKnownController
|
|
||||||
before_action :allow_cross_origin_requests
|
|
||||||
|
|
||||||
# /.well-known/openpgpkey/hu/:hashed_username(.txt)
|
|
||||||
def show
|
|
||||||
@user = User.find_by(cn: params[:l].downcase)
|
|
||||||
|
|
||||||
if @user.nil? ||
|
|
||||||
@user.pgp_pubkey.blank? ||
|
|
||||||
!@user.pgp_pubkey_contains_user_address?
|
|
||||||
http_status :not_found and return
|
|
||||||
end
|
|
||||||
|
|
||||||
if params[:hashed_username] != @user.wkd_hash
|
|
||||||
http_status :unprocessable_entity and return
|
|
||||||
end
|
|
||||||
|
|
||||||
respond_to do |format|
|
|
||||||
format.text do
|
|
||||||
response.headers['Content-Type'] = 'text/plain'
|
|
||||||
render plain: @user.pgp_pubkey
|
|
||||||
end
|
|
||||||
|
|
||||||
format.any do
|
|
||||||
key = @user.gnupg_key.export
|
|
||||||
send_data key, filename: "#{@user.wkd_hash}.pem",
|
|
||||||
type: "application/octet-stream"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def policy
|
|
||||||
head :ok
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -33,10 +33,6 @@ class WebfingerController < WellKnownController
|
|||||||
links: []
|
links: []
|
||||||
}
|
}
|
||||||
|
|
||||||
if @user.avatar.attached?
|
|
||||||
jrd[:links] += avatar_link
|
|
||||||
end
|
|
||||||
|
|
||||||
if Setting.mastodon_enabled && @user.service_enabled?(:mastodon)
|
if Setting.mastodon_enabled && @user.service_enabled?(:mastodon)
|
||||||
# https://docs.joinmastodon.org/spec/webfinger/
|
# https://docs.joinmastodon.org/spec/webfinger/
|
||||||
jrd[:aliases] += mastodon_aliases
|
jrd[:aliases] += mastodon_aliases
|
||||||
@@ -51,16 +47,6 @@ class WebfingerController < WellKnownController
|
|||||||
jrd
|
jrd
|
||||||
end
|
end
|
||||||
|
|
||||||
def avatar_link
|
|
||||||
[
|
|
||||||
{
|
|
||||||
rel: "http://webfinger.net/rel/avatar",
|
|
||||||
type: @user.avatar.content_type,
|
|
||||||
href: helpers.image_url_for(@user.avatar)
|
|
||||||
}
|
|
||||||
]
|
|
||||||
end
|
|
||||||
|
|
||||||
def mastodon_aliases
|
def mastodon_aliases
|
||||||
[
|
[
|
||||||
"#{Setting.mastodon_public_url}/@#{@user.cn}",
|
"#{Setting.mastodon_public_url}/@#{@user.cn}",
|
||||||
@@ -88,7 +74,7 @@ class WebfingerController < WellKnownController
|
|||||||
end
|
end
|
||||||
|
|
||||||
def remotestorage_link
|
def remotestorage_link
|
||||||
auth_url = new_rs_oauth_url(@username, host: Setting.rs_accounts_domain)
|
auth_url = new_rs_oauth_url(@username, host: Setting.accounts_domain)
|
||||||
storage_url = "#{Setting.rs_storage_url}/#{@username}"
|
storage_url = "#{Setting.rs_storage_url}/#{@username}"
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ class WebhooksController < ApplicationController
|
|||||||
before_action :process_payload
|
before_action :process_payload
|
||||||
|
|
||||||
def lndhub
|
def lndhub
|
||||||
@user = User.find_by!(lndhub_username: @payload[:user_login])
|
@user = User.find_by!(ln_account: @payload[:user_login])
|
||||||
|
|
||||||
if @zap = @user.zaps.find_by(payment_request: @payload[:payment_request])
|
if @zap = @user.zaps.find_by(payment_request: @payload[:payment_request])
|
||||||
settled_at = Time.parse(@payload[:settled_at])
|
settled_at = Time.parse(@payload[:settled_at])
|
||||||
|
|||||||
@@ -14,19 +14,4 @@ module ApplicationHelper
|
|||||||
def badge(text, color)
|
def badge(text, color)
|
||||||
tag.span text, class: "inline-flex items-center rounded-full bg-#{color}-100 px-2.5 py-0.5 text-xs font-medium text-#{color}-800"
|
tag.span text, class: "inline-flex items-center rounded-full bg-#{color}-100 px-2.5 py-0.5 text-xs font-medium text-#{color}-800"
|
||||||
end
|
end
|
||||||
|
|
||||||
def image_url_for(attachment)
|
|
||||||
return s3_image_url(attachment) if Setting.s3_enabled?
|
|
||||||
|
|
||||||
if attachment.record.is_a?(User) && attachment.name == "avatar"
|
|
||||||
hash, format = attachment.blob.filename.to_s.split(".", 2)
|
|
||||||
user_avatar_url(
|
|
||||||
username: attachment.record.cn,
|
|
||||||
hash: hash,
|
|
||||||
format: format
|
|
||||||
)
|
|
||||||
else
|
|
||||||
Rails.application.routes.url_helpers.rails_blob_path(attachment, only_path: true)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,8 +1,14 @@
|
|||||||
import { Controller } from "@hotwired/stimulus"
|
import { Controller } from "@hotwired/stimulus"
|
||||||
|
import { Nostrify } from "nostrify"
|
||||||
|
|
||||||
// Connects to data-controller="settings--nostr-pubkey"
|
// Connects to data-controller="settings--nostr-pubkey"
|
||||||
export default class extends Controller {
|
export default class extends Controller {
|
||||||
static targets = [ "noExtension", "setPubkey", "pubkeyBech32Input" ]
|
static targets = [
|
||||||
|
"noExtension",
|
||||||
|
"setPubkey", "pubkeyBech32Input",
|
||||||
|
"relayList", "relayListStatus",
|
||||||
|
"profileStatusNip05", "profileStatusLud16"
|
||||||
|
]
|
||||||
static values = {
|
static values = {
|
||||||
userAddress: String,
|
userAddress: String,
|
||||||
pubkeyHex: String,
|
pubkeyHex: String,
|
||||||
@@ -15,6 +21,14 @@ export default class extends Controller {
|
|||||||
if (this.hasSetPubkeyTarget) {
|
if (this.hasSetPubkeyTarget) {
|
||||||
this.setPubkeyTarget.disabled = false
|
this.setPubkeyTarget.disabled = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.pubkeyHexValue) {
|
||||||
|
this.discoverUserOnNostr().then(() => {
|
||||||
|
this.renderRelayStatus()
|
||||||
|
this.renderProfileNip05Status()
|
||||||
|
this.renderProfileLud16Status()
|
||||||
|
})
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
this.noExtensionTarget.classList.remove("hidden")
|
this.noExtensionTarget.classList.remove("hidden")
|
||||||
}
|
}
|
||||||
@@ -49,8 +63,172 @@ export default class extends Controller {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async discoverUserOnNostr () {
|
||||||
|
this.nip65Relays = await this.findUserRelays()
|
||||||
|
this.profile = await this.findUserProfile()
|
||||||
|
}
|
||||||
|
|
||||||
|
async findUserRelays () {
|
||||||
|
const controller = new AbortController();
|
||||||
|
const signal = controller.signal;
|
||||||
|
const filters = [{ kinds: [10002], authors: [this.pubkeyHexValue], limit: 1 }]
|
||||||
|
const messages = []
|
||||||
|
|
||||||
|
for await (const msg of this.discoveryPool.req(filters, { signal })) {
|
||||||
|
if (msg[0] === 'EVENT') {
|
||||||
|
if (!messages.find(m => m.id === msg[2].id)) {
|
||||||
|
messages.push(msg[2])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (msg[0] === 'EOSE') { break }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close the relay subscription
|
||||||
|
controller.abort()
|
||||||
|
if (messages.length === 0) { return messages }
|
||||||
|
|
||||||
|
const sortedMessages = messages.sort((a, b) => a.createdAt - b.createdAt)
|
||||||
|
const newestMessage = messages[messages.length - 1]
|
||||||
|
|
||||||
|
return newestMessage.tags.filter(t => t[0] === 'r')
|
||||||
|
.map(t => { return { url: t[1], marker: t[2] } })
|
||||||
|
}
|
||||||
|
|
||||||
|
async findUserProfile () {
|
||||||
|
const controller = new AbortController();
|
||||||
|
const signal = controller.signal;
|
||||||
|
const filters = [{ kinds: [0], authors: [this.pubkeyHexValue], limit: 1 }]
|
||||||
|
const messages = []
|
||||||
|
|
||||||
|
for await (const msg of this.discoveryPool.req(filters, { signal })) {
|
||||||
|
if (msg[0] === 'EVENT') {
|
||||||
|
if (!messages.find(m => m.id === msg[2].id)) {
|
||||||
|
messages.push(msg[2])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (msg[0] === 'EOSE') { break }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close the relay subscription
|
||||||
|
controller.abort()
|
||||||
|
if (messages.length === 0) { return null }
|
||||||
|
|
||||||
|
const sortedMessages = messages.sort((a, b) => a.createdAt - b.createdAt)
|
||||||
|
const newestMessage = messages[messages.length - 1]
|
||||||
|
|
||||||
|
return JSON.parse(newestMessage.content)
|
||||||
|
}
|
||||||
|
|
||||||
|
renderRelayStatus () {
|
||||||
|
let showStatus
|
||||||
|
|
||||||
|
if (this.nip65Relays.length > 0) {
|
||||||
|
if (this.relaysContainAccountsRelay) {
|
||||||
|
showStatus = 'green'
|
||||||
|
} else {
|
||||||
|
showStatus = 'orange'
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
showStatus = 'red'
|
||||||
|
}
|
||||||
|
// showStatus = 'red'
|
||||||
|
|
||||||
|
this.relayListStatusTarget
|
||||||
|
.querySelector(`.status-${showStatus}`)
|
||||||
|
.classList.remove("hidden")
|
||||||
|
}
|
||||||
|
|
||||||
|
renderProfileNip05Status () {
|
||||||
|
let showStatus
|
||||||
|
|
||||||
|
if (this.profile?.nip05) {
|
||||||
|
if (this.profile.nip05 === this.userAddressValue) {
|
||||||
|
showStatus = 'green'
|
||||||
|
} else {
|
||||||
|
showStatus = 'red'
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
showStatus = 'orange'
|
||||||
|
}
|
||||||
|
|
||||||
|
this.profileStatusNip05Target
|
||||||
|
.querySelector(`.status-${showStatus}`)
|
||||||
|
.classList.remove("hidden")
|
||||||
|
}
|
||||||
|
|
||||||
|
renderProfileLud16Status () {
|
||||||
|
let showStatus
|
||||||
|
|
||||||
|
if (this.profile?.lud16) {
|
||||||
|
if (this.profile.lud16 === this.userAddressValue) {
|
||||||
|
showStatus = 'green'
|
||||||
|
} else {
|
||||||
|
showStatus = 'red'
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
showStatus = 'orange'
|
||||||
|
}
|
||||||
|
|
||||||
|
this.profileStatusLud16Target
|
||||||
|
.querySelector(`.status-${showStatus}`)
|
||||||
|
.classList.remove("hidden")
|
||||||
|
}
|
||||||
|
|
||||||
|
// renderRelayList (relays) {
|
||||||
|
// const html = relays.map(relay => `
|
||||||
|
// <li class="flex items-center justify-between p-2 border-b">
|
||||||
|
// <span>${relay.url}</span>
|
||||||
|
// <button
|
||||||
|
// data-action="click->list#handleItemClick"
|
||||||
|
// data-item="${relay.url}"
|
||||||
|
// class="bg-blue-500 text-white px-3 py-1 rounded">
|
||||||
|
// Action
|
||||||
|
// </button>
|
||||||
|
// </li>
|
||||||
|
// `).join("")
|
||||||
|
//
|
||||||
|
// this.relayListTarget.innerHTML = html
|
||||||
|
// }
|
||||||
|
|
||||||
get csrfToken () {
|
get csrfToken () {
|
||||||
const element = document.head.querySelector('meta[name="csrf-token"]')
|
const element = document.head.querySelector('meta[name="csrf-token"]')
|
||||||
return element.getAttribute("content")
|
return element.getAttribute("content")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Used to find a user's profile and relays
|
||||||
|
get discoveryRelays () {
|
||||||
|
return [
|
||||||
|
'ws://localhost:4777',
|
||||||
|
'wss://nostr.kosmos.org',
|
||||||
|
'wss://purplepag.es',
|
||||||
|
// 'wss://relay.nostr.band',
|
||||||
|
// 'wss://njump.me',
|
||||||
|
// 'wss://relay.damus.io',
|
||||||
|
// 'wss://nos.lol',
|
||||||
|
// 'wss://eden.nostr.land',
|
||||||
|
// 'wss://relay.snort.social',
|
||||||
|
// 'wss://nostr.wine',
|
||||||
|
// 'wss://relay.primal.net',
|
||||||
|
// 'wss://nostr.bitcoiner.social',
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
get discoveryPool () {
|
||||||
|
if (!this._discoveryPool) {
|
||||||
|
this._discoveryPool = new Nostrify.NPool({
|
||||||
|
open: (url) => new Nostrify.NRelay1(url),
|
||||||
|
reqRouter: async (filters) => new Map(
|
||||||
|
this.discoveryRelays.map(relayUrl => [ relayUrl, filters ])
|
||||||
|
),
|
||||||
|
eventRouter: async (event) => [],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return this._discoveryPool
|
||||||
|
}
|
||||||
|
|
||||||
|
get relaysContainAccountsRelay () {
|
||||||
|
// TODO use URL from view/settings
|
||||||
|
return !!this.nip65Relays.find(r => r.url.match('wss://nostr.kosmos.org'))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ class CreateLdapUserJob < ApplicationJob
|
|||||||
def perform(username:, domain:, email:, hashed_pw:, confirmed: false)
|
def perform(username:, domain:, email:, hashed_pw:, confirmed: false)
|
||||||
dn = "cn=#{username},ou=#{domain},cn=users,dc=kosmos,dc=org"
|
dn = "cn=#{username},ou=#{domain},cn=users,dc=kosmos,dc=org"
|
||||||
attr = {
|
attr = {
|
||||||
objectclass: ["top", "account", "person", "inetOrgPerson", "extensibleObject"],
|
objectclass: ["top", "account", "person", "extensibleObject"],
|
||||||
cn: username,
|
cn: username,
|
||||||
sn: username,
|
sn: username,
|
||||||
uid: username,
|
uid: username,
|
||||||
|
|||||||
@@ -2,12 +2,12 @@ class CreateLndhubAccountJob < ApplicationJob
|
|||||||
queue_as :default
|
queue_as :default
|
||||||
|
|
||||||
def perform(user)
|
def perform(user)
|
||||||
return if user.lndhub_username.present? && user.lndhub_password.present?
|
return if user.ln_account.present? && user.ln_password.present?
|
||||||
|
|
||||||
lndhub = LndhubV2.new
|
lndhub = LndhubV2.new
|
||||||
credentials = lndhub.create_account
|
credentials = lndhub.create_account
|
||||||
|
|
||||||
user.update! lndhub_username: credentials["login"],
|
user.update! ln_account: credentials["login"],
|
||||||
lndhub_password: credentials["password"]
|
ln_password: credentials["password"]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ class RemoteStorageExpireAuthorizationJob < ApplicationJob
|
|||||||
|
|
||||||
def perform(rs_auth_id)
|
def perform(rs_auth_id)
|
||||||
rs_auth = RemoteStorageAuthorization.find rs_auth_id
|
rs_auth = RemoteStorageAuthorization.find rs_auth_id
|
||||||
|
return unless rs_auth.expire_at.nil? || rs_auth.expire_at <= DateTime.now
|
||||||
|
|
||||||
rs_auth.destroy!
|
rs_auth.destroy!
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,90 +1,3 @@
|
|||||||
class ApplicationMailer < ActionMailer::Base
|
class ApplicationMailer < ActionMailer::Base
|
||||||
default Rails.application.config.action_mailer.default_options
|
|
||||||
layout 'mailer'
|
layout 'mailer'
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def send_mail
|
|
||||||
@template ||= "#{self.class.name.underscore}/#{caller[0][/`([^']*)'/, 1]}"
|
|
||||||
headers['Message-ID'] = message_id
|
|
||||||
|
|
||||||
if @user.pgp_pubkey.present?
|
|
||||||
mail(to: @user.email, subject: "...", content_type: pgp_content_type) do |format|
|
|
||||||
format.text { render plain: pgp_content }
|
|
||||||
end
|
|
||||||
else
|
|
||||||
mail(to: @user.email, subject: @subject) do |format|
|
|
||||||
format.text { render @template }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def from_address
|
|
||||||
self.class.default[:from]
|
|
||||||
end
|
|
||||||
|
|
||||||
def from_domain
|
|
||||||
Mail::Address.new(from_address).domain
|
|
||||||
end
|
|
||||||
|
|
||||||
def message_id
|
|
||||||
@message_id ||= "#{SecureRandom.uuid}@#{from_domain}"
|
|
||||||
end
|
|
||||||
|
|
||||||
def boundary
|
|
||||||
@boundary ||= SecureRandom.hex(8)
|
|
||||||
end
|
|
||||||
|
|
||||||
def pgp_content_type
|
|
||||||
"multipart/encrypted; protocol=\"application/pgp-encrypted\"; boundary=\"------------#{boundary}\""
|
|
||||||
end
|
|
||||||
|
|
||||||
def pgp_nested_content
|
|
||||||
message_content = render_to_string(template: @template)
|
|
||||||
message_content_base64 = Base64.encode64(message_content)
|
|
||||||
nested_boundary = SecureRandom.hex(8)
|
|
||||||
|
|
||||||
<<~NESTED_CONTENT
|
|
||||||
Content-Type: multipart/mixed; boundary="------------#{nested_boundary}"; protected-headers="v1"
|
|
||||||
Subject: #{@subject}
|
|
||||||
From: <#{from_address}>
|
|
||||||
To: #{@user.display_name || @user.cn} <#{@user.email}>
|
|
||||||
Message-ID: <#{message_id}>
|
|
||||||
|
|
||||||
--------------#{nested_boundary}
|
|
||||||
Content-Type: text/plain; charset=UTF-8; format=flowed
|
|
||||||
Content-Transfer-Encoding: base64
|
|
||||||
|
|
||||||
#{message_content_base64}
|
|
||||||
|
|
||||||
--------------#{nested_boundary}--
|
|
||||||
NESTED_CONTENT
|
|
||||||
end
|
|
||||||
|
|
||||||
def pgp_content
|
|
||||||
encrypted_content = UserManager::PgpEncrypt.call(user: @user, text: pgp_nested_content)
|
|
||||||
encrypted_base64 = Base64.encode64(encrypted_content.to_s)
|
|
||||||
|
|
||||||
<<~EMAIL_CONTENT
|
|
||||||
This is an OpenPGP/MIME encrypted message (RFC 4880 and 3156)
|
|
||||||
--------------#{boundary}
|
|
||||||
Content-Type: application/pgp-encrypted
|
|
||||||
Content-Description: PGP/MIME version identification
|
|
||||||
|
|
||||||
Version: 1
|
|
||||||
|
|
||||||
--------------#{boundary}
|
|
||||||
Content-Type: application/octet-stream; name="encrypted.asc"
|
|
||||||
Content-Description: OpenPGP encrypted message
|
|
||||||
Content-Disposition: inline; filename="encrypted.asc"
|
|
||||||
|
|
||||||
-----BEGIN PGP MESSAGE-----
|
|
||||||
|
|
||||||
#{encrypted_base64}
|
|
||||||
|
|
||||||
-----END PGP MESSAGE-----
|
|
||||||
|
|
||||||
--------------#{boundary}--
|
|
||||||
EMAIL_CONTENT
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -18,6 +18,6 @@ class CustomMailer < ApplicationMailer
|
|||||||
@user = params[:user]
|
@user = params[:user]
|
||||||
@subject = params[:subject]
|
@subject = params[:subject]
|
||||||
@body = params[:body]
|
@body = params[:body]
|
||||||
send_mail
|
mail(to: @user.email, subject: @subject)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ class NotificationMailer < ApplicationMailer
|
|||||||
@user = params[:user]
|
@user = params[:user]
|
||||||
@amount_sats = params[:amount_sats]
|
@amount_sats = params[:amount_sats]
|
||||||
@subject = "Sats received"
|
@subject = "Sats received"
|
||||||
send_mail
|
mail to: @user.email, subject: @subject
|
||||||
end
|
end
|
||||||
|
|
||||||
def remotestorage_auth_created
|
def remotestorage_auth_created
|
||||||
@@ -15,19 +15,19 @@ class NotificationMailer < ApplicationMailer
|
|||||||
"#{access} #{directory}"
|
"#{access} #{directory}"
|
||||||
end
|
end
|
||||||
@subject = "New app connected to your storage"
|
@subject = "New app connected to your storage"
|
||||||
send_mail
|
mail to: @user.email, subject: @subject
|
||||||
end
|
end
|
||||||
|
|
||||||
def new_invitations_available
|
def new_invitations_available
|
||||||
@user = params[:user]
|
@user = params[:user]
|
||||||
@subject = "New invitations added to your account"
|
@subject = "New invitations added to your account"
|
||||||
send_mail
|
mail to: @user.email, subject: @subject
|
||||||
end
|
end
|
||||||
|
|
||||||
def bitcoin_donation_confirmed
|
def bitcoin_donation_confirmed
|
||||||
@user = params[:user]
|
@user = params[:user]
|
||||||
@donation = params[:donation]
|
@donation = params[:donation]
|
||||||
@subject = "Donation confirmed"
|
@subject = "Donation confirmed"
|
||||||
send_mail
|
mail to: @user.email, subject: @subject
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -6,9 +6,6 @@ module Settings
|
|||||||
field :remotestorage_enabled, type: :boolean,
|
field :remotestorage_enabled, type: :boolean,
|
||||||
default: ENV["RS_STORAGE_URL"].present?
|
default: ENV["RS_STORAGE_URL"].present?
|
||||||
|
|
||||||
field :rs_accounts_domain, type: :string,
|
|
||||||
default: ENV["RS_AKKOUNTS_DOMAIN"] || ENV["AKKOUNTS_DOMAIN"]
|
|
||||||
|
|
||||||
field :rs_storage_url, type: :string,
|
field :rs_storage_url, type: :string,
|
||||||
default: ENV["RS_STORAGE_URL"].presence
|
default: ENV["RS_STORAGE_URL"].presence
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ class LndhubUser < LndhubBase
|
|||||||
foreign_key: "user_id"
|
foreign_key: "user_id"
|
||||||
|
|
||||||
belongs_to :user, class_name: "User",
|
belongs_to :user, class_name: "User",
|
||||||
primary_key: "lndhub_username",
|
primary_key: "ln_account",
|
||||||
foreign_key: "login"
|
foreign_key: "login"
|
||||||
|
|
||||||
def balance
|
def balance
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ class RemoteStorageAuthorization < ApplicationRecord
|
|||||||
belongs_to :user
|
belongs_to :user
|
||||||
belongs_to :web_app, class_name: "AppCatalog::WebApp", optional: true
|
belongs_to :web_app, class_name: "AppCatalog::WebApp", optional: true
|
||||||
|
|
||||||
serialize :permissions, coder: YAML unless Rails.env.production?
|
serialize :permissions unless Rails.env.production?
|
||||||
|
|
||||||
validates_presence_of :permissions
|
validates_presence_of :permissions
|
||||||
validates_presence_of :client_id
|
validates_presence_of :client_id
|
||||||
@@ -69,19 +69,11 @@ class RemoteStorageAuthorization < ApplicationRecord
|
|||||||
end
|
end
|
||||||
|
|
||||||
def remove_token_expiry_job
|
def remove_token_expiry_job
|
||||||
job_class = RemoteStorageExpireAuthorizationJob
|
queue = Sidekiq::Queue.new(RemoteStorageExpireAuthorizationJob.queue_name)
|
||||||
job_args = [id]
|
queue.each do |job|
|
||||||
|
next unless job.display_class == "RemoteStorageExpireAuthorizationJob"
|
||||||
query = SolidQueue::Job.where(class_name: job_class.to_s)
|
job.delete if job.display_args == [id]
|
||||||
|
end
|
||||||
case ActiveRecord::Base.connection.adapter_name.downcase
|
|
||||||
when /sqlite/
|
|
||||||
query.where("json_extract(arguments, '$.arguments') = ?", job_args.to_json)
|
|
||||||
when /postgres/
|
|
||||||
query.where("CAST(arguments AS jsonb)->>'arguments' = ?", job_args.to_json)
|
|
||||||
else
|
|
||||||
raise "Unsupported database adapter"
|
|
||||||
end.destroy_all
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def find_or_create_web_app
|
def find_or_create_web_app
|
||||||
|
|||||||
@@ -3,10 +3,9 @@ require 'nostr'
|
|||||||
class User < ApplicationRecord
|
class User < ApplicationRecord
|
||||||
include EmailValidatable
|
include EmailValidatable
|
||||||
|
|
||||||
attr_accessor :current_password
|
|
||||||
attr_accessor :display_name
|
attr_accessor :display_name
|
||||||
attr_accessor :avatar_new
|
attr_accessor :avatar_new
|
||||||
attr_accessor :pgp_pubkey
|
attr_accessor :current_password
|
||||||
|
|
||||||
serialize :preferences, coder: UserPreferences
|
serialize :preferences, coder: UserPreferences
|
||||||
|
|
||||||
@@ -23,16 +22,10 @@ class User < ApplicationRecord
|
|||||||
has_many :zaps
|
has_many :zaps
|
||||||
|
|
||||||
has_one :lndhub_user, class_name: "LndhubUser", inverse_of: "user",
|
has_one :lndhub_user, class_name: "LndhubUser", inverse_of: "user",
|
||||||
primary_key: "lndhub_username", foreign_key: "login"
|
primary_key: "ln_account", foreign_key: "login"
|
||||||
|
|
||||||
has_many :accounts, through: :lndhub_user
|
has_many :accounts, through: :lndhub_user
|
||||||
|
|
||||||
#
|
|
||||||
# Attachments
|
|
||||||
#
|
|
||||||
|
|
||||||
has_one_attached :avatar
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Validations
|
# Validations
|
||||||
#
|
#
|
||||||
@@ -56,11 +49,8 @@ class User < ApplicationRecord
|
|||||||
validates_length_of :display_name, minimum: 3, maximum: 35, allow_blank: true,
|
validates_length_of :display_name, minimum: 3, maximum: 35, allow_blank: true,
|
||||||
if: -> { defined?(@display_name) }
|
if: -> { defined?(@display_name) }
|
||||||
|
|
||||||
|
|
||||||
validate :acceptable_avatar
|
validate :acceptable_avatar
|
||||||
|
|
||||||
validate :acceptable_pgp_key_format, if: -> { defined?(@pgp_pubkey) && @pgp_pubkey.present? }
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Scopes
|
# Scopes
|
||||||
#
|
#
|
||||||
@@ -73,7 +63,7 @@ class User < ApplicationRecord
|
|||||||
# Encrypted database columns
|
# Encrypted database columns
|
||||||
#
|
#
|
||||||
|
|
||||||
encrypts :lndhub_password
|
has_encrypted :ln_login, :ln_password
|
||||||
|
|
||||||
# Include default devise modules. Others available are:
|
# Include default devise modules. Others available are:
|
||||||
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
|
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
|
||||||
@@ -84,10 +74,6 @@ class User < ApplicationRecord
|
|||||||
:timeoutable,
|
:timeoutable,
|
||||||
:rememberable
|
:rememberable
|
||||||
|
|
||||||
#
|
|
||||||
# Methods
|
|
||||||
#
|
|
||||||
|
|
||||||
def ldap_before_save
|
def ldap_before_save
|
||||||
self.email = Devise::LDAP::Adapter.get_ldap_param(self.cn, "mail").first
|
self.email = Devise::LDAP::Adapter.get_ldap_param(self.cn, "mail").first
|
||||||
self.ou = dn.split(',')
|
self.ou = dn.split(',')
|
||||||
@@ -170,20 +156,6 @@ class User < ApplicationRecord
|
|||||||
@display_name ||= ldap_entry[:display_name]
|
@display_name ||= ldap_entry[:display_name]
|
||||||
end
|
end
|
||||||
|
|
||||||
# TODO Variant keys are currently broken for some reason
|
|
||||||
# (They use the same key as the main blob, when it should be
|
|
||||||
# "/variants/#{key)"
|
|
||||||
# def avatar_variant(size: :medium)
|
|
||||||
# dimensions = case size
|
|
||||||
# when :large then [400, 400]
|
|
||||||
# when :medium then [256, 256]
|
|
||||||
# when :small then [64, 64]
|
|
||||||
# else [256, 256]
|
|
||||||
# end
|
|
||||||
# format = avatar.content_type == "image/png" ? :png : :jpeg
|
|
||||||
# avatar.variant(resize_to_fill: dimensions, format: format)
|
|
||||||
# end
|
|
||||||
|
|
||||||
def nostr_pubkey
|
def nostr_pubkey
|
||||||
@nostr_pubkey ||= ldap_entry[:nostr_key]
|
@nostr_pubkey ||= ldap_entry[:nostr_key]
|
||||||
end
|
end
|
||||||
@@ -193,22 +165,8 @@ class User < ApplicationRecord
|
|||||||
Nostr::PublicKey.new(nostr_pubkey).to_bech32
|
Nostr::PublicKey.new(nostr_pubkey).to_bech32
|
||||||
end
|
end
|
||||||
|
|
||||||
def pgp_pubkey
|
def avatar
|
||||||
@pgp_pubkey ||= ldap_entry[:pgp_key]
|
@avatar_base64 ||= LdapManager::FetchAvatar.call(cn: cn)
|
||||||
end
|
|
||||||
|
|
||||||
def gnupg_key
|
|
||||||
return nil unless pgp_pubkey.present?
|
|
||||||
GPGME::Key.import(pgp_pubkey)
|
|
||||||
GPGME::Key.get(pgp_fpr)
|
|
||||||
end
|
|
||||||
|
|
||||||
def pgp_pubkey_contains_user_address?
|
|
||||||
gnupg_key.uids.map(&:email).include?(address)
|
|
||||||
end
|
|
||||||
|
|
||||||
def wkd_hash
|
|
||||||
ZBase32.encode(Digest::SHA1.digest(cn))
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def services_enabled
|
def services_enabled
|
||||||
@@ -248,7 +206,7 @@ class User < ApplicationRecord
|
|||||||
return unless avatar_new.present?
|
return unless avatar_new.present?
|
||||||
|
|
||||||
if avatar_new.size > 1.megabyte
|
if avatar_new.size > 1.megabyte
|
||||||
errors.add(:avatar, "must be less than 1MB file size")
|
errors.add(:avatar, "file size is too large")
|
||||||
end
|
end
|
||||||
|
|
||||||
acceptable_types = ["image/jpeg", "image/png"]
|
acceptable_types = ["image/jpeg", "image/png"]
|
||||||
@@ -256,10 +214,4 @@ class User < ApplicationRecord
|
|||||||
errors.add(:avatar, "must be a JPEG or PNG file")
|
errors.add(:avatar, "must be a JPEG or PNG file")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def acceptable_pgp_key_format
|
|
||||||
unless GPGME::Key.valid?(pgp_pubkey)
|
|
||||||
errors.add(:pgp_pubkey, 'is not a valid armored PGP public key block')
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|||||||
54
app/services/create_account.rb
Normal file
54
app/services/create_account.rb
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
class CreateAccount < ApplicationService
|
||||||
|
def initialize(account:)
|
||||||
|
@username = account[:username]
|
||||||
|
@domain = account[:ou] || Setting.primary_domain
|
||||||
|
@email = account[:email]
|
||||||
|
@password = account[:password]
|
||||||
|
@invitation = account[:invitation]
|
||||||
|
@confirmed = account[:confirmed]
|
||||||
|
end
|
||||||
|
|
||||||
|
def call
|
||||||
|
user = create_user_in_database
|
||||||
|
add_ldap_document
|
||||||
|
create_lndhub_account(user) if Setting.lndhub_enabled
|
||||||
|
|
||||||
|
if @invitation.present?
|
||||||
|
update_invitation(user.id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def create_user_in_database
|
||||||
|
User.create!(
|
||||||
|
cn: @username,
|
||||||
|
ou: @domain,
|
||||||
|
email: @email,
|
||||||
|
password: @password,
|
||||||
|
password_confirmation: @password,
|
||||||
|
confirmed_at: @confirmed ? DateTime.now : nil
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def update_invitation(user_id)
|
||||||
|
@invitation.update! invited_user_id: user_id, used_at: DateTime.now
|
||||||
|
end
|
||||||
|
|
||||||
|
def add_ldap_document
|
||||||
|
hashed_pw = Devise.ldap_auth_password_builder.call(@password)
|
||||||
|
CreateLdapUserJob.perform_later(
|
||||||
|
username: @username,
|
||||||
|
domain: @domain,
|
||||||
|
email: @email,
|
||||||
|
hashed_pw: hashed_pw,
|
||||||
|
confirmed: @confirmed
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def create_lndhub_account(user)
|
||||||
|
#TODO enable in development when we have a local lndhub (mock?) API
|
||||||
|
return if Rails.env.development?
|
||||||
|
CreateLndhubAccountJob.perform_later(user)
|
||||||
|
end
|
||||||
|
end
|
||||||
17
app/services/create_invitations.rb
Normal file
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
|
||||||
@@ -22,19 +22,8 @@ class EjabberdApiClient
|
|||||||
post "send_message", payload
|
post "send_message", payload
|
||||||
end
|
end
|
||||||
|
|
||||||
def private_get(user, element_name, namespace)
|
|
||||||
payload = {
|
|
||||||
user: user.cn, host: user.ou,
|
|
||||||
element: element_name, ns: namespace
|
|
||||||
}
|
|
||||||
post "private_get", payload
|
|
||||||
end
|
|
||||||
|
|
||||||
def private_set(user, content)
|
def private_set(user, content)
|
||||||
payload = {
|
payload = { user: user.cn, host: user.ou, element: content }
|
||||||
user: user.cn, host: user.ou,
|
|
||||||
element: content
|
|
||||||
}
|
|
||||||
post "private_set", payload
|
post "private_set", payload
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -5,12 +5,12 @@ module LdapManager
|
|||||||
end
|
end
|
||||||
|
|
||||||
def call
|
def call
|
||||||
treebase = 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)
|
||||||
|
|
||||||
entry = client.search(base: treebase, filter: filter, attributes: attributes).first
|
entry = client.search(base: treebase, filter: filter, attributes: attributes).first
|
||||||
entry[:jpegPhoto].present? ? entry.jpegPhoto.first : nil
|
entry.try(:jpegPhoto) ? entry.jpegPhoto.first : nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -2,41 +2,26 @@ require "image_processing/vips"
|
|||||||
|
|
||||||
module LdapManager
|
module LdapManager
|
||||||
class UpdateAvatar < LdapManagerService
|
class UpdateAvatar < LdapManagerService
|
||||||
def initialize(user:)
|
def initialize(dn:, file:)
|
||||||
@user = user
|
@dn = dn
|
||||||
@dn = user.dn
|
@img_data = process(file)
|
||||||
end
|
end
|
||||||
|
|
||||||
def call
|
def call
|
||||||
unless @user.avatar.attached?
|
replace_attribute @dn, :jpegPhoto, @img_data
|
||||||
Rails.logger.error { "Cannot store empty jpegPhoto for user #{@user.cn}" }
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
img_data = @user.avatar.blob.download
|
|
||||||
jpg_data = process_avatar
|
|
||||||
|
|
||||||
Rails.logger.debug { "Storing new jpegPhoto for user #{@user.cn} in LDAP" }
|
|
||||||
result = replace_attribute(@dn, :jpegPhoto, jpg_data)
|
|
||||||
result == 0
|
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def process_avatar
|
def process(file)
|
||||||
@user.avatar.blob.open do |file|
|
processed = ImageProcessing::Vips
|
||||||
processed = ImageProcessing::Vips
|
.resize_to_fill(512, 512)
|
||||||
.source(file)
|
.source(file)
|
||||||
.resize_to_fill(256, 256)
|
.convert("jpeg")
|
||||||
.convert("jpeg")
|
.saver(strip: true)
|
||||||
.saver(strip: true)
|
.call
|
||||||
.call
|
|
||||||
processed.read
|
Base64.strict_encode64 processed.read
|
||||||
end
|
|
||||||
rescue Vips::Error => e
|
|
||||||
Sentry.capture_exception(e) if Setting.sentry_enabled?
|
|
||||||
Rails.logger.error { "Image processing failed for LDAP avatar: #{e.message}" }
|
|
||||||
nil
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
module LdapManager
|
|
||||||
class UpdatePgpKey < LdapManagerService
|
|
||||||
def initialize(dn:, pubkey:)
|
|
||||||
@dn = dn
|
|
||||||
@pubkey = pubkey
|
|
||||||
end
|
|
||||||
|
|
||||||
def call
|
|
||||||
if @pubkey.present?
|
|
||||||
replace_attribute @dn, :pgpKey, @pubkey
|
|
||||||
else
|
|
||||||
delete_attribute @dn, :pgpKey
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -58,7 +58,7 @@ class LdapService < ApplicationService
|
|||||||
|
|
||||||
attributes = %w[
|
attributes = %w[
|
||||||
dn cn uid mail displayName admin serviceEnabled
|
dn cn uid mail displayName admin serviceEnabled
|
||||||
mailRoutingAddress mailpassword nostrKey pgpKey
|
mailRoutingAddress mailpassword nostrKey
|
||||||
]
|
]
|
||||||
filter = Net::LDAP::Filter.eq("uid", args[:uid] || "*")
|
filter = Net::LDAP::Filter.eq("uid", args[:uid] || "*")
|
||||||
|
|
||||||
@@ -73,8 +73,7 @@ class LdapService < ApplicationService
|
|||||||
services_enabled: e.try(:serviceEnabled),
|
services_enabled: e.try(:serviceEnabled),
|
||||||
email_maildrop: e.try(:mailRoutingAddress),
|
email_maildrop: e.try(:mailRoutingAddress),
|
||||||
email_password: e.try(:mailpassword),
|
email_password: e.try(:mailpassword),
|
||||||
nostr_key: e.try(:nostrKey) ? e.nostrKey.first : nil,
|
nostr_key: e.try(:nostrKey) ? e.nostrKey.first : nil
|
||||||
pgp_key: e.try(:pgpKey) ? e.pgpKey.first : nil
|
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -102,7 +101,7 @@ class LdapService < ApplicationService
|
|||||||
dn = "ou=#{ou},cn=users,#{ldap_suffix}"
|
dn = "ou=#{ou},cn=users,#{ldap_suffix}"
|
||||||
|
|
||||||
aci = <<-EOS
|
aci = <<-EOS
|
||||||
(target="ldap:///cn=*,ou=#{ou},cn=users,#{ldap_suffix}")(targetattr="cn || sn || uid || userPassword || mail || mailRoutingAddress || serviceEnabled || nostrKey || pgpKey || nsRole || objectClass") (version 3.0; acl "service-#{ou.gsub(".", "-")}-read-search"; allow (read,search) userdn="ldap:///uid=service,ou=#{ou},cn=applications,#{ldap_suffix}";)
|
(target="ldap:///cn=*,ou=#{ou},cn=users,#{ldap_suffix}")(targetattr="cn || sn || uid || userPassword || mail || mailRoutingAddress || serviceEnabled || nostrKey || nsRole || objectClass") (version 3.0; acl "service-#{ou.gsub(".", "-")}-read-search"; allow (read,search) userdn="ldap:///uid=service,ou=#{ou},cn=applications,#{ldap_suffix}";)
|
||||||
EOS
|
EOS
|
||||||
|
|
||||||
attrs = {
|
attrs = {
|
||||||
|
|||||||
@@ -33,10 +33,7 @@ class Lndhub < ApplicationService
|
|||||||
end
|
end
|
||||||
|
|
||||||
def authenticate(user)
|
def authenticate(user)
|
||||||
credentials = post "auth?type=auth", {
|
credentials = post "auth?type=auth", { login: user.ln_account, password: user.ln_password }
|
||||||
login: user.lndhub_username,
|
|
||||||
password: user.lndhub_password
|
|
||||||
}
|
|
||||||
self.auth_token = credentials["access_token"]
|
self.auth_token = credentials["access_token"]
|
||||||
self.auth_token
|
self.auth_token
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,56 +0,0 @@
|
|||||||
module UserManager
|
|
||||||
class CreateAccount < UserManagerService
|
|
||||||
def initialize(account:)
|
|
||||||
@username = account[:username]
|
|
||||||
@domain = account[:ou] || Setting.primary_domain
|
|
||||||
@email = account[:email]
|
|
||||||
@password = account[:password]
|
|
||||||
@invitation = account[:invitation]
|
|
||||||
@confirmed = account[:confirmed]
|
|
||||||
end
|
|
||||||
|
|
||||||
def call
|
|
||||||
user = create_user_in_database
|
|
||||||
add_ldap_document
|
|
||||||
create_lndhub_account(user) if Setting.lndhub_enabled
|
|
||||||
|
|
||||||
if @invitation.present?
|
|
||||||
update_invitation(user.id)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def create_user_in_database
|
|
||||||
User.create!(
|
|
||||||
cn: @username,
|
|
||||||
ou: @domain,
|
|
||||||
email: @email,
|
|
||||||
password: @password,
|
|
||||||
password_confirmation: @password,
|
|
||||||
confirmed_at: @confirmed ? DateTime.now : nil
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def update_invitation(user_id)
|
|
||||||
@invitation.update! invited_user_id: user_id, used_at: DateTime.now
|
|
||||||
end
|
|
||||||
|
|
||||||
def add_ldap_document
|
|
||||||
hashed_pw = Devise.ldap_auth_password_builder.call(@password)
|
|
||||||
CreateLdapUserJob.perform_later(
|
|
||||||
username: @username,
|
|
||||||
domain: @domain,
|
|
||||||
email: @email,
|
|
||||||
hashed_pw: hashed_pw,
|
|
||||||
confirmed: @confirmed
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def create_lndhub_account(user)
|
|
||||||
#TODO enable in development when we have a local lndhub (mock?) API
|
|
||||||
return if Rails.env.development?
|
|
||||||
CreateLndhubAccountJob.perform_later(user)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
module UserManager
|
|
||||||
class CreateInvitations < UserManagerService
|
|
||||||
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
|
|
||||||
end
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
require 'gpgme'
|
|
||||||
|
|
||||||
module UserManager
|
|
||||||
class PgpEncrypt < UserManagerService
|
|
||||||
def initialize(user:, text:)
|
|
||||||
@user = user
|
|
||||||
@text = text
|
|
||||||
end
|
|
||||||
|
|
||||||
def call
|
|
||||||
crypto = GPGME::Crypto.new
|
|
||||||
crypto.encrypt(
|
|
||||||
@text,
|
|
||||||
recipients: @user.gnupg_key,
|
|
||||||
always_trust: true
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
module UserManager
|
|
||||||
class UpdatePgpKey < UserManagerService
|
|
||||||
def initialize(user:)
|
|
||||||
@user = user
|
|
||||||
end
|
|
||||||
|
|
||||||
def call
|
|
||||||
if @user.pgp_pubkey.blank?
|
|
||||||
@user.update! pgp_fpr: nil
|
|
||||||
else
|
|
||||||
result = GPGME::Key.import(@user.pgp_pubkey)
|
|
||||||
|
|
||||||
if result.imports.present?
|
|
||||||
@user.update! pgp_fpr: result.imports.first.fpr
|
|
||||||
else
|
|
||||||
# TODO notify Sentry, user
|
|
||||||
raise "Failed to import OpenPGP pubkey"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
LdapManager::UpdatePgpKey.call(dn: @user.dn, pubkey: @user.pgp_pubkey)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
class UserManagerService < ApplicationService
|
|
||||||
end
|
|
||||||
@@ -89,47 +89,13 @@
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="sm:flex-1 sm:pt-0">
|
<section class="sm:flex-1 sm:pt-0">
|
||||||
<h3>LDAP</h3>
|
<% if @avatar.present? %>
|
||||||
<table class="divided">
|
<h3>LDAP<h3>
|
||||||
<tbody>
|
<p>
|
||||||
<tr>
|
<img src="data:image/jpeg;base64,<%= @avatar %>" class="h-48 w-48" />
|
||||||
<th>Avatar</th>
|
</p>
|
||||||
<td>
|
<% end %>
|
||||||
<% if @ldap_avatar.present? %>
|
<!-- <h3>Actions</h3> -->
|
||||||
JPEG size: <%= @ldap_avatar.size %>
|
|
||||||
<% else %>
|
|
||||||
—
|
|
||||||
<% end %>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th>Display name</th>
|
|
||||||
<td><%= @user.display_name || "—" %></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th class="align-top">PGP key</th>
|
|
||||||
<td class="align-top leading-5">
|
|
||||||
<% if @user.pgp_pubkey.present? %>
|
|
||||||
<span class="font-mono" title="<%= @user.pgp_fpr %>">
|
|
||||||
<% if @user.pgp_pubkey_contains_user_address? %>
|
|
||||||
<%= link_to wkd_key_url(hashed_username: @user.wkd_hash, l: @user.cn, format: :txt),
|
|
||||||
class: "ks-text-link", target: "_blank" do %>
|
|
||||||
<%= "#{@user.pgp_fpr[0, 8]}…#{@user.pgp_fpr[-8..-1]}" %>
|
|
||||||
<% end %>
|
|
||||||
<% else %>
|
|
||||||
<%= "#{@user.pgp_fpr[0, 8]}…#{@user.pgp_fpr[-8..-1]}" %>
|
|
||||||
<% end %>
|
|
||||||
</span><br />
|
|
||||||
<% @user.gnupg_key.uids.each do |uid| %>
|
|
||||||
<%= uid.uid %><br />
|
|
||||||
<% end %>
|
|
||||||
<% else %>
|
|
||||||
—
|
|
||||||
<% end %>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -276,7 +242,7 @@
|
|||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td><%= @user.lndhub_username %></td>
|
<td><%= @user.ln_account %></td>
|
||||||
<td><%= number_with_delimiter @lndhub_user.balance %> sats</td>
|
<td><%= number_with_delimiter @lndhub_user.balance %> sats</td>
|
||||||
<td><%= number_with_delimiter @lndhub_user.sum_incoming %> sats</td>
|
<td><%= number_with_delimiter @lndhub_user.sum_incoming %> sats</td>
|
||||||
<td><%= number_with_delimiter @lndhub_user.sum_outgoing %> sats</td>
|
<td><%= number_with_delimiter @lndhub_user.sum_outgoing %> sats</td>
|
||||||
@@ -285,7 +251,7 @@
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<% else %>
|
<% else %>
|
||||||
<p>No LndHub user found for account <strong class="font-mono"><%= @user.lndhub_username %></strong>.
|
<p>No LndHub user found for account <strong class="font-mono"><%= @user.ln_account %></strong>.
|
||||||
<% end %>
|
<% end %>
|
||||||
</section>
|
</section>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|||||||
@@ -14,9 +14,8 @@
|
|||||||
<p class="mb-6">
|
<p class="mb-6">
|
||||||
In order to connect an app to your storage account, give it your address:
|
In order to connect an app to your storage account, give it your address:
|
||||||
</p>
|
</p>
|
||||||
<p data-controller="clipboard" class="flex gap-1 sm:w-2/5">
|
<p data-controller="clipboard" class="flex items-center gap-1 sm:w-2/5">
|
||||||
<img src="/img/logos/icon_remotestorage.svg"
|
<img src="/img/logos/icon_remotestorage.svg" class="inline-block h-6 w-6 mr-1">
|
||||||
class="inline-block h-6 w-6 mr-1 self-center">
|
|
||||||
<input type="text" id="user_address" class="grow"
|
<input type="text" id="user_address" class="grow"
|
||||||
value=<%= current_user.address %> disabled="disabled"
|
value=<%= current_user.address %> disabled="disabled"
|
||||||
data-clipboard-target="source" />
|
data-clipboard-target="source" />
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<%= tag.section data: {
|
<%= tag.section data: {
|
||||||
controller: "settings--account--email",
|
controller: "settings--account--email",
|
||||||
"settings--account--email-validation-failed-value": @validation_errors&.[](:email)&.present?
|
"settings--account--email-validation-failed-value": @validation_errors.present?
|
||||||
} do %>
|
} do %>
|
||||||
<h3>E-Mail</h3>
|
<h3>E-Mail</h3>
|
||||||
<%= form_for(@user, url: update_email_settings_path, method: "post") do |f| %>
|
<%= form_for(@user, url: update_email_settings_path, method: "post") do |f| %>
|
||||||
@@ -23,7 +23,7 @@
|
|||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
</p>
|
</p>
|
||||||
<% if @validation_errors&.[](:email)&.present? %>
|
<% if @validation_errors.present? && @validation_errors[:email].present? %>
|
||||||
<p class="error-msg"><%= @validation_errors[:email].first %></p>
|
<p class="error-msg"><%= @validation_errors[:email].first %></p>
|
||||||
<% end %>
|
<% end %>
|
||||||
<div class="initial-hidden">
|
<div class="initial-hidden">
|
||||||
@@ -41,33 +41,10 @@
|
|||||||
<% end %>
|
<% end %>
|
||||||
<section>
|
<section>
|
||||||
<h3>Password</h3>
|
<h3>Password</h3>
|
||||||
<p class="mb-6">Use the following button to request an email with a password reset link:</p>
|
<p class="mb-8">Use the following button to request an email with a password reset link:</p>
|
||||||
<%= form_with(url: reset_password_settings_path, method: :post) do %>
|
<%= form_with(url: reset_password_settings_path, method: :post) do %>
|
||||||
<p>
|
<p>
|
||||||
<%= submit_tag("Send me a password reset link", class: 'btn-md btn-gray w-full sm:w-auto') %>
|
<%= submit_tag("Send me a password reset link", class: 'btn-md btn-gray w-full sm:w-auto') %>
|
||||||
</p>
|
</p>
|
||||||
<% end %>
|
<% end %>
|
||||||
</section>
|
</section>
|
||||||
<%= form_for(@user, url: setting_path(:account), html: { :method => :put }) do |f| %>
|
|
||||||
<section class="!pt-8 sm:!pt-12">
|
|
||||||
<h3>OpenPGP</h3>
|
|
||||||
<ul role="list">
|
|
||||||
<%= render FormElements::FieldsetComponent.new(
|
|
||||||
title: "Public key",
|
|
||||||
description: "Your OpenPGP public key in ASCII Armor format"
|
|
||||||
) do %>
|
|
||||||
<%= f.text_area :pgp_pubkey,
|
|
||||||
value: @user.pgp_pubkey,
|
|
||||||
class: "h-24 w-full" %>
|
|
||||||
<% if @validation_errors&.[](:pgp_pubkey)&.present? %>
|
|
||||||
<p class="error-msg">This <%= @validation_errors[:pgp_pubkey].first %></p>
|
|
||||||
<% end %>
|
|
||||||
<% end %>
|
|
||||||
</ul>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
<p class="pt-6 border-t border-gray-200 text-right">
|
|
||||||
<%= f.submit 'Save', class: "btn-md btn-blue w-full md:w-auto" %>
|
|
||||||
</p>
|
|
||||||
</section>
|
|
||||||
<% end %>
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
<h3>E-Mail Password</h3>
|
<h3>E-Mail Password</h3>
|
||||||
<%= form_for(@user, url: reset_email_password_settings_path, method: "post") do |f| %>
|
<%= form_for(@user, url: reset_email_password_settings_path, method: "post") do |f| %>
|
||||||
<%= hidden_field_tag :section, "email" %>
|
<%= hidden_field_tag :section, "email" %>
|
||||||
<p class="mb-6">
|
<p class="mb-8">
|
||||||
Use the following button to generate a new email password:
|
Use the following button to generate a new email password:
|
||||||
</p>
|
</p>
|
||||||
<p class="hidden initial-visible">
|
<p class="hidden initial-visible">
|
||||||
|
|||||||
@@ -1,46 +1,32 @@
|
|||||||
<section>
|
<div data-controller="settings--nostr-pubkey"
|
||||||
<h3>Nostr</h3>
|
data-settings--nostr-pubkey-user-address-value="<%= current_user.address %>"
|
||||||
<h4 class="mb-0">Public Key</h4>
|
data-settings--nostr-pubkey-site-value="<%= Setting.accounts_domain %>"
|
||||||
<div data-controller="settings--nostr-pubkey"
|
data-settings--nostr-pubkey-shared-secret-value="<%= session[:shared_secret] %>"
|
||||||
data-settings--nostr-pubkey-user-address-value="<%= current_user.address %>"
|
data-settings--nostr-pubkey-pubkey-hex-value="<%= current_user.nostr_pubkey %>">
|
||||||
data-settings--nostr-pubkey-site-value="<%= Setting.accounts_domain %>"
|
<section>
|
||||||
data-settings--nostr-pubkey-shared-secret-value="<%= session[:shared_secret] %>"
|
<h3>Nostr</h3>
|
||||||
data-settings--nostr-pubkey-pubkey-hex-value="<%= current_user.nostr_pubkey %>">
|
<h4 class="mb-0">
|
||||||
|
Public Key
|
||||||
<p class="<%= current_user.nostr_pubkey.present? ? '' : 'hidden' %> mt-2 flex gap-1">
|
</h4>
|
||||||
|
<p class="<%= current_user.nostr_pubkey.present? ? '' : 'hidden' %> mt-2 flex gap-x-1">
|
||||||
<input type="text" value="<%= current_user.nostr_pubkey_bech32 %>" disabled
|
<input type="text" value="<%= current_user.nostr_pubkey_bech32 %>" disabled
|
||||||
data-settings--nostr-pubkey-target="pubkeyBech32Input"
|
data-settings--nostr-pubkey-target="pubkeyBech32Input"
|
||||||
name="nostr_public_key" class="relative grow" />
|
name="nostr_public_key" class="w-full" />
|
||||||
<%= link_to nostr_pubkey_settings_path,
|
<%= link_to nostr_pubkey_settings_path,
|
||||||
class: 'btn-md btn-outline text-red-700 relative shrink-0',
|
class: 'btn-md btn-outline relative grow-0 shrink-0 text-red-700',
|
||||||
data: { turbo_method: :delete, turbo_confirm: 'Are you sure?' } do %>
|
data: { turbo_method: :delete, turbo_confirm: 'Are you sure?' } do %>
|
||||||
Remove
|
Remove
|
||||||
<% end %>
|
<% end %>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<% if current_user.nostr_pubkey.present? %>
|
<% if current_user.nostr_pubkey.present? %>
|
||||||
<div class="rounded-md bg-blue-50 p-4">
|
<!-- <div> -->
|
||||||
<div class="flex">
|
<!-- Pubkey present -->
|
||||||
<div class="flex-shrink-0">
|
<!-- </div> -->
|
||||||
<svg class="h-5 w-5 text-blue-400" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
|
|
||||||
<path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a.75.75 0 000 1.5h.253a.25.25 0 01.244.304l-.459 2.066A1.75 1.75 0 0010.747 15H11a.75.75 0 000-1.5h-.253a.25.25 0 01-.244-.304l.459-2.066A1.75 1.75 0 009.253 9H9z" clip-rule="evenodd" />
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
<div class="ml-3 flex-1">
|
|
||||||
<p class="text-sm text-blue-800">
|
|
||||||
Your user address <strong><%= current_user.address %></strong> is
|
|
||||||
also a Nostr address now. Use your favorite Nostr app, or for
|
|
||||||
example <a href="http://metadata.nostr.com" target="_blank"
|
|
||||||
class="underline">metadata.nostr.com</a>, to add this
|
|
||||||
<strong>NIP-05</strong> address to your public profile.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<% else %>
|
<% else %>
|
||||||
<p class="my-4">
|
<p class="my-4">
|
||||||
If you use any apps on the Nostr network, you can verify your public key
|
Verify your Nostr public key with us in order to enable Nostr-specific
|
||||||
with us in order to enable Nostr-specific features for your account.
|
features for your account.
|
||||||
</p>
|
</p>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
@@ -58,8 +44,8 @@
|
|||||||
</h3>
|
</h3>
|
||||||
<div class="mt-2 mb-0 text-sm text-blue-800">
|
<div class="mt-2 mb-0 text-sm text-blue-800">
|
||||||
<p>
|
<p>
|
||||||
We recommend Alby, which you can also use for your Lightning
|
We recommend Alby, which you can also use a wallet for your
|
||||||
Wallet.
|
Lightning account.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-4">
|
<div class="mt-4">
|
||||||
@@ -86,5 +72,113 @@
|
|||||||
</button>
|
</button>
|
||||||
</p>
|
</p>
|
||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
</section>
|
||||||
</section>
|
|
||||||
|
<% if current_user.nostr_pubkey.present? %>
|
||||||
|
<section>
|
||||||
|
<h3>Profile</h3>
|
||||||
|
<div data-settings--nostr-pubkey-target="profileStatus" class="mb-4">
|
||||||
|
<p class="status-green hidden flex gap-x-4 items-center">
|
||||||
|
<span class="inline-block h-6 w-6 grow-0 text-emerald-500 %>">
|
||||||
|
<%= render "icons/check-circle" %>
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
You already have a profile for your public key
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
<p class="status-orange hidden flex gap-x-4 items-center">
|
||||||
|
<span class="inline-block h-6 w-6 grow-0 text-amber-500 %>">
|
||||||
|
<%= render "icons/alert-octagon" %>
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
<strong><%= current_user.address %></strong> is not set as your Nostr address
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div data-settings--nostr-pubkey-target="profileStatusNip05" class="mb-4">
|
||||||
|
<p class="status-green hidden flex gap-x-4 items-center">
|
||||||
|
<span class="inline-block h-6 w-6 grow-0 text-emerald-500 %>">
|
||||||
|
<%= render "icons/check-circle" %>
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
<strong><%= current_user.address %></strong> is set as your Nostr address
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
<p class="status-orange hidden flex gap-x-4 items-center">
|
||||||
|
<span class="inline-block h-6 w-6 grow-0 text-amber-500 %>">
|
||||||
|
<%= render "icons/alert-octagon" %>
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
<strong><%= current_user.address %></strong> is not set as your Nostr address
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
<p class="status-red hidden flex gap-x-4 items-center">
|
||||||
|
<span class="inline-block h-6 w-6 grow-0 text-amber-500 %>">
|
||||||
|
<%= render "icons/alert-octagon" %>
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
Your profile's Nostr address is not set to <strong><%= current_user.address %></strong> yet
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div data-settings--nostr-pubkey-target="profileStatusLud16">
|
||||||
|
<p class="status-green hidden flex gap-x-4 items-center">
|
||||||
|
<span class="inline-block h-6 w-6 grow-0 text-emerald-500 %>">
|
||||||
|
<%= render "icons/check-circle" %>
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
<strong><%= current_user.address %></strong> is set as your Lightning address
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
<p class="status-orange hidden flex gap-x-4 items-center">
|
||||||
|
<span class="inline-block h-6 w-6 grow-0 text-amber-500 %>">
|
||||||
|
<%= render "icons/alert-octagon" %>
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
<strong><%= current_user.address %></strong> is not set as your Lightning address yet
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
<p class="status-red hidden flex gap-x-4 items-center">
|
||||||
|
<span class="inline-block h-6 w-6 grow-0 text-amber-500 %>">
|
||||||
|
<%= render "icons/alert-octagon" %>
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
Your profile's Lightning address is not set to <strong><%= current_user.address %></strong> yet
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h3>Relays</h3>
|
||||||
|
<div data-settings--nostr-pubkey-target="relayListStatus">
|
||||||
|
<p class="status-green hidden flex gap-x-4 items-center">
|
||||||
|
<span class="inline-block h-6 w-6 grow-0 text-emerald-500 %>">
|
||||||
|
<%= render "icons/check-circle" %>
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
You have a relay list, and the Kosmos relay is part of it
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
<p class="status-orange hidden flex gap-x-4 items-center">
|
||||||
|
<span class="inline-block h-6 w-6 grow-0 text-amber-500 %>">
|
||||||
|
<%= render "icons/alert-octagon" %>
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
The Kosmos relay is missing from your relay list
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
<p class="status-red hidden flex gap-x-4 items-center">
|
||||||
|
<span class="inline-block h-6 w-6 grow-0 text-amber-500 %>">
|
||||||
|
<%= render "icons/alert-octagon" %>
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
We could not find a relay list for your public key
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<ul data-settings--nostr-pubkey-target="relayList">
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
</button>
|
</button>
|
||||||
</p>
|
</p>
|
||||||
<p class="text-sm text-gray-500">
|
<p class="text-sm text-gray-500">
|
||||||
Your account's address on the Internet
|
Your user address for Chat and Lightning Network.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<%= form_for(@user, url: setting_path(:profile), html: { :method => :put }) do |f| %>
|
<%= form_for(@user, url: setting_path(:profile), html: { :method => :put }) do |f| %>
|
||||||
@@ -31,19 +31,23 @@
|
|||||||
<% end %>
|
<% end %>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
|
<% if Flipper.enabled?(:avatar_upload, current_user) %>
|
||||||
<label class="block">
|
<label class="block">
|
||||||
<p class="font-bold mb-1">Avatar</p>
|
<p class="font-bold mb-1">
|
||||||
<p class="text-gray-500">Default profile picture</p>
|
Avatar
|
||||||
|
</p>
|
||||||
|
<p class="text-gray-500">
|
||||||
|
Default profile picture
|
||||||
|
</p>
|
||||||
<div class="flex items-center gap-6">
|
<div class="flex items-center gap-6">
|
||||||
<% if @user.avatar.attached? %>
|
<% if current_user.avatar.present? %>
|
||||||
<p class="flex-none">
|
<p class="flex-none">
|
||||||
<%= image_tag image_url_for(@user.avatar), class: "h-24 w-24 rounded-lg" %>
|
<%= image_tag "data:image/jpeg;base64,#{current_user.avatar}", class: "h-24 w-24 rounded-lg" %>
|
||||||
</p>
|
</p>
|
||||||
<% end %>
|
<% end %>
|
||||||
<div class="grow">
|
<div class="grow">
|
||||||
<p class="mb-2">
|
<p class="mb-2">
|
||||||
<%= f.file_field :avatar_new, accept: "image/jpeg,image/png" %>
|
<%= f.file_field :avatar, class: "" %>
|
||||||
</p>
|
|
||||||
<p class="text-sm text-gray-500">
|
<p class="text-sm text-gray-500">
|
||||||
JPEG or PNG image, not larger than 1 megabyte
|
JPEG or PNG image, not larger than 1 megabyte
|
||||||
</p>
|
</p>
|
||||||
@@ -53,6 +57,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
<p class="mt-8 pt-6 border-t border-gray-200 text-right">
|
<p class="mt-8 pt-6 border-t border-gray-200 text-right">
|
||||||
<%= f.submit 'Save', class: "btn-md btn-blue w-full md:w-auto" %>
|
<%= f.submit 'Save', class: "btn-md btn-blue w-full md:w-auto" %>
|
||||||
|
|||||||
6
bin/jobs
6
bin/jobs
@@ -1,6 +0,0 @@
|
|||||||
#!/usr/bin/env ruby
|
|
||||||
|
|
||||||
require_relative "../config/environment"
|
|
||||||
require "solid_queue/cli"
|
|
||||||
|
|
||||||
SolidQueue::Cli.start(ARGV)
|
|
||||||
11
bin/rails
11
bin/rails
@@ -1,4 +1,9 @@
|
|||||||
#!/usr/bin/env ruby
|
#!/usr/bin/env ruby
|
||||||
APP_PATH = File.expand_path("../config/application", __dir__)
|
begin
|
||||||
require_relative "../config/boot"
|
load File.expand_path('../spring', __FILE__)
|
||||||
require "rails/commands"
|
rescue LoadError => e
|
||||||
|
raise unless e.message.include?('spring')
|
||||||
|
end
|
||||||
|
APP_PATH = File.expand_path('../config/application', __dir__)
|
||||||
|
require_relative '../config/boot'
|
||||||
|
require 'rails/commands'
|
||||||
|
|||||||
9
bin/rake
9
bin/rake
@@ -1,4 +1,9 @@
|
|||||||
#!/usr/bin/env ruby
|
#!/usr/bin/env ruby
|
||||||
require_relative "../config/boot"
|
begin
|
||||||
require "rake"
|
load File.expand_path('../spring', __FILE__)
|
||||||
|
rescue LoadError => e
|
||||||
|
raise unless e.message.include?('spring')
|
||||||
|
end
|
||||||
|
require_relative '../config/boot'
|
||||||
|
require 'rake'
|
||||||
Rake.application.run
|
Rake.application.run
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
#!/usr/bin/env ruby
|
|
||||||
require "rubygems"
|
|
||||||
require "bundler/setup"
|
|
||||||
|
|
||||||
# explicit rubocop config increases performance slightly while avoiding config confusion.
|
|
||||||
ARGV.unshift("--config", File.expand_path("../.rubocop.yml", __dir__))
|
|
||||||
|
|
||||||
load Gem.bin_path("rubocop", "rubocop")
|
|
||||||
34
bin/setup
34
bin/setup
@@ -1,34 +1,36 @@
|
|||||||
#!/usr/bin/env ruby
|
#!/usr/bin/env ruby
|
||||||
require "fileutils"
|
require 'fileutils'
|
||||||
|
|
||||||
APP_ROOT = File.expand_path("..", __dir__)
|
# path to your application root.
|
||||||
|
APP_ROOT = File.expand_path('..', __dir__)
|
||||||
|
|
||||||
def system!(*args)
|
def system!(*args)
|
||||||
system(*args, exception: true)
|
system(*args) || abort("\n== Command #{args} failed ==")
|
||||||
end
|
end
|
||||||
|
|
||||||
FileUtils.chdir APP_ROOT do
|
FileUtils.chdir APP_ROOT do
|
||||||
# This script is a way to set up or update your development environment automatically.
|
# This script is a way to setup or update your development environment automatically.
|
||||||
# This script is idempotent, so that you can run it at any time and get an expectable outcome.
|
# This script is idempotent, so that you can run it at anytime and get an expectable outcome.
|
||||||
# Add necessary setup steps to this file.
|
# Add necessary setup steps to this file.
|
||||||
|
|
||||||
puts "== Installing dependencies =="
|
puts '== Installing dependencies =='
|
||||||
system("bundle check") || system!("bundle install")
|
system! 'gem install bundler --conservative'
|
||||||
|
system('bundle check') || system!('bundle install')
|
||||||
|
|
||||||
|
# Install JavaScript dependencies
|
||||||
|
# system('bin/yarn')
|
||||||
|
|
||||||
# puts "\n== Copying sample files =="
|
# puts "\n== Copying sample files =="
|
||||||
# unless File.exist?("config/database.yml")
|
# unless File.exist?('config/database.yml')
|
||||||
# FileUtils.cp "config/database.yml.sample", "config/database.yml"
|
# FileUtils.cp 'config/database.yml.sample', 'config/database.yml'
|
||||||
# end
|
# end
|
||||||
|
|
||||||
puts "\n== Preparing database =="
|
puts "\n== Preparing database =="
|
||||||
system! "bin/rails db:prepare"
|
system! 'bin/rails db:prepare'
|
||||||
|
|
||||||
puts "\n== Removing old logs and tempfiles =="
|
puts "\n== Removing old logs and tempfiles =="
|
||||||
system! "bin/rails log:clear tmp:clear"
|
system! 'bin/rails log:clear tmp:clear'
|
||||||
|
|
||||||
unless ARGV.include?("--skip-server")
|
puts "\n== Restarting application server =="
|
||||||
puts "\n== Starting development server =="
|
system! 'bin/rails restart'
|
||||||
STDOUT.flush # flush the output before exec(2) so that it displays
|
|
||||||
exec "bin/dev"
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
require_relative "boot"
|
require_relative 'boot'
|
||||||
|
|
||||||
require "rails"
|
require "rails"
|
||||||
# Pick the frameworks you want:
|
# Pick the frameworks you want:
|
||||||
@@ -12,6 +12,7 @@ require "action_mailbox/engine"
|
|||||||
# require "action_text/engine"
|
# require "action_text/engine"
|
||||||
require "action_view/railtie"
|
require "action_view/railtie"
|
||||||
require "action_cable/engine"
|
require "action_cable/engine"
|
||||||
|
require "sprockets/railtie"
|
||||||
# require "rails/test_unit/railtie"
|
# require "rails/test_unit/railtie"
|
||||||
|
|
||||||
# Require the gems listed in Gemfile, including any gems
|
# Require the gems listed in Gemfile, including any gems
|
||||||
@@ -21,20 +22,12 @@ Bundler.require(*Rails.groups)
|
|||||||
module Akkounts
|
module Akkounts
|
||||||
class Application < Rails::Application
|
class Application < Rails::Application
|
||||||
# Initialize configuration defaults for originally generated Rails version.
|
# Initialize configuration defaults for originally generated Rails version.
|
||||||
config.load_defaults 8.0
|
config.load_defaults 7.0
|
||||||
|
|
||||||
# Please, add to the `ignore` list any other `lib` subdirectories that do
|
# Settings in config/environments/* take precedence over those specified here.
|
||||||
# not contain `.rb` files, or that should not be reloaded or eager loaded.
|
# Application configuration can go into files in config/initializers
|
||||||
# Common ones are `templates`, `generators`, or `middleware`, for example.
|
# -- all .rb files in that directory are automatically loaded after loading
|
||||||
config.autoload_lib(ignore: %w[assets tasks])
|
# the framework and any gems in your application.
|
||||||
|
|
||||||
# Configuration for the application, engines, and railties goes here.
|
|
||||||
#
|
|
||||||
# These settings can be overridden in specific environments using the files
|
|
||||||
# in config/environments, which are processed later.
|
|
||||||
#
|
|
||||||
# config.time_zone = "Central Time (US & Canada)"
|
|
||||||
# config.eager_load_paths << Rails.root.join("extras")
|
|
||||||
|
|
||||||
# Don't generate system test files.
|
# Don't generate system test files.
|
||||||
config.generators.system_tests = nil
|
config.generators.system_tests = nil
|
||||||
@@ -47,15 +40,7 @@ module Akkounts
|
|||||||
g.stylesheets false
|
g.stylesheets false
|
||||||
end
|
end
|
||||||
|
|
||||||
config.active_job.queue_adapter = :solid_queue
|
config.active_job.queue_adapter = :sidekiq
|
||||||
config.mission_control.jobs.http_basic_auth_enabled = false
|
|
||||||
|
|
||||||
config.action_mailer.deliver_later_queue_name = nil # use "default" queue
|
config.action_mailer.deliver_later_queue_name = nil # use "default" queue
|
||||||
|
|
||||||
# The default includes webp, which requires webp support everywhere
|
|
||||||
config.active_storage.web_image_content_types = %w[image/png image/jpeg image/gif]
|
|
||||||
|
|
||||||
config.active_record.encryption.primary_key = ENV["ENCRYPTION_PRIMARY_KEY"]
|
|
||||||
config.active_record.encryption.key_derivation_salt = ENV["ENCRYPTION_KEY_DERIVATION_SALT"]
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
1
config/credentials.yml.enc
Normal file
1
config/credentials.yml.enc
Normal file
@@ -0,0 +1 @@
|
|||||||
|
tmI5vm7qZhaigr52jEBVWkRdj+EE+9OmPh3vWXC7kA/OHuuucpr7SodychuMkQDPLM0BLk88LFsqvRIR+mqnLWpRC+P9aeUFE6ohxSWzcAd7Y4sgxUD8zpCRPndrwTw0hxXXj1WZSYeWn4BoAB34aV+gYen2MajZF3a95hJGtS5yjgWxvLVkQQKqRDfykkfX6fCS0BPo5X7sT7m4xwCATD/D4219wajm5W3TIdkriHtwt28ZLspaRWA5e0UkzKf8+/Gaj2CrW7UWcvew8R93zQ5RA2/Sp3sDTVN+kLz9I9Q095lQC0ywCAEFYHeKmc2tjrzqRaAAWu06xmWLqGIg21G+A/UU9lUJOkIpxQACWoOfS2IoXR1nXhgXMopkz3aCBXDxKw554v4H2QyOceOsuRf2C685ibMqzQkKMmJ4tcbiOJL77DUc08JTjB8Dq4Ohr8sMzXbV/hATevjYoRP0XarLekqhLv90ZLuIVY16DwB0CzACeNBKeKbeLqJF51upRRWgi+gTbYpV04yUwnXdyssF8mydWocgihrTryBi8F6PsuhBGcaYdP+0yibnGxDCC4x2rupbBfMj2OIX7pYzgtIHB3Eo954Y+bCoggqbE/Qrb9VVXNMgtKgLt8EGWU2tg6wl9QicitIq87uLDAade93zTn6rmcKPywjMDo6jbVIs653ZdUhiKdHGdpnJccbgQ/iLSPB1umNnCeaEX5jM+K9zBvl7ZMCdSk1YIQ==--ekKumqLiSlVJNwMe--K/ecXmmMT1x+WnIXMbHBDw==
|
||||||
1
config/credentials/test.key
Normal file
1
config/credentials/test.key
Normal file
@@ -0,0 +1 @@
|
|||||||
|
6b101c9addbfa5f959b5859f756bc9d7
|
||||||
1
config/credentials/test.yml.enc
Normal file
1
config/credentials/test.yml.enc
Normal file
@@ -0,0 +1 @@
|
|||||||
|
vqH5By5qFLImVjdlWj+7FwGg8APKnr/AEd7WqekG7L0vNA32WGBpwS1uGzs02LIcATRwGj8DyJxiBOB/w9z8cwoO+t6Woi5hAnOSCQwFWKLT0dZq7jgtT8pxK0Yu/Nf91PEFN1rc/8ZFy2KKVpbtMbMPyivT38e/ctBZD/lHrWkndvLXYvFVhqWjUnDOGbhwl/U0RZgqBBjvlm3B0JkQfiN8VXPlCJL2Cd8kd0+MpRCRTgtcxA==--OdVXnDP7OhzJxCsP--+8SI6IFIeXyDxXb+WpqhIQ==
|
||||||
@@ -1,37 +1,21 @@
|
|||||||
default: &default
|
default: &default
|
||||||
adapter: <%= ENV["DB_ADAPTER"] || "sqlite3" %>
|
adapter: sqlite3
|
||||||
pool: <%= ENV["DB_POOL"] || ENV['MAX_THREADS'] || 5 %>
|
pool: <%= ENV["DB_POOL"] || ENV['MAX_THREADS'] || 5 %>
|
||||||
timeout: 5000
|
timeout: 5000
|
||||||
<% if ENV["DB_ADAPTER"] == "postgresql" %>
|
|
||||||
host: <%= ENV["PG_HOST"] || 'localhost' %>
|
|
||||||
port: <%= ENV["PG_PORT"] || 5432 %>
|
|
||||||
username: <%= ENV["PG_USERNAME"] || 'akkounts' %>
|
|
||||||
password: <%= ENV["PG_PASSWORD"] %>
|
|
||||||
<% end %>
|
|
||||||
|
|
||||||
<% if ENV["LNDHUB_PG_HOST"].present? %>
|
|
||||||
lndhub: &lndhub
|
|
||||||
adapter: postgresql
|
|
||||||
database_tasks: false
|
|
||||||
host: <%= ENV["LNDHUB_PG_HOST"] %>
|
|
||||||
port: <%= ENV["LNDHUB_PG_PORT"] || 5432 %>
|
|
||||||
database: <%= ENV["LNDHUB_PG_DATABASE"] || 'lndhub' %>
|
|
||||||
username: <%= ENV["LNDHUB_PG_USERNAME"] || 'lndhub' %>
|
|
||||||
password: <%= ENV["LNDHUB_PG_PASSWORD"] %>
|
|
||||||
<% end %>
|
|
||||||
|
|
||||||
development:
|
development:
|
||||||
primary:
|
primary:
|
||||||
<<: *default
|
<<: *default
|
||||||
database: <%= ENV["DB_ADAPTER"] == "postgresql" ? ENV["PG_DATABASE"] : "db/development.sqlite3" %>
|
database: db/development.sqlite3
|
||||||
queue:
|
|
||||||
<<: *default
|
|
||||||
database: <%= ENV["DB_ADAPTER"] == "postgresql" ? ENV["PG_DATABASE_QUEUE"] : "db/development_queue.sqlite3" %>
|
|
||||||
migrations_paths: db/queue_migrate
|
|
||||||
<% if ENV["LNDHUB_PG_HOST"].present? %>
|
|
||||||
lndhub:
|
lndhub:
|
||||||
<<: *lndhub
|
<<: *default
|
||||||
<% end %>
|
adapter: postgresql
|
||||||
|
database_tasks: false
|
||||||
|
host: <%= ENV["LNDHUB_PG_HOST"] || 'localhost' %>
|
||||||
|
port: <%= ENV["LNDHUB_PG_PORT"] || 5432 %>
|
||||||
|
database: <%= ENV["LNDHUB_PG_DATABASE"] || 'lndhub' %>
|
||||||
|
username: <%= ENV["LNDHUB_PG_USERNAME"] || 'lndhub' %>
|
||||||
|
password: <%= ENV["LNDHUB_PG_PASSWORD"] %>
|
||||||
|
|
||||||
# Warning: The database defined as "test" will be erased and
|
# Warning: The database defined as "test" will be erased and
|
||||||
# re-generated from your development database when you run "rake".
|
# re-generated from your development database when you run "rake".
|
||||||
@@ -48,12 +32,18 @@ test:
|
|||||||
production:
|
production:
|
||||||
primary:
|
primary:
|
||||||
<<: *default
|
<<: *default
|
||||||
database: <%= ENV["DB_ADAPTER"] == "postgresql" ? ENV["PG_DATABASE"] : "db/production.sqlite3" %>
|
adapter: postgresql
|
||||||
queue:
|
database: akkounts
|
||||||
<<: *default
|
port: 5432
|
||||||
database: <%= ENV["DB_ADAPTER"] == "postgresql" ? ENV["PG_DATABASE_QUEUE"] : "db/production_queue.sqlite3" %>
|
host: <%= Rails.application.credentials.postgres[:host] rescue nil %>
|
||||||
migrations_paths: db/queue_migrate
|
username: <%= Rails.application.credentials.postgres[:username] rescue nil %>
|
||||||
<% if ENV["LNDHUB_PG_HOST"].present? %>
|
password: <%= Rails.application.credentials.postgres[:password] rescue nil %>
|
||||||
lndhub:
|
lndhub:
|
||||||
<<: *lndhub
|
<<: *default
|
||||||
<% end %>
|
adapter: postgresql
|
||||||
|
database_tasks: false
|
||||||
|
host: <%= ENV["LNDHUB_PG_HOST"] || 'localhost' %>
|
||||||
|
port: <%= ENV["LNDHUB_PG_PORT"] || 5432 %>
|
||||||
|
database: <%= ENV["LNDHUB_PG_DATABASE"] || 'lndhub' %>
|
||||||
|
username: <%= ENV["LNDHUB_PG_USERNAME"] || 'lndhub' %>
|
||||||
|
password: <%= ENV["LNDHUB_PG_PASSWORD"] %>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Load the Rails application.
|
# Load the Rails application.
|
||||||
require_relative "application"
|
require_relative 'application'
|
||||||
|
|
||||||
# Initialize the Rails application.
|
# Initialize the Rails application.
|
||||||
Rails.application.initialize!
|
Rails.application.initialize!
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
require "active_support/core_ext/integer/time"
|
|
||||||
|
|
||||||
Rails.application.configure do
|
Rails.application.configure do
|
||||||
# Settings specified here will take precedence over those in config/application.rb.
|
# Settings specified here will take precedence over those in config/application.rb.
|
||||||
|
|
||||||
# Make code changes take effect immediately without server restart.
|
# In the development environment your application's code is reloaded on
|
||||||
config.enable_reloading = true
|
# every request. This slows down response time but is perfect for development
|
||||||
|
# since you don't have to restart the web server when you make code changes.
|
||||||
|
config.cache_classes = false
|
||||||
|
|
||||||
# Do not eager load code on boot.
|
# Do not eager load code on boot.
|
||||||
config.eager_load = false
|
config.eager_load = false
|
||||||
@@ -12,15 +12,16 @@ Rails.application.configure do
|
|||||||
# Show full error reports.
|
# Show full error reports.
|
||||||
config.consider_all_requests_local = true
|
config.consider_all_requests_local = true
|
||||||
|
|
||||||
# Enable server timing.
|
# Enable/disable caching. By default caching is disabled.
|
||||||
config.server_timing = true
|
# Run rails dev:cache to toggle caching.
|
||||||
|
if Rails.root.join('tmp', 'caching-dev.txt').exist?
|
||||||
# Enable/disable Action Controller caching. By default Action Controller caching is disabled.
|
|
||||||
# Run rails dev:cache to toggle Action Controller caching.
|
|
||||||
if Rails.root.join("tmp/caching-dev.txt").exist?
|
|
||||||
config.action_controller.perform_caching = true
|
config.action_controller.perform_caching = true
|
||||||
config.action_controller.enable_fragment_cache_logging = true
|
config.action_controller.enable_fragment_cache_logging = true
|
||||||
config.public_file_server.headers = { "cache-control" => "public, max-age=#{2.days.to_i}" }
|
|
||||||
|
config.cache_store = :memory_store
|
||||||
|
config.public_file_server.headers = {
|
||||||
|
'Cache-Control' => "public, max-age=#{2.days.to_i}"
|
||||||
|
}
|
||||||
else
|
else
|
||||||
config.action_controller.perform_caching = false
|
config.action_controller.perform_caching = false
|
||||||
|
|
||||||
@@ -30,63 +31,42 @@ Rails.application.configure do
|
|||||||
# Don't care if the mailer can't send.
|
# Don't care if the mailer can't send.
|
||||||
config.action_mailer.raise_delivery_errors = false
|
config.action_mailer.raise_delivery_errors = false
|
||||||
|
|
||||||
# Make template changes take effect immediately.
|
|
||||||
config.action_mailer.perform_caching = false
|
config.action_mailer.perform_caching = false
|
||||||
|
|
||||||
# Print deprecation notices to the Rails logger.
|
# Print deprecation notices to the Rails logger.
|
||||||
config.active_support.deprecation = :log
|
config.active_support.deprecation = :log
|
||||||
|
|
||||||
# Raise exceptions for disallowed deprecations.
|
|
||||||
config.active_support.disallowed_deprecation = :raise
|
|
||||||
|
|
||||||
# Tell Active Support which deprecation messages to disallow.
|
|
||||||
config.active_support.disallowed_deprecation_warnings = []
|
|
||||||
|
|
||||||
# Raise an error on page load if there are pending migrations.
|
# Raise an error on page load if there are pending migrations.
|
||||||
config.active_record.migration_error = :page_load
|
config.active_record.migration_error = :page_load
|
||||||
|
|
||||||
# Highlight code that triggered database queries in logs.
|
# Highlight code that triggered database queries in logs.
|
||||||
config.active_record.verbose_query_logs = true
|
config.active_record.verbose_query_logs = true
|
||||||
|
|
||||||
# Append comments with runtime information tags to SQL queries in logs.
|
# Debug mode disables concatenation and preprocessing of assets.
|
||||||
config.active_record.query_log_tags_enabled = true
|
# This option may cause significant delays in view rendering with a large
|
||||||
|
# number of complex assets.
|
||||||
# Highlight code that enqueued background job in logs.
|
config.assets.debug = true
|
||||||
config.active_job.verbose_enqueue_logs = true
|
|
||||||
|
|
||||||
# Solid Queue database
|
|
||||||
config.solid_queue.connects_to = { database: { writing: :queue } }
|
|
||||||
|
|
||||||
# Suppress logger output for asset requests.
|
# Suppress logger output for asset requests.
|
||||||
# config.assets.quiet = true
|
config.assets.quiet = true
|
||||||
|
|
||||||
# Raises error for missing translations.
|
# Raises error for missing translations.
|
||||||
# config.i18n.raise_on_missing_translations = true
|
# config.action_view.raise_on_missing_translations = true
|
||||||
|
|
||||||
# Annotate rendered view with file names.
|
# Use an evented file watcher to asynchronously detect changes in source code,
|
||||||
config.action_view.annotate_rendered_view_with_filenames = true
|
# routes, locales, etc. This feature depends on the listen gem.
|
||||||
|
config.file_watcher = ActiveSupport::EventedFileUpdateChecker
|
||||||
|
|
||||||
|
config.action_mailer.default_options = {
|
||||||
|
from: "accounts@localhost"
|
||||||
|
}
|
||||||
|
|
||||||
# Don't actually send emails, cache them for viewing via letter opener
|
# Don't actually send emails, cache them for viewing via letter opener
|
||||||
config.action_mailer.delivery_method = :letter_opener
|
config.action_mailer.delivery_method = :letter_opener
|
||||||
# Uncomment if you wish to allow Action Cable access from any origin.
|
# Don't care if the mailer can't send
|
||||||
# config.action_cable.disable_request_forgery_protection = true
|
|
||||||
|
|
||||||
# Raise error when a before_action's only/except options reference missing actions.
|
|
||||||
config.action_controller.raise_on_missing_callback_actions = true
|
|
||||||
|
|
||||||
# Notice if the mailer can't send
|
|
||||||
config.action_mailer.raise_delivery_errors = true
|
config.action_mailer.raise_delivery_errors = true
|
||||||
|
|
||||||
# Base URL to be used by email template link helpers
|
# Base URL to be used by email template link helpers
|
||||||
config.action_mailer.default_url_options = {
|
config.action_mailer.default_url_options = { host: "localhost:3000", protocol: "http" }
|
||||||
host: "localhost:3000", # TODO port: 3000
|
|
||||||
protocol: "http"
|
|
||||||
}
|
|
||||||
|
|
||||||
config.action_mailer.default_options = {
|
|
||||||
from: "accounts@localhost",
|
|
||||||
message_id: -> { "<#{Mail.random_tag}@localhost>" },
|
|
||||||
}
|
|
||||||
|
|
||||||
# Allow requests from any IP
|
# Allow requests from any IP
|
||||||
config.web_console.permissions = '0.0.0.0/0'
|
config.web_console.permissions = '0.0.0.0/0'
|
||||||
|
|||||||
@@ -1,61 +1,61 @@
|
|||||||
require "active_support/core_ext/integer/time"
|
|
||||||
|
|
||||||
Rails.application.configure do
|
Rails.application.configure do
|
||||||
# Settings specified here will take precedence over those in config/application.rb.
|
# Settings specified here will take precedence over those in config/application.rb.
|
||||||
|
|
||||||
# Code is not reloaded between requests.
|
# Code is not reloaded between requests.
|
||||||
config.enable_reloading = false
|
config.cache_classes = true
|
||||||
|
|
||||||
# Eager load code on boot for better performance and memory savings (ignored by Rake tasks).
|
# Eager load code on boot. This eager loads most of Rails and
|
||||||
|
# your application in memory, allowing both threaded web servers
|
||||||
|
# and those relying on copy on write to perform better.
|
||||||
|
# Rake tasks automatically ignore this option for performance.
|
||||||
config.eager_load = true
|
config.eager_load = true
|
||||||
|
|
||||||
# Full error reports are disabled.
|
# Full error reports are disabled and caching is turned on.
|
||||||
config.consider_all_requests_local = false
|
config.consider_all_requests_local = false
|
||||||
|
config.action_controller.perform_caching = true
|
||||||
|
|
||||||
|
# Ensures that a master key has been made available in either ENV["RAILS_MASTER_KEY"]
|
||||||
|
# or in config/master.key. This key is used to decrypt credentials (and other encrypted files).
|
||||||
|
# config.require_master_key = true
|
||||||
|
|
||||||
# Disable serving static files from the `/public` folder by default since
|
# Disable serving static files from the `/public` folder by default since
|
||||||
# Apache or NGINX already handles this.
|
# Apache or NGINX already handles this.
|
||||||
config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present?
|
config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present?
|
||||||
|
|
||||||
# Turn on fragment caching in view templates.
|
# Compress CSS using a preprocessor.
|
||||||
config.action_controller.perform_caching = true
|
# config.assets.css_compressor = :sass
|
||||||
|
|
||||||
# Do not fallback to assets pipeline if a precompiled asset is missed.
|
# Do not fallback to assets pipeline if a precompiled asset is missed.
|
||||||
config.assets.compile = false
|
config.assets.compile = false
|
||||||
|
|
||||||
# Cache assets for far-future expiry since they are all digest stamped.
|
|
||||||
config.public_file_server.headers = { "cache-control" => "public, max-age=#{1.year.to_i}" }
|
|
||||||
|
|
||||||
# Enable serving of images, stylesheets, and JavaScripts from an asset server.
|
# Enable serving of images, stylesheets, and JavaScripts from an asset server.
|
||||||
# config.asset_host = "http://assets.example.com"
|
# config.action_controller.asset_host = 'http://assets.example.com'
|
||||||
|
|
||||||
|
# Specifies the header that your server uses for sending files.
|
||||||
|
# config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache
|
||||||
|
# config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX
|
||||||
|
|
||||||
# Assume all access to the app is happening through a SSL-terminating reverse proxy.
|
# Mount Action Cable outside main process or domain.
|
||||||
# config.assume_ssl = true
|
# config.action_cable.mount_path = nil
|
||||||
|
# config.action_cable.url = 'wss://example.com/cable'
|
||||||
|
# config.action_cable.allowed_request_origins = [ 'http://example.com', /http:\/\/example.*/ ]
|
||||||
|
|
||||||
# Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
|
# Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
|
||||||
# config.force_ssl = true
|
# config.force_ssl = true
|
||||||
|
|
||||||
# Skip http-to-https redirect for the default health check endpoint.
|
# Use the lowest log level to ensure availability of diagnostic information
|
||||||
# config.ssl_options = { redirect: { exclude: ->(request) { request.path == "/up" } } }
|
# when problems arise.
|
||||||
|
config.log_level = :debug
|
||||||
|
|
||||||
# Log to STDOUT with the current request id as a default log tag.
|
# Prepend all log lines with the following tags.
|
||||||
config.log_tags = [ :request_id ]
|
config.log_tags = [ :request_id ]
|
||||||
config.logger = ActiveSupport::TaggedLogging.logger(STDOUT)
|
|
||||||
|
|
||||||
# Change to "debug" to log everything (including potentially personally-identifiable information!)
|
# Use a different cache store in production.
|
||||||
config.log_level = ENV.fetch("RAILS_LOG_LEVEL", "info")
|
|
||||||
|
|
||||||
# Prevent health checks from clogging up the logs.
|
|
||||||
config.silence_healthcheck_path = "/up"
|
|
||||||
|
|
||||||
# Don't log any deprecations.
|
|
||||||
config.active_support.report_deprecations = false
|
|
||||||
|
|
||||||
# Replace the default in-process memory cache store with a durable alternative.
|
|
||||||
# config.cache_store = :mem_cache_store
|
# config.cache_store = :mem_cache_store
|
||||||
|
|
||||||
# Solid Queue database
|
# Use a real queuing backend for Active Job (and separate queues per environment).
|
||||||
config.solid_queue.connects_to = { database: { writing: :queue } }
|
# config.active_job.queue_adapter = :resque
|
||||||
|
# config.active_job.queue_name_prefix = "akkounts_production"
|
||||||
|
|
||||||
# E-mail settings, adapted from https://github.com/mastodon/mastodon
|
# E-mail settings, adapted from https://github.com/mastodon/mastodon
|
||||||
|
|
||||||
@@ -63,7 +63,7 @@ Rails.application.configure do
|
|||||||
outgoing_email_domain = Mail::Address.new(outgoing_email_address).domain
|
outgoing_email_domain = Mail::Address.new(outgoing_email_address).domain
|
||||||
|
|
||||||
config.action_mailer.default_url_options = {
|
config.action_mailer.default_url_options = {
|
||||||
host: ENV.fetch('AKKOUNTS_DOMAIN'),
|
host: ENV['AKKOUNTS_DOMAIN'],
|
||||||
protocol: "https",
|
protocol: "https",
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,10 +106,6 @@ Rails.application.configure do
|
|||||||
|
|
||||||
config.action_mailer.delivery_method = ENV.fetch('SMTP_DELIVERY_METHOD', 'smtp').to_sym
|
config.action_mailer.delivery_method = ENV.fetch('SMTP_DELIVERY_METHOD', 'smtp').to_sym
|
||||||
|
|
||||||
# Disable caching for Action Mailer templates even if Action Controller
|
|
||||||
# caching is enabled.
|
|
||||||
config.action_mailer.perform_caching = false
|
|
||||||
|
|
||||||
# Ignore bad email addresses and do not raise email delivery errors.
|
# Ignore bad email addresses and do not raise email delivery errors.
|
||||||
# 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
|
||||||
@@ -124,18 +120,43 @@ Rails.application.configure do
|
|||||||
# the I18n.default_locale when a translation cannot be found).
|
# the I18n.default_locale when a translation cannot be found).
|
||||||
config.i18n.fallbacks = true
|
config.i18n.fallbacks = true
|
||||||
|
|
||||||
|
# Send deprecation notices to registered listeners.
|
||||||
|
config.active_support.deprecation = :notify
|
||||||
|
|
||||||
|
# Use default logging formatter so that PID and timestamp are not suppressed.
|
||||||
|
config.log_formatter = ::Logger::Formatter.new
|
||||||
|
|
||||||
|
# Use a different logger for distributed setups.
|
||||||
|
# require 'syslog/logger'
|
||||||
|
# config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name')
|
||||||
|
|
||||||
|
if ENV["RAILS_LOG_TO_STDOUT"].present?
|
||||||
|
logger = ActiveSupport::Logger.new(STDOUT)
|
||||||
|
logger.formatter = config.log_formatter
|
||||||
|
config.logger = ActiveSupport::TaggedLogging.new(logger)
|
||||||
|
end
|
||||||
|
|
||||||
# Do not dump schema after migrations.
|
# Do not dump schema after migrations.
|
||||||
config.active_record.dump_schema_after_migration = false
|
config.active_record.dump_schema_after_migration = false
|
||||||
|
|
||||||
# Only use :id for inspections in production.
|
# Inserts middleware to perform automatic connection switching.
|
||||||
config.active_record.attributes_for_inspect = [ :id ]
|
# The `database_selector` hash is used to pass options to the DatabaseSelector
|
||||||
|
# middleware. The `delay` is used to determine how long to wait after a write
|
||||||
# Enable DNS rebinding protection and other `Host` header attacks.
|
# to send a subsequent read to the primary.
|
||||||
# config.hosts = [
|
|
||||||
# "example.com", # Allow requests from example.com
|
|
||||||
# /.*\.example\.com/ # Allow requests from subdomains like `www.example.com`
|
|
||||||
# ]
|
|
||||||
#
|
#
|
||||||
# Skip DNS rebinding protection for the default health check endpoint.
|
# The `database_resolver` class is used by the middleware to determine which
|
||||||
# config.host_authorization = { exclude: ->(request) { request.path == "/up" } }
|
# database is appropriate to use based on the time delay.
|
||||||
|
#
|
||||||
|
# The `database_resolver_context` class is used by the middleware to set
|
||||||
|
# timestamps for the last write to the primary. The resolver uses the context
|
||||||
|
# class timestamps to determine how long to wait before reading from the
|
||||||
|
# replica.
|
||||||
|
#
|
||||||
|
# By default Rails will store a last write timestamp in the session. The
|
||||||
|
# DatabaseSelector middleware is designed as such you can define your own
|
||||||
|
# strategy for connection switching and pass that into the middleware through
|
||||||
|
# these configuration options.
|
||||||
|
# config.active_record.database_selector = { delay: 2.seconds }
|
||||||
|
# config.active_record.database_resolver = ActiveRecord::Middleware::DatabaseSelector::Resolver
|
||||||
|
# config.active_record.database_resolver_context = ActiveRecord::Middleware::DatabaseSelector::Resolver::Session
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -6,33 +6,31 @@
|
|||||||
Rails.application.configure do
|
Rails.application.configure do
|
||||||
# Settings specified here will take precedence over those in config/application.rb.
|
# Settings specified here will take precedence over those in config/application.rb.
|
||||||
|
|
||||||
# While tests run files are not watched, reloading is not necessary.
|
config.cache_classes = false
|
||||||
config.enable_reloading = false
|
config.action_view.cache_template_loading = true
|
||||||
|
|
||||||
# Eager loading loads your entire application. When running a single test locally,
|
# Do not eager load code on boot. This avoids loading your whole application
|
||||||
# this is usually not necessary, and can slow down your test suite. However, it's
|
# just for the purpose of running a single test. If you are using a tool that
|
||||||
# recommended that you enable it in continuous integration systems to ensure eager
|
# preloads Rails for running tests, you may have to set it to true.
|
||||||
# loading is working properly before deploying your code.
|
config.eager_load = false
|
||||||
config.eager_load = ENV["CI"].present?
|
|
||||||
|
|
||||||
# Configure public file server for tests with cache-control for performance.
|
# Configure public file server for tests with Cache-Control for performance.
|
||||||
config.public_file_server.headers = { "cache-control" => "public, max-age=3600" }
|
config.public_file_server.enabled = true
|
||||||
|
config.public_file_server.headers = {
|
||||||
|
'Cache-Control' => "public, max-age=#{1.hour.to_i}"
|
||||||
|
}
|
||||||
|
|
||||||
# Show full error reports.
|
# Show full error reports and disable caching.
|
||||||
config.consider_all_requests_local = true
|
config.consider_all_requests_local = true
|
||||||
config.action_controller.perform_caching = false
|
config.action_controller.perform_caching = false
|
||||||
config.cache_store = :null_store
|
config.cache_store = :null_store
|
||||||
|
|
||||||
# Render exception templates for rescuable exceptions and raise for other exceptions.
|
# Raise exceptions instead of rendering exception templates.
|
||||||
config.action_dispatch.show_exceptions = :rescuable
|
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
|
||||||
|
|
||||||
config.active_job.queue_adapter = :test
|
|
||||||
|
|
||||||
# Disable caching for Action Mailer templates even if Action Controller
|
|
||||||
# caching is enabled.
|
|
||||||
config.action_mailer.perform_caching = false
|
config.action_mailer.perform_caching = false
|
||||||
|
|
||||||
# Tell Action Mailer not to deliver emails to the real world.
|
# Tell Action Mailer not to deliver emails to the real world.
|
||||||
@@ -40,28 +38,23 @@ Rails.application.configure do
|
|||||||
# ActionMailer::Base.deliveries array.
|
# ActionMailer::Base.deliveries array.
|
||||||
config.action_mailer.delivery_method = :test
|
config.action_mailer.delivery_method = :test
|
||||||
|
|
||||||
config.action_mailer.default_options = {
|
# Print deprecation notices to the stderr.
|
||||||
from: "accounts@kosmos.org",
|
config.active_support.deprecation = :stderr
|
||||||
message_id: -> { "<#{Mail.random_tag}@kosmos.org>" },
|
|
||||||
}
|
# Raises error for missing translations.
|
||||||
|
# config.action_view.raise_on_missing_translations = true
|
||||||
|
|
||||||
config.action_mailer.default_url_options = {
|
config.action_mailer.default_url_options = {
|
||||||
host: "accounts.kosmos.org",
|
host: "accounts.kosmos.org",
|
||||||
protocol: "https"
|
protocol: "https",
|
||||||
|
from: "accounts@kosmos.org"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Raises error for missing translations.
|
config.active_job.queue_adapter = :test
|
||||||
# config.i18n.raise_on_missing_translations = true
|
|
||||||
|
|
||||||
# Annotate rendered view with file names.
|
|
||||||
# config.action_view.annotate_rendered_view_with_filenames = true
|
|
||||||
|
|
||||||
if ENV["S3_ENABLED"] && ENV["S3_ENABLED"].to_s != "false"
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
# Raise error when a before_action's only/except options reference missing actions.
|
|
||||||
config.action_controller.raise_on_missing_callback_actions = true
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -6,3 +6,4 @@ pin "@hotwired/stimulus", to: "stimulus.min.js", preload: true
|
|||||||
pin "@hotwired/stimulus-loading", to: "stimulus-loading.js", preload: true
|
pin "@hotwired/stimulus-loading", to: "stimulus-loading.js", preload: true
|
||||||
pin_all_from "app/javascript/controllers", under: "controllers"
|
pin_all_from "app/javascript/controllers", under: "controllers"
|
||||||
pin "tailwindcss-stimulus-components" # @4.0.3
|
pin "tailwindcss-stimulus-components" # @4.0.3
|
||||||
|
pin "nostrify"
|
||||||
|
|||||||
@@ -1,9 +1,14 @@
|
|||||||
# Be sure to restart your server when you modify this file.
|
# Be sure to restart your server when you modify this file.
|
||||||
|
|
||||||
# Version of your assets, change this if you want to expire all your assets.
|
# Version of your assets, change this if you want to expire all your assets.
|
||||||
Rails.application.config.assets.version = "1.0"
|
Rails.application.config.assets.version = '1.0'
|
||||||
|
|
||||||
# Add additional assets to the asset load path.
|
# Add additional assets to the asset load path.
|
||||||
# Rails.application.config.assets.paths << Emoji.images_path
|
# Rails.application.config.assets.paths << Emoji.images_path
|
||||||
# Add Yarn node_modules folder to the asset load path.
|
# Add Yarn node_modules folder to the asset load path.
|
||||||
Rails.application.config.assets.paths << Rails.root.join('node_modules')
|
Rails.application.config.assets.paths << Rails.root.join('node_modules')
|
||||||
|
|
||||||
|
# Precompile additional assets.
|
||||||
|
# application.js, application.css, and all non-JS/CSS in the app/assets
|
||||||
|
# folder are already added.
|
||||||
|
# Rails.application.config.assets.precompile += %w( admin.js admin.css )
|
||||||
|
|||||||
@@ -1,25 +1,30 @@
|
|||||||
# Be sure to restart your server when you modify this file.
|
# Be sure to restart your server when you modify this file.
|
||||||
|
|
||||||
# Define an application-wide content security policy.
|
# Define an application-wide content security policy
|
||||||
# See the Securing Rails Applications Guide for more information:
|
# For further information see the following documentation
|
||||||
# https://guides.rubyonrails.org/security.html#content-security-policy-header
|
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy
|
||||||
|
|
||||||
# Rails.application.configure do
|
# Rails.application.config.content_security_policy do |policy|
|
||||||
# config.content_security_policy do |policy|
|
# policy.default_src :self, :https
|
||||||
# policy.default_src :self, :https
|
# policy.font_src :self, :https, :data
|
||||||
# policy.font_src :self, :https, :data
|
# policy.img_src :self, :https, :data
|
||||||
# policy.img_src :self, :https, :data
|
# policy.object_src :none
|
||||||
# policy.object_src :none
|
# policy.script_src :self, :https
|
||||||
# policy.script_src :self, :https
|
# policy.style_src :self, :https
|
||||||
# policy.style_src :self, :https
|
# # If you are using webpack-dev-server then specify webpack-dev-server host
|
||||||
# # Specify URI for violation reports
|
# policy.connect_src :self, :https, "http://localhost:3035", "ws://localhost:3035" if Rails.env.development?
|
||||||
# # policy.report_uri "/csp-violation-report-endpoint"
|
|
||||||
# end
|
# # Specify URI for violation reports
|
||||||
#
|
# # policy.report_uri "/csp-violation-report-endpoint"
|
||||||
# # Generate session nonces for permitted importmap, inline scripts, and inline styles.
|
|
||||||
# config.content_security_policy_nonce_generator = ->(request) { request.session.id.to_s }
|
|
||||||
# config.content_security_policy_nonce_directives = %w(script-src style-src)
|
|
||||||
#
|
|
||||||
# # Report violations without enforcing the policy.
|
|
||||||
# # config.content_security_policy_report_only = true
|
|
||||||
# end
|
# end
|
||||||
|
|
||||||
|
# If you are using UJS then enable automatic nonce generation
|
||||||
|
# Rails.application.config.content_security_policy_nonce_generator = -> request { SecureRandom.base64(16) }
|
||||||
|
|
||||||
|
# Set the nonce only to specific directives
|
||||||
|
# Rails.application.config.content_security_policy_nonce_directives = %w(script-src)
|
||||||
|
|
||||||
|
# Report CSP violations to a specified URI
|
||||||
|
# For further information see the following documentation:
|
||||||
|
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only
|
||||||
|
# Rails.application.config.content_security_policy_report_only = true
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ Devise.setup do |config|
|
|||||||
# Configure the e-mail address which will be shown in Devise::Mailer,
|
# Configure the e-mail address which will be shown in Devise::Mailer,
|
||||||
# note that it will be overwritten if you use your own mailer class
|
# note that it will be overwritten if you use your own mailer class
|
||||||
# with default "from" parameter.
|
# with default "from" parameter.
|
||||||
config.mailer_sender = ENV["SMTP_FROM_ADDRESS"] || 'accounts@localhost'
|
config.mailer_sender = 'accounts@kosmos.org'
|
||||||
|
|
||||||
# Configure the class responsible to send e-mails.
|
# Configure the class responsible to send e-mails.
|
||||||
# config.mailer = 'Devise::Mailer'
|
# config.mailer = 'Devise::Mailer'
|
||||||
|
|||||||
@@ -1,11 +0,0 @@
|
|||||||
# See https://alvincrespo.hashnode.dev/rails-8s-lazy-route-loading-devise
|
|
||||||
# TODO remove when Devise is fixed
|
|
||||||
require 'devise'
|
|
||||||
Devise # make sure it's already loaded
|
|
||||||
|
|
||||||
module Devise
|
|
||||||
def self.mappings
|
|
||||||
Rails.application.try(:reload_routes_unless_loaded)
|
|
||||||
@@mappings
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -1,8 +1,4 @@
|
|||||||
# Be sure to restart your server when you modify this file.
|
# Be sure to restart your server when you modify this file.
|
||||||
|
|
||||||
# Configure parameters to be partially matched (e.g. passw matches password) and filtered from the log file.
|
# Configure sensitive parameters which will be filtered from the log file.
|
||||||
# Use this to limit dissemination of sensitive information.
|
Rails.application.config.filter_parameters += [:password]
|
||||||
# See the ActiveSupport::ParameterFilter documentation for supported notations and behaviors.
|
|
||||||
Rails.application.config.filter_parameters += [
|
|
||||||
:password, :email, :secret, :token, :_key, :crypt, :salt, :certificate, :otp, :ssn
|
|
||||||
]
|
|
||||||
|
|||||||
@@ -4,13 +4,13 @@
|
|||||||
# are locale specific, and you may define rules for as many different
|
# are locale specific, and you may define rules for as many different
|
||||||
# locales as you wish. All of these examples are active by default:
|
# locales as you wish. All of these examples are active by default:
|
||||||
# ActiveSupport::Inflector.inflections(:en) do |inflect|
|
# ActiveSupport::Inflector.inflections(:en) do |inflect|
|
||||||
# inflect.plural /^(ox)$/i, "\\1en"
|
# inflect.plural /^(ox)$/i, '\1en'
|
||||||
# inflect.singular /^(ox)en/i, "\\1"
|
# inflect.singular /^(ox)en/i, '\1'
|
||||||
# inflect.irregular "person", "people"
|
# inflect.irregular 'person', 'people'
|
||||||
# inflect.uncountable %w( fish sheep )
|
# inflect.uncountable %w( fish sheep )
|
||||||
# end
|
# end
|
||||||
|
|
||||||
# These inflection rules are supported but not enabled by default:
|
# These inflection rules are supported but not enabled by default:
|
||||||
# ActiveSupport::Inflector.inflections(:en) do |inflect|
|
# ActiveSupport::Inflector.inflections(:en) do |inflect|
|
||||||
# inflect.acronym "RESTful"
|
# inflect.acronym 'RESTful'
|
||||||
# end
|
# end
|
||||||
|
|||||||
1
config/initializers/mailer_host.rb
Normal file
1
config/initializers/mailer_host.rb
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Rails.application.routes.default_url_options[:host] = ENV['APP_DOMAIN']
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
# Be sure to restart your server when you modify this file.
|
|
||||||
|
|
||||||
# Define an application-wide HTTP permissions policy. For further
|
|
||||||
# information see: https://developers.google.com/web/updates/2018/06/feature-policy
|
|
||||||
|
|
||||||
# Rails.application.config.permissions_policy do |policy|
|
|
||||||
# policy.camera :none
|
|
||||||
# policy.gyroscope :none
|
|
||||||
# policy.microphone :none
|
|
||||||
# policy.usb :none
|
|
||||||
# policy.fullscreen :self
|
|
||||||
# policy.payment :self, "https://secure.example.com"
|
|
||||||
# end
|
|
||||||
5
config/initializers/sidekiq.rb
Normal file
5
config/initializers/sidekiq.rb
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
require_relative "../../app/models/setting"
|
||||||
|
|
||||||
|
Sidekiq.configure_server do |config|
|
||||||
|
config.redis = { url: Setting.redis_url }
|
||||||
|
end
|
||||||
@@ -28,11 +28,11 @@ authorizations: &AUTHORIZATIONS
|
|||||||
development:
|
development:
|
||||||
host: <%= ENV["LDAP_HOST"] || "localhost" %>
|
host: <%= ENV["LDAP_HOST"] || "localhost" %>
|
||||||
port: <%= ENV["LDAP_PORT"] || "389" %>
|
port: <%= ENV["LDAP_PORT"] || "389" %>
|
||||||
ssl: <%= ENV["LDAP_USE_TLS"] || "false" %>
|
attribute: cn
|
||||||
attribute: <%= ENV["LDAP_UID_ATTR"] || "cn" %>
|
|
||||||
base: <%= ENV["LDAP_BASE"] || "ou=kosmos.org,cn=users,dc=kosmos,dc=org" %>
|
base: <%= ENV["LDAP_BASE"] || "ou=kosmos.org,cn=users,dc=kosmos,dc=org" %>
|
||||||
admin_user: <%= ENV["LDAP_ADMIN_USER"] || "cn=Directory Manager" %>
|
admin_user: "cn=Directory Manager"
|
||||||
admin_password: <%= ENV["LDAP_ADMIN_PASSWORD"] %>
|
admin_password: <%= ENV["LDAP_ADMIN_PASSWORD"] %>
|
||||||
|
ssl: <%= ENV["LDAP_USE_TLS"] || "false" %>
|
||||||
# <<: *AUTHORIZATIONS
|
# <<: *AUTHORIZATIONS
|
||||||
|
|
||||||
test:
|
test:
|
||||||
@@ -46,11 +46,11 @@ test:
|
|||||||
# <<: *AUTHORIZATIONS
|
# <<: *AUTHORIZATIONS
|
||||||
|
|
||||||
production:
|
production:
|
||||||
host: <%= ENV["LDAP_HOST"] || "localhost" %>
|
host: ldap.kosmos.local
|
||||||
port: <%= ENV["LDAP_PORT"] || "389" %>
|
port: 389
|
||||||
ssl: <%= ENV["LDAP_USE_TLS"] || "false" %>
|
attribute: cn
|
||||||
attribute: <%= ENV["LDAP_UID_ATTR"] || "cn" %>
|
base: ou=kosmos.org,cn=users,dc=kosmos,dc=org
|
||||||
base: <%= ENV["LDAP_BASE"] || "ou=kosmos.org,cn=users,dc=kosmos,dc=org" %>
|
admin_user: <%= Rails.application.credentials.ldap[:username] rescue nil %>
|
||||||
admin_user: <%= ENV["LDAP_ADMIN_USER"] || "cn=Directory Manager" %>
|
admin_password: <%= Rails.application.credentials.ldap[:password] rescue nil %>
|
||||||
admin_password: <%= ENV["LDAP_ADMIN_PASSWORD"] %>
|
# ssl: false
|
||||||
# <<: *AUTHORIZATIONS
|
# <<: *AUTHORIZATIONS
|
||||||
|
|||||||
@@ -1,43 +1,38 @@
|
|||||||
# This configuration file will be evaluated by Puma. The top-level methods that
|
# Puma can serve each request in a thread from an internal thread pool.
|
||||||
# are invoked here are part of Puma's configuration DSL. For more information
|
# The `threads` method setting takes two numbers: a minimum and maximum.
|
||||||
# about methods provided by the DSL, see https://puma.io/puma/Puma/DSL.html.
|
# Any libraries that use thread pools should be configured to match
|
||||||
|
# the maximum value specified for Puma. Default is set to 5 threads for minimum
|
||||||
|
# and maximum; this matches the default thread size of Active Record.
|
||||||
#
|
#
|
||||||
# Puma starts a configurable number of processes (workers) and each process
|
max_threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }
|
||||||
# serves each request in a thread from an internal thread pool.
|
min_threads_count = ENV.fetch("RAILS_MIN_THREADS") { max_threads_count }
|
||||||
#
|
|
||||||
# You can control the number of workers using ENV["WEB_CONCURRENCY"]. You
|
|
||||||
# should only set this value when you want to run 2 or more workers. The
|
|
||||||
# default is already 1.
|
|
||||||
#
|
|
||||||
# The ideal number of threads per worker depends both on how much time the
|
|
||||||
# application spends waiting for IO operations and on how much you wish to
|
|
||||||
# prioritize throughput over latency.
|
|
||||||
#
|
|
||||||
# As a rule of thumb, increasing the number of threads will increase how much
|
|
||||||
# traffic a given process can handle (throughput), but due to CRuby's
|
|
||||||
# Global VM Lock (GVL) it has diminishing returns and will degrade the
|
|
||||||
# response time (latency) of the application.
|
|
||||||
#
|
|
||||||
# The default is set to 3 threads as it's deemed a decent compromise between
|
|
||||||
# throughput and latency for the average Rails application.
|
|
||||||
#
|
|
||||||
# Any libraries that use a connection pool or another resource pool should
|
|
||||||
# be configured to provide at least as many connections as the number of
|
|
||||||
# threads. This includes Active Record's `pool` parameter in `database.yml`.
|
|
||||||
max_threads_count = ENV.fetch("RAILS_MAX_THREADS", 5)
|
|
||||||
min_threads_count = ENV.fetch("RAILS_MAX_THREADS", 3)
|
|
||||||
threads min_threads_count, max_threads_count
|
threads min_threads_count, max_threads_count
|
||||||
|
|
||||||
port ENV.fetch("PORT", 3000)
|
# Specifies the `port` that Puma will listen on to receive requests; default is 3000.
|
||||||
|
#
|
||||||
|
port ENV.fetch("PORT") { 3000 }
|
||||||
|
|
||||||
|
# Specifies the `environment` that Puma will run in.
|
||||||
|
#
|
||||||
environment ENV.fetch("RAILS_ENV") { "development" }
|
environment ENV.fetch("RAILS_ENV") { "development" }
|
||||||
|
|
||||||
# Allow puma to be restarted by `bin/rails restart` command.
|
# Specifies the `pidfile` that Puma will use.
|
||||||
|
pidfile ENV.fetch("PIDFILE") { "tmp/pids/server.pid" }
|
||||||
|
|
||||||
|
# Specifies the number of `workers` to boot in clustered mode.
|
||||||
|
# Workers are forked web server processes. If using threads and workers together
|
||||||
|
# the concurrency of the application would be max `threads` * `workers`.
|
||||||
|
# Workers do not work on JRuby or Windows (both of which do not support
|
||||||
|
# processes).
|
||||||
|
#
|
||||||
|
# workers ENV.fetch("WEB_CONCURRENCY") { 2 }
|
||||||
|
|
||||||
|
# Use the `preload_app!` method when specifying a `workers` number.
|
||||||
|
# This directive tells Puma to first boot the application and load code
|
||||||
|
# before forking the application. This takes advantage of Copy On Write
|
||||||
|
# process behavior so workers use less memory.
|
||||||
|
#
|
||||||
|
# preload_app!
|
||||||
|
|
||||||
|
# Allow puma to be restarted by `rails restart` command.
|
||||||
plugin :tmp_restart
|
plugin :tmp_restart
|
||||||
|
|
||||||
# Run the Solid Queue supervisor inside of Puma for single-server deployments
|
|
||||||
plugin :solid_queue if ENV["SOLID_QUEUE_IN_PUMA"]
|
|
||||||
|
|
||||||
# Specify the PID file. Defaults to tmp/pids/server.pid in development.
|
|
||||||
# In other environments, only set the PID file if requested.
|
|
||||||
pidfile ENV["PIDFILE"] if ENV["PIDFILE"]
|
|
||||||
|
|||||||
@@ -1,21 +0,0 @@
|
|||||||
default: &default
|
|
||||||
dispatchers:
|
|
||||||
- polling_interval: 1
|
|
||||||
batch_size: 500
|
|
||||||
workers:
|
|
||||||
- queues: "*"
|
|
||||||
threads: 3
|
|
||||||
processes: <%= ENV.fetch("JOB_CONCURRENCY", 1) %>
|
|
||||||
polling_interval: 0.1
|
|
||||||
|
|
||||||
development:
|
|
||||||
<<: *default
|
|
||||||
workers:
|
|
||||||
- queues: "*"
|
|
||||||
threads: 1
|
|
||||||
|
|
||||||
test:
|
|
||||||
<<: *default
|
|
||||||
|
|
||||||
production:
|
|
||||||
<<: *default
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
# production:
|
|
||||||
# periodic_cleanup:
|
|
||||||
# class: CleanSoftDeletedRecordsJob
|
|
||||||
# queue: background
|
|
||||||
# args: [ 1000, { batch_size: 500 } ]
|
|
||||||
# schedule: every hour
|
|
||||||
# periodic_command:
|
|
||||||
# command: "SoftDeletedRecord.due.delete_all"
|
|
||||||
# priority: 2
|
|
||||||
# schedule: at 5am every day
|
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
require 'sidekiq/web'
|
||||||
|
|
||||||
Rails.application.routes.draw do
|
Rails.application.routes.draw do
|
||||||
devise_for :users, controllers: {
|
devise_for :users, controllers: {
|
||||||
confirmations: 'users/confirmations',
|
confirmations: 'users/confirmations',
|
||||||
@@ -15,9 +17,6 @@ Rails.application.routes.draw do
|
|||||||
match 'signup/:step', to: 'signup#steps', as: :signup_steps, via: [:get, :post]
|
match 'signup/:step', to: 'signup#steps', as: :signup_steps, via: [:get, :post]
|
||||||
post 'signup_validate', to: 'signup#validate'
|
post 'signup_validate', to: 'signup#validate'
|
||||||
|
|
||||||
|
|
||||||
get "users/:username/avatars/:hash", to: "avatars#show", as: :user_avatar
|
|
||||||
|
|
||||||
namespace :contributions do
|
namespace :contributions do
|
||||||
root to: 'donations#index'
|
root to: 'donations#index'
|
||||||
resources :donations, only: ['index', 'create'] do
|
resources :donations, only: ['index', 'create'] do
|
||||||
@@ -71,12 +70,10 @@ Rails.application.routes.draw do
|
|||||||
|
|
||||||
get '.well-known/webfinger', to: 'webfinger#show'
|
get '.well-known/webfinger', to: 'webfinger#show'
|
||||||
get '.well-known/nostr', to: 'well_known#nostr'
|
get '.well-known/nostr', to: 'well_known#nostr'
|
||||||
get '.well-known/lnurlp/:username', to: 'lnurlpay#index', as: :lightning_address
|
get '.well-known/lnurlp/:username', to: 'lnurlpay#index', as: 'lightning_address'
|
||||||
get '.well-known/keysend/:username', to: 'lnurlpay#keysend', as: :lightning_address_keysend
|
get '.well-known/keysend/:username', to: 'lnurlpay#keysend', as: 'lightning_address_keysend'
|
||||||
get '.well-known/openpgpkey/hu/:hashed_username(.:format)', to: 'web_key_directory#show', as: :wkd_key
|
|
||||||
get '.well-known/openpgpkey/policy', to: 'web_key_directory#policy'
|
|
||||||
|
|
||||||
get 'lnurlpay/:username/invoice', to: 'lnurlpay#invoice', as: :lnurlpay_invoice
|
get 'lnurlpay/:username/invoice', to: 'lnurlpay#invoice', as: 'lnurlpay_invoice'
|
||||||
|
|
||||||
post 'webhooks/lndhub', to: 'webhooks#lndhub'
|
post 'webhooks/lndhub', to: 'webhooks#lndhub'
|
||||||
|
|
||||||
@@ -124,7 +121,7 @@ Rails.application.routes.draw do
|
|||||||
end
|
end
|
||||||
|
|
||||||
authenticate :user, ->(user) { user.is_admin? } do
|
authenticate :user, ->(user) { user.is_admin? } do
|
||||||
mount MissionControl::Jobs::Engine, at: "/jobs"
|
mount Sidekiq::Web, at: '/sidekiq'
|
||||||
mount Flipper::UI.app(Flipper), at: '/flipper'
|
mount Flipper::UI.app(Flipper), at: '/flipper'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
class AddPgpFprToUsers < ActiveRecord::Migration[7.1]
|
|
||||||
def change
|
|
||||||
add_column :users, :pgp_fpr, :string
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
# This migration comes from active_storage (originally 20190112182829)
|
|
||||||
class AddServiceNameToActiveStorageBlobs < ActiveRecord::Migration[6.0]
|
|
||||||
def up
|
|
||||||
return unless table_exists?(:active_storage_blobs)
|
|
||||||
|
|
||||||
unless column_exists?(:active_storage_blobs, :service_name)
|
|
||||||
add_column :active_storage_blobs, :service_name, :string
|
|
||||||
|
|
||||||
if configured_service = ActiveStorage::Blob.service.name
|
|
||||||
ActiveStorage::Blob.unscoped.update_all(service_name: configured_service)
|
|
||||||
end
|
|
||||||
|
|
||||||
change_column :active_storage_blobs, :service_name, :string, null: false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def down
|
|
||||||
return unless table_exists?(:active_storage_blobs)
|
|
||||||
|
|
||||||
remove_column :active_storage_blobs, :service_name
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
# This migration comes from active_storage (originally 20191206030411)
|
|
||||||
class CreateActiveStorageVariantRecords < ActiveRecord::Migration[6.0]
|
|
||||||
def change
|
|
||||||
return unless table_exists?(:active_storage_blobs)
|
|
||||||
|
|
||||||
# Use Active Record's configured type for primary key
|
|
||||||
create_table :active_storage_variant_records, id: primary_key_type, if_not_exists: true do |t|
|
|
||||||
t.belongs_to :blob, null: false, index: false, type: blobs_primary_key_type
|
|
||||||
t.string :variation_digest, null: false
|
|
||||||
|
|
||||||
t.index %i[ blob_id variation_digest ], name: "index_active_storage_variant_records_uniqueness", unique: true
|
|
||||||
t.foreign_key :active_storage_blobs, column: :blob_id
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
def primary_key_type
|
|
||||||
config = Rails.configuration.generators
|
|
||||||
config.options[config.orm][:primary_key_type] || :primary_key
|
|
||||||
end
|
|
||||||
|
|
||||||
def blobs_primary_key_type
|
|
||||||
pkey_name = connection.primary_key(:active_storage_blobs)
|
|
||||||
pkey_column = connection.columns(:active_storage_blobs).find { |c| c.name == pkey_name }
|
|
||||||
pkey_column.bigint? ? :bigint : pkey_column.type
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
# This migration comes from active_storage (originally 20211119233751)
|
|
||||||
class RemoveNotNullOnActiveStorageBlobsChecksum < ActiveRecord::Migration[6.0]
|
|
||||||
def change
|
|
||||||
return unless table_exists?(:active_storage_blobs)
|
|
||||||
|
|
||||||
change_column_null(:active_storage_blobs, :checksum, true)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
class AddLndhubPasswordToUsers < ActiveRecord::Migration[8.0]
|
|
||||||
def change
|
|
||||||
add_column :users, :lndhub_username, :string
|
|
||||||
add_column :users, :lndhub_password, :text
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
class MigrateLockboxData < ActiveRecord::Migration[8.0]
|
|
||||||
def up
|
|
||||||
User.find_each do |user|
|
|
||||||
ln_account = user.ln_account
|
|
||||||
ln_password = user.ln_password
|
|
||||||
user.lndhub_username = ln_account
|
|
||||||
user.lndhub_password = ln_password
|
|
||||||
user.save!
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
class RemoveLnFieldsFromUsers < ActiveRecord::Migration[8.0]
|
|
||||||
def change
|
|
||||||
remove_column :users, :ln_account
|
|
||||||
remove_column :users, :ln_password_ciphertext
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -1,141 +0,0 @@
|
|||||||
# This file is auto-generated from the current state of the database. Instead
|
|
||||||
# of editing this file, please use the migrations feature of Active Record to
|
|
||||||
# incrementally modify your database, and then regenerate this schema definition.
|
|
||||||
#
|
|
||||||
# This file is the source Rails uses to define your schema when running `bin/rails
|
|
||||||
# db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to
|
|
||||||
# be faster and is potentially less error prone than running all of your
|
|
||||||
# migrations from scratch. Old migrations may fail to apply correctly if those
|
|
||||||
# migrations use external dependencies or application code.
|
|
||||||
#
|
|
||||||
# It's strongly recommended that you check this file into your version control system.
|
|
||||||
|
|
||||||
ActiveRecord::Schema[8.0].define(version: 1) do
|
|
||||||
create_table "solid_queue_blocked_executions", force: :cascade do |t|
|
|
||||||
t.bigint "job_id", null: false
|
|
||||||
t.string "queue_name", null: false
|
|
||||||
t.integer "priority", default: 0, null: false
|
|
||||||
t.string "concurrency_key", null: false
|
|
||||||
t.datetime "expires_at", null: false
|
|
||||||
t.datetime "created_at", null: false
|
|
||||||
t.index ["concurrency_key", "priority", "job_id"], name: "index_solid_queue_blocked_executions_for_release"
|
|
||||||
t.index ["expires_at", "concurrency_key"], name: "index_solid_queue_blocked_executions_for_maintenance"
|
|
||||||
t.index ["job_id"], name: "index_solid_queue_blocked_executions_on_job_id", unique: true
|
|
||||||
end
|
|
||||||
|
|
||||||
create_table "solid_queue_claimed_executions", force: :cascade do |t|
|
|
||||||
t.bigint "job_id", null: false
|
|
||||||
t.bigint "process_id"
|
|
||||||
t.datetime "created_at", null: false
|
|
||||||
t.index ["job_id"], name: "index_solid_queue_claimed_executions_on_job_id", unique: true
|
|
||||||
t.index ["process_id", "job_id"], name: "index_solid_queue_claimed_executions_on_process_id_and_job_id"
|
|
||||||
end
|
|
||||||
|
|
||||||
create_table "solid_queue_failed_executions", force: :cascade do |t|
|
|
||||||
t.bigint "job_id", null: false
|
|
||||||
t.text "error"
|
|
||||||
t.datetime "created_at", null: false
|
|
||||||
t.index ["job_id"], name: "index_solid_queue_failed_executions_on_job_id", unique: true
|
|
||||||
end
|
|
||||||
|
|
||||||
create_table "solid_queue_jobs", force: :cascade do |t|
|
|
||||||
t.string "queue_name", null: false
|
|
||||||
t.string "class_name", null: false
|
|
||||||
t.text "arguments"
|
|
||||||
t.integer "priority", default: 0, null: false
|
|
||||||
t.string "active_job_id"
|
|
||||||
t.datetime "scheduled_at"
|
|
||||||
t.datetime "finished_at"
|
|
||||||
t.string "concurrency_key"
|
|
||||||
t.datetime "created_at", null: false
|
|
||||||
t.datetime "updated_at", null: false
|
|
||||||
t.index ["active_job_id"], name: "index_solid_queue_jobs_on_active_job_id"
|
|
||||||
t.index ["class_name"], name: "index_solid_queue_jobs_on_class_name"
|
|
||||||
t.index ["finished_at"], name: "index_solid_queue_jobs_on_finished_at"
|
|
||||||
t.index ["queue_name", "finished_at"], name: "index_solid_queue_jobs_for_filtering"
|
|
||||||
t.index ["scheduled_at", "finished_at"], name: "index_solid_queue_jobs_for_alerting"
|
|
||||||
end
|
|
||||||
|
|
||||||
create_table "solid_queue_pauses", force: :cascade do |t|
|
|
||||||
t.string "queue_name", null: false
|
|
||||||
t.datetime "created_at", null: false
|
|
||||||
t.index ["queue_name"], name: "index_solid_queue_pauses_on_queue_name", unique: true
|
|
||||||
end
|
|
||||||
|
|
||||||
create_table "solid_queue_processes", force: :cascade do |t|
|
|
||||||
t.string "kind", null: false
|
|
||||||
t.datetime "last_heartbeat_at", null: false
|
|
||||||
t.bigint "supervisor_id"
|
|
||||||
t.integer "pid", null: false
|
|
||||||
t.string "hostname"
|
|
||||||
t.text "metadata"
|
|
||||||
t.datetime "created_at", null: false
|
|
||||||
t.string "name", null: false
|
|
||||||
t.index ["last_heartbeat_at"], name: "index_solid_queue_processes_on_last_heartbeat_at"
|
|
||||||
t.index ["name", "supervisor_id"], name: "index_solid_queue_processes_on_name_and_supervisor_id", unique: true
|
|
||||||
t.index ["supervisor_id"], name: "index_solid_queue_processes_on_supervisor_id"
|
|
||||||
end
|
|
||||||
|
|
||||||
create_table "solid_queue_ready_executions", force: :cascade do |t|
|
|
||||||
t.bigint "job_id", null: false
|
|
||||||
t.string "queue_name", null: false
|
|
||||||
t.integer "priority", default: 0, null: false
|
|
||||||
t.datetime "created_at", null: false
|
|
||||||
t.index ["job_id"], name: "index_solid_queue_ready_executions_on_job_id", unique: true
|
|
||||||
t.index ["priority", "job_id"], name: "index_solid_queue_poll_all"
|
|
||||||
t.index ["queue_name", "priority", "job_id"], name: "index_solid_queue_poll_by_queue"
|
|
||||||
end
|
|
||||||
|
|
||||||
create_table "solid_queue_recurring_executions", force: :cascade do |t|
|
|
||||||
t.bigint "job_id", null: false
|
|
||||||
t.string "task_key", null: false
|
|
||||||
t.datetime "run_at", null: false
|
|
||||||
t.datetime "created_at", null: false
|
|
||||||
t.index ["job_id"], name: "index_solid_queue_recurring_executions_on_job_id", unique: true
|
|
||||||
t.index ["task_key", "run_at"], name: "index_solid_queue_recurring_executions_on_task_key_and_run_at", unique: true
|
|
||||||
end
|
|
||||||
|
|
||||||
create_table "solid_queue_recurring_tasks", force: :cascade do |t|
|
|
||||||
t.string "key", null: false
|
|
||||||
t.string "schedule", null: false
|
|
||||||
t.string "command", limit: 2048
|
|
||||||
t.string "class_name"
|
|
||||||
t.text "arguments"
|
|
||||||
t.string "queue_name"
|
|
||||||
t.integer "priority", default: 0
|
|
||||||
t.boolean "static", default: true, null: false
|
|
||||||
t.text "description"
|
|
||||||
t.datetime "created_at", null: false
|
|
||||||
t.datetime "updated_at", null: false
|
|
||||||
t.index ["key"], name: "index_solid_queue_recurring_tasks_on_key", unique: true
|
|
||||||
t.index ["static"], name: "index_solid_queue_recurring_tasks_on_static"
|
|
||||||
end
|
|
||||||
|
|
||||||
create_table "solid_queue_scheduled_executions", force: :cascade do |t|
|
|
||||||
t.bigint "job_id", null: false
|
|
||||||
t.string "queue_name", null: false
|
|
||||||
t.integer "priority", default: 0, null: false
|
|
||||||
t.datetime "scheduled_at", null: false
|
|
||||||
t.datetime "created_at", null: false
|
|
||||||
t.index ["job_id"], name: "index_solid_queue_scheduled_executions_on_job_id", unique: true
|
|
||||||
t.index ["scheduled_at", "priority", "job_id"], name: "index_solid_queue_dispatch_all"
|
|
||||||
end
|
|
||||||
|
|
||||||
create_table "solid_queue_semaphores", force: :cascade do |t|
|
|
||||||
t.string "key", null: false
|
|
||||||
t.integer "value", default: 1, null: false
|
|
||||||
t.datetime "expires_at", null: false
|
|
||||||
t.datetime "created_at", null: false
|
|
||||||
t.datetime "updated_at", null: false
|
|
||||||
t.index ["expires_at"], name: "index_solid_queue_semaphores_on_expires_at"
|
|
||||||
t.index ["key", "value"], name: "index_solid_queue_semaphores_on_key_and_value"
|
|
||||||
t.index ["key"], name: "index_solid_queue_semaphores_on_key", unique: true
|
|
||||||
end
|
|
||||||
|
|
||||||
add_foreign_key "solid_queue_blocked_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade
|
|
||||||
add_foreign_key "solid_queue_claimed_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade
|
|
||||||
add_foreign_key "solid_queue_failed_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade
|
|
||||||
add_foreign_key "solid_queue_ready_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade
|
|
||||||
add_foreign_key "solid_queue_recurring_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade
|
|
||||||
add_foreign_key "solid_queue_scheduled_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade
|
|
||||||
end
|
|
||||||
@@ -10,7 +10,7 @@
|
|||||||
#
|
#
|
||||||
# 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[8.0].define(version: 2025_05_06_154628) do
|
ActiveRecord::Schema[7.1].define(version: 2024_06_07_123654) 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
|
||||||
@@ -127,12 +127,11 @@ ActiveRecord::Schema[8.0].define(version: 2025_05_06_154628) do
|
|||||||
t.datetime "confirmed_at", precision: nil
|
t.datetime "confirmed_at", precision: nil
|
||||||
t.datetime "confirmation_sent_at", precision: nil
|
t.datetime "confirmation_sent_at", precision: nil
|
||||||
t.string "unconfirmed_email"
|
t.string "unconfirmed_email"
|
||||||
|
t.text "ln_password_ciphertext"
|
||||||
|
t.string "ln_account"
|
||||||
t.datetime "remember_created_at"
|
t.datetime "remember_created_at"
|
||||||
t.string "remember_token"
|
t.string "remember_token"
|
||||||
t.text "preferences"
|
t.text "preferences"
|
||||||
t.string "pgp_fpr"
|
|
||||||
t.string "lndhub_username"
|
|
||||||
t.text "lndhub_password"
|
|
||||||
t.index ["email"], name: "index_users_on_email", unique: true
|
t.index ["email"], name: "index_users_on_email", unique: true
|
||||||
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
|
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ Sidekiq::Testing.inline! do
|
|||||||
|
|
||||||
puts "Create user: admin"
|
puts "Create user: admin"
|
||||||
|
|
||||||
UserManager::CreateAccount.call(account: {
|
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
|
||||||
})
|
})
|
||||||
@@ -20,7 +20,7 @@ Sidekiq::Testing.inline! do
|
|||||||
email = Faker::Internet.unique.email
|
email = Faker::Internet.unique.email
|
||||||
next if username.length < 3
|
next if username.length < 3
|
||||||
|
|
||||||
UserManager::CreateAccount.call(account: {
|
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
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,13 +0,0 @@
|
|||||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
|
||||||
|
|
||||||
mDMEZvGiUxYJKwYBBAHaRw8BAQdARPZXLqyB3nylJuzuARlOJxqc9mchMKHI4Cy+
|
|
||||||
hPWlzja0GEFkbWluIDxhZG1pbkBrb3Ntb3Mub3JnPoiZBBMWCgBBFiEE0pie1+fG
|
|
||||||
ImdZwzGnwgEYSg8AulYFAmbxolMCGwMFCQWjmoAFCwkIBwICIgIGFQoJCAsCBBYC
|
|
||||||
AwECHgcCF4AACgkQwgEYSg8AulaldAEA7yzh7XRCdIJDHgLUvKHsy2NnyLaDD1Tl
|
|
||||||
hyZWbl5og0IBAJAQ2Dm82YXMdUK3X1OGlK8KH5O4E5lSFY4+8/xx0UEJuDgEZvGi
|
|
||||||
UxIKKwYBBAGXVQEFAQEHQJc8pzzeIF7Hm5z1eseRAqGvFa+V1BIDf+1XQzuJhhxi
|
|
||||||
AwEIB4h+BBgWCgAmFiEE0pie1+fGImdZwzGnwgEYSg8AulYFAmbxolMCGwwFCQWj
|
|
||||||
moAACgkQwgEYSg8AulbLtgEApZvuDqSP77lrl1jmtCAJEEZk/ofsRFkf1g3U3Zhm
|
|
||||||
9PcA/1+AbcyqjLTcqIPjHmZyGEPiaAvEsBzbPKEPiL3JYhkG
|
|
||||||
=45sx
|
|
||||||
-----END PGP PUBLIC KEY BLOCK-----
|
|
||||||
@@ -37,25 +37,43 @@ services:
|
|||||||
- "3000:3000"
|
- "3000:3000"
|
||||||
environment:
|
environment:
|
||||||
RAILS_ENV: development
|
RAILS_ENV: development
|
||||||
SOLID_QUEUE_IN_PUMA: true
|
|
||||||
LAUNCHY_DRY_RUN: true
|
|
||||||
BROWSER: /dev/null
|
|
||||||
ENCRYPTION_PRIMARY_KEY: YhNLBgCFMAzw5dV3gISxnGrhNDMQwRdn
|
|
||||||
ENCRYPTION_KEY_DERIVATION_SALT: h28g16MRZ1sghF2jTCos1DiLZXUswinR
|
|
||||||
PRIMARY_DOMAIN: kosmos.org
|
PRIMARY_DOMAIN: kosmos.org
|
||||||
AKKOUNTS_DOMAIN: accounts.kosmos.org
|
|
||||||
LDAP_HOST: ldap
|
LDAP_HOST: ldap
|
||||||
LDAP_PORT: 3389
|
LDAP_PORT: 3389
|
||||||
LDAP_ADMIN_PASSWORD: passthebutter
|
LDAP_ADMIN_PASSWORD: passthebutter
|
||||||
|
LDAP_USE_TLS: "false"
|
||||||
REDIS_URL: redis://redis:6379/0
|
REDIS_URL: redis://redis:6379/0
|
||||||
ACTIVE_STORAGE_PATH: /akkounts/tmp/attachments
|
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"
|
||||||
RS_AKKOUNTS_DOMAIN: localhost
|
|
||||||
S3_ENABLED: false
|
S3_ENABLED: false
|
||||||
NOSTR_PUBLIC_KEY: bdd76ce2934b2f591f9fad2ebe9da18f20d2921de527494ba00eeaa0a0efadcf
|
NOSTR_PUBLIC_KEY: bdd76ce2934b2f591f9fad2ebe9da18f20d2921de527494ba00eeaa0a0efadcf
|
||||||
NOSTR_PRIVATE_KEY: 7c3ef7e448505f0615137af38569d01807d3b05b5005d5ecf8aaafcd40323cea
|
NOSTR_PRIVATE_KEY: 7c3ef7e448505f0615137af38569d01807d3b05b5005d5ecf8aaafcd40323cea
|
||||||
NOSTR_RELAY_URL: ws://strfry:7777
|
NOSTR_RELAY_URL: "ws://strfry:7777"
|
||||||
|
depends_on:
|
||||||
|
- ldap
|
||||||
|
- redis
|
||||||
|
|
||||||
|
sidekiq:
|
||||||
|
build: .
|
||||||
|
command: bash -c "bundle exec sidekiq -C config/sidekiq.yml"
|
||||||
|
volumes:
|
||||||
|
- .:/akkounts
|
||||||
|
networks:
|
||||||
|
- internal_network
|
||||||
|
environment:
|
||||||
|
RAILS_ENV: development
|
||||||
|
PRIMARY_DOMAIN: kosmos.org
|
||||||
|
LDAP_HOST: ldap
|
||||||
|
LDAP_PORT: 3389
|
||||||
|
LDAP_ADMIN_PASSWORD: passthebutter
|
||||||
|
LDAP_USE_TLS: "false"
|
||||||
|
LAUNCHY_DRY_RUN: true
|
||||||
|
BROWSER: /dev/null
|
||||||
|
REDIS_URL: redis://redis:6379/0
|
||||||
|
RS_REDIS_URL: redis://redis:6379/1
|
||||||
|
RS_STORAGE_URL: "http://localhost:4567"
|
||||||
|
S3_ENABLED: false
|
||||||
depends_on:
|
depends_on:
|
||||||
- ldap
|
- ldap
|
||||||
- redis
|
- redis
|
||||||
@@ -93,7 +111,7 @@ services:
|
|||||||
- redis
|
- redis
|
||||||
|
|
||||||
strfry:
|
strfry:
|
||||||
image: gitea.kosmos.org/kosmos/strfry-deno:2.0.0
|
image: gitea.kosmos.org/kosmos/strfry-deno:1.1.1
|
||||||
volumes:
|
volumes:
|
||||||
- ./docker/strfry/strfry.conf:/etc/strfry.conf
|
- ./docker/strfry/strfry.conf:/etc/strfry.conf
|
||||||
- ./extras/strfry:/opt/strfry
|
- ./extras/strfry:/opt/strfry
|
||||||
|
|||||||
@@ -1,57 +0,0 @@
|
|||||||
# Nostr
|
|
||||||
|
|
||||||
## strfry
|
|
||||||
|
|
||||||
The `extras/strfry` directory contains code to integrate [strfry][1] with
|
|
||||||
akkounts, so that notes published to the relay have to be authored by (or in
|
|
||||||
some cases just related to) local users who have verified their Nostr public
|
|
||||||
key.
|
|
||||||
|
|
||||||
### Requirements
|
|
||||||
|
|
||||||
[Deno](https://deno.com/) needs to be installed on the machine that you run
|
|
||||||
strfry on.
|
|
||||||
|
|
||||||
We provide a Docker image with recent strfry and Deno builds:
|
|
||||||
|
|
||||||
https://gitea.kosmos.org/kosmos/-/packages/container/strfry-deno/
|
|
||||||
|
|
||||||
### Configuration
|
|
||||||
|
|
||||||
You can use either environment variables (see e.g. the `strfry` service in
|
|
||||||
`docker-compose-yml`) or a local `.env` file in the same working directory
|
|
||||||
that you place the extra files in (e.g. `/opt/strfry`).
|
|
||||||
|
|
||||||
In your `strfry.conf`, configure `strfry-policy.ts` as the write policy, like so:
|
|
||||||
|
|
||||||
```
|
|
||||||
writePolicy {
|
|
||||||
plugin = "/opt/strfry/strfry-policy.ts"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
All dependencies will be downloaded and cached automatically when the plugin is
|
|
||||||
called for the first time.
|
|
||||||
|
|
||||||
### Manual tasks
|
|
||||||
|
|
||||||
You can sync all notes authored by local users (any account that has verified
|
|
||||||
their Nostr pubkey with akkounts) from a remote [strfry][1] relay via negentropy
|
|
||||||
sync:
|
|
||||||
|
|
||||||
deno run -A /opt/strfry/strfry-sync.ts wss://nostr.kosmos.org
|
|
||||||
|
|
||||||
Or, in the running container when using Docker Compose:
|
|
||||||
|
|
||||||
docker compose exec strfry deno run -A /opt/strfry/strfry-sync.ts wss://nostr.kosmos.org
|
|
||||||
|
|
||||||
The `strfry` service container also exposes the local relay on your local host
|
|
||||||
on port 4777.
|
|
||||||
|
|
||||||
[nak](https://github.com/fiatjaf/nak) is a helpful tool for manual Nostr tasks.
|
|
||||||
Here's how you can grab a note by its event ID from a remote relay and publish
|
|
||||||
it to your local strfry for example:
|
|
||||||
|
|
||||||
nak req -i 0fb010192685b86b0810b3de3706fbbf3b8c1db30b14533094a2b9700c820cdc nostr.kosmos.org | nak event ws://localhost:4777
|
|
||||||
|
|
||||||
[1]: https://github.com/hoytech/strfry
|
|
||||||
320
extras/strfry/deno.lock
generated
320
extras/strfry/deno.lock
generated
@@ -1,231 +1,101 @@
|
|||||||
{
|
{
|
||||||
"version": "4",
|
"version": "3",
|
||||||
"specifiers": {
|
"packages": {
|
||||||
"jsr:@nostr/tools@*": "2.3.1",
|
"specifiers": {
|
||||||
"jsr:@nostr/tools@^2.3.1": "2.3.1",
|
"jsr:@nostr/tools@^2.3.1": "jsr:@nostr/tools@2.3.1",
|
||||||
"jsr:@nostrify/nostrify@0.36": "0.36.2",
|
"npm:@noble/ciphers@^0.5.1": "npm:@noble/ciphers@0.5.3",
|
||||||
"jsr:@nostrify/policies@*": "0.36.1",
|
"npm:@noble/curves@1.2.0": "npm:@noble/curves@1.2.0",
|
||||||
"jsr:@nostrify/strfry@*": "0.2.1",
|
"npm:@noble/hashes@1.3.1": "npm:@noble/hashes@1.3.1",
|
||||||
"jsr:@nostrify/types@0.35": "0.35.0",
|
"npm:@scure/base@1.1.1": "npm:@scure/base@1.1.1",
|
||||||
"jsr:@nostrify/types@0.36": "0.36.0",
|
"npm:ldapts": "npm:ldapts@7.0.12"
|
||||||
"jsr:@std/bytes@^1.0.5": "1.0.5",
|
|
||||||
"jsr:@std/encoding@~0.224.1": "0.224.3",
|
|
||||||
"jsr:@std/json@^1.0.1": "1.0.1",
|
|
||||||
"jsr:@std/streams@^1.0.7": "1.0.9",
|
|
||||||
"jsr:@std/streams@^1.0.8": "1.0.9",
|
|
||||||
"npm:@noble/ciphers@~0.5.1": "0.5.3",
|
|
||||||
"npm:@noble/curves@1.2.0": "1.2.0",
|
|
||||||
"npm:@noble/hashes@1.3.1": "1.3.1",
|
|
||||||
"npm:@scure/base@1.1.1": "1.1.1",
|
|
||||||
"npm:@scure/bip32@^1.4.0": "1.6.2",
|
|
||||||
"npm:@scure/bip39@^1.3.0": "1.5.4",
|
|
||||||
"npm:ldapts@*": "7.0.12",
|
|
||||||
"npm:lru-cache@^10.2.0": "10.4.3",
|
|
||||||
"npm:nostr-tools@^2.7.0": "2.12.0",
|
|
||||||
"npm:websocket-ts@^2.1.5": "2.2.1",
|
|
||||||
"npm:zod@^3.23.8": "3.24.2"
|
|
||||||
},
|
|
||||||
"jsr": {
|
|
||||||
"@nostr/tools@2.3.1": {
|
|
||||||
"integrity": "af01dc45cb28784c584d7a0699707196f397bcc53946efa582a01b11ddde4d61",
|
|
||||||
"dependencies": [
|
|
||||||
"npm:@noble/ciphers",
|
|
||||||
"npm:@noble/curves",
|
|
||||||
"npm:@noble/hashes",
|
|
||||||
"npm:@scure/base"
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"@nostrify/nostrify@0.36.2": {
|
"jsr": {
|
||||||
"integrity": "cc4787ca170b623a2e5dfed1baa4426077daa6143af728ea7dd325d58f4d04d6",
|
"@nostr/tools@2.3.1": {
|
||||||
"dependencies": [
|
"integrity": "af01dc45cb28784c584d7a0699707196f397bcc53946efa582a01b11ddde4d61",
|
||||||
"jsr:@nostrify/types@0.35",
|
"dependencies": [
|
||||||
"jsr:@std/encoding",
|
"npm:@noble/ciphers@^0.5.1",
|
||||||
"npm:@scure/bip32",
|
"npm:@noble/curves@1.2.0",
|
||||||
"npm:@scure/bip39",
|
"npm:@noble/hashes@1.3.1",
|
||||||
"npm:lru-cache",
|
"npm:@scure/base@1.1.1"
|
||||||
"npm:nostr-tools",
|
]
|
||||||
"npm:websocket-ts",
|
}
|
||||||
"npm:zod"
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"@nostrify/policies@0.36.1": {
|
"npm": {
|
||||||
"integrity": "6d59af115a687fcd18b6caebab0e4f50ee6cdb0aafa2aacd0aec2065021275b4",
|
"@noble/ciphers@0.5.3": {
|
||||||
"dependencies": [
|
"integrity": "sha512-B0+6IIHiqEs3BPMT0hcRmHvEj2QHOLu+uwt+tqDDeVd0oyVzh7BPrDcPjRnV1PV/5LaknXJJQvOuRGR0zQJz+w==",
|
||||||
"jsr:@nostrify/nostrify",
|
"dependencies": {}
|
||||||
"jsr:@nostrify/types@0.35",
|
},
|
||||||
"npm:nostr-tools"
|
"@noble/curves@1.2.0": {
|
||||||
]
|
"integrity": "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==",
|
||||||
},
|
"dependencies": {
|
||||||
"@nostrify/strfry@0.2.1": {
|
"@noble/hashes": "@noble/hashes@1.3.2"
|
||||||
"integrity": "be437b13f49e6564e557da23072bf642723a603568f672543a64d9fda6663432",
|
}
|
||||||
"dependencies": [
|
},
|
||||||
"jsr:@nostrify/types@0.36",
|
"@noble/hashes@1.3.1": {
|
||||||
"jsr:@std/json",
|
"integrity": "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==",
|
||||||
"jsr:@std/streams@^1.0.8"
|
"dependencies": {}
|
||||||
]
|
},
|
||||||
},
|
"@noble/hashes@1.3.2": {
|
||||||
"@nostrify/types@0.35.0": {
|
"integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==",
|
||||||
"integrity": "b8d515563d467072694557d5626fa1600f74e83197eef45dd86a9a99c64f7fe6"
|
"dependencies": {}
|
||||||
},
|
},
|
||||||
"@nostrify/types@0.36.0": {
|
"@scure/base@1.1.1": {
|
||||||
"integrity": "b3413467debcbd298d217483df4e2aae6c335a34765c90ac7811cf7c637600e7"
|
"integrity": "sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA==",
|
||||||
},
|
"dependencies": {}
|
||||||
"@std/bytes@1.0.5": {
|
},
|
||||||
"integrity": "4465dd739d7963d964c809202ebea6d5c6b8e3829ef25c6a224290fbb8a1021e"
|
"@types/asn1@0.2.4": {
|
||||||
},
|
"integrity": "sha512-V91DSJ2l0h0gRhVP4oBfBzRBN9lAbPUkGDMCnwedqPKX2d84aAMc9CulOvxdw1f7DfEYx99afab+Rsm3e52jhA==",
|
||||||
"@std/encoding@0.224.3": {
|
"dependencies": {
|
||||||
"integrity": "5e861b6d81be5359fad4155e591acf17c0207b595112d1840998bb9f476dbdaf"
|
"@types/node": "@types/node@18.16.19"
|
||||||
},
|
}
|
||||||
"@std/json@1.0.1": {
|
},
|
||||||
"integrity": "1f0f70737e8827f9acca086282e903677bc1bb0c8ffcd1f21bca60039563049f",
|
"@types/node@18.16.19": {
|
||||||
"dependencies": [
|
"integrity": "sha512-IXl7o+R9iti9eBW4Wg2hx1xQDig183jj7YLn8F7udNceyfkbn1ZxmzZXuak20gR40D7pIkIY1kYGx5VIGbaHKA==",
|
||||||
"jsr:@std/streams@^1.0.7"
|
"dependencies": {}
|
||||||
]
|
},
|
||||||
},
|
"@types/uuid@9.0.8": {
|
||||||
"@std/streams@1.0.9": {
|
"integrity": "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==",
|
||||||
"integrity": "a9d26b1988cdd7aa7b1f4b51e1c36c1557f3f252880fa6cc5b9f37078b1a5035",
|
"dependencies": {}
|
||||||
"dependencies": [
|
},
|
||||||
"jsr:@std/bytes"
|
"asn1@0.2.6": {
|
||||||
]
|
"integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==",
|
||||||
}
|
"dependencies": {
|
||||||
},
|
"safer-buffer": "safer-buffer@2.1.2"
|
||||||
"npm": {
|
}
|
||||||
"@noble/ciphers@0.5.3": {
|
},
|
||||||
"integrity": "sha512-B0+6IIHiqEs3BPMT0hcRmHvEj2QHOLu+uwt+tqDDeVd0oyVzh7BPrDcPjRnV1PV/5LaknXJJQvOuRGR0zQJz+w=="
|
"debug@4.3.5": {
|
||||||
},
|
"integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==",
|
||||||
"@noble/curves@1.1.0": {
|
"dependencies": {
|
||||||
"integrity": "sha512-091oBExgENk/kGj3AZmtBDMpxQPDtxQABR2B9lb1JbVTs6ytdzZNwvhxQ4MWasRNEzlbEH8jCWFCwhF/Obj5AA==",
|
"ms": "ms@2.1.2"
|
||||||
"dependencies": [
|
}
|
||||||
"@noble/hashes@1.3.1"
|
},
|
||||||
]
|
"ldapts@7.0.12": {
|
||||||
},
|
"integrity": "sha512-orwgIejUi/ZyGah9y8jWZmFUg8Ci5M8WAv0oZjSf3MVuk1sRBdor9Qy1ttGHbYpWj96HXKFunQ8AYZ8WWGp17g==",
|
||||||
"@noble/curves@1.2.0": {
|
"dependencies": {
|
||||||
"integrity": "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==",
|
"@types/asn1": "@types/asn1@0.2.4",
|
||||||
"dependencies": [
|
"@types/uuid": "@types/uuid@9.0.8",
|
||||||
"@noble/hashes@1.3.2"
|
"asn1": "asn1@0.2.6",
|
||||||
]
|
"debug": "debug@4.3.5",
|
||||||
},
|
"strict-event-emitter-types": "strict-event-emitter-types@2.0.0",
|
||||||
"@noble/curves@1.8.2": {
|
"uuid": "uuid@9.0.1"
|
||||||
"integrity": "sha512-vnI7V6lFNe0tLAuJMu+2sX+FcL14TaCWy1qiczg1VwRmPrpQCdq5ESXQMqUc2tluRNf6irBXrWbl1mGN8uaU/g==",
|
}
|
||||||
"dependencies": [
|
},
|
||||||
"@noble/hashes@1.7.2"
|
"ms@2.1.2": {
|
||||||
]
|
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
|
||||||
},
|
"dependencies": {}
|
||||||
"@noble/hashes@1.3.1": {
|
},
|
||||||
"integrity": "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA=="
|
"safer-buffer@2.1.2": {
|
||||||
},
|
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
|
||||||
"@noble/hashes@1.3.2": {
|
"dependencies": {}
|
||||||
"integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ=="
|
},
|
||||||
},
|
"strict-event-emitter-types@2.0.0": {
|
||||||
"@noble/hashes@1.7.2": {
|
"integrity": "sha512-Nk/brWYpD85WlOgzw5h173aci0Teyv8YdIAEtV+N88nDB0dLlazZyJMIsN6eo1/AR61l+p6CJTG1JIyFaoNEEA==",
|
||||||
"integrity": "sha512-biZ0NUSxyjLLqo6KxEJ1b+C2NAx0wtDoFvCaXHGgUkeHzf3Xc1xKumFKREuT7f7DARNZ/slvYUwFG6B0f2b6hQ=="
|
"dependencies": {}
|
||||||
},
|
},
|
||||||
"@scure/base@1.1.1": {
|
"uuid@9.0.1": {
|
||||||
"integrity": "sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA=="
|
"integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==",
|
||||||
},
|
"dependencies": {}
|
||||||
"@scure/base@1.2.4": {
|
}
|
||||||
"integrity": "sha512-5Yy9czTO47mqz+/J8GM6GIId4umdCk1wc1q8rKERQulIoc8VP9pzDcghv10Tl2E7R96ZUx/PhND3ESYUQX8NuQ=="
|
|
||||||
},
|
|
||||||
"@scure/bip32@1.3.1": {
|
|
||||||
"integrity": "sha512-osvveYtyzdEVbt3OfwwXFr4P2iVBL5u1Q3q4ONBfDY/UpOuXmOlbgwc1xECEboY8wIays8Yt6onaWMUdUbfl0A==",
|
|
||||||
"dependencies": [
|
|
||||||
"@noble/curves@1.1.0",
|
|
||||||
"@noble/hashes@1.3.2",
|
|
||||||
"@scure/base@1.1.1"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"@scure/bip32@1.6.2": {
|
|
||||||
"integrity": "sha512-t96EPDMbtGgtb7onKKqxRLfE5g05k7uHnHRM2xdE6BP/ZmxaLtPek4J4KfVn/90IQNrU1IOAqMgiDtUdtbe3nw==",
|
|
||||||
"dependencies": [
|
|
||||||
"@noble/curves@1.8.2",
|
|
||||||
"@noble/hashes@1.7.2",
|
|
||||||
"@scure/base@1.2.4"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"@scure/bip39@1.2.1": {
|
|
||||||
"integrity": "sha512-Z3/Fsz1yr904dduJD0NpiyRHhRYHdcnyh73FZWiV+/qhWi83wNJ3NWolYqCEN+ZWsUz2TWwajJggcRE9r1zUYg==",
|
|
||||||
"dependencies": [
|
|
||||||
"@noble/hashes@1.3.2",
|
|
||||||
"@scure/base@1.1.1"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"@scure/bip39@1.5.4": {
|
|
||||||
"integrity": "sha512-TFM4ni0vKvCfBpohoh+/lY05i9gRbSwXWngAsF4CABQxoaOHijxuaZ2R6cStDQ5CHtHO9aGJTr4ksVJASRRyMA==",
|
|
||||||
"dependencies": [
|
|
||||||
"@noble/hashes@1.7.2",
|
|
||||||
"@scure/base@1.2.4"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"@types/asn1@0.2.4": {
|
|
||||||
"integrity": "sha512-V91DSJ2l0h0gRhVP4oBfBzRBN9lAbPUkGDMCnwedqPKX2d84aAMc9CulOvxdw1f7DfEYx99afab+Rsm3e52jhA==",
|
|
||||||
"dependencies": [
|
|
||||||
"@types/node"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"@types/node@18.16.19": {
|
|
||||||
"integrity": "sha512-IXl7o+R9iti9eBW4Wg2hx1xQDig183jj7YLn8F7udNceyfkbn1ZxmzZXuak20gR40D7pIkIY1kYGx5VIGbaHKA=="
|
|
||||||
},
|
|
||||||
"@types/uuid@9.0.8": {
|
|
||||||
"integrity": "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA=="
|
|
||||||
},
|
|
||||||
"asn1@0.2.6": {
|
|
||||||
"integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==",
|
|
||||||
"dependencies": [
|
|
||||||
"safer-buffer"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"debug@4.3.5": {
|
|
||||||
"integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==",
|
|
||||||
"dependencies": [
|
|
||||||
"ms"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"ldapts@7.0.12": {
|
|
||||||
"integrity": "sha512-orwgIejUi/ZyGah9y8jWZmFUg8Ci5M8WAv0oZjSf3MVuk1sRBdor9Qy1ttGHbYpWj96HXKFunQ8AYZ8WWGp17g==",
|
|
||||||
"dependencies": [
|
|
||||||
"@types/asn1",
|
|
||||||
"@types/uuid",
|
|
||||||
"asn1",
|
|
||||||
"debug",
|
|
||||||
"strict-event-emitter-types",
|
|
||||||
"uuid"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"lru-cache@10.4.3": {
|
|
||||||
"integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="
|
|
||||||
},
|
|
||||||
"ms@2.1.2": {
|
|
||||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
|
||||||
},
|
|
||||||
"nostr-tools@2.12.0": {
|
|
||||||
"integrity": "sha512-pUWEb020gTvt1XZvTa8AKNIHWFapjsv2NKyk43Ez2nnvz6WSXsrTFE0XtkNLSRBjPn6EpxumKeNiVzLz74jNSA==",
|
|
||||||
"dependencies": [
|
|
||||||
"@noble/ciphers",
|
|
||||||
"@noble/curves@1.2.0",
|
|
||||||
"@noble/hashes@1.3.1",
|
|
||||||
"@scure/base@1.1.1",
|
|
||||||
"@scure/bip32@1.3.1",
|
|
||||||
"@scure/bip39@1.2.1",
|
|
||||||
"nostr-wasm"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"nostr-wasm@0.1.0": {
|
|
||||||
"integrity": "sha512-78BTryCLcLYv96ONU8Ws3Q1JzjlAt+43pWQhIl86xZmWeegYCNLPml7yQ+gG3vR6V5h4XGj+TxO+SS5dsThQIA=="
|
|
||||||
},
|
|
||||||
"safer-buffer@2.1.2": {
|
|
||||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
|
|
||||||
},
|
|
||||||
"strict-event-emitter-types@2.0.0": {
|
|
||||||
"integrity": "sha512-Nk/brWYpD85WlOgzw5h173aci0Teyv8YdIAEtV+N88nDB0dLlazZyJMIsN6eo1/AR61l+p6CJTG1JIyFaoNEEA=="
|
|
||||||
},
|
|
||||||
"uuid@9.0.1": {
|
|
||||||
"integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA=="
|
|
||||||
},
|
|
||||||
"websocket-ts@2.2.1": {
|
|
||||||
"integrity": "sha512-YKPDfxlK5qOheLZ2bTIiktZO1bpfGdNCPJmTEaPW7G9UXI1GKjDdeacOrsULUS000OPNxDVOyAuKLuIWPqWM0Q=="
|
|
||||||
},
|
|
||||||
"zod@3.24.2": {
|
|
||||||
"integrity": "sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ=="
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"remote": {
|
"remote": {
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { NostrEvent, NostrRelayInfo, NostrRelayOK, NPolicy } from 'jsr:@nostrify/types@^0.35.0';
|
import type { IterablePubkeys, Policy } from 'https://gitlab.com/soapbox-pub/strfry-policies/-/raw/develop/mod.ts';
|
||||||
import { nip57 } from 'jsr:@nostr/tools';
|
|
||||||
import { Client } from 'npm:ldapts';
|
import { Client } from 'npm:ldapts';
|
||||||
|
import { nip57 } from '@nostr/tools';
|
||||||
|
|
||||||
export interface LdapConfig {
|
interface LdapConfig {
|
||||||
url: string;
|
url: string;
|
||||||
bindDN: string;
|
bindDN: string;
|
||||||
password: string;
|
password: string;
|
||||||
@@ -10,73 +10,68 @@ export interface LdapConfig {
|
|||||||
whitelistPubkeys?: IterablePubkeys;
|
whitelistPubkeys?: IterablePubkeys;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class LdapPolicy implements NPolicy {
|
const ldapPolicy: Policy<LdapConfig> = async (msg, opts) => {
|
||||||
constructor(private opts: LdapConfig) {}
|
const client = new Client({ url: opts.url });
|
||||||
|
const { kind, tags } = msg.event;
|
||||||
|
let { pubkey } = msg.event;
|
||||||
|
let out = { id: msg.event.id }
|
||||||
|
|
||||||
// deno-lint-ignore require-await
|
if (opts.whitelistPubkeys.includes(pubkey)) {
|
||||||
async call(event: NostrEvent): Promise<NostrRelayOK> {
|
out['action'] = 'accept';
|
||||||
const client = new Client({ url: this.opts.url });
|
out['msg'] = '';
|
||||||
const { id, kind, tags } = event;
|
return out;
|
||||||
let { pubkey } = event;
|
}
|
||||||
|
|
||||||
if (this.opts.whitelistPubkeys.includes(pubkey)) {
|
// Zap receipt
|
||||||
return ['OK', id, true, ''];
|
if (kind === 9735) {
|
||||||
|
const descriptionTag = tags.find(([t, v]) => t === 'description' && v);
|
||||||
|
const invalidZapRequestMsg = 'Zap receipts must contain a valid zap request from a relay member';
|
||||||
|
|
||||||
|
if (typeof descriptionTag === 'undefined') {
|
||||||
|
out['action'] = 'reject';
|
||||||
|
out['msg'] = invalidZapRequestMsg;
|
||||||
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Zap receipt
|
const zapRequestJSON = descriptionTag[1];
|
||||||
if (kind === 9735) {
|
const validationResult = nip57.validateZapRequest(zapRequestJSON);
|
||||||
const descriptionTag = tags.find(([t, v]) => t === 'description' && v);
|
|
||||||
const invalidZapRequestMsg = 'Zap receipts must contain a valid zap request from a relay member';
|
|
||||||
|
|
||||||
if (typeof descriptionTag === 'undefined') {
|
// TODO
|
||||||
return ['OK', id, false, invalidZapRequestMsg];
|
// The zap receipt event's pubkey MUST be the same as the recipient's lnurl provider's nostrPubkey (retrieved in step 1 of the protocol flow).
|
||||||
}
|
// The invoiceAmount contained in the bolt11 tag of the zap receipt MUST equal the amount tag of the zap request (if present).
|
||||||
|
|
||||||
const zapRequestJSON = descriptionTag[1];
|
if (validationResult === null) {
|
||||||
const validationResult = nip57.validateZapRequest(zapRequestJSON);
|
pubkey = JSON.parse(zapRequestJSON).pubkey;
|
||||||
|
} else {
|
||||||
// TODO
|
out['action'] = 'reject';
|
||||||
// The zap receipt event's pubkey MUST be the same as the recipient's lnurl provider's nostrPubkey (retrieved in step 1 of the protocol flow).
|
out['msg'] = invalidZapRequestMsg;
|
||||||
// The invoiceAmount contained in the bolt11 tag of the zap receipt MUST equal the amount tag of the zap request (if present).
|
return out;
|
||||||
|
|
||||||
if (validationResult === null) {
|
|
||||||
pubkey = JSON.parse(zapRequestJSON).pubkey;
|
|
||||||
} else {
|
|
||||||
return ['OK', id, false, invalidZapRequestMsg];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const out = { accept: true, msg: ''};
|
|
||||||
|
|
||||||
try {
|
|
||||||
await client.bind(this.opts.bindDN, this.opts.password);
|
|
||||||
|
|
||||||
const { searchEntries } = await client.search(this.opts.searchDN, {
|
|
||||||
filter: `(nostrKey=${pubkey})`,
|
|
||||||
attributes: ['nostrKey']
|
|
||||||
});
|
|
||||||
const memberKey = searchEntries[0]?.nostrKey;
|
|
||||||
|
|
||||||
if (memberKey === pubkey) {
|
|
||||||
out['accept'] = true;
|
|
||||||
} else {
|
|
||||||
out['accept'] = false;
|
|
||||||
out['msg'] = 'Only members can publish notes on this relay';
|
|
||||||
}
|
|
||||||
} catch (ex) {
|
|
||||||
out['accept'] = false;
|
|
||||||
out['msg'] = 'Auth service temporarily unavailable';
|
|
||||||
} finally {
|
|
||||||
await client.unbind();
|
|
||||||
return ['OK', id, out['accept'], out['msg']];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get info(): NostrRelayInfo {
|
try {
|
||||||
return {
|
await client.bind(opts.bindDN, opts.password);
|
||||||
limitation: {
|
|
||||||
restricted_writes: true,
|
const { searchEntries } = await client.search(opts.searchDN, {
|
||||||
},
|
filter: `(nostrKey=${pubkey})`,
|
||||||
};
|
attributes: ['nostrKey']
|
||||||
|
});
|
||||||
|
const memberKey = searchEntries[0]?.nostrKey;
|
||||||
|
|
||||||
|
if (memberKey === pubkey) {
|
||||||
|
out['action'] = 'accept';
|
||||||
|
out['msg'] = '';
|
||||||
|
} else {
|
||||||
|
out['action'] = 'reject';
|
||||||
|
out['msg'] = 'Only members can publish notes on this relay';
|
||||||
|
}
|
||||||
|
} catch (ex) {
|
||||||
|
out['action'] = 'reject';
|
||||||
|
out['msg'] = 'Auth service temporarily unavailable';
|
||||||
|
} finally {
|
||||||
|
await client.unbind();
|
||||||
|
return out;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
|
export default ldapPolicy;
|
||||||
|
|||||||
@@ -1,20 +1,20 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
//bin/true; exec deno run --unstable-kv -A "$0" "$@"
|
//bin/true; exec deno run -A "$0" "$@"
|
||||||
import {
|
import {
|
||||||
AntiDuplicationPolicy,
|
antiDuplicationPolicy,
|
||||||
HellthreadPolicy,
|
hellthreadPolicy,
|
||||||
PipePolicy,
|
pipeline,
|
||||||
|
rateLimitPolicy,
|
||||||
readStdin,
|
readStdin,
|
||||||
writeStdout,
|
writeStdout,
|
||||||
} from 'jsr:@nostrify/policies';
|
} from 'https://gitlab.com/soapbox-pub/strfry-policies/-/raw/develop/mod.ts';
|
||||||
import { strfry } from 'jsr:@nostrify/strfry';
|
import ldapPolicy from './ldap-policy.ts';
|
||||||
import { LdapConfig, LdapPolicy } from './ldap-policy.ts';
|
|
||||||
import { load } from "https://deno.land/std@0.224.0/dotenv/mod.ts";
|
import { load } from "https://deno.land/std@0.224.0/dotenv/mod.ts";
|
||||||
|
|
||||||
const dirname = new URL('.', import.meta.url).pathname;
|
const dirname = new URL('.', import.meta.url).pathname;
|
||||||
await load({ envPath: `${dirname}/.env`, export: true });
|
await load({ envPath: `${dirname}/.env`, export: true });
|
||||||
|
|
||||||
const ldapConfig: LdapConfig = {
|
const ldapConfig = {
|
||||||
url: Deno.env.get("LDAP_URL"),
|
url: Deno.env.get("LDAP_URL"),
|
||||||
bindDN: Deno.env.get("LDAP_BIND_DN"),
|
bindDN: Deno.env.get("LDAP_BIND_DN"),
|
||||||
password: Deno.env.get("LDAP_PASSWORD"),
|
password: Deno.env.get("LDAP_PASSWORD"),
|
||||||
@@ -22,10 +22,13 @@ const ldapConfig: LdapConfig = {
|
|||||||
whitelistPubkeys: Deno.env.get("WHITELIST_PUBKEYS")?.split(',')
|
whitelistPubkeys: Deno.env.get("WHITELIST_PUBKEYS")?.split(',')
|
||||||
}
|
}
|
||||||
|
|
||||||
const policy = new PipePolicy([
|
for await (const msg of readStdin()) {
|
||||||
new HellthreadPolicy({ limit: 10 }),
|
const result = await pipeline(msg, [
|
||||||
new AntiDuplicationPolicy({ kv: await Deno.openKv(), expireIn: 60000, minLength: 50 }),
|
[hellthreadPolicy, { limit: 10 }],
|
||||||
new LdapPolicy(ldapConfig)
|
[antiDuplicationPolicy, { ttl: 60000, minLength: 50 }],
|
||||||
]);
|
[rateLimitPolicy, { whitelist: ['127.0.0.1'] }],
|
||||||
|
[ldapPolicy, ldapConfig],
|
||||||
|
]);
|
||||||
|
|
||||||
await strfry(policy);
|
writeStdout(result);
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,29 +0,0 @@
|
|||||||
module Kosmos
|
|
||||||
class Ctags
|
|
||||||
def self.generate_app_tags
|
|
||||||
excludes = %w[.git gitno log tmp public].join(" --exclude ")
|
|
||||||
cmd = "ctags -R --languages=ruby --exclude #{excludes} ."
|
|
||||||
system cmd
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.generate_bundler_tags
|
|
||||||
runtime = ::Bundler::Runtime.new Dir.pwd, ::Bundler.definition
|
|
||||||
paths = runtime.specs.map(&:full_gem_path)
|
|
||||||
generate_tags(paths, "gems.tags")
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.generate_tags(paths, tag_file)
|
|
||||||
paths = paths.join(' ').strip
|
|
||||||
cmd = "find #{paths} -ignore_readdir_race -type f -name '*.rb' 2>/dev/null | ctags -f #{tag_file} -L -"
|
|
||||||
system cmd
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
namespace :ctags do
|
|
||||||
desc 'generate ctags'
|
|
||||||
task :create do
|
|
||||||
Kosmos::Ctags.generate_app_tags
|
|
||||||
Kosmos::Ctags.generate_bundler_tags
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -21,7 +21,7 @@ namespace :ldap do
|
|||||||
|
|
||||||
desc "Add custom attributes to schema"
|
desc "Add custom attributes to schema"
|
||||||
task add_custom_attributes: :environment do |t, args|
|
task add_custom_attributes: :environment do |t, args|
|
||||||
%w[ admin service_enabled nostr_key pgp_key ].each do |name|
|
%w[ admin service_enabled nostr_key ].each do |name|
|
||||||
Rake::Task["ldap:modify_ldap_schema"].invoke(name, "add")
|
Rake::Task["ldap:modify_ldap_schema"].invoke(name, "add")
|
||||||
Rake::Task['ldap:modify_ldap_schema'].reenable
|
Rake::Task['ldap:modify_ldap_schema'].reenable
|
||||||
end
|
end
|
||||||
@@ -29,7 +29,7 @@ namespace :ldap do
|
|||||||
|
|
||||||
desc "Delete custom attributes from schema"
|
desc "Delete custom attributes from schema"
|
||||||
task delete_custom_attributes: :environment do |t, args|
|
task delete_custom_attributes: :environment do |t, args|
|
||||||
%w[ admin service_enabled nostr_key pgp_key ].each do |name|
|
%w[ admin service_enabled nostr_key ].each do |name|
|
||||||
Rake::Task["ldap:modify_ldap_schema"].invoke(name, "delete")
|
Rake::Task["ldap:modify_ldap_schema"].invoke(name, "delete")
|
||||||
Rake::Task['ldap:modify_ldap_schema'].reenable
|
Rake::Task['ldap:modify_ldap_schema'].reenable
|
||||||
end
|
end
|
||||||
|
|||||||
114
public/400.html
114
public/400.html
@@ -1,114 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
|
|
||||||
<html lang="en">
|
|
||||||
|
|
||||||
<head>
|
|
||||||
|
|
||||||
<title>The server cannot process the request due to a client error (400 Bad Request)</title>
|
|
||||||
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<meta name="viewport" content="initial-scale=1, width=device-width">
|
|
||||||
<meta name="robots" content="noindex, nofollow">
|
|
||||||
|
|
||||||
<style>
|
|
||||||
|
|
||||||
*, *::before, *::after {
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
* {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
html {
|
|
||||||
font-size: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
background: #FFF;
|
|
||||||
color: #261B23;
|
|
||||||
display: grid;
|
|
||||||
font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, Aptos, Roboto, "Segoe UI", "Helvetica Neue", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
|
|
||||||
font-size: clamp(1rem, 2.5vw, 2rem);
|
|
||||||
-webkit-font-smoothing: antialiased;
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 400;
|
|
||||||
letter-spacing: -0.0025em;
|
|
||||||
line-height: 1.4;
|
|
||||||
min-height: 100vh;
|
|
||||||
place-items: center;
|
|
||||||
text-rendering: optimizeLegibility;
|
|
||||||
-webkit-text-size-adjust: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
color: inherit;
|
|
||||||
font-weight: 700;
|
|
||||||
text-decoration: underline;
|
|
||||||
text-underline-offset: 0.0925em;
|
|
||||||
}
|
|
||||||
|
|
||||||
b, strong {
|
|
||||||
font-weight: 700;
|
|
||||||
}
|
|
||||||
|
|
||||||
i, em {
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
|
|
||||||
main {
|
|
||||||
display: grid;
|
|
||||||
gap: 1em;
|
|
||||||
padding: 2em;
|
|
||||||
place-items: center;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
main header {
|
|
||||||
width: min(100%, 12em);
|
|
||||||
}
|
|
||||||
|
|
||||||
main header svg {
|
|
||||||
height: auto;
|
|
||||||
max-width: 100%;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
main article {
|
|
||||||
width: min(100%, 30em);
|
|
||||||
}
|
|
||||||
|
|
||||||
main article p {
|
|
||||||
font-size: 75%;
|
|
||||||
}
|
|
||||||
|
|
||||||
main article br {
|
|
||||||
|
|
||||||
display: none;
|
|
||||||
|
|
||||||
@media(min-width: 48em) {
|
|
||||||
display: inline;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
</style>
|
|
||||||
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
|
|
||||||
<!-- This file lives in public/400.html -->
|
|
||||||
|
|
||||||
<main>
|
|
||||||
<header>
|
|
||||||
<svg height="172" viewBox="0 0 480 172" width="480" xmlns="http://www.w3.org/2000/svg"><path d="m124.48 3.00509-45.6889 100.02991h26.2239v-28.1168h38.119v28.1168h21.628v35.145h-21.628v30.82h-37.308v-30.82h-72.1833v-31.901l50.2851-103.27391zm115.583 168.69891c-40.822 0-64.884-35.146-64.884-85.7015 0-50.5554 24.062-85.700907 64.884-85.700907 40.823 0 64.884 35.145507 64.884 85.700907 0 50.5555-24.061 85.7015-64.884 85.7015zm0-133.2831c-17.572 0-22.709 21.8984-22.709 47.5816 0 25.6835 5.137 47.5815 22.709 47.5815 17.303 0 22.71-21.898 22.71-47.5815 0-25.6832-5.407-47.5816-22.71-47.5816zm140.456 133.2831c-40.823 0-64.884-35.146-64.884-85.7015 0-50.5554 24.061-85.700907 64.884-85.700907 40.822 0 64.884 35.145507 64.884 85.700907 0 50.5555-24.062 85.7015-64.884 85.7015zm0-133.2831c-17.573 0-22.71 21.8984-22.71 47.5816 0 25.6835 5.137 47.5815 22.71 47.5815 17.302 0 22.709-21.898 22.709-47.5815 0-25.6832-5.407-47.5816-22.709-47.5816z" fill="#f0eff0"/><path d="m123.606 85.4445c3.212 1.0523 5.538 4.2089 5.538 8.0301 0 6.1472-4.209 9.5254-11.298 9.5254h-15.617v-34.0033h14.565c7.089 0 11.353 3.1566 11.353 9.2484 0 3.6551-2.049 6.3134-4.541 7.1994zm-12.904-2.9905h5.095c2.603 0 3.988-.9968 3.988-3.1013 0-2.1044-1.385-3.0459-3.988-3.0459h-5.095zm0 6.6456v6.5902h5.981c2.492 0 3.877-1.3291 3.877-3.2674 0-2.049-1.385-3.3228-3.877-3.3228zm43.786 13.9004h-8.362v-1.274c-.831.831-3.323 1.717-5.981 1.717-4.929 0-9.083-2.769-9.083-8.0301 0-4.818 4.154-7.9193 9.581-7.9193 2.049 0 4.486.6646 5.483 1.3845v-1.606c0-1.606-.942-2.9905-3.046-2.9905-1.606 0-2.548.7199-2.935 1.8275h-8.197c.72-4.8181 4.985-8.6393 11.409-8.6393 7.088 0 11.131 3.7659 11.131 10.2453zm-8.362-6.9779v-1.4399c-.554-1.0522-2.049-1.7167-3.655-1.7167-1.717 0-3.434.7199-3.434 2.3813 0 1.7168 1.717 2.4367 3.434 2.4367 1.606 0 3.101-.6645 3.655-1.6614zm27.996 6.9779v-1.994c-1.163 1.329-3.599 2.548-6.147 2.548-7.199 0-11.131-5.8151-11.131-13.0145s3.932-13.0143 11.131-13.0143c2.548 0 4.984 1.2184 6.147 2.5475v-13.0697h8.695v35.997zm0-9.1931v-6.5902c-.664-1.3291-2.159-2.326-3.821-2.326-2.99 0-4.763 2.4368-4.763 5.6488s1.773 5.5934 4.763 5.5934c1.717 0 3.157-.9415 3.821-2.326zm35.471-2.049h-3.101v11.2421h-8.806v-34.0033h15.285c7.31 0 12.35 4.1535 12.35 11.5744 0 5.1503-2.603 8.6947-6.757 10.2453l7.975 12.1836h-9.858zm-3.101-15.2849v8.1962h5.538c3.156 0 4.596-1.606 4.596-4.0981s-1.44-4.0981-4.596-4.0981zm36.957 17.8323h8.03c-.886 5.7597-5.206 9.2487-11.685 9.2487-7.643 0-12.682-5.2613-12.682-13.0145 0-7.6978 5.316-13.0143 12.515-13.0143 7.643 0 11.962 5.095 11.962 12.5159v2.1598h-16.115c.277 2.9905 1.827 4.5965 4.32 4.5965 1.772 0 3.156-.7753 3.655-2.4921zm-3.822-10.0237c-2.049 0-3.433 1.2737-3.987 3.5997h7.532c-.111-2.0491-1.385-3.5997-3.545-3.5997zm30.98 27.5234v-10.799c-1.163 1.329-3.6 2.548-6.147 2.548-7.2 0-11.132-5.9259-11.132-13.0145 0-7.144 3.932-13.0143 11.132-13.0143 2.547 0 4.984 1.2184 6.147 2.5475v-1.9937h8.695v33.726zm0-17.9981v-6.5902c-.665-1.3291-2.105-2.326-3.821-2.326-2.991 0-4.763 2.4368-4.763 5.6488s1.772 5.5934 4.763 5.5934c1.661 0 3.156-.9415 3.821-2.326zm36.789-15.7279v24.921h-8.695v-2.16c-1.329 1.551-3.821 2.714-6.646 2.714-5.482 0-8.75-3.5999-8.75-9.1379v-16.3371h8.64v14.288c0 2.1045.996 3.5997 3.212 3.5997 1.606 0 3.101-1.0522 3.544-2.769v-15.1187zm19.084 16.2263h8.03c-.886 5.7597-5.206 9.2487-11.685 9.2487-7.643 0-12.682-5.2613-12.682-13.0145 0-7.6978 5.316-13.0143 12.515-13.0143 7.643 0 11.963 5.095 11.963 12.5159v2.1598h-16.116c.277 2.9905 1.828 4.5965 4.32 4.5965 1.772 0 3.156-.7753 3.655-2.4921zm-3.822-10.0237c-2.049 0-3.433 1.2737-3.987 3.5997h7.532c-.111-2.0491-1.385-3.5997-3.545-3.5997zm13.428 11.0206h8.474c.387 1.3845 1.606 2.1598 3.156 2.1598 1.44 0 2.548-.5538 2.548-1.7168 0-.9414-.72-1.2737-1.939-1.5506l-4.873-.9969c-4.154-.886-6.867-2.8797-6.867-7.2547 0-5.3165 4.762-8.4178 10.633-8.4178 6.812 0 10.522 3.1567 11.297 8.0855h-8.03c-.277-1.0522-1.052-1.9937-3.046-1.9937-1.273 0-2.326.5538-2.326 1.6614 0 .7753.554 1.163 1.717 1.3845l4.929 1.163c4.541 1.0522 6.978 3.4335 6.978 7.4763 0 5.3168-4.818 8.2518-10.91 8.2518-6.369 0-10.965-2.88-11.741-8.2518zm27.538-.8861v-9.5807h-3.655v-6.7564h3.655v-6.8671h8.584v6.8671h5.205v6.7564h-5.205v8.307c0 1.9383.941 2.769 2.658 2.769.941 0 1.993-.2216 2.769-.5538v7.3654c-.997.443-2.88.775-4.818.775-5.871 0-9.193-2.769-9.193-9.0819z" fill="#d30001"/></svg>
|
|
||||||
</header>
|
|
||||||
<article>
|
|
||||||
<p><strong>The server cannot process the request due to a client error.</strong> Please check the request and try again. If you’re the application owner check the logs for more information.</p>
|
|
||||||
</article>
|
|
||||||
</main>
|
|
||||||
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
||||||
169
public/404.html
169
public/404.html
@@ -1,114 +1,67 @@
|
|||||||
<!doctype html>
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>The page you were looking for doesn't exist (404)</title>
|
||||||
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||||
|
<style>
|
||||||
|
.rails-default-error-page {
|
||||||
|
background-color: #EFEFEF;
|
||||||
|
color: #2E2F30;
|
||||||
|
text-align: center;
|
||||||
|
font-family: arial, sans-serif;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
<html lang="en">
|
.rails-default-error-page div.dialog {
|
||||||
|
width: 95%;
|
||||||
|
max-width: 33em;
|
||||||
|
margin: 4em auto 0;
|
||||||
|
}
|
||||||
|
|
||||||
<head>
|
.rails-default-error-page div.dialog > div {
|
||||||
|
border: 1px solid #CCC;
|
||||||
|
border-right-color: #999;
|
||||||
|
border-left-color: #999;
|
||||||
|
border-bottom-color: #BBB;
|
||||||
|
border-top: #B00100 solid 4px;
|
||||||
|
border-top-left-radius: 9px;
|
||||||
|
border-top-right-radius: 9px;
|
||||||
|
background-color: white;
|
||||||
|
padding: 7px 12% 0;
|
||||||
|
box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
|
||||||
|
}
|
||||||
|
|
||||||
<title>The page you were looking for doesn’t exist (404 Not found)</title>
|
.rails-default-error-page h1 {
|
||||||
|
font-size: 100%;
|
||||||
|
color: #730E15;
|
||||||
|
line-height: 1.5em;
|
||||||
|
}
|
||||||
|
|
||||||
<meta charset="utf-8">
|
.rails-default-error-page div.dialog > p {
|
||||||
<meta name="viewport" content="initial-scale=1, width=device-width">
|
margin: 0 0 1em;
|
||||||
<meta name="robots" content="noindex, nofollow">
|
padding: 1em;
|
||||||
|
background-color: #F7F7F7;
|
||||||
<style>
|
border: 1px solid #CCC;
|
||||||
|
border-right-color: #999;
|
||||||
*, *::before, *::after {
|
border-left-color: #999;
|
||||||
box-sizing: border-box;
|
border-bottom-color: #999;
|
||||||
}
|
border-bottom-left-radius: 4px;
|
||||||
|
border-bottom-right-radius: 4px;
|
||||||
* {
|
border-top-color: #DADADA;
|
||||||
margin: 0;
|
color: #666;
|
||||||
}
|
box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
|
||||||
|
}
|
||||||
html {
|
</style>
|
||||||
font-size: 16px;
|
</head>
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
background: #FFF;
|
|
||||||
color: #261B23;
|
|
||||||
display: grid;
|
|
||||||
font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, Aptos, Roboto, "Segoe UI", "Helvetica Neue", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
|
|
||||||
font-size: clamp(1rem, 2.5vw, 2rem);
|
|
||||||
-webkit-font-smoothing: antialiased;
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 400;
|
|
||||||
letter-spacing: -0.0025em;
|
|
||||||
line-height: 1.4;
|
|
||||||
min-height: 100vh;
|
|
||||||
place-items: center;
|
|
||||||
text-rendering: optimizeLegibility;
|
|
||||||
-webkit-text-size-adjust: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
color: inherit;
|
|
||||||
font-weight: 700;
|
|
||||||
text-decoration: underline;
|
|
||||||
text-underline-offset: 0.0925em;
|
|
||||||
}
|
|
||||||
|
|
||||||
b, strong {
|
|
||||||
font-weight: 700;
|
|
||||||
}
|
|
||||||
|
|
||||||
i, em {
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
|
|
||||||
main {
|
|
||||||
display: grid;
|
|
||||||
gap: 1em;
|
|
||||||
padding: 2em;
|
|
||||||
place-items: center;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
main header {
|
|
||||||
width: min(100%, 12em);
|
|
||||||
}
|
|
||||||
|
|
||||||
main header svg {
|
|
||||||
height: auto;
|
|
||||||
max-width: 100%;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
main article {
|
|
||||||
width: min(100%, 30em);
|
|
||||||
}
|
|
||||||
|
|
||||||
main article p {
|
|
||||||
font-size: 75%;
|
|
||||||
}
|
|
||||||
|
|
||||||
main article br {
|
|
||||||
|
|
||||||
display: none;
|
|
||||||
|
|
||||||
@media(min-width: 48em) {
|
|
||||||
display: inline;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
</style>
|
|
||||||
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
|
|
||||||
<!-- This file lives in public/404.html -->
|
|
||||||
|
|
||||||
<main>
|
|
||||||
<header>
|
|
||||||
<svg height="172" viewBox="0 0 480 172" width="480" xmlns="http://www.w3.org/2000/svg"><path d="m124.48 3.00509-45.6889 100.02991h26.2239v-28.1168h38.119v28.1168h21.628v35.145h-21.628v30.82h-37.308v-30.82h-72.1833v-31.901l50.2851-103.27391zm115.583 168.69891c-40.822 0-64.884-35.146-64.884-85.7015 0-50.5554 24.062-85.700907 64.884-85.700907 40.823 0 64.884 35.145507 64.884 85.700907 0 50.5555-24.061 85.7015-64.884 85.7015zm0-133.2831c-17.572 0-22.709 21.8984-22.709 47.5816 0 25.6835 5.137 47.5815 22.709 47.5815 17.303 0 22.71-21.898 22.71-47.5815 0-25.6832-5.407-47.5816-22.71-47.5816zm165.328-35.41581-45.689 100.02991h26.224v-28.1168h38.119v28.1168h21.628v35.145h-21.628v30.82h-37.308v-30.82h-72.184v-31.901l50.285-103.27391z" fill="#f0eff0"/><path d="m157.758 68.9967v34.0033h-7.199l-14.233-19.8814v19.8814h-8.584v-34.0033h8.307l13.125 18.7184v-18.7184zm28.454 21.5428c0 7.6978-5.15 13.0145-12.737 13.0145-7.532 0-12.738-5.3167-12.738-13.0145s5.206-13.0143 12.738-13.0143c7.587 0 12.737 5.3165 12.737 13.0143zm-8.528 0c0-3.4336-1.496-5.8703-4.209-5.8703-2.659 0-4.154 2.4367-4.154 5.8703s1.495 5.8149 4.154 5.8149c2.713 0 4.209-2.3813 4.209-5.8149zm13.184 3.8766v-9.5807h-3.655v-6.7564h3.655v-6.8671h8.584v6.8671h5.205v6.7564h-5.205v8.307c0 1.9383.941 2.769 2.658 2.769.941 0 1.994-.2216 2.769-.5538v7.3654c-.997.443-2.88.775-4.818.775-5.87 0-9.193-2.769-9.193-9.0819zm37.027 8.5839h-8.806v-34.0033h23.924v7.6978h-15.118v6.7564h13.9v7.5316h-13.9zm41.876-12.4605c0 7.6978-5.15 13.0145-12.737 13.0145-7.532 0-12.738-5.3167-12.738-13.0145s5.206-13.0143 12.738-13.0143c7.587 0 12.737 5.3165 12.737 13.0143zm-8.529 0c0-3.4336-1.495-5.8703-4.208-5.8703-2.659 0-4.154 2.4367-4.154 5.8703s1.495 5.8149 4.154 5.8149c2.713 0 4.208-2.3813 4.208-5.8149zm35.337-12.4605v24.921h-8.695v-2.16c-1.329 1.551-3.821 2.714-6.646 2.714-5.482 0-8.75-3.5999-8.75-9.1379v-16.3371h8.64v14.288c0 2.1045.997 3.5997 3.212 3.5997 1.606 0 3.101-1.0522 3.544-2.769v-15.1187zm4.076 24.921v-24.921h8.694v2.1598c1.385-1.5506 3.822-2.7136 6.701-2.7136 5.538 0 8.806 3.5997 8.806 9.1377v16.3371h-8.639v-14.2327c0-2.049-1.053-3.5443-3.268-3.5443-1.717 0-3.156.9969-3.6 2.7136v15.0634zm44.113 0v-1.994c-1.163 1.329-3.6 2.548-6.147 2.548-7.2 0-11.132-5.8151-11.132-13.0145s3.932-13.0143 11.132-13.0143c2.547 0 4.984 1.2184 6.147 2.5475v-13.0697h8.695v35.997zm0-9.1931v-6.5902c-.665-1.3291-2.16-2.326-3.821-2.326-2.991 0-4.763 2.4368-4.763 5.6488s1.772 5.5934 4.763 5.5934c1.717 0 3.156-.9415 3.821-2.326z" fill="#d30001"/></svg>
|
|
||||||
</header>
|
|
||||||
<article>
|
|
||||||
<p><strong>The page you were looking for doesn’t exist.</strong> You may have mistyped the address or the page may have moved. If you’re the application owner check the logs for more information.</p>
|
|
||||||
</article>
|
|
||||||
</main>
|
|
||||||
|
|
||||||
</body>
|
|
||||||
|
|
||||||
|
<body class="rails-default-error-page">
|
||||||
|
<!-- This file lives in public/404.html -->
|
||||||
|
<div class="dialog">
|
||||||
|
<div>
|
||||||
|
<h1>The page you were looking for doesn't exist.</h1>
|
||||||
|
<p>You may have mistyped the address or the page may have moved.</p>
|
||||||
|
</div>
|
||||||
|
<p>If you are the application owner check the logs for more information.</p>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
169
public/422.html
169
public/422.html
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user