Compare commits
114 Commits
v0.2.1
...
1b17cfb396
| Author | SHA1 | Date | |
|---|---|---|---|
|
1b17cfb396
|
|||
| e5aa5a665c | |||
| d37b68a6e5 | |||
|
56936916ff
|
|||
|
c93a460cff
|
|||
| f5ceda35c1 | |||
|
eb0439d6dc
|
|||
|
c3dde3506e
|
|||
| f22ffe373c | |||
|
bc20e89617
|
|||
| 0f0f296a5e | |||
|
78aea5d608
|
|||
|
f1d3e3d8ec
|
|||
| 2706c76890 | |||
| 17f5eb56cd | |||
| aa6b677b13 | |||
|
9abdab2274
|
|||
|
dd49d1208f
|
|||
|
db9118cb7c
|
|||
|
89913ba60b
|
|||
|
8cf631fd94
|
|||
| d0b359039b | |||
|
84cf523049
|
|||
| a7390ba00b | |||
|
67d148d117
|
|||
|
83ad6f4eef
|
|||
|
2e31268698
|
|||
|
f3b22c02ef
|
|||
| dbe65b4b5a | |||
|
2871fc0f53
|
|||
|
968689a512
|
|||
|
ab29f618f4
|
|||
| 94975a1b30 | |||
|
cd8880d9dc
|
|||
|
f59182b9c1
|
|||
|
941cb4a571
|
|||
|
f534898d8b
|
|||
| 18c7c54403 | |||
|
12a9d4674b
|
|||
|
1af8e068c5
|
|||
| 669b163814 | |||
| 46c7affd1f | |||
| 7ab107b689 | |||
| 5aee1a4100 | |||
|
1578fb9976
|
|||
| 8e64a7cf78 | |||
|
8b5bd66598
|
|||
| ac8552362c | |||
|
99c86c42c5
|
|||
|
d0267cb760
|
|||
| 25ddab9241 | |||
|
bf76ac55ee
|
|||
|
40e5c3609e
|
|||
|
1078c034ad
|
|||
|
bfa38ad7b2
|
|||
|
4f20cd0d0a
|
|||
|
e2ee33a1da
|
|||
|
8662a4c8c1
|
|||
|
dbc811b840
|
|||
|
884070a3cb
|
|||
|
3c350155de
|
|||
|
21c6ebc137
|
|||
|
0a1052fcb7
|
|||
|
f94227f9f3
|
|||
| 088961dfec | |||
|
31cf353d3a
|
|||
|
4eb40abc9c
|
|||
|
682c78c7c3
|
|||
|
f9726ad9be
|
|||
|
89188f5081
|
|||
| 6a6ff84ff2 | |||
|
b6949acc96
|
|||
|
814633034f
|
|||
| 260dedb6cf | |||
|
656c887811
|
|||
|
7e9af716ac
|
|||
|
58cc6811f9
|
|||
|
8ad85636d9
|
|||
|
35e2c8cd30
|
|||
| 4526c941b8 | |||
| 4f5ebd5330 | |||
|
d7e4c6f3ae
|
|||
|
14caefe2d1
|
|||
|
0110f27ada
|
|||
|
dc7cf107c2
|
|||
| 4fbfaadb44 | |||
|
a01cb9ae21
|
|||
|
698e4381c2
|
|||
|
8997349186
|
|||
|
92bfc33bf0
|
|||
|
c6eb21faad
|
|||
| 2d9bc90b16 | |||
|
a0c579e319
|
|||
|
f289ee9365
|
|||
| 46a7345ce9 | |||
|
e12d02a988
|
|||
| 5e8618f25a | |||
|
2bdf08a523
|
|||
|
9ddd36c414
|
|||
|
9372ea7343
|
|||
|
c62ce00184
|
|||
|
4d8cd740ba
|
|||
|
9858572a2f
|
|||
|
51edf55ae9
|
|||
| 75485ce8e9 | |||
|
fcbfcc4007
|
|||
|
cdcb7b3aef
|
|||
| bcf5172956 | |||
|
26c6c5a3b2
|
|||
|
4a65573934
|
|||
|
5e2d5c3b28
|
|||
|
2f70bae523
|
|||
|
40f3e8327a
|
|||
|
f3d6e29e4e
|
@@ -1 +1,3 @@
|
|||||||
EJABBERD_API_URL='https://xmpp.kosmos.org/api'
|
EJABBERD_API_URL='https://xmpp.kosmos.org/api'
|
||||||
|
LNDHUB_API_URL='http://localhost:3023'
|
||||||
|
LNDHUB_PUBLIC_URL='https://lndhub.kosmos.org'
|
||||||
|
|||||||
@@ -1 +1,3 @@
|
|||||||
EJABBERD_API_URL='https://xmpp.kosmos.org:5443/api'
|
EJABBERD_API_URL='https://xmpp.kosmos.org:5443/api'
|
||||||
|
LNDHUB_API_URL='http://10.1.1.163:3023'
|
||||||
|
LNDHUB_PUBLIC_URL='https://lndhub.kosmos.org'
|
||||||
|
|||||||
@@ -1 +1,3 @@
|
|||||||
EJABBERD_API_URL='http://xmpp.example.com/api'
|
EJABBERD_API_URL='http://xmpp.example.com/api'
|
||||||
|
LNDHUB_API_URL='http://localhost:3023'
|
||||||
|
LNDHUB_PUBLIC_URL='https://lndhub.kosmos.org'
|
||||||
|
|||||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -39,3 +39,6 @@ yarn-debug.log*
|
|||||||
|
|
||||||
# Ignore local dotenv config file
|
# Ignore local dotenv config file
|
||||||
.env
|
.env
|
||||||
|
|
||||||
|
# Ignore redis dumps from sidekiq
|
||||||
|
dump.rdb
|
||||||
|
|||||||
13
Gemfile
13
Gemfile
@@ -21,15 +21,28 @@ gem 'jbuilder', '~> 2.7'
|
|||||||
# Reduces boot times through caching; required in config/boot.rb
|
# Reduces boot times through caching; required in config/boot.rb
|
||||||
gem 'bootsnap', '>= 1.4.2', require: false
|
gem 'bootsnap', '>= 1.4.2', require: false
|
||||||
|
|
||||||
|
# Configuration
|
||||||
gem 'dotenv-rails'
|
gem 'dotenv-rails'
|
||||||
|
|
||||||
|
# Security
|
||||||
|
gem 'lockbox'
|
||||||
|
|
||||||
|
# Authentication
|
||||||
gem 'warden'
|
gem 'warden'
|
||||||
gem 'devise'
|
gem 'devise'
|
||||||
gem 'devise_ldap_authenticatable'
|
gem 'devise_ldap_authenticatable'
|
||||||
gem 'net-ldap'
|
gem 'net-ldap'
|
||||||
|
|
||||||
|
# Utilities
|
||||||
|
gem "rqrcode", "~> 2.0"
|
||||||
|
|
||||||
|
# HTTP requests
|
||||||
gem 'faraday'
|
gem 'faraday'
|
||||||
|
|
||||||
|
# Background/scheduled jobs
|
||||||
|
gem 'sidekiq'
|
||||||
|
gem 'sidekiq-scheduler'
|
||||||
|
|
||||||
group :development, :test do
|
group :development, :test do
|
||||||
# Use sqlite3 as the database for Active Record
|
# Use sqlite3 as the database for Active Record
|
||||||
gem 'sqlite3', '~> 1.4'
|
gem 'sqlite3', '~> 1.4'
|
||||||
|
|||||||
300
Gemfile.lock
300
Gemfile.lock
@@ -1,83 +1,91 @@
|
|||||||
GEM
|
GEM
|
||||||
remote: https://rubygems.org/
|
remote: https://rubygems.org/
|
||||||
specs:
|
specs:
|
||||||
actioncable (6.0.3.4)
|
actioncable (6.0.4.1)
|
||||||
actionpack (= 6.0.3.4)
|
actionpack (= 6.0.4.1)
|
||||||
nio4r (~> 2.0)
|
nio4r (~> 2.0)
|
||||||
websocket-driver (>= 0.6.1)
|
websocket-driver (>= 0.6.1)
|
||||||
actionmailbox (6.0.3.4)
|
actionmailbox (6.0.4.1)
|
||||||
actionpack (= 6.0.3.4)
|
actionpack (= 6.0.4.1)
|
||||||
activejob (= 6.0.3.4)
|
activejob (= 6.0.4.1)
|
||||||
activerecord (= 6.0.3.4)
|
activerecord (= 6.0.4.1)
|
||||||
activestorage (= 6.0.3.4)
|
activestorage (= 6.0.4.1)
|
||||||
activesupport (= 6.0.3.4)
|
activesupport (= 6.0.4.1)
|
||||||
mail (>= 2.7.1)
|
mail (>= 2.7.1)
|
||||||
actionmailer (6.0.3.4)
|
actionmailer (6.0.4.1)
|
||||||
actionpack (= 6.0.3.4)
|
actionpack (= 6.0.4.1)
|
||||||
actionview (= 6.0.3.4)
|
actionview (= 6.0.4.1)
|
||||||
activejob (= 6.0.3.4)
|
activejob (= 6.0.4.1)
|
||||||
mail (~> 2.5, >= 2.5.4)
|
mail (~> 2.5, >= 2.5.4)
|
||||||
rails-dom-testing (~> 2.0)
|
rails-dom-testing (~> 2.0)
|
||||||
actionpack (6.0.3.4)
|
actionpack (6.0.4.1)
|
||||||
actionview (= 6.0.3.4)
|
actionview (= 6.0.4.1)
|
||||||
activesupport (= 6.0.3.4)
|
activesupport (= 6.0.4.1)
|
||||||
rack (~> 2.0, >= 2.0.8)
|
rack (~> 2.0, >= 2.0.8)
|
||||||
rack-test (>= 0.6.3)
|
rack-test (>= 0.6.3)
|
||||||
rails-dom-testing (~> 2.0)
|
rails-dom-testing (~> 2.0)
|
||||||
rails-html-sanitizer (~> 1.0, >= 1.2.0)
|
rails-html-sanitizer (~> 1.0, >= 1.2.0)
|
||||||
actiontext (6.0.3.4)
|
actiontext (6.0.4.1)
|
||||||
actionpack (= 6.0.3.4)
|
actionpack (= 6.0.4.1)
|
||||||
activerecord (= 6.0.3.4)
|
activerecord (= 6.0.4.1)
|
||||||
activestorage (= 6.0.3.4)
|
activestorage (= 6.0.4.1)
|
||||||
activesupport (= 6.0.3.4)
|
activesupport (= 6.0.4.1)
|
||||||
nokogiri (>= 1.8.5)
|
nokogiri (>= 1.8.5)
|
||||||
actionview (6.0.3.4)
|
actionview (6.0.4.1)
|
||||||
activesupport (= 6.0.3.4)
|
activesupport (= 6.0.4.1)
|
||||||
builder (~> 3.1)
|
builder (~> 3.1)
|
||||||
erubi (~> 1.4)
|
erubi (~> 1.4)
|
||||||
rails-dom-testing (~> 2.0)
|
rails-dom-testing (~> 2.0)
|
||||||
rails-html-sanitizer (~> 1.1, >= 1.2.0)
|
rails-html-sanitizer (~> 1.1, >= 1.2.0)
|
||||||
activejob (6.0.3.4)
|
activejob (6.0.4.1)
|
||||||
activesupport (= 6.0.3.4)
|
activesupport (= 6.0.4.1)
|
||||||
globalid (>= 0.3.6)
|
globalid (>= 0.3.6)
|
||||||
activemodel (6.0.3.4)
|
activemodel (6.0.4.1)
|
||||||
activesupport (= 6.0.3.4)
|
activesupport (= 6.0.4.1)
|
||||||
activerecord (6.0.3.4)
|
activerecord (6.0.4.1)
|
||||||
activemodel (= 6.0.3.4)
|
activemodel (= 6.0.4.1)
|
||||||
activesupport (= 6.0.3.4)
|
activesupport (= 6.0.4.1)
|
||||||
activestorage (6.0.3.4)
|
activestorage (6.0.4.1)
|
||||||
actionpack (= 6.0.3.4)
|
actionpack (= 6.0.4.1)
|
||||||
activejob (= 6.0.3.4)
|
activejob (= 6.0.4.1)
|
||||||
activerecord (= 6.0.3.4)
|
activerecord (= 6.0.4.1)
|
||||||
marcel (~> 0.3.1)
|
marcel (~> 1.0.0)
|
||||||
activesupport (6.0.3.4)
|
activesupport (6.0.4.1)
|
||||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||||
i18n (>= 0.7, < 2)
|
i18n (>= 0.7, < 2)
|
||||||
minitest (~> 5.1)
|
minitest (~> 5.1)
|
||||||
tzinfo (~> 1.1)
|
tzinfo (~> 1.1)
|
||||||
zeitwerk (~> 2.2, >= 2.2.2)
|
zeitwerk (~> 2.2, >= 2.2.2)
|
||||||
addressable (2.7.0)
|
addressable (2.8.0)
|
||||||
public_suffix (>= 2.0.2, < 5.0)
|
public_suffix (>= 2.0.2, < 5.0)
|
||||||
bcrypt (3.1.16)
|
bcrypt (3.1.16)
|
||||||
bindex (0.8.1)
|
bindex (0.8.1)
|
||||||
bootsnap (1.5.0)
|
bootsnap (1.9.1)
|
||||||
msgpack (~> 1.0)
|
msgpack (~> 1.0)
|
||||||
builder (3.2.4)
|
builder (3.2.4)
|
||||||
byebug (11.1.3)
|
byebug (11.1.3)
|
||||||
capybara (3.33.0)
|
capybara (3.36.0)
|
||||||
addressable
|
addressable
|
||||||
|
matrix
|
||||||
mini_mime (>= 0.1.3)
|
mini_mime (>= 0.1.3)
|
||||||
nokogiri (~> 1.8)
|
nokogiri (~> 1.8)
|
||||||
rack (>= 1.6.0)
|
rack (>= 1.6.0)
|
||||||
rack-test (>= 0.6.3)
|
rack-test (>= 0.6.3)
|
||||||
regexp_parser (~> 1.5)
|
regexp_parser (>= 1.5, < 3.0)
|
||||||
xpath (~> 3.2)
|
xpath (~> 3.2)
|
||||||
concurrent-ruby (1.1.7)
|
chunky_png (1.4.0)
|
||||||
crack (0.4.3)
|
concurrent-ruby (1.1.9)
|
||||||
safe_yaml (~> 1.0.0)
|
connection_pool (2.2.5)
|
||||||
|
crack (0.4.5)
|
||||||
|
rexml
|
||||||
crass (1.0.6)
|
crass (1.0.6)
|
||||||
database_cleaner (1.8.5)
|
database_cleaner (2.0.1)
|
||||||
devise (4.7.3)
|
database_cleaner-active_record (~> 2.0.0)
|
||||||
|
database_cleaner-active_record (2.0.1)
|
||||||
|
activerecord (>= 5.a)
|
||||||
|
database_cleaner-core (~> 2.0.0)
|
||||||
|
database_cleaner-core (2.0.1)
|
||||||
|
devise (4.8.0)
|
||||||
bcrypt (~> 3.0)
|
bcrypt (~> 3.0)
|
||||||
orm_adapter (~> 0.1)
|
orm_adapter (~> 0.1)
|
||||||
railties (>= 4.1.0)
|
railties (>= 4.1.0)
|
||||||
@@ -87,117 +95,149 @@ GEM
|
|||||||
devise (>= 3.4.1)
|
devise (>= 3.4.1)
|
||||||
net-ldap (>= 0.16.0)
|
net-ldap (>= 0.16.0)
|
||||||
diff-lcs (1.4.4)
|
diff-lcs (1.4.4)
|
||||||
dotenv (2.7.2)
|
dotenv (2.7.6)
|
||||||
dotenv-rails (2.7.2)
|
dotenv-rails (2.7.6)
|
||||||
dotenv (= 2.7.2)
|
dotenv (= 2.7.6)
|
||||||
railties (>= 3.2, < 6.1)
|
railties (>= 3.2)
|
||||||
erubi (1.9.0)
|
e2mmap (0.1.0)
|
||||||
factory_bot (6.1.0)
|
erubi (1.10.0)
|
||||||
|
et-orbi (1.2.6)
|
||||||
|
tzinfo
|
||||||
|
factory_bot (6.2.0)
|
||||||
activesupport (>= 5.0.0)
|
activesupport (>= 5.0.0)
|
||||||
factory_bot_rails (6.1.0)
|
factory_bot_rails (6.2.0)
|
||||||
factory_bot (~> 6.1.0)
|
factory_bot (~> 6.2.0)
|
||||||
railties (>= 5.0.0)
|
railties (>= 5.0.0)
|
||||||
faraday (0.17.0)
|
faraday (1.8.0)
|
||||||
|
faraday-em_http (~> 1.0)
|
||||||
|
faraday-em_synchrony (~> 1.0)
|
||||||
|
faraday-excon (~> 1.1)
|
||||||
|
faraday-httpclient (~> 1.0.1)
|
||||||
|
faraday-net_http (~> 1.0)
|
||||||
|
faraday-net_http_persistent (~> 1.1)
|
||||||
|
faraday-patron (~> 1.0)
|
||||||
|
faraday-rack (~> 1.0)
|
||||||
multipart-post (>= 1.2, < 3)
|
multipart-post (>= 1.2, < 3)
|
||||||
ffi (1.13.1)
|
ruby2_keywords (>= 0.0.4)
|
||||||
globalid (0.4.2)
|
faraday-em_http (1.0.0)
|
||||||
activesupport (>= 4.2.0)
|
faraday-em_synchrony (1.0.0)
|
||||||
hashdiff (0.4.0)
|
faraday-excon (1.1.0)
|
||||||
i18n (1.8.5)
|
faraday-httpclient (1.0.1)
|
||||||
|
faraday-net_http (1.0.1)
|
||||||
|
faraday-net_http_persistent (1.2.0)
|
||||||
|
faraday-patron (1.0.0)
|
||||||
|
faraday-rack (1.0.0)
|
||||||
|
ffi (1.15.4)
|
||||||
|
fugit (1.5.2)
|
||||||
|
et-orbi (~> 1.1, >= 1.1.8)
|
||||||
|
raabro (~> 1.4)
|
||||||
|
globalid (0.5.2)
|
||||||
|
activesupport (>= 5.0)
|
||||||
|
hashdiff (1.0.1)
|
||||||
|
i18n (1.8.11)
|
||||||
concurrent-ruby (~> 1.0)
|
concurrent-ruby (~> 1.0)
|
||||||
jbuilder (2.10.1)
|
jbuilder (2.11.3)
|
||||||
activesupport (>= 5.0.0)
|
activesupport (>= 5.0.0)
|
||||||
launchy (2.4.3)
|
launchy (2.5.0)
|
||||||
addressable (~> 2.3)
|
addressable (~> 2.7)
|
||||||
letter_opener (1.7.0)
|
letter_opener (1.7.0)
|
||||||
launchy (~> 2.2)
|
launchy (~> 2.2)
|
||||||
letter_opener_web (1.3.4)
|
letter_opener_web (1.4.1)
|
||||||
actionmailer (>= 3.2)
|
actionmailer (>= 3.2)
|
||||||
letter_opener (~> 1.0)
|
letter_opener (~> 1.0)
|
||||||
railties (>= 3.2)
|
railties (>= 3.2)
|
||||||
listen (3.2.1)
|
listen (3.7.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)
|
||||||
loofah (2.7.0)
|
lockbox (0.6.6)
|
||||||
|
loofah (2.12.0)
|
||||||
crass (~> 1.0.2)
|
crass (~> 1.0.2)
|
||||||
nokogiri (>= 1.5.9)
|
nokogiri (>= 1.5.9)
|
||||||
mail (2.7.1)
|
mail (2.7.1)
|
||||||
mini_mime (>= 0.1.1)
|
mini_mime (>= 0.1.1)
|
||||||
marcel (0.3.3)
|
marcel (1.0.2)
|
||||||
mimemagic (~> 0.3.2)
|
matrix (0.4.2)
|
||||||
method_source (1.0.0)
|
method_source (1.0.0)
|
||||||
mimemagic (0.3.5)
|
mini_mime (1.1.2)
|
||||||
mini_mime (1.0.2)
|
minitest (5.14.4)
|
||||||
mini_portile2 (2.4.0)
|
msgpack (1.4.2)
|
||||||
minitest (5.14.2)
|
|
||||||
msgpack (1.3.3)
|
|
||||||
multipart-post (2.1.1)
|
multipart-post (2.1.1)
|
||||||
net-ldap (0.16.3)
|
net-ldap (0.17.0)
|
||||||
nio4r (2.5.4)
|
nio4r (2.5.8)
|
||||||
nokogiri (1.10.10)
|
nokogiri (1.12.5-x86_64-linux)
|
||||||
mini_portile2 (~> 2.4.0)
|
racc (~> 1.4)
|
||||||
orm_adapter (0.5.0)
|
orm_adapter (0.5.0)
|
||||||
pg (1.2.3)
|
pg (1.2.3)
|
||||||
public_suffix (4.0.6)
|
public_suffix (4.0.6)
|
||||||
puma (4.3.6)
|
puma (4.3.10)
|
||||||
nio4r (~> 2.0)
|
nio4r (~> 2.0)
|
||||||
|
raabro (1.4.0)
|
||||||
|
racc (1.6.0)
|
||||||
rack (2.2.3)
|
rack (2.2.3)
|
||||||
rack-proxy (0.6.5)
|
rack-proxy (0.7.0)
|
||||||
rack
|
rack
|
||||||
rack-test (1.1.0)
|
rack-test (1.1.0)
|
||||||
rack (>= 1.0, < 3)
|
rack (>= 1.0, < 3)
|
||||||
rails (6.0.3.4)
|
rails (6.0.4.1)
|
||||||
actioncable (= 6.0.3.4)
|
actioncable (= 6.0.4.1)
|
||||||
actionmailbox (= 6.0.3.4)
|
actionmailbox (= 6.0.4.1)
|
||||||
actionmailer (= 6.0.3.4)
|
actionmailer (= 6.0.4.1)
|
||||||
actionpack (= 6.0.3.4)
|
actionpack (= 6.0.4.1)
|
||||||
actiontext (= 6.0.3.4)
|
actiontext (= 6.0.4.1)
|
||||||
actionview (= 6.0.3.4)
|
actionview (= 6.0.4.1)
|
||||||
activejob (= 6.0.3.4)
|
activejob (= 6.0.4.1)
|
||||||
activemodel (= 6.0.3.4)
|
activemodel (= 6.0.4.1)
|
||||||
activerecord (= 6.0.3.4)
|
activerecord (= 6.0.4.1)
|
||||||
activestorage (= 6.0.3.4)
|
activestorage (= 6.0.4.1)
|
||||||
activesupport (= 6.0.3.4)
|
activesupport (= 6.0.4.1)
|
||||||
bundler (>= 1.3.0)
|
bundler (>= 1.3.0)
|
||||||
railties (= 6.0.3.4)
|
railties (= 6.0.4.1)
|
||||||
sprockets-rails (>= 2.0.0)
|
sprockets-rails (>= 2.0.0)
|
||||||
rails-dom-testing (2.0.3)
|
rails-dom-testing (2.0.3)
|
||||||
activesupport (>= 4.2.0)
|
activesupport (>= 4.2.0)
|
||||||
nokogiri (>= 1.6)
|
nokogiri (>= 1.6)
|
||||||
rails-html-sanitizer (1.3.0)
|
rails-html-sanitizer (1.4.2)
|
||||||
loofah (~> 2.3)
|
loofah (~> 2.3)
|
||||||
railties (6.0.3.4)
|
railties (6.0.4.1)
|
||||||
actionpack (= 6.0.3.4)
|
actionpack (= 6.0.4.1)
|
||||||
activesupport (= 6.0.3.4)
|
activesupport (= 6.0.4.1)
|
||||||
method_source
|
method_source
|
||||||
rake (>= 0.8.7)
|
rake (>= 0.8.7)
|
||||||
thor (>= 0.20.3, < 2.0)
|
thor (>= 0.20.3, < 2.0)
|
||||||
rake (13.0.1)
|
rake (13.0.6)
|
||||||
rb-fsevent (0.10.4)
|
rb-fsevent (0.11.0)
|
||||||
rb-inotify (0.10.1)
|
rb-inotify (0.10.1)
|
||||||
ffi (~> 1.0)
|
ffi (~> 1.0)
|
||||||
regexp_parser (1.8.2)
|
redis (4.5.1)
|
||||||
|
regexp_parser (2.1.1)
|
||||||
responders (3.0.1)
|
responders (3.0.1)
|
||||||
actionpack (>= 5.0)
|
actionpack (>= 5.0)
|
||||||
railties (>= 5.0)
|
railties (>= 5.0)
|
||||||
rspec-core (3.10.0)
|
rexml (3.2.5)
|
||||||
|
rqrcode (2.1.0)
|
||||||
|
chunky_png (~> 1.0)
|
||||||
|
rqrcode_core (~> 1.0)
|
||||||
|
rqrcode_core (1.2.0)
|
||||||
|
rspec-core (3.10.1)
|
||||||
rspec-support (~> 3.10.0)
|
rspec-support (~> 3.10.0)
|
||||||
rspec-expectations (3.10.0)
|
rspec-expectations (3.10.1)
|
||||||
diff-lcs (>= 1.2.0, < 2.0)
|
diff-lcs (>= 1.2.0, < 2.0)
|
||||||
rspec-support (~> 3.10.0)
|
rspec-support (~> 3.10.0)
|
||||||
rspec-mocks (3.10.0)
|
rspec-mocks (3.10.2)
|
||||||
diff-lcs (>= 1.2.0, < 2.0)
|
diff-lcs (>= 1.2.0, < 2.0)
|
||||||
rspec-support (~> 3.10.0)
|
rspec-support (~> 3.10.0)
|
||||||
rspec-rails (4.0.1)
|
rspec-rails (5.0.2)
|
||||||
actionpack (>= 4.2)
|
actionpack (>= 5.2)
|
||||||
activesupport (>= 4.2)
|
activesupport (>= 5.2)
|
||||||
railties (>= 4.2)
|
railties (>= 5.2)
|
||||||
rspec-core (~> 3.9)
|
rspec-core (~> 3.10)
|
||||||
rspec-expectations (~> 3.9)
|
rspec-expectations (~> 3.10)
|
||||||
rspec-mocks (~> 3.9)
|
rspec-mocks (~> 3.10)
|
||||||
rspec-support (~> 3.9)
|
rspec-support (~> 3.10)
|
||||||
rspec-support (3.10.0)
|
rspec-support (3.10.3)
|
||||||
safe_yaml (1.0.5)
|
ruby2_keywords (0.0.5)
|
||||||
|
rufus-scheduler (3.8.0)
|
||||||
|
fugit (~> 1.1, >= 1.1.6)
|
||||||
sass-rails (6.0.0)
|
sass-rails (6.0.0)
|
||||||
sassc-rails (~> 2.1, >= 2.1.1)
|
sassc-rails (~> 2.1, >= 2.1.1)
|
||||||
sassc (2.4.0)
|
sassc (2.4.0)
|
||||||
@@ -208,6 +248,17 @@ GEM
|
|||||||
sprockets (> 3.0)
|
sprockets (> 3.0)
|
||||||
sprockets-rails
|
sprockets-rails
|
||||||
tilt
|
tilt
|
||||||
|
sidekiq (6.3.1)
|
||||||
|
connection_pool (>= 2.2.2)
|
||||||
|
rack (~> 2.0)
|
||||||
|
redis (>= 4.2.0)
|
||||||
|
sidekiq-scheduler (3.1.0)
|
||||||
|
e2mmap
|
||||||
|
redis (>= 3, < 5)
|
||||||
|
rufus-scheduler (~> 3.2)
|
||||||
|
sidekiq (>= 3)
|
||||||
|
thwait
|
||||||
|
tilt (>= 1.4.0)
|
||||||
spring (2.1.1)
|
spring (2.1.1)
|
||||||
spring-watcher-listen (2.0.1)
|
spring-watcher-listen (2.0.1)
|
||||||
listen (>= 2.7, < 4.0)
|
listen (>= 2.7, < 4.0)
|
||||||
@@ -215,43 +266,46 @@ GEM
|
|||||||
sprockets (4.0.2)
|
sprockets (4.0.2)
|
||||||
concurrent-ruby (~> 1.0)
|
concurrent-ruby (~> 1.0)
|
||||||
rack (> 1, < 3)
|
rack (> 1, < 3)
|
||||||
sprockets-rails (3.2.2)
|
sprockets-rails (3.4.0)
|
||||||
actionpack (>= 4.0)
|
actionpack (>= 5.2)
|
||||||
activesupport (>= 4.0)
|
activesupport (>= 5.2)
|
||||||
sprockets (>= 3.0.0)
|
sprockets (>= 3.0.0)
|
||||||
sqlite3 (1.4.2)
|
sqlite3 (1.4.2)
|
||||||
thor (1.0.1)
|
thor (1.1.0)
|
||||||
thread_safe (0.3.6)
|
thread_safe (0.3.6)
|
||||||
|
thwait (0.2.0)
|
||||||
|
e2mmap
|
||||||
tilt (2.0.10)
|
tilt (2.0.10)
|
||||||
turbolinks (5.2.1)
|
turbolinks (5.2.1)
|
||||||
turbolinks-source (~> 5.2)
|
turbolinks-source (~> 5.2)
|
||||||
turbolinks-source (5.2.0)
|
turbolinks-source (5.2.0)
|
||||||
tzinfo (1.2.7)
|
tzinfo (1.2.9)
|
||||||
thread_safe (~> 0.1)
|
thread_safe (~> 0.1)
|
||||||
warden (1.2.9)
|
warden (1.2.9)
|
||||||
rack (>= 2.0.9)
|
rack (>= 2.0.9)
|
||||||
web-console (4.1.0)
|
web-console (4.2.0)
|
||||||
actionview (>= 6.0.0)
|
actionview (>= 6.0.0)
|
||||||
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.6.0)
|
webmock (3.14.0)
|
||||||
addressable (>= 2.3.6)
|
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)
|
||||||
webpacker (4.3.0)
|
webpacker (4.3.0)
|
||||||
activesupport (>= 4.2)
|
activesupport (>= 4.2)
|
||||||
rack-proxy (>= 0.6.1)
|
rack-proxy (>= 0.6.1)
|
||||||
railties (>= 4.2)
|
railties (>= 4.2)
|
||||||
websocket-driver (0.7.3)
|
websocket-driver (0.7.5)
|
||||||
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)
|
||||||
zeitwerk (2.4.1)
|
zeitwerk (2.5.1)
|
||||||
|
|
||||||
PLATFORMS
|
PLATFORMS
|
||||||
ruby
|
ruby
|
||||||
|
x86_64-linux
|
||||||
|
|
||||||
DEPENDENCIES
|
DEPENDENCIES
|
||||||
bootsnap (>= 1.4.2)
|
bootsnap (>= 1.4.2)
|
||||||
@@ -267,12 +321,16 @@ DEPENDENCIES
|
|||||||
letter_opener
|
letter_opener
|
||||||
letter_opener_web
|
letter_opener_web
|
||||||
listen (~> 3.2)
|
listen (~> 3.2)
|
||||||
|
lockbox
|
||||||
net-ldap
|
net-ldap
|
||||||
pg (~> 1.2.3)
|
pg (~> 1.2.3)
|
||||||
puma (~> 4.1)
|
puma (~> 4.1)
|
||||||
rails (~> 6.0.3, >= 6.0.3.4)
|
rails (~> 6.0.3, >= 6.0.3.4)
|
||||||
|
rqrcode (~> 2.0)
|
||||||
rspec-rails
|
rspec-rails
|
||||||
sass-rails (>= 6)
|
sass-rails (>= 6)
|
||||||
|
sidekiq
|
||||||
|
sidekiq-scheduler
|
||||||
spring
|
spring
|
||||||
spring-watcher-listen (~> 2.0.0)
|
spring-watcher-listen (~> 2.0.0)
|
||||||
sqlite3 (~> 1.4)
|
sqlite3 (~> 1.4)
|
||||||
@@ -284,4 +342,4 @@ DEPENDENCIES
|
|||||||
webpacker (~> 4.0)
|
webpacker (~> 4.0)
|
||||||
|
|
||||||
BUNDLED WITH
|
BUNDLED WITH
|
||||||
2.0.2
|
2.2.2
|
||||||
|
|||||||
37
README.md
37
README.md
@@ -1,25 +1,10 @@
|
|||||||
|
[](https://drone.kosmos.org/kosmos/akkounts)
|
||||||
|
|
||||||
# Akkounts
|
# Akkounts
|
||||||
|
|
||||||
This app allows Kosmos/LDAP users to manage their accounts, including
|
This app allows Kosmos/LDAP users to manage their accounts, including
|
||||||
credentials, invites, donations, etc..
|
credentials, invites, donations, etc..
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
* [x] Log in with existing LDAP account
|
|
||||||
* [x] Reset account password by providing both username and email address
|
|
||||||
* [x] Reset account password when logged in, via reset email
|
|
||||||
* [x] Log in with admin permissions
|
|
||||||
* [x] View LDAP users as admin
|
|
||||||
* [x] Sign up for a new account via invitation
|
|
||||||
* [ ] List my donations
|
|
||||||
* [ ] Invite new users from your account
|
|
||||||
* [ ] Sign up for a new account by donating upfront
|
|
||||||
* [ ] Sign up for a new account via proving contributions (via cryptographic signature)
|
|
||||||
* [ ] ...
|
|
||||||
|
|
||||||
_Planned features are not at all a complete or static list, of course.
|
|
||||||
Suggestions and pull requests welcome!_
|
|
||||||
|
|
||||||
## Development
|
## Development
|
||||||
|
|
||||||
### Rails app
|
### Rails app
|
||||||
@@ -38,6 +23,10 @@ Running the dev server:
|
|||||||
|
|
||||||
bundle exec rails server
|
bundle exec rails server
|
||||||
|
|
||||||
|
Running the background workers (requires Redis):
|
||||||
|
|
||||||
|
bundle exec sidekiq -C config/sidekiq.yml
|
||||||
|
|
||||||
Running all specs:
|
Running all specs:
|
||||||
|
|
||||||
bundle exec rspec
|
bundle exec rspec
|
||||||
@@ -52,6 +41,15 @@ manual LDIF imports etc. (or provide a staging instance)
|
|||||||
* [Ruby on Rails](https://guides.rubyonrails.org/)
|
* [Ruby on Rails](https://guides.rubyonrails.org/)
|
||||||
* [Sass](https://sass-lang.com/documentation)
|
* [Sass](https://sass-lang.com/documentation)
|
||||||
|
|
||||||
|
### Front-end
|
||||||
|
|
||||||
|
* [Tailwind CSS](https://tailwindcss.com/)
|
||||||
|
|
||||||
|
**Caveat:** if you only add Tailwind classes/directives to templates or
|
||||||
|
helpers, but there's no change in the stylesheet files, then the new directives
|
||||||
|
won't be compiled in production. In this case, count up the version comment at
|
||||||
|
the top of `app/javascript/stylesheets/application.scss` to trigger compilation.
|
||||||
|
|
||||||
### Testing
|
### Testing
|
||||||
|
|
||||||
* [RSpec](https://rspec.info/documentation/)
|
* [RSpec](https://rspec.info/documentation/)
|
||||||
@@ -62,6 +60,11 @@ manual LDIF imports etc. (or provide a staging instance)
|
|||||||
* [devise_ldap_authenticatable](https://github.com/cschiewek/devise_ldap_authenticatable)
|
* [devise_ldap_authenticatable](https://github.com/cschiewek/devise_ldap_authenticatable)
|
||||||
* [net/ldap](https://www.rubydoc.info/gems/net-ldap/Net/LDAP)
|
* [net/ldap](https://www.rubydoc.info/gems/net-ldap/Net/LDAP)
|
||||||
|
|
||||||
|
### Asynchronous jobs/workers
|
||||||
|
|
||||||
|
* [Sidekiq](https://github.com/mperham/sidekiq/wiki/)
|
||||||
|
* [ActiveJob](https://github.com/mperham/sidekiq/wiki/Active-Job)
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
[GNU Affero General Public License v3.0](https://choosealicense.com/licenses/agpl-3.0/)
|
[GNU Affero General Public License v3.0](https://choosealicense.com/licenses/agpl-3.0/)
|
||||||
|
|||||||
@@ -1,13 +0,0 @@
|
|||||||
html, body, h1, h2, h3, h4, h5, h6, p, pre, a, dl, dt, dd, ol, ul, li {
|
|
||||||
font-size: 100%;
|
|
||||||
vertical-align: baseline;
|
|
||||||
background: transparent;
|
|
||||||
box-sizing: border-box;
|
|
||||||
overflow: visible;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
line-height: 1;
|
|
||||||
}
|
|
||||||
13
app/assets/stylesheets/_variables.scss
Normal file
13
app/assets/stylesheets/_variables.scss
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
$content-width: 800px;
|
||||||
|
$content-max-width: 100%;
|
||||||
|
|
||||||
|
$text-color-body: #222;
|
||||||
|
$text-color-discreet: #888;
|
||||||
|
|
||||||
|
$background-color-notice: #efffc4;
|
||||||
|
$background-color-alert: #fff4c2;
|
||||||
|
|
||||||
|
$color-blue: #0d4f99;
|
||||||
|
$color-purple: #8955a0;
|
||||||
|
$color-red-bright: #c00;
|
||||||
|
$color-red-dark: #990c0e;
|
||||||
25
app/assets/stylesheets/admin.scss
Normal file
25
app/assets/stylesheets/admin.scss
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
@import "variables";
|
||||||
|
|
||||||
|
body#admin-panel {
|
||||||
|
#wrapper {
|
||||||
|
> header {
|
||||||
|
background: $color-red-bright;
|
||||||
|
background: linear-gradient(35deg, rgba(255,0,255,0.2) 0, rgba(153,12,14,0.9) 100%),
|
||||||
|
url('/img/bg-1.jpg');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#main-nav {
|
||||||
|
ul {
|
||||||
|
grid-template-columns: repeat(4, 1fr);
|
||||||
|
|
||||||
|
li {
|
||||||
|
a {
|
||||||
|
&.active {
|
||||||
|
border-bottom: 2px solid $color-red-bright;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
@font-face {
|
|
||||||
font-family: 'Raleway';
|
|
||||||
src: url('/fonts/raleway-light.woff') format('woff2');
|
|
||||||
font-weight: 300;
|
|
||||||
font-style: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
font-family: "Open Sans", Helvetica, Arial, sans-serif;
|
|
||||||
font-weight: 400;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1, h2, h3 {
|
|
||||||
font-family: Raleway, inherit;
|
|
||||||
font-weight: 300;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
text-transform: uppercase;
|
|
||||||
}
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
form {
|
|
||||||
.field_with_errors {
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.layout-signup {
|
|
||||||
label {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type=text], input[type=email], input[type=password] {
|
|
||||||
font-size: 1.25rem;
|
|
||||||
padding: 0.5rem 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
span.at-sign, span.domain {
|
|
||||||
font-size: 1.25rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.error-msg {
|
|
||||||
color: #bc0101;
|
|
||||||
}
|
|
||||||
|
|
||||||
.actions {
|
|
||||||
margin-top: 2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.accept-terms {
|
|
||||||
margin-top: 2rem;
|
|
||||||
font-size: 0.85rem;
|
|
||||||
line-height: 1.5em;
|
|
||||||
color: #888;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +1,6 @@
|
|||||||
|
@import "variables";
|
||||||
@import "mediaqueries";
|
@import "mediaqueries";
|
||||||
|
|
||||||
$content-width: 800px;
|
|
||||||
$content-max-width: 100%;
|
|
||||||
|
|
||||||
body {
|
|
||||||
}
|
|
||||||
|
|
||||||
#wrapper {
|
#wrapper {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
@@ -14,50 +9,39 @@ body {
|
|||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
padding: 4rem 0;
|
padding: 4rem 0;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
background: #0d4f99;
|
background: linear-gradient(35deg, rgba(255,0,255,0.2) 0, rgba(13,79,153,0.8) 100%),
|
||||||
background: linear-gradient(35deg, #8955a0 0, #0d4f99 100%);
|
url('/img/bg-1.jpg');
|
||||||
|
background-size: cover;
|
||||||
|
|
||||||
@include media-max(small) {
|
@include media-max(small) {
|
||||||
padding: 3rem 0;
|
padding: 3rem 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
h1 {
|
h1 {
|
||||||
font-size: 1.8rem;
|
|
||||||
color: #fff;
|
color: #fff;
|
||||||
|
|
||||||
span.project-name {
|
span.project-name {
|
||||||
display: none;
|
display: none;
|
||||||
// font-size: .5em;
|
|
||||||
// text-transform: none;
|
|
||||||
// vertical-align: super;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
span.beta {
|
span.icon {
|
||||||
font-size: .5em;
|
svg {
|
||||||
font-style: italic;
|
display: inline-block;
|
||||||
text-transform: none;
|
height: 1.875rem;
|
||||||
vertical-align: super;
|
vertical-align: top;
|
||||||
}
|
width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
span.bolt {
|
margin-right: 0.5rem;
|
||||||
color: #ffd000;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
p.current-user {
|
p.current-user {
|
||||||
margin-top: 2rem;
|
|
||||||
color: rgba(255,255,255,0.6);
|
color: rgba(255,255,255,0.6);
|
||||||
|
|
||||||
@include media-max(small) {
|
@include media-max(small) {
|
||||||
font-size: 0.85rem;
|
font-size: 0.85rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
strong {
|
|
||||||
font-weight: 400;
|
|
||||||
color: #fff;
|
|
||||||
// color: #ffd000;
|
|
||||||
// color: #ccff40;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
@@ -77,18 +61,18 @@ body {
|
|||||||
padding: 2rem 0;
|
padding: 2rem 0;
|
||||||
|
|
||||||
&.notice {
|
&.notice {
|
||||||
background: #efffc4;
|
background: $background-color-notice;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.alert {
|
&.alert {
|
||||||
background: #fff4c2;
|
background: $background-color-alert;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
main {
|
main {
|
||||||
width: $content-width;
|
width: $content-width;
|
||||||
max-width: $content-max-width;
|
max-width: $content-max-width;
|
||||||
margin: 4rem auto;
|
margin: 4rem auto 6rem auto;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
|
|
||||||
@include media-max(medium) {
|
@include media-max(medium) {
|
||||||
@@ -99,18 +83,6 @@ main {
|
|||||||
margin: 3rem auto;
|
margin: 3rem auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
h2, h3 {
|
|
||||||
margin-bottom: 1.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
h2 {
|
|
||||||
font-size: 1.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
h3 {
|
|
||||||
font-size: 1.25rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
p {
|
||||||
line-height: 1.5rem;
|
line-height: 1.5rem;
|
||||||
margin-bottom: 1rem;
|
margin-bottom: 1rem;
|
||||||
@@ -128,19 +100,36 @@ main {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
th, td {
|
section {
|
||||||
line-height: 1.5rem;
|
margin-bottom: 3rem;
|
||||||
padding-right: 1rem;
|
|
||||||
|
h2 {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
section {
|
table {
|
||||||
border-bottom: 1px dotted #ccc;
|
width: 100%;
|
||||||
padding-bottom: 4rem;
|
|
||||||
margin-bottom: 4rem;
|
|
||||||
|
|
||||||
@include media-max(small) {
|
th, td {
|
||||||
padding-bottom: 3rem;
|
&.hide-small {
|
||||||
margin-bottom: 3rem;
|
@include media-max(small) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
th {
|
||||||
|
color: $text-color-discreet;
|
||||||
|
font-weight: normal;
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
padding-bottom: 0.825rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
td {
|
||||||
|
padding-top: 0.5rem;
|
||||||
|
padding-bottom: 0.5rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -156,18 +145,5 @@ main {
|
|||||||
@include media-max(small) {
|
@include media-max(small) {
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
}
|
}
|
||||||
|
|
||||||
margin-top: 3rem;
|
|
||||||
|
|
||||||
h3 {
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.grid-item {
|
|
||||||
p {
|
|
||||||
color: #888;
|
|
||||||
font-size: 0.85rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
54
app/assets/stylesheets/main_nav.scss
Normal file
54
app/assets/stylesheets/main_nav.scss
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
@import "variables";
|
||||||
|
@import "mediaqueries";
|
||||||
|
|
||||||
|
#main-nav {
|
||||||
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
background-color: #efefef;
|
||||||
|
|
||||||
|
.wrapper {
|
||||||
|
width: $content-width;
|
||||||
|
max-width: $content-max-width;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
@include media-max(large) {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr 1fr 1fr 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
li {
|
||||||
|
@include media-min(large) {
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
|
||||||
|
@include media-max(large) {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 1.5rem 2rem;
|
||||||
|
text-decoration: none;
|
||||||
|
color: $text-color-discreet;
|
||||||
|
|
||||||
|
@include media-max(large) {
|
||||||
|
display: block;
|
||||||
|
text-align: center;
|
||||||
|
padding-left: 0;
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@include media-max(small) {
|
||||||
|
font-size: 0.85rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
color: $text-color-body;
|
||||||
|
border-bottom: 2px solid #4ea2df;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,4 +3,6 @@ class Admin::BaseController < ApplicationController
|
|||||||
before_action :authenticate_user!
|
before_action :authenticate_user!
|
||||||
before_action :authorize_admin
|
before_action :authorize_admin
|
||||||
|
|
||||||
|
layout "admin"
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
class Admin::DashboardController < Admin::BaseController
|
class Admin::DashboardController < Admin::BaseController
|
||||||
def index
|
def index
|
||||||
|
@current_section = :dashboard
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
79
app/controllers/admin/donations_controller.rb
Normal file
79
app/controllers/admin/donations_controller.rb
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
class Admin::DonationsController < Admin::BaseController
|
||||||
|
before_action :set_donation, only: [:show, :edit, :update, :destroy]
|
||||||
|
before_action :set_current_section, only: [:index, :show, :new, :edit]
|
||||||
|
|
||||||
|
# GET /donations
|
||||||
|
# GET /donations.json
|
||||||
|
def index
|
||||||
|
@donations = Donation.all
|
||||||
|
end
|
||||||
|
|
||||||
|
# GET /donations/1
|
||||||
|
# GET /donations/1.json
|
||||||
|
def show
|
||||||
|
end
|
||||||
|
|
||||||
|
# GET /donations/new
|
||||||
|
def new
|
||||||
|
@donation = Donation.new
|
||||||
|
end
|
||||||
|
|
||||||
|
# GET /donations/1/edit
|
||||||
|
def edit
|
||||||
|
end
|
||||||
|
|
||||||
|
# POST /donations
|
||||||
|
# POST /donations.json
|
||||||
|
def create
|
||||||
|
@donation = Donation.new(donation_params)
|
||||||
|
|
||||||
|
respond_to do |format|
|
||||||
|
if @donation.save
|
||||||
|
format.html { redirect_to admin_donation_url(@donation), notice: 'Donation was successfully created.' }
|
||||||
|
format.json { render :show, status: :created, location: @donation }
|
||||||
|
else
|
||||||
|
format.html { render :new }
|
||||||
|
format.json { render json: @donation.errors, status: :unprocessable_entity }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# PATCH/PUT /donations/1
|
||||||
|
# PATCH/PUT /donations/1.json
|
||||||
|
def update
|
||||||
|
respond_to do |format|
|
||||||
|
if @donation.update(donation_params)
|
||||||
|
format.html { redirect_to admin_donation_url(@donation), notice: 'Donation was successfully updated.' }
|
||||||
|
format.json { render :show, status: :ok, location: @donation }
|
||||||
|
else
|
||||||
|
format.html { render :edit }
|
||||||
|
format.json { render json: @donation.errors, status: :unprocessable_entity }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# DELETE /donations/1
|
||||||
|
# DELETE /donations/1.json
|
||||||
|
def destroy
|
||||||
|
@donation.destroy
|
||||||
|
respond_to do |format|
|
||||||
|
format.html { redirect_to admin_donations_url, notice: 'Donation was successfully destroyed.' }
|
||||||
|
format.json { head :no_content }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
# Use callbacks to share common setup or constraints between actions.
|
||||||
|
def set_donation
|
||||||
|
@donation = Donation.find(params[:id])
|
||||||
|
end
|
||||||
|
|
||||||
|
# Only allow a list of trusted parameters through.
|
||||||
|
def donation_params
|
||||||
|
params.require(:donation).permit(:user_id, :amount_sats, :amount_eur, :amount_usd, :public_name, :paid_at)
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_current_section
|
||||||
|
@current_section = :donations
|
||||||
|
end
|
||||||
|
end
|
||||||
8
app/controllers/admin/invitations_controller.rb
Normal file
8
app/controllers/admin/invitations_controller.rb
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
class Admin::InvitationsController < Admin::BaseController
|
||||||
|
def index
|
||||||
|
@current_section = :invitations
|
||||||
|
@invitations_unused_count = Invitation.unused.count
|
||||||
|
@users_with_referrals_count = Invitation.used.distinct.count(:user_id)
|
||||||
|
@invitations_used = Invitation.used.order('used_at desc')
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
class Admin::LdapUsersController < Admin::BaseController
|
class Admin::LdapUsersController < Admin::BaseController
|
||||||
|
before_action :set_current_section
|
||||||
|
|
||||||
def index
|
def index
|
||||||
attributes = %w{dn cn uid mail admin}
|
attributes = %w{dn cn uid mail admin}
|
||||||
filter = Net::LDAP::Filter.eq("uid", "*")
|
filter = Net::LDAP::Filter.eq("uid", "*")
|
||||||
if params[:ou]
|
|
||||||
treebase = "ou=#{params[:ou]},cn=users,dc=kosmos,dc=org"
|
@ou = params[:ou] || "kosmos.org"
|
||||||
else
|
treebase = "ou=#{@ou},cn=users,dc=kosmos,dc=org"
|
||||||
treebase = "ou=kosmos.org,cn=users,dc=kosmos,dc=org"
|
|
||||||
end
|
|
||||||
|
|
||||||
entries = ldap_client.search(base: treebase, filter: filter, attributes: attributes)
|
entries = ldap_client.search(base: treebase, filter: filter, attributes: attributes)
|
||||||
entries.sort_by! { |e| e.cn[0] }
|
entries.sort_by! { |e| e.cn[0] }
|
||||||
@@ -38,4 +38,8 @@ class Admin::LdapUsersController < Admin::BaseController
|
|||||||
def ldap_config
|
def ldap_config
|
||||||
ldap_config ||= YAML.load(ERB.new(File.read("#{Rails.root}/config/ldap.yml")).result)[Rails.env]
|
ldap_config ||= YAML.load(ERB.new(File.read("#{Rails.root}/config/ldap.yml")).result)[Rails.env]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def set_current_section
|
||||||
|
@current_section = :ldap_users
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -2,5 +2,6 @@ class DashboardController < ApplicationController
|
|||||||
before_action :require_user_signed_in
|
before_action :require_user_signed_in
|
||||||
|
|
||||||
def index
|
def index
|
||||||
|
@current_section = :dashboard
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
10
app/controllers/donations_controller.rb
Normal file
10
app/controllers/donations_controller.rb
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
class DonationsController < ApplicationController
|
||||||
|
before_action :require_user_signed_in
|
||||||
|
|
||||||
|
# GET /donations
|
||||||
|
# GET /donations.json
|
||||||
|
def index
|
||||||
|
@donations = current_user.donations.completed
|
||||||
|
@current_section = :contributions
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -8,6 +8,7 @@ class InvitationsController < ApplicationController
|
|||||||
def index
|
def index
|
||||||
@invitations_unused = current_user.invitations.unused
|
@invitations_unused = current_user.invitations.unused
|
||||||
@invitations_used = current_user.invitations.used
|
@invitations_used = current_user.invitations.used
|
||||||
|
@current_section = :invitations
|
||||||
end
|
end
|
||||||
|
|
||||||
# GET /invitations/a-random-invitation-token
|
# GET /invitations/a-random-invitation-token
|
||||||
|
|||||||
60
app/controllers/lnurlpay_controller.rb
Normal file
60
app/controllers/lnurlpay_controller.rb
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
class LnurlpayController < ApplicationController
|
||||||
|
before_action :find_user_by_address
|
||||||
|
|
||||||
|
MIN_SATS = 100
|
||||||
|
MAX_SATS = 1_000_000
|
||||||
|
|
||||||
|
def index
|
||||||
|
render json: {
|
||||||
|
status: "OK",
|
||||||
|
callback: "https://accounts.kosmos.org/lnurlpay/#{@user.address}/invoice",
|
||||||
|
tag: "payRequest",
|
||||||
|
maxSendable: MAX_SATS * 1000, # msat
|
||||||
|
minSendable: MIN_SATS * 1000, # msat
|
||||||
|
metadata: metadata(@user.address),
|
||||||
|
commentAllowed: 0
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def invoice
|
||||||
|
amount = params[:amount].to_i / 1000 # msats
|
||||||
|
address = params[:address]
|
||||||
|
|
||||||
|
if !valid_amount?(amount)
|
||||||
|
render json: { status: "ERROR", reason: "Invalid amount" }
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
payment_request = @user.ln_create_invoice({
|
||||||
|
amount: amount, # we create invoices in sats
|
||||||
|
description_hash: Digest::SHA2.hexdigest(metadata(address))
|
||||||
|
})
|
||||||
|
|
||||||
|
render json: {
|
||||||
|
status: "OK",
|
||||||
|
successAction: {
|
||||||
|
tag: "message",
|
||||||
|
message: "Sats received. Thank you!"
|
||||||
|
},
|
||||||
|
routes: [],
|
||||||
|
pr: payment_request
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def find_user_by_address
|
||||||
|
address = params[:address].split("@")
|
||||||
|
@user = User.where(cn: address.first, ou: address.last).first
|
||||||
|
http_status :not_found if @user.nil?
|
||||||
|
end
|
||||||
|
|
||||||
|
def metadata(address)
|
||||||
|
"[[\"text/identifier\", \"#{address}\"], [\"text/plain\", \"Send sats, receive thanks.\"]]"
|
||||||
|
end
|
||||||
|
|
||||||
|
def valid_amount?(amount_in_sats)
|
||||||
|
amount_in_sats <= MAX_SATS && amount_in_sats >= MIN_SATS
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
7
app/controllers/security_controller.rb
Normal file
7
app/controllers/security_controller.rb
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
class SecurityController < ApplicationController
|
||||||
|
before_action :require_user_signed_in
|
||||||
|
|
||||||
|
def index
|
||||||
|
@current_section = :security
|
||||||
|
end
|
||||||
|
end
|
||||||
47
app/controllers/wallet_controller.rb
Normal file
47
app/controllers/wallet_controller.rb
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
require "rqrcode"
|
||||||
|
|
||||||
|
class WalletController < ApplicationController
|
||||||
|
before_action :require_user_signed_in
|
||||||
|
before_action :authenticate_with_lndhub
|
||||||
|
|
||||||
|
def index
|
||||||
|
@current_section = :wallet
|
||||||
|
|
||||||
|
@wallet_url = "lndhub://#{current_user.ln_login}:#{current_user.ln_password}@#{ENV['LNDHUB_PUBLIC_URL']}"
|
||||||
|
|
||||||
|
qrcode = RQRCode::QRCode.new(@wallet_url)
|
||||||
|
@svg = qrcode.as_svg(
|
||||||
|
color: "000",
|
||||||
|
shape_rendering: "crispEdges",
|
||||||
|
module_size: 6,
|
||||||
|
standalone: true,
|
||||||
|
use_path: true,
|
||||||
|
svg_attributes: {
|
||||||
|
class: 'inline-block'
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
@balance = fetch_balance rescue nil
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def authenticate_with_lndhub
|
||||||
|
if session["ln_auth_token"].present?
|
||||||
|
@ln_auth_token = session["ln_auth_token"]
|
||||||
|
else
|
||||||
|
lndhub = Lndhub.new
|
||||||
|
auth_token = lndhub.authenticate(current_user)
|
||||||
|
session["ln_auth_token"] = auth_token
|
||||||
|
@ln_auth_token = auth_token
|
||||||
|
end
|
||||||
|
rescue
|
||||||
|
# TODO add exception tracking
|
||||||
|
end
|
||||||
|
|
||||||
|
def fetch_balance
|
||||||
|
lndhub = Lndhub.new
|
||||||
|
data = lndhub.balance @ln_auth_token
|
||||||
|
data["BTC"]["AvailableBalance"]
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -1,2 +1,5 @@
|
|||||||
module ApplicationHelper
|
module ApplicationHelper
|
||||||
|
def sats_to_btc(sats)
|
||||||
|
sats.to_f / 100000000
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
2
app/helpers/donations_helper.rb
Normal file
2
app/helpers/donations_helper.rb
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
module DonationsHelper
|
||||||
|
end
|
||||||
2
app/helpers/lnurlpay_helper.rb
Normal file
2
app/helpers/lnurlpay_helper.rb
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
module LnurlpayHelper
|
||||||
|
end
|
||||||
2
app/helpers/wallet_helper.rb
Normal file
2
app/helpers/wallet_helper.rb
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
module WalletHelper
|
||||||
|
end
|
||||||
@@ -7,7 +7,7 @@ require("@rails/ujs").start()
|
|||||||
require("turbolinks").start()
|
require("turbolinks").start()
|
||||||
require("channels")
|
require("channels")
|
||||||
|
|
||||||
|
import "stylesheets/application"
|
||||||
// Uncomment to copy all static images under ../images to the output folder and reference
|
// Uncomment to copy all static images under ../images to the output folder and reference
|
||||||
// them with the image_pack_tag helper in views (e.g <%= image_pack_tag 'rails.png' %>)
|
// them with the image_pack_tag helper in views (e.g <%= image_pack_tag 'rails.png' %>)
|
||||||
// or the `imagePath` JavaScript helper below.
|
// or the `imagePath` JavaScript helper below.
|
||||||
|
|||||||
9
app/javascript/stylesheets/application.scss
Normal file
9
app/javascript/stylesheets/application.scss
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
// version 6
|
||||||
|
@import "tailwindcss/base";
|
||||||
|
@import "tailwindcss/components";
|
||||||
|
@import "tailwindcss/utilities";
|
||||||
|
|
||||||
|
@import "base";
|
||||||
|
@import "buttons";
|
||||||
|
@import "forms";
|
||||||
|
@import "links";
|
||||||
21
app/javascript/stylesheets/base.scss
Normal file
21
app/javascript/stylesheets/base.scss
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
@layer base {
|
||||||
|
body {
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1, h2, h3 {
|
||||||
|
@apply font-light;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
@apply text-3xl uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
@apply text-2xl mb-8;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
@apply text-xl mb-6;
|
||||||
|
}
|
||||||
|
}
|
||||||
31
app/javascript/stylesheets/buttons.scss
Normal file
31
app/javascript/stylesheets/buttons.scss
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
@layer components {
|
||||||
|
.btn {
|
||||||
|
@apply font-semibold rounded-md leading-none cursor-pointer
|
||||||
|
transition-colors duration-75 focus:outline-none focus:ring-4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-md {
|
||||||
|
@apply btn;
|
||||||
|
@apply py-2.5 px-5 shadow-md;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-sm {
|
||||||
|
@apply btn;
|
||||||
|
@apply py-1 px-2 text-sm shadow-sm;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-gray {
|
||||||
|
@apply bg-gray-100 hover:bg-gray-200
|
||||||
|
focus:ring-gray-300 focus:ring-opacity-75;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-blue {
|
||||||
|
@apply bg-blue-500 hover:bg-blue-600 text-white
|
||||||
|
focus:ring-blue-400 focus:ring-opacity-75;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-red {
|
||||||
|
@apply bg-red-600 hover:bg-red-700 text-white
|
||||||
|
focus:ring-red-500 focus:ring-opacity-75;
|
||||||
|
}
|
||||||
|
}
|
||||||
17
app/javascript/stylesheets/forms.scss
Normal file
17
app/javascript/stylesheets/forms.scss
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
@layer components {
|
||||||
|
form {
|
||||||
|
input[type=text], input[type=email], input[type=password],
|
||||||
|
input[type=number], select {
|
||||||
|
@apply mt-1 rounded-md bg-gray-100 focus:bg-white
|
||||||
|
border-transparent focus:border-gray-500 focus:ring-0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.field_with_errors {
|
||||||
|
@apply inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-msg {
|
||||||
|
@apply text-red-700;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
14
app/javascript/stylesheets/links.scss
Normal file
14
app/javascript/stylesheets/links.scss
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
@layer components {
|
||||||
|
.ks-text-link {
|
||||||
|
@apply text-blue-600;
|
||||||
|
&:hover { @apply underline; }
|
||||||
|
&:visited { @apply text-indigo-600; }
|
||||||
|
&:active { @apply text-red-600; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.devise-links {
|
||||||
|
a {
|
||||||
|
@apply ks-text-link;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
26
app/javascript/stylesheets/tailwind.config.js
Normal file
26
app/javascript/stylesheets/tailwind.config.js
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
const defaultTheme = require('tailwindcss/defaultTheme')
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
purge: {
|
||||||
|
layers: ['base', 'components', 'utilities'],
|
||||||
|
content: [
|
||||||
|
"./app/**/*.html.erb",
|
||||||
|
"./app/helpers/**/*.rb",
|
||||||
|
"./app/javascript/**/*.js"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
darkMode: false, // or 'media' or 'class'
|
||||||
|
theme: {
|
||||||
|
extend: {
|
||||||
|
fontFamily: {
|
||||||
|
sans: ['Open Sans', 'sans-serif']
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
variants: {
|
||||||
|
extend: {},
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
require('@tailwindcss/forms')
|
||||||
|
],
|
||||||
|
}
|
||||||
32
app/jobs/create_ldap_user_job.rb
Normal file
32
app/jobs/create_ldap_user_job.rb
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
class CreateLdapUserJob < ApplicationJob
|
||||||
|
queue_as :default
|
||||||
|
|
||||||
|
def perform(username, domain, email, hashed_pw)
|
||||||
|
dn = "cn=#{username},ou=#{domain},cn=users,dc=kosmos,dc=org"
|
||||||
|
attr = {
|
||||||
|
objectclass: ["top", "account", "person", "extensibleObject"],
|
||||||
|
cn: username,
|
||||||
|
sn: username,
|
||||||
|
uid: username,
|
||||||
|
mail: email,
|
||||||
|
userPassword: hashed_pw
|
||||||
|
}
|
||||||
|
|
||||||
|
ldap_client.add(dn: dn, attributes: attr)
|
||||||
|
end
|
||||||
|
|
||||||
|
def ldap_client
|
||||||
|
ldap_client ||= Net::LDAP.new host: ldap_config['host'],
|
||||||
|
port: ldap_config['port'],
|
||||||
|
encryption: ldap_config['ssl'],
|
||||||
|
auth: {
|
||||||
|
method: :simple,
|
||||||
|
username: ldap_config['admin_user'],
|
||||||
|
password: ldap_config['admin_password']
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def ldap_config
|
||||||
|
ldap_config ||= YAML.load(ERB.new(File.read("#{Rails.root}/config/ldap.yml")).result)[Rails.env]
|
||||||
|
end
|
||||||
|
end
|
||||||
13
app/jobs/create_lndhub_wallet_job.rb
Normal file
13
app/jobs/create_lndhub_wallet_job.rb
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
class CreateLndhubWalletJob < ApplicationJob
|
||||||
|
queue_as :default
|
||||||
|
|
||||||
|
def perform(user)
|
||||||
|
return if user.ln_login.present? && user.ln_password.present?
|
||||||
|
|
||||||
|
lndhub = Lndhub.new
|
||||||
|
credentials = lndhub.create({ partnerid: user.ou, accounttype: "user" })
|
||||||
|
|
||||||
|
user.update! ln_login: credentials["login"],
|
||||||
|
ln_password: credentials["password"]
|
||||||
|
end
|
||||||
|
end
|
||||||
18
app/jobs/exchange_xmpp_contacts_job.rb
Normal file
18
app/jobs/exchange_xmpp_contacts_job.rb
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
class ExchangeXmppContactsJob < ApplicationJob
|
||||||
|
queue_as :default
|
||||||
|
|
||||||
|
def perform(inviter, username, domain)
|
||||||
|
ejabberd = EjabberdApiClient.new
|
||||||
|
|
||||||
|
ejabberd.add_rosteritem({
|
||||||
|
"localuser": username, "localhost": domain,
|
||||||
|
"user": inviter.cn, "host": inviter.ou,
|
||||||
|
"nick": inviter.cn, "group": "Friends", "subs": "both"
|
||||||
|
})
|
||||||
|
ejabberd.add_rosteritem({
|
||||||
|
"localuser": inviter.cn, "localhost": inviter.ou,
|
||||||
|
"user": username, "host": domain,
|
||||||
|
"nick": username, "group": "Friends", "subs": "both"
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
13
app/models/donation.rb
Normal file
13
app/models/donation.rb
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
class Donation < ApplicationRecord
|
||||||
|
# Relations
|
||||||
|
belongs_to :user
|
||||||
|
|
||||||
|
# Validations
|
||||||
|
validates_presence_of :amount_sats
|
||||||
|
|
||||||
|
# Hooks
|
||||||
|
# TODO before_create :store_fiat_value
|
||||||
|
|
||||||
|
#Scopes
|
||||||
|
scope :completed, -> { where.not(paid_at: nil) }
|
||||||
|
end
|
||||||
@@ -3,12 +3,16 @@ class User < ApplicationRecord
|
|||||||
|
|
||||||
# Relations
|
# Relations
|
||||||
has_many :invitations, dependent: :destroy
|
has_many :invitations, dependent: :destroy
|
||||||
|
has_many :donations, dependent: :nullify
|
||||||
|
|
||||||
validates_uniqueness_of :cn
|
validates_uniqueness_of :cn
|
||||||
validates_length_of :cn, :minimum => 3
|
validates_length_of :cn, :minimum => 3
|
||||||
validates_uniqueness_of :email
|
validates_uniqueness_of :email
|
||||||
validates :email, email: true
|
validates :email, email: true
|
||||||
|
|
||||||
|
encrypts :ln_login
|
||||||
|
encrypts :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
|
||||||
devise :ldap_authenticatable,
|
devise :ldap_authenticatable,
|
||||||
@@ -52,4 +56,10 @@ class User < ApplicationRecord
|
|||||||
self.valid?
|
self.valid?
|
||||||
self.errors[attribute_name].blank?
|
self.errors[attribute_name].blank?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def ln_create_invoice(payload)
|
||||||
|
lndhub = Lndhub.new
|
||||||
|
lndhub.authenticate self
|
||||||
|
lndhub.addinvoice payload
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ class CreateAccount < ApplicationService
|
|||||||
def call
|
def call
|
||||||
user = create_user_in_database
|
user = create_user_in_database
|
||||||
add_ldap_document
|
add_ldap_document
|
||||||
|
create_lndhub_wallet(user)
|
||||||
|
|
||||||
if @invitation.present?
|
if @invitation.present?
|
||||||
update_invitation(user.id)
|
update_invitation(user.id)
|
||||||
@@ -33,51 +34,19 @@ class CreateAccount < ApplicationService
|
|||||||
@invitation.update! invited_user_id: user_id, used_at: DateTime.now
|
@invitation.update! invited_user_id: user_id, used_at: DateTime.now
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# TODO move to confirmation
|
||||||
def add_ldap_document
|
def add_ldap_document
|
||||||
dn = "cn=#{@username},ou=kosmos.org,cn=users,dc=kosmos,dc=org"
|
hashed_pw = Devise.ldap_auth_password_builder.call(@password)
|
||||||
attr = {
|
CreateLdapUserJob.perform_later(@username, @domain, @email, hashed_pw)
|
||||||
objectclass: ["top", "account", "person", "extensibleObject"],
|
|
||||||
cn: @username,
|
|
||||||
sn: @username,
|
|
||||||
uid: @username,
|
|
||||||
mail: @email,
|
|
||||||
userPassword: Devise.ldap_auth_password_builder.call(@password)
|
|
||||||
}
|
|
||||||
|
|
||||||
ldap_client.add(dn: dn, attributes: attr)
|
|
||||||
end
|
|
||||||
|
|
||||||
def ldap_client
|
|
||||||
ldap_client ||= Net::LDAP.new host: ldap_config['host'],
|
|
||||||
port: ldap_config['port'],
|
|
||||||
encryption: ldap_config['ssl'],
|
|
||||||
auth: {
|
|
||||||
method: :simple,
|
|
||||||
username: ldap_config['admin_user'],
|
|
||||||
password: ldap_config['admin_password']
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
def ldap_config
|
|
||||||
ldap_config ||= YAML.load(ERB.new(File.read("#{Rails.root}/config/ldap.yml")).result)[Rails.env]
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def exchange_xmpp_contacts
|
def exchange_xmpp_contacts
|
||||||
#TODO enable in development when we have easy setup of ejabberd etc.
|
#TODO enable in development when we have easy setup of ejabberd etc.
|
||||||
return if Rails.env.development?
|
return if Rails.env.development?
|
||||||
|
ExchangeXmppContactsJob.perform_later(@invitation.user, @username, @domain)
|
||||||
|
end
|
||||||
|
|
||||||
ejabberd = EjabberdApiClient.new
|
def create_lndhub_wallet(user)
|
||||||
inviter = @invitation.user
|
CreateLndhubWalletJob.perform_later(user)
|
||||||
|
|
||||||
ejabberd.add_rosteritem({
|
|
||||||
"localuser": @username, "localhost": @domain,
|
|
||||||
"user": inviter.cn, "host": inviter.ou,
|
|
||||||
"nick": inviter.cn, "group": "Friends", "subs": "both"
|
|
||||||
})
|
|
||||||
ejabberd.add_rosteritem({
|
|
||||||
"localuser": inviter.cn, "localhost": inviter.ou,
|
|
||||||
"user": @username, "host": @domain,
|
|
||||||
"nick": @username, "group": "Friends", "subs": "both"
|
|
||||||
})
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
57
app/services/lndhub.rb
Normal file
57
app/services/lndhub.rb
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
class Lndhub
|
||||||
|
attr_accessor :auth_token
|
||||||
|
|
||||||
|
def initialize
|
||||||
|
@base_url = ENV["LNDHUB_API_URL"]
|
||||||
|
end
|
||||||
|
|
||||||
|
def post(endpoint, payload)
|
||||||
|
headers = { "Content-Type" => "application/json" }
|
||||||
|
if auth_token
|
||||||
|
headers.merge!({ "Authorization" => "Bearer #{auth_token}" })
|
||||||
|
end
|
||||||
|
|
||||||
|
res = Faraday.post "#{@base_url}/#{endpoint}", payload.to_json, headers
|
||||||
|
|
||||||
|
if res.status != 200
|
||||||
|
Rails.logger.error "[lndhub] API request failed:"
|
||||||
|
Rails.logger.error res.body
|
||||||
|
#TODO add some kind of exception tracking/notifications
|
||||||
|
end
|
||||||
|
|
||||||
|
JSON.parse(res.body)
|
||||||
|
end
|
||||||
|
|
||||||
|
def get(endpoint, auth_token)
|
||||||
|
res = Faraday.get("#{@base_url}/#{endpoint}", {}, {
|
||||||
|
"Content-Type" => "application/json",
|
||||||
|
"Accept" => "application/json",
|
||||||
|
"Authorization" => "Bearer #{auth_token}"
|
||||||
|
})
|
||||||
|
|
||||||
|
JSON.parse(res.body)
|
||||||
|
end
|
||||||
|
|
||||||
|
def create(payload)
|
||||||
|
post "create", payload
|
||||||
|
end
|
||||||
|
|
||||||
|
def authenticate(user)
|
||||||
|
credentials = post "auth?type=auth", { login: user.ln_login, password: user.ln_password }
|
||||||
|
self.auth_token = credentials["access_token"]
|
||||||
|
self.auth_token
|
||||||
|
end
|
||||||
|
|
||||||
|
def balance(user_token)
|
||||||
|
get "balance", user_token || auth_token
|
||||||
|
end
|
||||||
|
|
||||||
|
def addinvoice(payload)
|
||||||
|
invoice = post "addinvoice", {
|
||||||
|
amt: payload[:amount],
|
||||||
|
description_hash: payload[:description_hash]
|
||||||
|
}
|
||||||
|
|
||||||
|
invoice["payment_request"]
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -1,7 +1,3 @@
|
|||||||
<h2>Admin Panel</h2>
|
<p class="text-center">
|
||||||
<p>
|
With great power comes great responsibility.
|
||||||
Ohai there, admin human.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
<%= link_to 'LDAP users', admin_ldap_users_path %>
|
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
2
app/views/admin/donations/_donation.json.jbuilder
Normal file
2
app/views/admin/donations/_donation.json.jbuilder
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
json.extract! donation, :id, :user_id, :amount_sats, :amount_eur, :amount_usd, :public_name, :created_at, :updated_at
|
||||||
|
json.url donation_url(donation, format: :json)
|
||||||
58
app/views/admin/donations/_form.html.erb
Normal file
58
app/views/admin/donations/_form.html.erb
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
<%= form_with(url: url, model: donation, local: true) do |form| %>
|
||||||
|
<% if donation.errors.any? %>
|
||||||
|
<div id="error_explanation">
|
||||||
|
<h3><%= pluralize(donation.errors.count, "error") %> prohibited this donation from being saved:</h3>
|
||||||
|
<ul>
|
||||||
|
<% donation.errors.full_messages.each do |message| %>
|
||||||
|
<li><%= message %></li>
|
||||||
|
<% end %>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<div class="field">
|
||||||
|
<p>
|
||||||
|
<%= form.label :user_id %>
|
||||||
|
<%= form.collection_select :user_id, User.where(ou: "kosmos.org").order(:cn), :id, :cn %>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="field">
|
||||||
|
<p>
|
||||||
|
<%= form.label :amount_sats, "Amount BTC (sats)" %>
|
||||||
|
<%= form.number_field :amount_sats %>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="field">
|
||||||
|
<p>
|
||||||
|
<%= form.label :amount_eur, "Amount EUR (cents)" %>
|
||||||
|
<%= form.number_field :amount_eur %>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="field">
|
||||||
|
<p>
|
||||||
|
<%= form.label :amount_usd, "Amount USD (cents)"%>
|
||||||
|
<%= form.number_field :amount_usd %>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="field">
|
||||||
|
<p>
|
||||||
|
<%= form.label :public_name %>
|
||||||
|
<%= form.text_field :public_name %>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="field">
|
||||||
|
<p>
|
||||||
|
<%= form.label :paid_at %>
|
||||||
|
<%= form.text_field :paid_at %>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p class="mt-8">
|
||||||
|
<%= form.submit class: 'btn-md btn-blue' %>
|
||||||
|
</p>
|
||||||
|
<% end %>
|
||||||
8
app/views/admin/donations/edit.html.erb
Normal file
8
app/views/admin/donations/edit.html.erb
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<h2>Editing Donation</h2>
|
||||||
|
|
||||||
|
<%= render 'form', donation: @donation, url: admin_donation_path(@donation) %>
|
||||||
|
|
||||||
|
<p class="mt-8">
|
||||||
|
<%= link_to 'Show', admin_donation_path(@donation), class: 'ks-text-link' %> |
|
||||||
|
<%= link_to 'Back', admin_donations_path, class: 'ks-text-link' %>
|
||||||
|
<p>
|
||||||
41
app/views/admin/donations/index.html.erb
Normal file
41
app/views/admin/donations/index.html.erb
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
<h2>Donations</h2>
|
||||||
|
|
||||||
|
<% if @donations.any? %>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>User</th>
|
||||||
|
<th>Amount BTC</th>
|
||||||
|
<th>in EUR</th>
|
||||||
|
<th>in USD</th>
|
||||||
|
<th>Public name</th>
|
||||||
|
<th>Date</th>
|
||||||
|
<th colspan="3"></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
|
||||||
|
<tbody>
|
||||||
|
<% @donations.each do |donation| %>
|
||||||
|
<tr>
|
||||||
|
<td><%= donation.user.cn %></td>
|
||||||
|
<td><%= sats_to_btc donation.amount_sats %> BTC</td>
|
||||||
|
<td><%= number_to_currency donation.amount_eur / 100, unit: "" %></td>
|
||||||
|
<td><%= number_to_currency donation.amount_usd / 100, unit: "" %></td>
|
||||||
|
<td><%= donation.public_name %></td>
|
||||||
|
<td><%= donation.paid_at ? donation.paid_at.strftime("%Y-%m-%d") : "" %></td>
|
||||||
|
<td><%= link_to 'Show', admin_donation_path(donation), class: 'btn btn-sm btn-gray' %></td>
|
||||||
|
<td><%= link_to 'Edit', edit_admin_donation_path(donation), class: 'btn btn-sm btn-gray' %></td>
|
||||||
|
<td><%= link_to 'Destroy', admin_donation_path(donation), class: 'btn btn-sm btn-red', method: :delete, data: { confirm: 'Are you sure?' } %></td>
|
||||||
|
</tr>
|
||||||
|
<% end %>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<% else %>
|
||||||
|
<p>
|
||||||
|
No donations yet.
|
||||||
|
</p>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<p class="mt-12">
|
||||||
|
<%= link_to 'Record an out-of-system donation', new_admin_donation_path, class: 'btn-md btn-gray' %>
|
||||||
|
</p>
|
||||||
1
app/views/admin/donations/index.json.jbuilder
Normal file
1
app/views/admin/donations/index.json.jbuilder
Normal file
@@ -0,0 +1 @@
|
|||||||
|
json.array! @donations, partial: "donations/donation", as: :donation
|
||||||
7
app/views/admin/donations/new.html.erb
Normal file
7
app/views/admin/donations/new.html.erb
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<h2>New Donation</h2>
|
||||||
|
|
||||||
|
<%= render 'form', donation: @donation, url: admin_donations_path %>
|
||||||
|
|
||||||
|
<p class="mt-8">
|
||||||
|
<%= link_to 'Back', admin_donations_path, class: 'ks-text-link' %>
|
||||||
|
</p>
|
||||||
36
app/views/admin/donations/show.html.erb
Normal file
36
app/views/admin/donations/show.html.erb
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
<p id="notice"><%= notice %></p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<strong>User:</strong>
|
||||||
|
<%= @donation.user_id %>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<strong>Amount sats:</strong>
|
||||||
|
<%= @donation.amount_sats %>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<strong>Amount eur:</strong>
|
||||||
|
<%= @donation.amount_eur %>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<strong>Amount usd:</strong>
|
||||||
|
<%= @donation.amount_usd %>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<strong>Public name:</strong>
|
||||||
|
<%= @donation.public_name %>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<strong>Date:</strong>
|
||||||
|
<%= @donation.paid_at %>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p class="mt-8">
|
||||||
|
<%= link_to 'Edit', edit_admin_donation_path(@donation), class: 'ks-text-link' %> |
|
||||||
|
<%= link_to 'Back', admin_donations_path, class: 'ks-text-link' %>
|
||||||
|
</p>
|
||||||
1
app/views/admin/donations/show.json.jbuilder
Normal file
1
app/views/admin/donations/show.json.jbuilder
Normal file
@@ -0,0 +1 @@
|
|||||||
|
json.partial! "donations/donation", donation: @donation
|
||||||
32
app/views/admin/invitations/index.html.erb
Normal file
32
app/views/admin/invitations/index.html.erb
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
<section>
|
||||||
|
<h2>Invitations</h2>
|
||||||
|
<p>
|
||||||
|
There are currently <strong><%= @invitations_unused_count %>
|
||||||
|
unused invitations</strong> available to existing users.
|
||||||
|
<strong><%= @users_with_referrals_count %> users</strong> have successfully
|
||||||
|
invited new users.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
<% if @invitations_used.any? %>
|
||||||
|
<section>
|
||||||
|
<h3>Accepted (<%= @invitations_used.length %>)</h3>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Token</th>
|
||||||
|
<th>Accepted</th>
|
||||||
|
<th>Invited user</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<% @invitations_used.each do |invitation| %>
|
||||||
|
<tr>
|
||||||
|
<td class="overflow-ellipsis"><%= invitation.token %></td>
|
||||||
|
<td><%= invitation.used_at.strftime("%Y-%m-%d") %></td>
|
||||||
|
<td><%= User.find(invitation.invited_user_id).address %></td>
|
||||||
|
</tr>
|
||||||
|
<% end %>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</section>
|
||||||
|
<% end %>
|
||||||
@@ -1,8 +1,13 @@
|
|||||||
<h2>LDAP users</h2>
|
<h2>LDAP users: <%= @ou %></h2>
|
||||||
|
|
||||||
<ul>
|
<h3 class="hidden">Domains</h3>
|
||||||
<li><%= link_to 'kosmos.org', admin_ldap_users_path %></li>
|
<ul class="mb-10">
|
||||||
<li><%= link_to '5apps.com', admin_ldap_users_path(ou: '5apps.com') %></li>
|
<li class="inline-block">
|
||||||
|
<%= link_to 'kosmos.org', admin_ldap_users_path, class: "ks-text-link" %>
|
||||||
|
</li>
|
||||||
|
<li class="inline-block ml-6">
|
||||||
|
<%= link_to '5apps.com', admin_ldap_users_path(ou: '5apps.com'), class: "ks-text-link" %>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<table>
|
<table>
|
||||||
|
|||||||
@@ -4,45 +4,55 @@
|
|||||||
Your Kosmos account and password currently give you access to these
|
Your Kosmos account and password currently give you access to these
|
||||||
services:
|
services:
|
||||||
</p>
|
</p>
|
||||||
<div class="grid services">
|
<div class="grid services mt-12">
|
||||||
<div class="grid-item chat">
|
<div>
|
||||||
<h3><%= link_to "Chat", "https://wiki.kosmos.org/Services:Chat" %></h3>
|
<h3 class="mb-3.5">
|
||||||
<p>
|
<%= link_to "Chat", "https://wiki.kosmos.org/Services:Chat", class: "ks-text-link" %>
|
||||||
|
</h3>
|
||||||
|
<p class="text-gray-500">
|
||||||
Chat rooms and instant messaging (XMPP/Jabber)
|
Chat rooms and instant messaging (XMPP/Jabber)
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="grid-item wiki">
|
<div>
|
||||||
<h3><%= link_to "Wiki", "https://wiki.kosmos.org" %></h3>
|
<h3 class="mb-3.5">
|
||||||
<p>
|
<%= link_to "Discourse", "https://community.kosmos.org", class: "ks-text-link" %>
|
||||||
Kosmos documentation and knowledge base
|
</h3>
|
||||||
</p>
|
<p class="text-gray-500">
|
||||||
</div>
|
|
||||||
<div class="grid-item discourse">
|
|
||||||
<h3><%= link_to "Discourse", "https://community.kosmos.org" %></h3>
|
|
||||||
<p>
|
|
||||||
Kosmos community forums and user support/help site
|
Kosmos community forums and user support/help site
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="grid-item gitea">
|
<div>
|
||||||
<h3><%= link_to "Gitea", "https://gitea.kosmos.org" %></h3>
|
<h3 class="mb-3.5">
|
||||||
<p>
|
<span class="text-yellow-500">🗲</span>
|
||||||
|
<%= link_to "Lightning Wallet", wallet_path, class: "ks-text-link" %>
|
||||||
|
</h3>
|
||||||
|
<p class="text-gray-500">
|
||||||
|
Send and receive sats over the Bitcoin Lightning Network
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h3 class="mb-3.5">
|
||||||
|
<%= link_to "Wiki", "https://wiki.kosmos.org", class: "ks-text-link" %>
|
||||||
|
</h3>
|
||||||
|
<p class="text-gray-500">
|
||||||
|
Kosmos documentation and knowledge base
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h3 class="mb-3.5">
|
||||||
|
<%= link_to "Gitea", "https://gitea.kosmos.org", class: "ks-text-link" %>
|
||||||
|
</h3>
|
||||||
|
<p class="text-gray-500">
|
||||||
Code hosting and collaboration for software projects
|
Code hosting and collaboration for software projects
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="grid-item gitea">
|
<div>
|
||||||
<h3><%= link_to "Drone CI", "https://drone.kosmos.org" %></h3>
|
<h3 class="mb-3.5">
|
||||||
<p>
|
<%= link_to "Drone CI", "https://drone.kosmos.org", class: "ks-text-link" %>
|
||||||
Continuous integration for software projects, tied to our Gitea
|
</h3>
|
||||||
|
<p class="text-gray-500">
|
||||||
|
Continuous integration for software projects on Gitea
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section>
|
|
||||||
<h3>Password change</h3>
|
|
||||||
<p>
|
|
||||||
<%= form_with(url: settings_reset_password_path, method: :post) do %>
|
|
||||||
<%= submit_tag("Send me a password reset link") %>
|
|
||||||
<% end %>
|
|
||||||
</p>
|
|
||||||
</section>
|
|
||||||
|
|||||||
@@ -2,19 +2,13 @@
|
|||||||
|
|
||||||
<%= form_for(resource, as: resource_name, url: confirmation_path(resource_name), html: { method: :post }) do |f| %>
|
<%= form_for(resource, as: resource_name, url: confirmation_path(resource_name), html: { method: :post }) do |f| %>
|
||||||
<%= render "devise/shared/error_messages", resource: resource %>
|
<%= render "devise/shared/error_messages", resource: resource %>
|
||||||
|
<p>
|
||||||
<div class="field">
|
<%= f.label :email, 'Email address', class: 'block mb-1' %>
|
||||||
<p>
|
<%= f.email_field :email, required: true, autofocus: true, autocomplete: "email", value: (resource.pending_reconfirmation? ? resource.unconfirmed_email : resource.email) %>
|
||||||
<%= f.label :email, 'Email address' %><br />
|
</p>
|
||||||
<%= f.email_field :email, required: true, autofocus: true, autocomplete: "email", value: (resource.pending_reconfirmation? ? resource.unconfirmed_email : resource.email) %>
|
<p class="mt-8">
|
||||||
</p>
|
<%= f.submit "Resend confirmation instructions", class: 'btn-md btn-blue' %>
|
||||||
</div>
|
</p>
|
||||||
|
|
||||||
<div class="actions">
|
|
||||||
<p>
|
|
||||||
<%= f.submit "Resend confirmation instructions" %>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<%= render "devise/shared/links" %>
|
<%= render "devise/shared/links" %>
|
||||||
|
|||||||
@@ -4,22 +4,24 @@
|
|||||||
<%= render "devise/shared/error_messages", resource: resource %>
|
<%= render "devise/shared/error_messages", resource: resource %>
|
||||||
<%= f.hidden_field :reset_password_token %>
|
<%= f.hidden_field :reset_password_token %>
|
||||||
|
|
||||||
<div class="field">
|
<p class="mb-1">
|
||||||
<%= f.label :password, "New password" %><br />
|
<%= f.label :password, "New password" %>
|
||||||
<% if @minimum_password_length %>
|
</p>
|
||||||
<em>(<%= @minimum_password_length %> characters minimum)</em><br />
|
<p>
|
||||||
<% end %>
|
|
||||||
<%= f.password_field :password, autofocus: true, autocomplete: "new-password" %>
|
<%= f.password_field :password, autofocus: true, autocomplete: "new-password" %>
|
||||||
</div>
|
<% if @minimum_password_length %>
|
||||||
|
<br><em class="text-sm text-gray-500">(<%= @minimum_password_length %> characters minimum)</em>
|
||||||
<div class="field">
|
<% end %>
|
||||||
<%= f.label :password_confirmation, "Confirm new password" %><br />
|
</p>
|
||||||
|
<p class="mb-1">
|
||||||
|
<%= f.label :password_confirmation, "Confirm new password" %>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
<%= f.password_field :password_confirmation, autocomplete: "new-password" %>
|
<%= f.password_field :password_confirmation, autocomplete: "new-password" %>
|
||||||
</div>
|
</p>
|
||||||
|
<p class="mt-8">
|
||||||
<div class="actions">
|
<%= f.submit "Change my password", class: 'btn-md btn-blue' %>
|
||||||
<%= f.submit "Change my password" %>
|
</p>
|
||||||
</div>
|
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<%= render "devise/shared/links" %>
|
<%= render "devise/shared/links" %>
|
||||||
|
|||||||
@@ -2,26 +2,17 @@
|
|||||||
|
|
||||||
<%= form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :post }) do |f| %>
|
<%= form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :post }) do |f| %>
|
||||||
<%= render "devise/shared/error_messages", resource: resource %>
|
<%= render "devise/shared/error_messages", resource: resource %>
|
||||||
|
<p>
|
||||||
<div class="field">
|
<%= f.label :cn, 'User', class: 'block' %>
|
||||||
<p>
|
<%= f.text_field :cn, autofocus: true, autocomplete: "username", required: true %> @ kosmos.org
|
||||||
<%= f.label :cn, 'User' %><br />
|
</p>
|
||||||
<%= f.text_field :cn, autofocus: true, autocomplete: "username", required: true %> @ kosmos.org
|
<p>
|
||||||
</p>
|
<%= f.label :email, 'Email address', class: 'block' %>
|
||||||
</div>
|
<%= f.email_field :email, autocomplete: "email", required: true %>
|
||||||
|
</p>
|
||||||
<div class="field">
|
<p class="mt-8">
|
||||||
<p>
|
<%= f.submit "Send me reset password instructions", class: 'btn-md btn-blue' %>
|
||||||
<%= f.label :email, 'Email address' %><br />
|
</p>
|
||||||
<%= f.email_field :email, autocomplete: "email", required: true %>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="actions">
|
|
||||||
<p>
|
|
||||||
<%= f.submit "Send me reset password instructions" %>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<%= render "devise/shared/links" %>
|
<%= render "devise/shared/links" %>
|
||||||
|
|||||||
@@ -31,9 +31,9 @@
|
|||||||
<%= f.password_field :current_password, autocomplete: "current-password" %>
|
<%= f.password_field :current_password, autocomplete: "current-password" %>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="actions">
|
<p class="mt-8">
|
||||||
<%= f.submit "Update" %>
|
<%= f.submit "Update", class: 'btn-md btn-blue' %>
|
||||||
</div>
|
</p>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<h3>Cancel my account</h3>
|
<h3>Cancel my account</h3>
|
||||||
|
|||||||
@@ -2,24 +2,17 @@
|
|||||||
|
|
||||||
<%= form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| %>
|
<%= form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| %>
|
||||||
<%= render "devise/shared/error_messages", resource: resource %>
|
<%= render "devise/shared/error_messages", resource: resource %>
|
||||||
|
<p>
|
||||||
<div class="field">
|
<%= f.label :cn, 'User', class: 'block' %>
|
||||||
<p>
|
<%= f.text_field :cn, autofocus: true, autocomplete: "username" %> @ kosmos.org
|
||||||
<%= f.label :cn, 'User' %><br />
|
</p>
|
||||||
<%= f.text_field :cn, autofocus: true, autocomplete: "username" %> @ kosmos.org
|
<p>
|
||||||
</p>
|
<%= f.label :password, class: 'block' %>
|
||||||
</div>
|
<%= f.password_field :password, autocomplete: "current-password" %>
|
||||||
<div class="field">
|
</p>
|
||||||
<p>
|
<p class="mt-8">
|
||||||
<%= f.label :password %><br />
|
<%= f.submit "Log in", class: 'btn-md btn-blue' %>
|
||||||
<%= f.password_field :password, autocomplete: "current-password" %>
|
</p>
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div class="actions">
|
|
||||||
<p>
|
|
||||||
<%= f.submit "Log in" %>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<%= render "devise/shared/links" %>
|
<%= render "devise/shared/links" %>
|
||||||
|
|||||||
@@ -1,24 +1,24 @@
|
|||||||
<div class="devise-links">
|
<div class="devise-links mt-8 text-sm">
|
||||||
<%- if controller_name != 'sessions' %>
|
<%- if controller_name != 'sessions' %>
|
||||||
<p>
|
<p class="mb-1.5">
|
||||||
<%= link_to "Log in", new_session_path(resource_name) %><br />
|
<%= link_to "Log in", new_session_path(resource_name) %><br />
|
||||||
</p>
|
</p>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<%- if devise_mapping.recoverable? && controller_name != 'passwords' && controller_name != 'registrations' %>
|
<%- if devise_mapping.recoverable? && controller_name != 'passwords' && controller_name != 'registrations' %>
|
||||||
<p>
|
<p class="mb-1.5">
|
||||||
<%= link_to "Forgot your password?", new_password_path(resource_name) %><br />
|
<%= link_to "Forgot your password?", new_password_path(resource_name) %><br />
|
||||||
</p>
|
</p>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<%- if devise_mapping.confirmable? && controller_name != 'confirmations' %>
|
<%- if devise_mapping.confirmable? && controller_name != 'confirmations' %>
|
||||||
<p>
|
<p class="mb-1.5">
|
||||||
<%= link_to "Didn't receive confirmation instructions?", new_confirmation_path(resource_name) %><br />
|
<%= link_to "Didn't receive confirmation instructions?", new_confirmation_path(resource_name) %><br />
|
||||||
</p>
|
</p>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<%- if devise_mapping.lockable? && resource_class.unlock_strategy_enabled?(:email) && controller_name != 'unlocks' %>
|
<%- if devise_mapping.lockable? && resource_class.unlock_strategy_enabled?(:email) && controller_name != 'unlocks' %>
|
||||||
<p>
|
<p class="mb-1.5">
|
||||||
<%= link_to "Didn't receive unlock instructions?", new_unlock_path(resource_name) %><br />
|
<%= link_to "Didn't receive unlock instructions?", new_unlock_path(resource_name) %><br />
|
||||||
</p>
|
</p>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|||||||
@@ -2,15 +2,13 @@
|
|||||||
|
|
||||||
<%= form_for(resource, as: resource_name, url: unlock_path(resource_name), html: { method: :post }) do |f| %>
|
<%= form_for(resource, as: resource_name, url: unlock_path(resource_name), html: { method: :post }) do |f| %>
|
||||||
<%= render "devise/shared/error_messages", resource: resource %>
|
<%= render "devise/shared/error_messages", resource: resource %>
|
||||||
|
<p>
|
||||||
<div class="field">
|
|
||||||
<%= f.label :email %><br />
|
<%= f.label :email %><br />
|
||||||
<%= f.email_field :email, autofocus: true, autocomplete: "email" %>
|
<%= f.email_field :email, autofocus: true, autocomplete: "email" %>
|
||||||
</div>
|
</p>
|
||||||
|
<p class="mt-8">
|
||||||
<div class="actions">
|
<%= f.submit "Resend unlock instructions", class: 'btn-md btn-blue' %>
|
||||||
<%= f.submit "Resend unlock instructions" %>
|
</p>
|
||||||
</div>
|
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<%= render "devise/shared/links" %>
|
<%= render "devise/shared/links" %>
|
||||||
|
|||||||
41
app/views/donations/index.html.erb
Normal file
41
app/views/donations/index.html.erb
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
<section>
|
||||||
|
<h2>Donations</h2>
|
||||||
|
<p>
|
||||||
|
Your financial contributions to the development and
|
||||||
|
upkeep of Kosmos software and services.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<% if @donations.any? %>
|
||||||
|
<ul class="donations list-none">
|
||||||
|
<% @donations.each do |donation| %>
|
||||||
|
<li class="mb-8 grid gap-y-2 gap-x-8 grid-cols-2 items-center">
|
||||||
|
<h3 class="mb-0">
|
||||||
|
<%= donation.paid_at.strftime("%B %d, %Y") %>
|
||||||
|
</h3>
|
||||||
|
<p class="row-span-2 font-mono text-right mb-0">
|
||||||
|
<span class="text-xl">
|
||||||
|
<%= number_with_delimiter donation.amount_sats %> sats
|
||||||
|
</span>
|
||||||
|
<br>
|
||||||
|
<span class="text-sm text-gray-500">
|
||||||
|
(~ <%= number_to_currency donation.amount_eur / 100, unit: "" %> EUR)
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
<p class="mb-0">
|
||||||
|
<% if donation.public_name.present? %>
|
||||||
|
Public name: <%= donation.public_name %>
|
||||||
|
<% else %>
|
||||||
|
Anonymous
|
||||||
|
<% end %>
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
<% end %>
|
||||||
|
</ul>
|
||||||
|
<% else %>
|
||||||
|
<p>
|
||||||
|
No donations to show.
|
||||||
|
</p>
|
||||||
|
<% end %>
|
||||||
|
</section>
|
||||||
1
app/views/donations/index.json.jbuilder
Normal file
1
app/views/donations/index.json.jbuilder
Normal file
@@ -0,0 +1 @@
|
|||||||
|
json.array! @donations, partial: "donations/donation", as: :donation
|
||||||
@@ -1,5 +1,13 @@
|
|||||||
<section>
|
<section>
|
||||||
<h2>Invitations</h2>
|
<h2>Invitations</h2>
|
||||||
|
<% if @invitations_unused.any? %>
|
||||||
|
<p>
|
||||||
|
Invite your friends to a Kosmos account by sharing an invitation URL with them:
|
||||||
|
</p>
|
||||||
|
<% end %>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
<% if @invitations_unused.any? %>
|
<% if @invitations_unused.any? %>
|
||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
@@ -24,23 +32,25 @@
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
<% if @invitations_used.any? %>
|
<% if @invitations_used.any? %>
|
||||||
<h3>Accepted Invitations</h3>
|
<section>
|
||||||
<table>
|
<h3>Accepted Invitations</h3>
|
||||||
<thead>
|
<table>
|
||||||
<tr>
|
<thead>
|
||||||
<th>URL</th>
|
|
||||||
<th>Used at</th>
|
|
||||||
<th>Invited user</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<% @invitations_used.each do |invitation| %>
|
|
||||||
<tr>
|
<tr>
|
||||||
<td><%= invitation_url(invitation.token) %></td>
|
<th class="hide-small">ID</th>
|
||||||
<td><%= invitation.used_at %></td>
|
<th>Accepted</th>
|
||||||
<td><%= User.find(invitation.invited_user_id).address %></td>
|
<th>Invited user</th>
|
||||||
</tr>
|
</tr>
|
||||||
<% end %>
|
</thead>
|
||||||
</tbody>
|
<tbody>
|
||||||
</table>
|
<% @invitations_used.each do |invitation| %>
|
||||||
|
<tr>
|
||||||
|
<td class="hide-small"><%= invitation.token %></td>
|
||||||
|
<td><%= invitation.used_at.strftime("%Y-%m-%d") %></td>
|
||||||
|
<td><%= User.find(invitation.invited_user_id).address %></td>
|
||||||
|
</tr>
|
||||||
|
<% end %>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</section>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|||||||
39
app/views/layouts/admin.html.erb
Normal file
39
app/views/layouts/admin.html.erb
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Admin Panel | Kosmos Accounts</title>
|
||||||
|
<%= csrf_meta_tags %>
|
||||||
|
<%= csp_meta_tag %>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<link href="https://assets.kosmos.org/fonts/open-sans/open-sans.css" rel="stylesheet">
|
||||||
|
<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
|
||||||
|
<%= stylesheet_pack_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
|
||||||
|
<%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
|
||||||
|
</head>
|
||||||
|
<body id="admin-panel">
|
||||||
|
<div id="wrapper">
|
||||||
|
<header>
|
||||||
|
<h1>
|
||||||
|
<span class ="icon"><%= render partial: "shared/icons/comet" %></span>
|
||||||
|
<span class ="project-name">Kosmos</span>
|
||||||
|
<span class ="site-name">Accounts</span>
|
||||||
|
</h1>
|
||||||
|
<%= render partial: 'shared/header_account' %>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<% if user_signed_in? && current_user.confirmed? %>
|
||||||
|
<%= render partial: 'shared/admin_nav' %>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<% flash.each do |type, msg| %>
|
||||||
|
<div class="flash-msg <%= type %>">
|
||||||
|
<p><%= msg %></p>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<main>
|
||||||
|
<%= yield %>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -4,29 +4,27 @@
|
|||||||
<title>Kosmos Accounts</title>
|
<title>Kosmos Accounts</title>
|
||||||
<%= csrf_meta_tags %>
|
<%= csrf_meta_tags %>
|
||||||
<%= csp_meta_tag %>
|
<%= csp_meta_tag %>
|
||||||
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<link href="https://assets.kosmos.org/fonts/open-sans/open-sans.css" rel="stylesheet">
|
||||||
<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
|
<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
|
||||||
|
<%= stylesheet_pack_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
|
||||||
<%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
|
<%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<div id="wrapper">
|
<div id="wrapper">
|
||||||
<header>
|
<header>
|
||||||
<h1>
|
<h1>
|
||||||
|
<span class ="icon"><%= render partial: "shared/icons/comet" %></span>
|
||||||
<span class ="project-name">Kosmos</span>
|
<span class ="project-name">Kosmos</span>
|
||||||
<span class ="site-name">Akkounts</span>
|
<span class ="site-name">Account</span>
|
||||||
<span class="beta"><span class="bolt">⚡</span> beta</span>
|
|
||||||
</h1>
|
</h1>
|
||||||
<% if user_signed_in? %>
|
<%= render partial: 'shared/header_account' %>
|
||||||
<p class="current-user">
|
|
||||||
Signed in as <strong><%= current_user.cn %>@kosmos.org</strong>.
|
|
||||||
<%= link_to "Log out", destroy_user_session_path, method: :delete %>
|
|
||||||
</p>
|
|
||||||
<% end %>
|
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
|
<% if user_signed_in? && current_user.confirmed? %>
|
||||||
|
<%= render partial: 'shared/main_nav' %>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
<% flash.each do |type, msg| %>
|
<% flash.each do |type, msg| %>
|
||||||
<div class="flash-msg <%= type %>">
|
<div class="flash-msg <%= type %>">
|
||||||
<p><%= msg %></p>
|
<p><%= msg %></p>
|
||||||
|
|||||||
@@ -4,20 +4,19 @@
|
|||||||
<title>Sign up | Kosmos Accounts</title>
|
<title>Sign up | Kosmos Accounts</title>
|
||||||
<%= csrf_meta_tags %>
|
<%= csrf_meta_tags %>
|
||||||
<%= csp_meta_tag %>
|
<%= csp_meta_tag %>
|
||||||
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<link href="https://assets.kosmos.org/fonts/open-sans/open-sans.css" rel="stylesheet">
|
||||||
<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
|
<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
|
||||||
|
<%= stylesheet_pack_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
|
||||||
<%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
|
<%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="layout-signup">
|
<body class="layout-signup">
|
||||||
<div id="wrapper">
|
<div id="wrapper">
|
||||||
<header>
|
<header>
|
||||||
<h1>
|
<h1>
|
||||||
|
<span class ="icon"><%= render partial: "shared/icons/comet" %></span>
|
||||||
<span class ="project-name">Kosmos</span>
|
<span class ="project-name">Kosmos</span>
|
||||||
<span class ="site-name">Sign Up</span>
|
<span class ="site-name">Sign Up</span>
|
||||||
<!-- <span class="beta"><span class="bolt">⚡</span> beta</span> -->
|
|
||||||
</h1>
|
</h1>
|
||||||
<% if user_signed_in? %>
|
<% if user_signed_in? %>
|
||||||
<p class="current-user">
|
<p class="current-user">
|
||||||
|
|||||||
12
app/views/security/index.html.erb
Normal file
12
app/views/security/index.html.erb
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<section>
|
||||||
|
<h2>Security</h2>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h3>Password change</h3>
|
||||||
|
<p>
|
||||||
|
<%= form_with(url: settings_reset_password_path, method: :post) do %>
|
||||||
|
<%= submit_tag("Send me a password reset link", class: 'btn-md btn-gray') %>
|
||||||
|
<% end %>
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
22
app/views/shared/_admin_nav.html.erb
Normal file
22
app/views/shared/_admin_nav.html.erb
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
<nav id="main-nav">
|
||||||
|
<div class="wrapper">
|
||||||
|
<ul class="pages">
|
||||||
|
<li>
|
||||||
|
<%= link_to "Dashboard", admin_root_path,
|
||||||
|
class: @current_section == :dashboard ? "active" : nil %>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<%= link_to "Invitations", admin_invitations_path,
|
||||||
|
class: @current_section == :invitations ? "active" : nil %>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<%= link_to "Donations", admin_donations_path,
|
||||||
|
class: @current_section == :donations ? "active" : nil %>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<%= link_to "LDAP Users", admin_ldap_users_path,
|
||||||
|
class: @current_section == :ldap_users ? "active" : nil %>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
6
app/views/shared/_header_account.html.erb
Normal file
6
app/views/shared/_header_account.html.erb
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<% if user_signed_in? %>
|
||||||
|
<p class="current-user mt-8">
|
||||||
|
Signed in as <strong class="text-white font-normal"><%= current_user.cn %>@kosmos.org</strong>.
|
||||||
|
<%= link_to "Log out", destroy_user_session_path, method: :delete, class: 'underline' %>
|
||||||
|
</p>
|
||||||
|
<% end %>
|
||||||
26
app/views/shared/_main_nav.html.erb
Normal file
26
app/views/shared/_main_nav.html.erb
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<nav id="main-nav">
|
||||||
|
<div class="wrapper">
|
||||||
|
<ul class="pages">
|
||||||
|
<li>
|
||||||
|
<%= link_to "Services", root_path,
|
||||||
|
class: @current_section == :dashboard ? "active" : nil %>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<%= link_to "Invitations", invitations_path,
|
||||||
|
class: @current_section == :invitations ? "active" : nil %>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<%= link_to "Donations", donations_path,
|
||||||
|
class: @current_section == :contributions ? "active" : nil %>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<%= link_to "Wallet", wallet_path,
|
||||||
|
class: @current_section == :wallet ? "active" : nil %>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<%= link_to "Security", security_path,
|
||||||
|
class: @current_section == :security ? "active" : nil %>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
1
app/views/shared/icons/_comet.html.erb
Normal file
1
app/views/shared/icons/_comet.html.erb
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg id="icon-comet" width="65.364" height="55.773" enable-background="new 0 0 100 100" version="1.1" viewBox="0 0 65.364 55.773" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><g id="layer1" transform="translate(28.868 20.259)" fill="#fff"><path id="path2" d="m22.81-9.2546-0.0137-0.0072c-0.0445-0.0196-0.0895-0.04052-0.13335-0.06078l-23.822-10.937s2.0034 9.219 2.914 11.778c0 0-27.292-8.1582-30.623-8.9354 1.0916 4.2618 20.006 40.848 20.006 40.848 3.8225 7.7608 12.677 12.083 21.912 12.083 12.949 0 23.446-10.497 23.446-23.446 6.6e-4 -9.4655-5.609-17.62-13.685-21.323z" fill="#fff" stroke-width=".65365"/></g></svg>
|
||||||
|
After Width: | Height: | Size: 627 B |
@@ -7,6 +7,6 @@
|
|||||||
This invitation can only be used once, and sign-up is currently only possible
|
This invitation can only be used once, and sign-up is currently only possible
|
||||||
by invitation. Seems like you have good friends!
|
by invitation. Seems like you have good friends!
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p class="mt-12">
|
||||||
<%= link_to "Get started", signup_steps_path(1), class: "next-step" %>
|
<%= link_to "Get started", signup_steps_path(1), class: "btn btn-md btn-blue" %>
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@@ -2,60 +2,53 @@
|
|||||||
<% when 1 %>
|
<% when 1 %>
|
||||||
<h2>Choose a username</h2>
|
<h2>Choose a username</h2>
|
||||||
<%= form_for @user, :url => signup_validate_url do |f| %>
|
<%= form_for @user, :url => signup_validate_url do |f| %>
|
||||||
<div class="field">
|
<p>
|
||||||
<p>
|
<%= f.label :cn, 'Username', class: 'hidden' %>
|
||||||
<%= f.label :cn, 'Username' %><br />
|
<%= f.text_field :cn, autofocus: true, autocomplete: "username",
|
||||||
<%= f.text_field :cn, autofocus: true, autocomplete: "username" %>
|
class: 'text-xl' %>
|
||||||
<span class="at-sign">@</span>
|
<span class="text-xl ml-1">@</span>
|
||||||
<span class="domain">kosmos.org</span>
|
<span class="text-xl">kosmos.org</span>
|
||||||
</p>
|
</p>
|
||||||
<% if @validation_error.present? %>
|
<% if @validation_error.present? %>
|
||||||
<p class="error-msg">Username <%= @validation_error %></p>
|
<p class="error-msg">Username <%= @validation_error %></p>
|
||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
<p class="mt-12">
|
||||||
<div class="actions">
|
<%= f.submit "Continue", class: 'btn btn-md btn-blue' %>
|
||||||
<p><%= f.submit "Continue" %></p>
|
</p>
|
||||||
</div>
|
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<% when 2 %>
|
<% when 2 %>
|
||||||
<h2>What's your email?</h2>
|
<h2>What's your email?</h2>
|
||||||
<%= form_for @user, :url => signup_validate_url do |f| %>
|
<%= form_for @user, :url => signup_validate_url do |f| %>
|
||||||
<div class="field">
|
<p>
|
||||||
<p>
|
<%= f.label :email, 'Email address', class: 'hidden' %>
|
||||||
<%= f.label :email, 'Email address' %><br />
|
<%= f.email_field :email, autofocus: true, autocomplete: 'email', class: 'text-xl' %>
|
||||||
<%= f.email_field :email, autofocus: true, autocomplete: "email" %>
|
</p>
|
||||||
</p>
|
<% if @validation_error.present? %>
|
||||||
<% if @validation_error.present? %>
|
<p class="error-msg">Email <%= @validation_error %></p>
|
||||||
<p class="error-msg">Email <%= @validation_error %></p>
|
<% end %>
|
||||||
<% end %>
|
<p class="mt-12">
|
||||||
</div>
|
<%= f.submit "Continue", class: 'btn btn-md btn-blue' %>
|
||||||
<div class="actions">
|
</p>
|
||||||
<p><%= f.submit "Continue" %></p>
|
|
||||||
</div>
|
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<% when 3 %>
|
<% when 3 %>
|
||||||
<h2>Choose a password</h2>
|
<h2>Choose a password</h2>
|
||||||
|
|
||||||
<%= form_for @user, :url => signup_validate_url do |f| %>
|
<%= form_for @user, :url => signup_validate_url do |f| %>
|
||||||
<div class="field">
|
<p>
|
||||||
<p>
|
<%= f.label :password, 'Password', class: 'hidden' %>
|
||||||
<%= f.label :password, 'Password' %><br />
|
<%= f.password_field :password, autofocus: true, class: 'text-xl' %>
|
||||||
<%= f.password_field :password, autofocus: true %>
|
</p>
|
||||||
</p>
|
<% if @validation_error.present? %>
|
||||||
<% if @validation_error.present? %>
|
<p class="error-msg">Password <%= @validation_error %></p>
|
||||||
<p class="error-msg">Password <%= @validation_error %></p>
|
<% end %>
|
||||||
<% end %>
|
<p class="mt-8 text-sm text-gray-500">
|
||||||
</div>
|
By clicking the button below, you accept our future Terms of Service
|
||||||
<p class="accept-terms">
|
and Privacy Policy. Don't worry, they will be excellent!
|
||||||
<small>
|
</p>
|
||||||
By clicking the button below, you accept our future Terms of Service
|
<p class="mt-8">
|
||||||
and Privacy Policy. Don't worry, they will be excellent!
|
<%= f.submit "Create account", class: 'btn-md btn-blue' %>
|
||||||
</small>
|
|
||||||
</p>
|
</p>
|
||||||
<div class="actions">
|
|
||||||
<p><%= f.submit "Create account" %></p>
|
|
||||||
</div>
|
|
||||||
<% end %>
|
<% end %>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|||||||
120
app/views/wallet/index.html.erb
Normal file
120
app/views/wallet/index.html.erb
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
<section class="w-full grid grid-cols-1 md:grid-cols-12 md:mb-0">
|
||||||
|
<div class="md:col-span-8">
|
||||||
|
<h2>Wallet</h2>
|
||||||
|
<p>
|
||||||
|
Send and receive sats via the Bitcoin Lightning Network.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="md:col-span-4 mt-4 md:mt-0">
|
||||||
|
<p class="font-mono md:text-right mb-0 p-4 border border-gray-300 rounded-lg overflow-hidden">
|
||||||
|
<% if @balance %>
|
||||||
|
<span class="text-xl"><%= number_with_delimiter @balance %> sats</span><br>
|
||||||
|
<span class="text-sm text-gray-500">Available balance</span>
|
||||||
|
<% else %>
|
||||||
|
<span class="text-xl">n/a sats</span><br>
|
||||||
|
<span class="text-sm text-gray-500">Balance unavailable</span>
|
||||||
|
<% end %>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h3>Lightning Address</h3>
|
||||||
|
<p>
|
||||||
|
Your Kosmos user address is also a
|
||||||
|
<a class="ks-text-link" href="https://lightningaddress.com/" target="_blank">Lightning Address</a>!
|
||||||
|
The easiest way to receive sats is by just giving out your address:
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<strong><%= current_user.address %></strong>
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h3>Wallet Apps</h3>
|
||||||
|
<p>
|
||||||
|
You can connect various wallet apps to your Kosmos account. This allows
|
||||||
|
you to both receive and send sats. Any wallet that supports
|
||||||
|
<a href="https://bluewallet.io/lndhub/" class="ks-text-link" target="_blank">LNDHub</a>
|
||||||
|
accounts should be able to add/import your account using our setup
|
||||||
|
code/URL:
|
||||||
|
</p>
|
||||||
|
<p class="my-6 text-center md:text-left">
|
||||||
|
<button id="copy-setup-code" class="btn-md btn-blue">Copy setup code/URL</button>
|
||||||
|
<span class="mx-2 my-2 md:my-0 block md:inline">or</span>
|
||||||
|
<button id="show-setup-code" class="btn-md btn-blue">Show setup QR code</button>
|
||||||
|
<button id="hide-setup-code" class="btn-md btn-blue hidden">Hide setup QR code</button>
|
||||||
|
</p>
|
||||||
|
<p id="setup-code" class="hidden my-10 w-full text-center">
|
||||||
|
<%= raw @svg %>
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h3>Recommended Apps</h3>
|
||||||
|
<div class="w-full grid grid-cols-1 gap-y-4 md:grid-cols-12
|
||||||
|
md:gap-y-6 md:gap-x-4 md:items-center">
|
||||||
|
<h4 class="md:col-span-3">
|
||||||
|
<a href="https://bluewallet.io" class="ks-text-link text-xl"
|
||||||
|
title="Blue Wallet" target="_blank">
|
||||||
|
<%= image_tag("/img/logos/bluewallet.svg", class: 'h-16') %>
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
<p class="md:col-span-4 mb-0 text-gray-500">
|
||||||
|
Android / iOS / macOS
|
||||||
|
</p>
|
||||||
|
<p class="md:col-span-5 mb-0">
|
||||||
|
When adding a wallet, choose "Import wallet" on the bottom of the screen,
|
||||||
|
then scan the setup QR code.
|
||||||
|
</p>
|
||||||
|
<h4 class="md:col-span-3">
|
||||||
|
<a href="https://getalby.com/" class="ks-text-link text-xl"
|
||||||
|
title="Alby" target="_blank">
|
||||||
|
<%= image_tag("/img/logos/alby.svg", class: 'h-16') %>
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
<p class="md:col-span-4 mb-0 text-gray-500">
|
||||||
|
Firefox / Chrome (Opera, Brave, Chromium-based browsers)
|
||||||
|
</p>
|
||||||
|
<p class="md:col-span-5 mb-0">
|
||||||
|
Choose "LNDHub (Bluewallet)" in the connect dialog and paste the setup
|
||||||
|
URL in the "LNDHub Export URI" field.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
(function () {
|
||||||
|
const buttonShow = document.querySelector('#show-setup-code');
|
||||||
|
const buttonHide = document.querySelector('#hide-setup-code');
|
||||||
|
const buttonCopy = document.querySelector('#copy-setup-code');
|
||||||
|
const setupCode = document.querySelector('#setup-code');
|
||||||
|
|
||||||
|
buttonShow.addEventListener('click', function(ev) {
|
||||||
|
ev.preventDefault();
|
||||||
|
setupCode.classList.remove('hidden');
|
||||||
|
buttonHide.classList.remove('hidden');
|
||||||
|
buttonShow.classList.add('hidden');
|
||||||
|
setupCode.scrollIntoView({behavior: "smooth", block: "nearest"});
|
||||||
|
});
|
||||||
|
|
||||||
|
buttonHide.addEventListener('click', function(ev) {
|
||||||
|
ev.preventDefault();
|
||||||
|
const el = document.querySelector('#setup-code');
|
||||||
|
setupCode.classList.add('hidden');
|
||||||
|
buttonHide.classList.add('hidden');
|
||||||
|
buttonShow.classList.remove('hidden');
|
||||||
|
});
|
||||||
|
|
||||||
|
buttonCopy.addEventListener('click', function(ev) {
|
||||||
|
ev.preventDefault();
|
||||||
|
navigator.clipboard.writeText('<%= @wallet_url %>').then(() => {
|
||||||
|
const buttonText = buttonCopy.innerText;
|
||||||
|
buttonCopy.innerText = 'Copied ✔';
|
||||||
|
setTimeout(() => {
|
||||||
|
buttonCopy.innerText = buttonText;
|
||||||
|
}, 2000);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
@@ -39,5 +39,8 @@ module Akkounts
|
|||||||
g.fixture_replacement :factory_bot, suffix_factory: 'factory', dir: 'spec/factories'
|
g.fixture_replacement :factory_bot, suffix_factory: 'factory', dir: 'spec/factories'
|
||||||
g.stylesheets false
|
g.stylesheets false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
config.active_job.queue_adapter = :sidekiq
|
||||||
|
config.action_mailer.deliver_later_queue_name = nil # use "default" queue
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
LWyKwPZq9Kd97rn/7+q3MEkh7kITScDMHD3JvVuaV3A4YIHJHU+460k+PaEGlsH1xkbuClGiAb57rk1XLyDnmVGtbSueYOtinkw6kar8ZfKWZob061LwGjpMVRQkS49TjCUZlqCFrXeKxlH03mXWBnqAj9RUIPrm7eibb3c7qmJFglR1380RSVsfZnp8A3QwGm4Wh9OWtpUa6P2lne0jQsOuSe8ur3DUF0LplzS4CbkMxAUDOom+pXB13AlxOH9NQE7F4dsYHugHkh1tG3r3ER3xAUD/9Kn6UZZP7BnwUs3zqhoZdULRpRgA5dK7ueTIAnO/jtJDF4562VS8ECo7AnNoVxNe8/mBMFIOUfqg+db/72N2pIk3r4lK7Uzm/4jJ5/99ItnQjHQPcApiwZXIr3OyDLUvq5+d0UVmAMXdwcAjvctVQXFx5imG149Y0ISHKWVm1ca37aAspxWPU+CIj8/HW0yEpjp3vhwDUbjCaZeAPm8UQC14MxZwSK3N+EUSQXdltiweFynabDB7zGGQsjMM8LwMtyo9bTBzJA78Cl96MDyd20i1zSF9ntLuKulwGm3oZowpbNuvo2anY6r9yBlDJBOEISbtXv2tLX4SqcM=--bRcsE4K/29XzyZat--+G3iQCLBqgSwLaQ+7+4YvA==
|
PRI1laGy6XiLpsII0ixFOTCpzzDbrRpIQ7GJ2flvA+UEEiPVQQ4e/pt6ZjumaZ/geTBHCRZklLtS1u6HjucVKHGSR4K/lE1q7u6nfi/wAvwGQdCTJeKf8PetdlsiVZg2OW4Wi2YF7qtb1ipGS/uRlOeLP8JOwp5BNVS1J6tvNX4T5gA7rrB4Cvtfwp1dwLWODHiXbeZK7SN2j7V3NPGZl0LbNg2jquC4DzQOBhMOlwu1IAgOAcqKbHh9tDabpVOcOD5pNSaVaoBO01P8ObbX8x0S0Ug7BPkM5nnyJTQdNH7LHKvtNmrUEk7+fd+VZR6WqEp6SFBoAA7XxvOLy9dIOqvXbAYnrNZX7iI4IpgZWRNIQ7G1qSBjpyPLKC7+nSPmvd4IcgCHVUBdYp0+yYDxLzwuuhPVtyAY4JAIdZY1mhYxx7BzGNiMc3p8AlMS6hPeO9lVQZykViBGnTjR+iah9OQYAihX9FYywtBkNvMXLiYqsVZmv6em9Uk/ivKSIsaFwNwrelCI5H4Q/+/hKFJ1JaMjbYheXCJgcGgfhiC+fIHig/8y8rXd9lWGa1T+PrpA8akcq/K8I6gbxYxpLWcy5Vnz/SyaMurLPVfCq4cD/JfpLSfy0HophaiycZ7D6dr43rnHsPfrt6kveDOgWUK9d7CqHxsWdXbCpJeBoh8/1usIsGsmyhfwHq0dEpRx78bR2EJeM4LOAuQgf69/5rFJydIbXq76l1BftTls1Pgh--OI8c/RQGQ30wT1Ff--M/ltnmdl+FRdlNliTb79lg==
|
||||||
1
config/credentials/development.key
Normal file
1
config/credentials/development.key
Normal file
@@ -0,0 +1 @@
|
|||||||
|
2a8a17892dd9f41ea50c61310c83240b
|
||||||
1
config/credentials/development.yml.enc
Normal file
1
config/credentials/development.yml.enc
Normal file
@@ -0,0 +1 @@
|
|||||||
|
zdpQvlbfqXzaExLzw2LGZkXj97HH42jWZFSTlINpf/HlFr6NubPxLkVxeGsittJj5rm8yL+V21zPxp30Z7Q7R45qyCKFtevtVeqb+u1nZ/FsVfkwb/7wDW9scurgXw==--SB6C0aaNf8qPdteG--9hP+6tpsMnyiAVVvAIq+7w==
|
||||||
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 @@
|
|||||||
|
xgPOFd8315z7lFtTR5/nD6WDBM2M6Grt/pmkCPdaqlw0WAmFKzbiRGFsXoUQ02JNzvT1/FVtBSsAcyK1Pdr1QQztlWC+/ywaflloMBS4//D8IEXvEgCK6uff5gcf1A==--WbFrw9advCJ4mqsK--HTVHZqO0ddG1toFpY0KKgQ==
|
||||||
@@ -49,4 +49,6 @@ Rails.application.configure do
|
|||||||
protocol: "https",
|
protocol: "https",
|
||||||
from: "accounts@kosmos.org"
|
from: "accounts@kosmos.org"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
config.active_job.queue_adapter = :test
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
require 'sidekiq/web'
|
||||||
|
|
||||||
Rails.application.routes.draw do
|
Rails.application.routes.draw do
|
||||||
devise_for :users
|
devise_for :users
|
||||||
|
|
||||||
@@ -11,11 +13,26 @@ Rails.application.routes.draw do
|
|||||||
get 'settings', to: 'settings#index'
|
get 'settings', to: 'settings#index'
|
||||||
post 'settings_reset_password', to: 'settings#reset_password'
|
post 'settings_reset_password', to: 'settings#reset_password'
|
||||||
|
|
||||||
|
get 'security', to: 'security#index'
|
||||||
|
|
||||||
resources :invitations, only: ['index', 'show', 'create', 'destroy']
|
resources :invitations, only: ['index', 'show', 'create', 'destroy']
|
||||||
|
|
||||||
|
resources :donations
|
||||||
|
|
||||||
|
get 'wallet', to: 'wallet#index'
|
||||||
|
|
||||||
|
get 'lnurlpay/:address', to: 'lnurlpay#index', constraints: { address: /[^\/]+/}
|
||||||
|
get 'lnurlpay/:address/invoice', to: 'lnurlpay#invoice', constraints: { address: /[^\/]+/}
|
||||||
|
|
||||||
namespace :admin do
|
namespace :admin do
|
||||||
root to: 'dashboard#index'
|
root to: 'dashboard#index'
|
||||||
|
get 'invitations', to: 'invitations#index'
|
||||||
get 'ldap_users', to: 'ldap_users#index'
|
get 'ldap_users', to: 'ldap_users#index'
|
||||||
|
resources :donations
|
||||||
|
end
|
||||||
|
|
||||||
|
authenticate :user, ->(user) { user.is_admin? } do
|
||||||
|
mount Sidekiq::Web => '/sidekiq'
|
||||||
end
|
end
|
||||||
|
|
||||||
# Letter Opener (open "sent" emails in dev and staging)
|
# Letter Opener (open "sent" emails in dev and staging)
|
||||||
|
|||||||
3
config/sidekiq.yml
Normal file
3
config/sidekiq.yml
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
:concurrency: 2
|
||||||
|
:queues:
|
||||||
|
- default
|
||||||
@@ -8,6 +8,7 @@ class CreateInvitations < ActiveRecord::Migration[6.0]
|
|||||||
|
|
||||||
t.timestamps
|
t.timestamps
|
||||||
end
|
end
|
||||||
|
|
||||||
add_index :invitations, :user_id
|
add_index :invitations, :user_id
|
||||||
add_index :invitations, :invited_user_id
|
add_index :invitations, :invited_user_id
|
||||||
end
|
end
|
||||||
|
|||||||
15
db/migrate/20201217161544_create_donations.rb
Normal file
15
db/migrate/20201217161544_create_donations.rb
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
class CreateDonations < ActiveRecord::Migration[6.0]
|
||||||
|
def change
|
||||||
|
create_table :donations do |t|
|
||||||
|
t.integer :user_id
|
||||||
|
t.integer :amount_sats
|
||||||
|
t.integer :amount_eur
|
||||||
|
t.integer :amount_usd
|
||||||
|
t.string :public_name
|
||||||
|
|
||||||
|
t.timestamps
|
||||||
|
end
|
||||||
|
|
||||||
|
add_index :donations, :user_id
|
||||||
|
end
|
||||||
|
end
|
||||||
5
db/migrate/20201219121808_add_paid_at_to_donations.rb
Normal file
5
db/migrate/20201219121808_add_paid_at_to_donations.rb
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
class AddPaidAtToDonations < ActiveRecord::Migration[6.0]
|
||||||
|
def change
|
||||||
|
add_column :donations, :paid_at, :datetime
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
class AddLndhubCredentialsToUser < ActiveRecord::Migration[6.0]
|
||||||
|
def change
|
||||||
|
add_column :users, :ln_login_ciphertext, :text
|
||||||
|
add_column :users, :ln_password_ciphertext, :text
|
||||||
|
end
|
||||||
|
end
|
||||||
16
db/schema.rb
16
db/schema.rb
@@ -10,7 +10,19 @@
|
|||||||
#
|
#
|
||||||
# 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.define(version: 2020_11_30_132533) do
|
ActiveRecord::Schema.define(version: 2021_11_20_010540) do
|
||||||
|
|
||||||
|
create_table "donations", force: :cascade do |t|
|
||||||
|
t.integer "user_id"
|
||||||
|
t.integer "amount_sats"
|
||||||
|
t.integer "amount_eur"
|
||||||
|
t.integer "amount_usd"
|
||||||
|
t.string "public_name"
|
||||||
|
t.datetime "created_at", precision: 6, null: false
|
||||||
|
t.datetime "updated_at", precision: 6, null: false
|
||||||
|
t.datetime "paid_at"
|
||||||
|
t.index ["user_id"], name: "index_donations_on_user_id"
|
||||||
|
end
|
||||||
|
|
||||||
create_table "invitations", force: :cascade do |t|
|
create_table "invitations", force: :cascade do |t|
|
||||||
t.string "token"
|
t.string "token"
|
||||||
@@ -33,6 +45,8 @@ ActiveRecord::Schema.define(version: 2020_11_30_132533) do
|
|||||||
t.datetime "confirmed_at"
|
t.datetime "confirmed_at"
|
||||||
t.datetime "confirmation_sent_at"
|
t.datetime "confirmation_sent_at"
|
||||||
t.string "unconfirmed_email"
|
t.string "unconfirmed_email"
|
||||||
|
t.text "ln_login_ciphertext"
|
||||||
|
t.text "ln_password_ciphertext"
|
||||||
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
|
||||||
|
|||||||
@@ -2,12 +2,21 @@ namespace :invitations do
|
|||||||
desc "Generate invitations for all users"
|
desc "Generate invitations for all users"
|
||||||
task :generate_for_all_users, [:amount_per_user] => :environment do |t, args|
|
task :generate_for_all_users, [:amount_per_user] => :environment do |t, args|
|
||||||
count = 0
|
count = 0
|
||||||
|
|
||||||
User.all.each do |user|
|
User.all.each do |user|
|
||||||
args[:amount_per_user].to_i.times do
|
amt_to_create = args[:amount_per_user].to_i - user.invitations.unused.count
|
||||||
|
next unless amt_to_create > 0
|
||||||
|
amt_created = 0
|
||||||
|
|
||||||
|
amt_to_create.times do
|
||||||
user.invitations << Invitation.create(user: user)
|
user.invitations << Invitation.create(user: user)
|
||||||
count += 1
|
count += 1
|
||||||
|
amt_created += 1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
puts "Created #{amt_created} new invitations for #{user.address}"
|
||||||
end
|
end
|
||||||
puts "Created #{count} new invitations"
|
|
||||||
|
puts "---\nCreated #{count} new invitations overall"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
8
lib/tasks/lndhub.rake
Normal file
8
lib/tasks/lndhub.rake
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
namespace :lndhub do
|
||||||
|
desc "Generate wallets for all users"
|
||||||
|
task :generate_wallets => :environment do |t, args|
|
||||||
|
User.all.each do |user|
|
||||||
|
CreateLndhubWalletJob.perform_later(user)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -5,6 +5,10 @@
|
|||||||
"@rails/actioncable": "^6.0.0",
|
"@rails/actioncable": "^6.0.0",
|
||||||
"@rails/ujs": "^6.0.0",
|
"@rails/ujs": "^6.0.0",
|
||||||
"@rails/webpacker": "4.3.0",
|
"@rails/webpacker": "4.3.0",
|
||||||
|
"@tailwindcss/forms": "^0.2.1",
|
||||||
|
"autoprefixer": "^9",
|
||||||
|
"postcss": "^7",
|
||||||
|
"tailwindcss": "npm:@tailwindcss/postcss7-compat",
|
||||||
"turbolinks": "^5.2.0"
|
"turbolinks": "^5.2.0"
|
||||||
},
|
},
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
plugins: [
|
plugins: [
|
||||||
|
require("tailwindcss")("./app/javascript/stylesheets/tailwind.config.js"),
|
||||||
require('postcss-import'),
|
require('postcss-import'),
|
||||||
require('postcss-flexbugs-fixes'),
|
require('postcss-flexbugs-fixes'),
|
||||||
require('postcss-preset-env')({
|
require('postcss-preset-env')({
|
||||||
|
|||||||
BIN
public/img/bg-1.jpg
Normal file
BIN
public/img/bg-1.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 291 KiB |
12
public/img/logos/alby.svg
Normal file
12
public/img/logos/alby.svg
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<svg width="1210" height="525" viewBox="0 0 1210 525" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<circle r="36.675" transform="matrix(-1 0 0 1 94.1325 138.143)" fill="black"/>
|
||||||
|
<path d="M88.02 132.641L156.48 201.101" stroke="black" stroke-width="18.3375"/>
|
||||||
|
<circle cx="393.645" cy="138.143" r="36.675" fill="black"/>
|
||||||
|
<path d="M400.369 132.641L331.909 201.101" stroke="black" stroke-width="18.3375"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M104.543 356.993C84.7762 347.583 73.2723 326.451 77.1258 304.901C93.6652 212.406 162.593 143.032 245.111 143.032C327.83 143.032 396.891 212.743 413.216 305.573C417.013 327.163 405.416 348.284 385.581 357.618C343.14 377.591 295.735 388.755 245.723 388.755C195.191 388.755 147.32 377.357 104.543 356.993Z" fill="#FFDF6F"/>
|
||||||
|
<path d="M413.216 305.573L404.186 307.161L413.216 305.573ZM385.581 357.618L381.677 349.322L381.677 349.322L385.581 357.618ZM86.1514 306.515C102.133 217.14 168.195 152.201 245.111 152.201V133.864C156.991 133.864 85.1975 207.672 68.1002 303.287L86.1514 306.515ZM245.111 152.201C322.214 152.201 388.411 217.457 404.186 307.161L422.246 303.985C405.372 208.028 333.446 133.864 245.111 133.864V152.201ZM381.677 349.322C340.432 368.732 294.358 379.586 245.723 379.586V397.924C297.113 397.924 345.848 386.449 389.485 365.914L381.677 349.322ZM245.723 379.586C196.583 379.586 150.056 368.505 108.484 348.714L100.602 365.271C144.584 386.21 193.798 397.924 245.723 397.924V379.586ZM404.186 307.161C407.23 324.47 397.962 341.659 381.677 349.322L389.485 365.914C412.869 354.91 426.796 329.855 422.246 303.985L404.186 307.161ZM68.1002 303.287C63.4826 329.11 77.2985 354.178 100.602 365.271L108.484 348.714C92.254 340.988 83.062 323.792 86.1514 306.515L68.1002 303.287Z" fill="black"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M139.812 333.998C123.901 327.517 114.463 310.589 119.976 294.317C136.978 244.138 186.576 207.825 245.112 207.825C303.648 207.825 353.246 244.138 370.249 294.317C375.762 310.589 366.324 327.517 350.413 333.998C317.915 347.233 282.365 354.525 245.112 354.525C207.859 354.525 172.309 347.233 139.812 333.998Z" fill="black"/>
|
||||||
|
<ellipse cx="287.288" cy="286.065" rx="30.5625" ry="24.45" fill="white"/>
|
||||||
|
<ellipse cx="199.795" cy="286.08" rx="30.5625" ry="24.45" fill="white"/>
|
||||||
|
<path d="M623.9 134.4H682.14L763.42 349.44L706.14 355.2L690.46 309.12H615.26L600.22 352H542.3L623.9 134.4ZM673.82 263.36L652.06 204.16L631.26 263.36H673.82ZM837.64 352H779.4V129.6L837.64 126.08V352ZM899.003 352H869.563V129.6L927.803 126.08V195.52C931.429 194.24 935.163 193.28 939.003 192.64C942.843 191.787 946.789 191.36 950.843 191.36C966.203 191.36 979.856 194.24 991.803 200C1003.75 205.76 1013.14 214.613 1019.96 226.56C1027 238.507 1030.52 253.76 1030.52 272.32C1030.52 290.667 1027 306.027 1019.96 318.4C1013.14 330.773 1003.54 340.053 991.163 346.24C979.003 352.427 964.709 355.52 948.283 355.52C941.456 355.52 934.736 354.453 928.123 352.32C921.723 350.187 915.323 346.773 908.923 342.08L899.003 352ZM941.883 236.16C937.616 236.16 932.923 237.76 927.803 240.96V306.24C933.563 309.227 939.109 310.72 944.443 310.72C951.696 310.72 957.776 307.413 962.683 300.8C967.803 293.973 970.363 284.587 970.363 272.64C970.363 260.693 967.909 251.627 963.003 245.44C958.096 239.253 951.056 236.16 941.883 236.16ZM1088.12 352.64L1089.4 349.12L1033.72 206.4L1087.48 191.04L1119.16 284.48L1148.92 196.8H1206.84L1138.69 370.24C1135.48 378.56 1130.26 386.24 1123 393.28C1115.75 400.32 1107.33 406.293 1097.72 411.2C1088.12 416.107 1078.2 419.733 1067.96 422.08L1048.44 376.96C1053.56 374.827 1058.9 372.48 1064.44 369.92C1070.2 367.36 1075.32 364.587 1079.8 361.6C1084.28 358.613 1087.06 355.627 1088.12 352.64Z" fill="black"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 3.6 KiB |
38
public/img/logos/bluewallet.svg
Normal file
38
public/img/logos/bluewallet.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 28 KiB |
9
spec/factories/donations.rb
Normal file
9
spec/factories/donations.rb
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
FactoryBot.define do
|
||||||
|
factory :donation do
|
||||||
|
user_id { 1 }
|
||||||
|
amount_sats { 100000 }
|
||||||
|
amount_eur { 10 }
|
||||||
|
amount_usd { 13 }
|
||||||
|
public_name { nil }
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -10,6 +10,6 @@ RSpec.describe 'Admin dashboard', type: :feature do
|
|||||||
|
|
||||||
scenario 'View dashboard' do
|
scenario 'View dashboard' do
|
||||||
visit admin_root_path
|
visit admin_root_path
|
||||||
expect(page).to have_content('Admin Panel')
|
expect(page).to have_content('great power')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
9
spec/helpers/application_helper_spec.rb
Normal file
9
spec/helpers/application_helper_spec.rb
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
describe ApplicationHelper do
|
||||||
|
describe "sats_to_btc" do
|
||||||
|
it "converts satoshis to BTC" do
|
||||||
|
expect(helper.sats_to_btc(120000000)).to eq(1.2)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user