59 Commits

Author SHA1 Message Date
Râu Cao
7f5b8c22b7 Make empty donations page prettier
Some checks failed
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is passing
2023-01-10 14:09:15 +08:00
Râu Cao
44ec856091 Make invitation page prettier when it's empty 2023-01-10 14:09:15 +08:00
Râu Cao
6ac9f68566 Add coming-soon note to disabled settings nav items 2023-01-10 14:09:15 +08:00
Râu Cao
69bd9dcf78 Allow to copy invitation URLs via button 2023-01-10 14:09:14 +08:00
Râu Cao
2c6e6caab6 Add more info about project contributions 2023-01-10 14:09:14 +08:00
Râu Cao
a551e8d2ef Add Zeus to recommended wallet apps 2023-01-10 14:09:14 +08:00
Râu Cao
9166d887c4 Replace vanilla JS with new clipboard code 2023-01-10 14:09:14 +08:00
Râu Cao
d3313a202b Add a clipboard controller and wire up the copy button 2023-01-10 14:09:13 +08:00
Râu Cao
029e6b011d WIP Profile settings page
Show the user's user address, and provide a button for copying it to the
clipboard
2023-01-10 14:09:13 +08:00
Râu Cao
f1ef257c97 Rename settings page 2023-01-10 14:09:13 +08:00
Râu Cao
3f1c4f17a7 Add inviter and time to admin invitations list 2023-01-10 14:09:12 +08:00
Râu Cao
8c5852fefe Fix devise not rendering errors as flash messages
https://github.com/heartcombo/devise/issues/5446

closes #63
2023-01-10 14:09:12 +08:00
Râu Cao
317dba0b2d Set a minimum height for content with sidenav 2023-01-10 14:09:12 +08:00
Râu Cao
ab08c9ecf5 Improve button style 2023-01-10 14:09:12 +08:00
Râu Cao
49dd7bd96d Refactor settings routes and menu
Use sub controllers/routes for the sections
2023-01-10 14:09:11 +08:00
Râu Cao
c650b73ff9 Fix web container start when offline 2023-01-10 14:09:11 +08:00
Râu Cao
ea6173c60f Use tabnav component for wallet view 2023-01-10 14:09:11 +08:00
Râu Cao
cd46daa6ba Wording 2023-01-10 14:09:11 +08:00
Râu Cao
df3f91e2d0 Change donations to contrbutions, add tabbed nav
Introduces components for tabbed navigation and adds a tab menu and item
for non-financial contributions to the donations/contributions page.
2023-01-10 14:09:11 +08:00
Râu Cao
7c4106d7a2 Use more appropriate icon in sidenav 2023-01-10 14:09:10 +08:00
Râu Cao
cb79884c53 Improve README, add quick start instructions 2023-01-10 14:09:10 +08:00
Râu Cao
9266fdcfc2 Remove pid dir from git 2023-01-10 14:09:10 +08:00
Râu Cao
b04d822586 Comment encryption option in admin ldap users controller
Refactor to use the service later
2023-01-10 14:09:10 +08:00
Râu Cao
ba7b10fbc8 Add db/user seeds 2023-01-10 14:09:09 +08:00
Râu Cao
3b51670850 Rename ldap seed task to setup 2023-01-10 14:09:09 +08:00
Râu Cao
5b2a9e8c0c Don't start phpldapadmin by default 2023-01-10 14:09:09 +08:00
Râu Cao
ae239d584f Remove obsolete method 2023-01-10 14:09:09 +08:00
Râu Cao
e6d65ee582 Add flag for creating pre-confirmed users 2023-01-10 14:09:08 +08:00
Râu Cao
08b3ec499e Define patch version for Ruby base image
No need to re-download new images for every patch version
2023-01-10 14:09:08 +08:00
Râu Cao
6c17fbbbeb Delete admin role manually on reset 2023-01-10 14:09:08 +08:00
Râu Cao
76877645ce Add missing ACI and role to LDAP seeds 2023-01-10 14:09:08 +08:00
Râu Cao
7d143fabb8 Add note about resetting LDAP server 2023-01-10 14:09:07 +08:00
Râu Cao
efafd38f68 Update Docker image in CI
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
We need a newer node.js version.
2022-12-06 10:19:47 +01:00
Râu Cao
537e1a4774 Update database schema (from Rails upgrade)
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
2022-12-05 13:36:49 +01:00
Râu Cao
c3b9ff8b4a Add LDAP service and seed task 2022-12-05 13:36:33 +01:00
Râu Cao
93d56f79d5 Add config and documentation for running dirsrv with Docker 2022-12-05 13:35:30 +01:00
Râu Cao
1a30345f46 Add byebug for debugging in development 2022-12-05 13:20:47 +01:00
Râu Cao
778babcc05 Add Docker Compose config and 389ds service
Some checks failed
continuous-integration/drone/push Build is failing
refs #2
2022-12-02 19:21:13 +01:00
Râu Cao
fa3b53d3b3 Add Dockerfile for development 2022-12-02 19:19:02 +01:00
Râu Cao
0ca85656b7 Update dependencies 2022-12-02 19:16:56 +01:00
Râu Cao
f7183f68d5 Decrease mininum sats for Lighting invoices
All checks were successful
continuous-integration/drone/push Build is passing
2022-09-16 11:20:29 +02:00
87027b514b Merge pull request 'Update gems' (#72) from chore/update_gems into master
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #72
2022-07-27 13:47:33 +00:00
Râu Cao
16ad621365 Update gems
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
In particular Rails due to security updates:

https://rubyonrails.org/2022/7/12/Rails-Versions-7-0-3-1-6-1-6-1-6-0-5-1-and-5-2-8-1-have-been-released
2022-07-27 15:22:24 +02:00
33e87d6472 Merge pull request 'Add BTCPay service, Kredits API' (#71) from feature/community_funds_balance into master
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #71
Reviewed-by: bumi <bumi@noreply.kosmos.org>
2022-06-12 05:15:05 +00:00
03dc6c7a9c Log unexpected kredits API errors
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2022-05-24 13:42:00 +02:00
897b5bf4ea Specify whole API base URL in config
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2022-05-23 22:49:39 +02:00
caea2d0121 Add kredits API with wallet balance endpoint
All checks were successful
continuous-integration/drone/push Build is passing
2022-05-23 22:47:08 +02:00
e1ff5c479e Initial BTCPay integration 2022-05-23 21:35:03 +02:00
9b3386de30 Update credentials 2022-05-23 18:49:37 +02:00
f2287c1186 Remove separate development credentials files 2022-05-23 18:49:22 +02:00
b29197cf4e Merge pull request 'Various UI improvements' (#70) from feature/ui_improvements into master
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #70
2022-04-28 13:05:10 +00:00
5c48055ac8 Use feather icon for wallet on dashboard
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
fixes #51
2022-04-28 15:01:20 +02:00
5ead3476b7 Normalize overall (font) size
The entire UI is a bit too large. This normalizes the font size and
dimensions, so it doesn't look zoomed in on most screens.
2022-04-28 14:56:03 +02:00
fbf163740a Merge pull request 'Replace the LDAP production config for the new server' (#69) from chore/new_ldap_server into master
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #69
2022-04-28 10:11:01 +00:00
Greg Karékinian
1fc1457e97 Replace the LDAP production config for the new server
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
Refs kosmos/chef#227
2022-04-28 11:54:14 +02:00
1f57bbd9c2 Merge pull request 'Add admin task to list LndHub balances' (#68) from feature/list_lndhub_balances into master
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #68
2022-04-18 08:41:40 +00:00
2a2793ae44 Print sum of user balances
All checks were successful
continuous-integration/drone/pr Build is passing
2022-04-12 16:05:46 +02:00
8773bf5f9e Slow down LndHub auth requests in task 2022-04-12 15:42:44 +02:00
d9970c126a List balances of LndHub accounts 2022-04-12 15:36:45 +02:00
76 changed files with 1474 additions and 761 deletions

View File

@@ -17,7 +17,7 @@ steps:
branch:
- master
- name: rspec
image: guildeducation/rails:2.7.2-12.22.0
image: guildeducation/rails:2.7.2-14.20.0
environment:
RAILS_ENV: test
commands:

View File

@@ -1,3 +1,11 @@
LDAP_HOST=localhost
LDAP_PORT=389
LDAP_ADMIN_PASSWORD=passthebutter
LDAP_SUFFIX="dc=kosmos,dc=org"
EJABBERD_API_URL='https://xmpp.kosmos.org/api'
BTCPAY_API_URL='http://localhost:23001/api/v1'
LNDHUB_API_URL='http://localhost:3023'
LNDHUB_PUBLIC_URL='https://lndhub.kosmos.org'

View File

@@ -1,3 +1,4 @@
EJABBERD_API_URL='https://xmpp.kosmos.org:5443/api'
BTCPAY_API_URL='http://10.1.1.163:23001/api/v1'
LNDHUB_API_URL='http://10.1.1.163:3023'
LNDHUB_PUBLIC_URL='https://lndhub.kosmos.org'

View File

@@ -1,3 +1,4 @@
EJABBERD_API_URL='http://xmpp.example.com/api'
BTCPAY_API_URL='http://btcpay.example.com/api/v1'
LNDHUB_API_URL='http://localhost:3023'
LNDHUB_PUBLIC_URL='https://lndhub.kosmos.org'

22
Dockerfile Normal file
View File

@@ -0,0 +1,22 @@
# syntax=docker/dockerfile:1
FROM ruby:2.7.6
RUN apt-get update -qq && apt-get install -y curl ldap-utils
RUN curl -fsSL https://deb.nodesource.com/setup_lts.x | bash -
RUN apt-get update && apt-get install -y nodejs
WORKDIR /akkounts
COPY Gemfile /akkounts/Gemfile
COPY Gemfile.lock /akkounts/Gemfile.lock
COPY package.json /akkounts/package.json
RUN bundle install
RUN gem install foreman
RUN npm install -g yarn
RUN yarn install
# Add a script to be executed every time the container starts.
COPY docker/entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]
EXPOSE 3000
# Configure the main process to run when running the image
CMD ["bin", "dev"]

View File

@@ -43,13 +43,14 @@ gem "rqrcode", "~> 2.0"
gem 'faraday'
# Background/scheduled jobs
gem 'sidekiq'
gem 'sidekiq', '< 7'
gem 'sidekiq-scheduler'
group :development, :test do
# Use sqlite3 as the database for Active Record
gem 'sqlite3', '~> 1.4'
gem 'rspec-rails'
gem "byebug", "~> 11.1"
end
group :development do
@@ -58,6 +59,7 @@ group :development do
gem 'listen', '~> 3.2'
gem 'letter_opener'
gem 'letter_opener_web'
gem 'faker'
end
group :test do

View File

@@ -1,77 +1,78 @@
GEM
remote: https://rubygems.org/
specs:
actioncable (7.0.2.2)
actionpack (= 7.0.2.2)
activesupport (= 7.0.2.2)
actioncable (7.0.4)
actionpack (= 7.0.4)
activesupport (= 7.0.4)
nio4r (~> 2.0)
websocket-driver (>= 0.6.1)
actionmailbox (7.0.2.2)
actionpack (= 7.0.2.2)
activejob (= 7.0.2.2)
activerecord (= 7.0.2.2)
activestorage (= 7.0.2.2)
activesupport (= 7.0.2.2)
actionmailbox (7.0.4)
actionpack (= 7.0.4)
activejob (= 7.0.4)
activerecord (= 7.0.4)
activestorage (= 7.0.4)
activesupport (= 7.0.4)
mail (>= 2.7.1)
net-imap
net-pop
net-smtp
actionmailer (7.0.2.2)
actionpack (= 7.0.2.2)
actionview (= 7.0.2.2)
activejob (= 7.0.2.2)
activesupport (= 7.0.2.2)
actionmailer (7.0.4)
actionpack (= 7.0.4)
actionview (= 7.0.4)
activejob (= 7.0.4)
activesupport (= 7.0.4)
mail (~> 2.5, >= 2.5.4)
net-imap
net-pop
net-smtp
rails-dom-testing (~> 2.0)
actionpack (7.0.2.2)
actionview (= 7.0.2.2)
activesupport (= 7.0.2.2)
actionpack (7.0.4)
actionview (= 7.0.4)
activesupport (= 7.0.4)
rack (~> 2.0, >= 2.2.0)
rack-test (>= 0.6.3)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.2.0)
actiontext (7.0.2.2)
actionpack (= 7.0.2.2)
activerecord (= 7.0.2.2)
activestorage (= 7.0.2.2)
activesupport (= 7.0.2.2)
actiontext (7.0.4)
actionpack (= 7.0.4)
activerecord (= 7.0.4)
activestorage (= 7.0.4)
activesupport (= 7.0.4)
globalid (>= 0.6.0)
nokogiri (>= 1.8.5)
actionview (7.0.2.2)
activesupport (= 7.0.2.2)
actionview (7.0.4)
activesupport (= 7.0.4)
builder (~> 3.1)
erubi (~> 1.4)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.1, >= 1.2.0)
activejob (7.0.2.2)
activesupport (= 7.0.2.2)
activejob (7.0.4)
activesupport (= 7.0.4)
globalid (>= 0.3.6)
activemodel (7.0.2.2)
activesupport (= 7.0.2.2)
activerecord (7.0.2.2)
activemodel (= 7.0.2.2)
activesupport (= 7.0.2.2)
activestorage (7.0.2.2)
actionpack (= 7.0.2.2)
activejob (= 7.0.2.2)
activerecord (= 7.0.2.2)
activesupport (= 7.0.2.2)
activemodel (7.0.4)
activesupport (= 7.0.4)
activerecord (7.0.4)
activemodel (= 7.0.4)
activesupport (= 7.0.4)
activestorage (7.0.4)
actionpack (= 7.0.4)
activejob (= 7.0.4)
activerecord (= 7.0.4)
activesupport (= 7.0.4)
marcel (~> 1.0)
mini_mime (>= 1.1.0)
activesupport (7.0.2.2)
activesupport (7.0.4)
concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 1.6, < 2)
minitest (>= 5.1)
tzinfo (~> 2.0)
addressable (2.8.0)
public_suffix (>= 2.0.2, < 5.0)
bcrypt (3.1.16)
addressable (2.8.1)
public_suffix (>= 2.0.2, < 6.0)
bcrypt (3.1.18)
bindex (0.8.1)
builder (3.2.4)
capybara (3.36.0)
byebug (11.1.3)
capybara (3.38.0)
addressable
matrix
mini_mime (>= 0.1.3)
@@ -81,12 +82,12 @@ GEM
regexp_parser (>= 1.5, < 3.0)
xpath (~> 3.2)
chunky_png (1.4.0)
concurrent-ruby (1.1.9)
connection_pool (2.2.5)
concurrent-ruby (1.1.10)
connection_pool (2.3.0)
crack (0.4.5)
rexml
crass (1.0.6)
cssbundling-rails (1.0.0)
cssbundling-rails (1.1.1)
railties (>= 6.0.0)
database_cleaner (2.0.1)
database_cleaner-active_record (~> 2.0.0)
@@ -104,44 +105,43 @@ GEM
devise (>= 3.4.1)
net-ldap (>= 0.16.0)
diff-lcs (1.5.0)
digest (3.1.0)
dotenv (2.7.6)
dotenv-rails (2.7.6)
dotenv (= 2.7.6)
dotenv (2.8.1)
dotenv-rails (2.8.1)
dotenv (= 2.8.1)
railties (>= 3.2)
e2mmap (0.1.0)
erubi (1.10.0)
et-orbi (1.2.6)
erubi (1.11.0)
et-orbi (1.2.7)
tzinfo
factory_bot (6.2.0)
factory_bot (6.2.1)
activesupport (>= 5.0.0)
factory_bot_rails (6.2.0)
factory_bot (~> 6.2.0)
railties (>= 5.0.0)
faraday (2.2.0)
faraday-net_http (~> 2.0)
faker (3.0.0)
i18n (>= 1.8.11, < 2)
faraday (2.7.1)
faraday-net_http (>= 2.0, < 3.1)
ruby2_keywords (>= 0.0.4)
faraday-net_http (2.0.1)
faraday-net_http (3.0.2)
ffi (1.15.5)
fugit (1.5.2)
et-orbi (~> 1.1, >= 1.1.8)
fugit (1.7.2)
et-orbi (~> 1, >= 1.2.7)
raabro (~> 1.4)
globalid (1.0.0)
activesupport (>= 5.0)
hashdiff (1.0.1)
i18n (1.9.1)
i18n (1.12.0)
concurrent-ruby (~> 1.0)
importmap-rails (1.0.2)
importmap-rails (1.1.5)
actionpack (>= 6.0.0)
railties (>= 6.0.0)
io-wait (0.2.1)
jbuilder (2.11.5)
actionview (>= 5.0.0)
activesupport (>= 5.0.0)
launchy (2.5.0)
addressable (~> 2.7)
letter_opener (1.7.0)
launchy (~> 2.2)
letter_opener (1.8.1)
launchy (>= 2.2, < 3)
letter_opener_web (2.0.0)
actionmailer (>= 5.2)
letter_opener (~> 1.7)
@@ -150,8 +150,8 @@ GEM
listen (3.7.1)
rb-fsevent (~> 0.10, >= 0.10.3)
rb-inotify (~> 0.9, >= 0.9.10)
lockbox (0.6.8)
loofah (2.14.0)
lockbox (1.1.0)
loofah (2.19.0)
crass (~> 1.0.2)
nokogiri (>= 1.5.9)
mail (2.7.1)
@@ -160,130 +160,129 @@ GEM
matrix (0.4.2)
method_source (1.0.0)
mini_mime (1.1.2)
minitest (5.15.0)
net-imap (0.2.3)
digest
mini_portile2 (2.8.0)
minitest (5.16.3)
net-imap (0.3.1)
net-protocol
strscan
net-ldap (0.17.0)
net-pop (0.1.1)
digest
net-ldap (0.17.1)
net-pop (0.1.2)
net-protocol
net-protocol (0.1.3)
timeout
net-protocol (0.1.2)
io-wait
timeout
net-smtp (0.3.1)
digest
net-smtp (0.3.3)
net-protocol
timeout
nio4r (2.5.8)
nokogiri (1.13.1-x86_64-linux)
nokogiri (1.13.9)
mini_portile2 (~> 2.8.0)
racc (~> 1.4)
nokogiri (1.13.9-x86_64-linux)
racc (~> 1.4)
orm_adapter (0.5.0)
pg (1.2.3)
public_suffix (4.0.6)
puma (4.3.11)
public_suffix (5.0.0)
puma (4.3.12)
nio4r (~> 2.0)
raabro (1.4.0)
racc (1.6.0)
rack (2.2.3)
rack-test (1.1.0)
rack (>= 1.0, < 3)
rails (7.0.2.2)
actioncable (= 7.0.2.2)
actionmailbox (= 7.0.2.2)
actionmailer (= 7.0.2.2)
actionpack (= 7.0.2.2)
actiontext (= 7.0.2.2)
actionview (= 7.0.2.2)
activejob (= 7.0.2.2)
activemodel (= 7.0.2.2)
activerecord (= 7.0.2.2)
activestorage (= 7.0.2.2)
activesupport (= 7.0.2.2)
rack (2.2.4)
rack-test (2.0.2)
rack (>= 1.3)
rails (7.0.4)
actioncable (= 7.0.4)
actionmailbox (= 7.0.4)
actionmailer (= 7.0.4)
actionpack (= 7.0.4)
actiontext (= 7.0.4)
actionview (= 7.0.4)
activejob (= 7.0.4)
activemodel (= 7.0.4)
activerecord (= 7.0.4)
activestorage (= 7.0.4)
activesupport (= 7.0.4)
bundler (>= 1.15.0)
railties (= 7.0.2.2)
railties (= 7.0.4)
rails-dom-testing (2.0.3)
activesupport (>= 4.2.0)
nokogiri (>= 1.6)
rails-html-sanitizer (1.4.2)
rails-html-sanitizer (1.4.3)
loofah (~> 2.3)
railties (7.0.2.2)
actionpack (= 7.0.2.2)
activesupport (= 7.0.2.2)
railties (7.0.4)
actionpack (= 7.0.4)
activesupport (= 7.0.4)
method_source
rake (>= 12.2)
thor (~> 1.0)
zeitwerk (~> 2.5)
rake (13.0.6)
rb-fsevent (0.11.1)
rb-fsevent (0.11.2)
rb-inotify (0.10.1)
ffi (~> 1.0)
redis (4.6.0)
regexp_parser (2.2.1)
redis (5.0.5)
redis-client (>= 0.9.0)
redis-client (0.11.2)
connection_pool
regexp_parser (2.6.1)
responders (3.0.1)
actionpack (>= 5.0)
railties (>= 5.0)
rexml (3.2.5)
rqrcode (2.1.1)
rqrcode (2.1.2)
chunky_png (~> 1.0)
rqrcode_core (~> 1.0)
rqrcode_core (1.2.0)
rspec-core (3.11.0)
rspec-support (~> 3.11.0)
rspec-expectations (3.11.0)
rspec-core (3.12.0)
rspec-support (~> 3.12.0)
rspec-expectations (3.12.0)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.11.0)
rspec-mocks (3.11.0)
rspec-support (~> 3.12.0)
rspec-mocks (3.12.0)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.11.0)
rspec-rails (5.1.0)
actionpack (>= 5.2)
activesupport (>= 5.2)
railties (>= 5.2)
rspec-core (~> 3.10)
rspec-expectations (~> 3.10)
rspec-mocks (~> 3.10)
rspec-support (~> 3.10)
rspec-support (3.11.0)
rspec-support (~> 3.12.0)
rspec-rails (6.0.1)
actionpack (>= 6.1)
activesupport (>= 6.1)
railties (>= 6.1)
rspec-core (~> 3.11)
rspec-expectations (~> 3.11)
rspec-mocks (~> 3.11)
rspec-support (~> 3.11)
rspec-support (3.12.0)
ruby2_keywords (0.0.5)
rufus-scheduler (3.8.1)
rufus-scheduler (3.8.2)
fugit (~> 1.1, >= 1.1.6)
sidekiq (6.4.1)
sidekiq (6.5.5)
connection_pool (>= 2.2.2)
rack (~> 2.0)
redis (>= 4.5.0)
sidekiq-scheduler (4.0.3)
redis (>= 4.2.0)
sidekiq-scheduler (3.1.1)
e2mmap
redis (>= 3, < 5)
rufus-scheduler (~> 3.2)
sidekiq (>= 3)
thwait
sidekiq (>= 4, < 7)
tilt (>= 1.4.0)
sprockets (4.0.2)
sprockets (4.1.1)
concurrent-ruby (~> 1.0)
rack (> 1, < 3)
sprockets-rails (3.4.2)
actionpack (>= 5.2)
activesupport (>= 5.2)
sprockets (>= 3.0.0)
sqlite3 (1.4.2)
stimulus-rails (1.0.2)
sqlite3 (1.5.4)
mini_portile2 (~> 2.8.0)
sqlite3 (1.5.4-x86_64-linux)
stimulus-rails (1.2.1)
railties (>= 6.0.0)
strscan (3.0.1)
thor (1.2.1)
thwait (0.2.0)
e2mmap
tilt (2.0.10)
timeout (0.2.0)
turbo-rails (1.0.1)
tilt (2.0.11)
timeout (0.3.0)
turbo-rails (1.3.2)
actionpack (>= 6.0.0)
activejob (>= 6.0.0)
railties (>= 6.0.0)
tzinfo (2.0.4)
tzinfo (2.0.5)
concurrent-ruby (~> 1.0)
view_component (2.49.0)
view_component (2.78.0)
activesupport (>= 5.0.0, < 8.0)
concurrent-ruby (~> 1.0)
method_source (~> 1.0)
warden (1.2.9)
rack (>= 2.0.9)
@@ -292,7 +291,7 @@ GEM
activemodel (>= 6.0.0)
bindex (>= 0.4.0)
railties (>= 6.0.0)
webmock (3.14.0)
webmock (3.18.1)
addressable (>= 2.8.0)
crack (>= 0.3.2)
hashdiff (>= 0.4.0, < 2.0.0)
@@ -301,12 +300,14 @@ GEM
websocket-extensions (0.1.5)
xpath (3.2.0)
nokogiri (~> 1.8)
zeitwerk (2.5.4)
zeitwerk (2.6.6)
PLATFORMS
ruby
x86_64-linux
DEPENDENCIES
byebug (~> 11.1)
capybara
cssbundling-rails
database_cleaner
@@ -314,6 +315,7 @@ DEPENDENCIES
devise_ldap_authenticatable
dotenv-rails
factory_bot_rails
faker
faraday
importmap-rails
jbuilder (~> 2.7)
@@ -327,7 +329,7 @@ DEPENDENCIES
rails (~> 7.0.2)
rqrcode (~> 2.0)
rspec-rails
sidekiq
sidekiq (< 7)
sidekiq-scheduler
sprockets-rails
sqlite3 (~> 1.4)

View File

@@ -1,2 +1,2 @@
web: bin/rails server -p 3000
web: bin/rails server -b 0.0.0.0 -p 3000
css: yarn build:css --watch

View File

@@ -7,6 +7,27 @@ credentials, invites, donations, etc..
## Development
### Quick Start
The easiest way to get a working development setup is using Docker Compose like
so:
1. Make sure [Docker Compose is installed][1] and Docker is running (included in
Docker Desktop)
2. Uncomment the `web` section in `docker-compose.yml`
3. Run `docker compose up` and wait until 389ds announces its successful start
in the log output
4. `docker-compose exec ldap dsconf localhost backend create --suffix="dc=kosmos,dc=org" --be-name="dev"`
5. `docker compose run web rails ldap:setup`
5. `docker compose run web rails db:setup`
After these steps, you should have a working Rails app with a handful of test
users running on [http://localhost:3000](http://localhost:3000).
Log in with username "admin" and password "admin is admin". All users listed on
[http://localhost:3000/admin/ldap_users](http://localhost:3000/admin/ldap_users)
have the password "user is user".
### Rails app
Installing dependencies:
@@ -31,10 +52,32 @@ Running all specs:
bundle exec rspec
### LDAP server
### Docker (Compose)
TODO make it easy to run a local Kosmos LDAP server for development, without
manual LDIF imports etc. (or provide a staging instance)
There is a working Docker Compose config file, which allows you to spin up both
an app server for Rails as well as a local 389ds (LDAP) server.
By default, `docker-compose up` will only start the LDAP server, listening on
port 389 on your machine. Uncomment other services in `docker-compose.yml` if
you want to use them.
#### LDAP server
After creating the Docker container for the first time (or after deleting it),
you need to run the following command once, in order to create the dirsrv
back-end:
docker-compose exec ldap dsconf localhost backend create --suffix="dc=kosmos,dc=org" --be-name="dev"
Now you can seed the back-end with data using this Rails task:
bundle exec rails ldap:setup
The setup task will first delete any existing entries in the directory tree
("dc=kosmos,dc=org"), and then create our development entries.
Note that all 389ds data is stored in `tmp/389ds`. So if you want to start over
with a fresh installation, delete both that directory as well as the container.
## Documentation
@@ -63,3 +106,5 @@ manual LDIF imports etc. (or provide a staging instance)
## License
[GNU Affero General Public License v3.0](https://choosealicense.com/licenses/agpl-3.0/)
[1]: https://docs.docker.com/compose/install/

View File

@@ -1,4 +1,8 @@
@layer base {
html {
font-size: 14px;
}
body {
@apply leading-none bg-cover bg-fixed;
background-image: linear-gradient(35deg, rgba(255,0,255,0.2) 0, rgba(13,79,153,0.8) 100%), url('/img/bg-1.jpg');

View File

@@ -6,12 +6,16 @@
.btn-md {
@apply btn;
@apply py-2.5 px-5 shadow-md;
@apply py-3 px-6;
}
.btn-sm {
@apply btn;
@apply py-1 px-2 text-sm shadow-sm;
@apply py-1 px-2 text-sm;
}
.btn-icon {
@apply px-3;
}
.btn-gray {
@@ -28,4 +32,8 @@
@apply bg-red-600 hover:bg-red-700 text-white
focus:ring-red-500 focus:ring-opacity-75;
}
input[type=text]:disabled {
@apply text-gray-700;
}
}

View File

@@ -1,5 +1,5 @@
<main class="w-full max-w-6xl mx-auto pb-12 px-4 md:px-6 lg:px-8">
<div class="bg-white rounded-lg shadow px-6 sm:px-12 py-8 sm:py-12">
<div class="md:min-h-[50vh] bg-white rounded-lg shadow px-6 sm:px-12 py-8 sm:py-12">
<%= content %>
</div>
</main>

View File

@@ -1,6 +1,6 @@
<main class="w-full max-w-6xl mx-auto pb-12 px-4 md:px-6 lg:px-8">
<div class="bg-white rounded-lg shadow">
<div class="divide-y divide-gray-200 lg:grid lg:grid-cols-12 lg:divide-y-0 lg:divide-x">
<div class="md:min-h-[50vh] divide-y divide-gray-200 lg:grid lg:grid-cols-12 lg:divide-y-0 lg:divide-x">
<aside class="py-6 sm:py-8 lg:col-span-3">
<nav class="space-y-1">
<%= render partial: @sidenav_partial %>

View File

@@ -0,0 +1,10 @@
<main class="w-full max-w-6xl mx-auto pb-12 px-4 md:px-6 lg:px-8">
<div class="bg-white rounded-lg shadow">
<div class="px-6 sm:px-12 pt-2 sm:pt-4">
<%= render partial: @tabnav_partial %>
</div>
<div class="px-6 sm:px-12 py-8 sm:py-12">
<%= content %>
</div>
</div>
</main>

View File

@@ -0,0 +1,7 @@
# frozen_string_literal: true
class MainWithTabnavComponent < ViewComponent::Base
def initialize(tabnav_partial:)
@tabnav_partial = tabnav_partial
end
end

View File

@@ -1,4 +1,4 @@
<%= link_to @path, class: @link_class do %>
<%= link_to @path, class: @link_class, title: (@disabled ? "Coming soon" : nil) do %>
<%= render partial: "icons/#{@icon}", locals: { custom_class: @icon_class } %>
<span class="truncate"><%= @name %></span>
<% end %>

View File

@@ -0,0 +1,3 @@
<%= link_to @path, class: @link_class do %>
<%= @name %>
<% end %>

View File

@@ -0,0 +1,21 @@
# frozen_string_literal: true
class TabnavLinkComponent < ViewComponent::Base
def initialize(name:, path:, active: false, disabled: false)
@name = name
@path = path
@active = active
@disabled = disabled
@link_class = class_names_link(path)
end
def class_names_link(path)
if @active
"border-indigo-500 text-indigo-600 w-1/2 py-4 px-1 text-center border-b-2"
elsif @disabled
"border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300 w-1/2 py-4 px-1 text-center border-b-2"
else
"border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300 w-1/2 py-4 px-1 text-center border-b-2"
end
end
end

View File

@@ -0,0 +1,7 @@
class AccountController < ApplicationController
before_action :require_user_signed_in
def index
@current_section = :account
end
end

View File

@@ -27,7 +27,7 @@ class Admin::LdapUsersController < Admin::BaseController
def ldap_client
ldap_client ||= Net::LDAP.new host: ldap_config['host'],
port: ldap_config['port'],
encryption: ldap_config['ssl'],
# encryption: ldap_config['ssl'],
auth: {
method: :simple,
username: ldap_config['admin_user'],

View File

@@ -0,0 +1,5 @@
class Api::BaseController < ApplicationController
layout false
end

View File

@@ -0,0 +1,13 @@
class Api::KreditsController < Api::BaseController
def onchain_btc_balance
btcpay = BtcPay.new
balance = btcpay.onchain_wallet_balance
render json: balance
rescue => error
Rails.logger.warn "Failed to fetch kredits BTC wallet balance: #{error.message}"
render json: { error: 'Failed to fetch wallet balance' },
status: 500
end
end

View File

@@ -1,4 +1,4 @@
class DonationsController < ApplicationController
class Contributions::DonationsController < ApplicationController
before_action :require_user_signed_in
# GET /donations

View File

@@ -0,0 +1,8 @@
class Contributions::ProjectsController < ApplicationController
before_action :require_user_signed_in
# GET /contributions
def index
@current_section = :contributions
end
end

View File

@@ -1,7 +1,7 @@
class LnurlpayController < ApplicationController
before_action :find_user_by_address
MIN_SATS = 100
MIN_SATS = 10
MAX_SATS = 1_000_000
MAX_COMMENT_CHARS = 100

View File

@@ -1,7 +0,0 @@
class SecurityController < ApplicationController
before_action :require_user_signed_in
def index
@current_section = :security
end
end

View File

@@ -0,0 +1,13 @@
class Settings::AccountController < SettingsController
def index
end
def reset_password
current_user.send_reset_password_instructions
sign_out current_user
msg = "We have sent you an email with a link to reset your password."
redirect_to check_your_email_path, notice: msg
end
end

View File

@@ -0,0 +1,11 @@
class Settings::ProfileController < SettingsController
def index
@user = current_user
end
def update
end
end

View File

@@ -1,13 +1,13 @@
class SettingsController < ApplicationController
before_action :require_user_signed_in
before_action :set_current_section
def index
end
def reset_password
current_user.send_reset_password_instructions
sign_out current_user
msg = "We have sent you an email with a link to reset your password."
redirect_to check_your_email_path, notice: msg
private
def set_current_section
@current_section = :settings
end
end

View File

@@ -0,0 +1,18 @@
class TurboController < ApplicationController
class Responder < ActionController::Responder
def to_turbo_stream
controller.render(options.merge(formats: :html))
rescue ActionView::MissingTemplate => error
if get?
raise error
elsif has_errors? && default_action
render rendering_options.merge(formats: :html, status: :unprocessable_entity)
else
redirect_to navigation_location
end
end
end
self.responder = Responder
respond_to :html, :turbo_stream
end

View File

@@ -0,0 +1,18 @@
class Users::DeviseController < ApplicationController
class Responder < ActionController::Responder
def to_turbo_stream
controller.render(options.merge(formats: :html))
rescue ActionView::MissingTemplate => error
if get?
raise error
elsif has_errors? && default_action
render rendering_options.merge(formats: :html, status: :unprocessable_entity)
else
redirect_to navigation_location
end
end
end
self.responder = Responder
respond_to :html, :turbo_stream
end

View File

@@ -0,0 +1,16 @@
import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
static targets = ["source", "trigger"]
copy (event) {
event.preventDefault();
navigator.clipboard.writeText(this.sourceTarget.value);
this.triggerTarget.querySelector('.content-initial').classList.add('hidden');
this.triggerTarget.querySelector('.content-active').classList.remove('hidden');
setTimeout(() => {
this.triggerTarget.querySelector('.content-initial').classList.remove('hidden');
this.triggerTarget.querySelector('.content-active').classList.add('hidden');
}, 2000)
}
}

View File

@@ -18,7 +18,7 @@ class CreateLdapUserJob < ApplicationJob
def ldap_client
ldap_client ||= Net::LDAP.new host: ldap_config['host'],
port: ldap_config['port'],
encryption: ldap_config['ssl'],
# encryption: ldap_config['ssl'],
auth: {
method: :simple,
username: ldap_config['admin_user'],

32
app/services/btc_pay.rb Normal file
View File

@@ -0,0 +1,32 @@
#
# API Docs: https://docs.btcpayserver.org/API/Greenfield/v1/
#
class BtcPay
def initialize
@base_url = ENV["BTCPAY_API_URL"]
@store_id = Rails.application.credentials.btcpay[:store_id]
@auth_token = Rails.application.credentials.btcpay[:auth_token]
end
def onchain_wallet_balance
res = get "stores/#{@store_id}/payment-methods/onchain/BTC/wallet"
{
balance: res["balance"].to_f,
unconfirmed_balance: res["unconfirmedBalance"].to_f,
confirmed_balance: res["confirmedBalance"].to_f
}
end
private
def get(endpoint)
res = Faraday.get("#{@base_url}/#{endpoint}", {}, {
"Content-Type" => "application/json",
"Accept" => "application/json",
"Authorization" => "token #{@auth_token}"
})
JSON.parse(res.body)
end
end

View File

@@ -5,6 +5,7 @@ class CreateAccount < ApplicationService
@email = args[:email]
@password = args[:password]
@invitation = args[:invitation]
@confirmed = args[:confirmed]
end
def call
@@ -26,7 +27,8 @@ class CreateAccount < ApplicationService
ou: @domain,
email: @email,
password: @password,
password_confirmation: @password
password_confirmation: @password,
confirmed_at: @confirmed ? DateTime.now : nil
)
end
@@ -35,6 +37,7 @@ class CreateAccount < ApplicationService
end
# TODO move to confirmation
# (and/or add email_confirmed to entry and use in login filter)
def add_ldap_document
hashed_pw = Devise.ldap_auth_password_builder.call(@password)
CreateLdapUserJob.perform_later(@username, @domain, @email, hashed_pw)
@@ -47,20 +50,8 @@ class CreateAccount < ApplicationService
end
def create_lndhub_wallet(user)
#TODO enable in development when we have a local lndhub (mock?) API
return if Rails.env.development?
CreateLndhubWalletJob.perform_later(user)
end
def exchange_xmpp_contacts_between_inviter_and_invitee
ejabberd = EjabberdApiClient.new
EjabberdApiClient.add_roster_item({
"localuser": @username,
"localhost": @domain,
"user": @inviter.cn,
"host": @inviter.ou,
"nick": @username,
"group": "Friends",
"subs": "both"
})
end
end

View File

@@ -0,0 +1,135 @@
class LdapService < ApplicationService
def initialize
@suffix = ENV["LDAP_SUFFIX"] || "dc=kosmos,dc=org"
end
def add_entry(dn, attrs, interactive=false)
puts "Adding entry: #{dn}" if interactive
res = ldap_client.add dn: dn, attributes: attrs
puts res.inspect if interactive && !res
res
end
def add_attribute(dn, attr, value)
ldap_client.add_attribute dn, attr, value
end
def delete_entry(dn, interactive=false)
puts "Deleting entry: #{dn}" if interactive
res = ldap_client.delete dn: dn
puts res.inspect if interactive && !res
res
end
def delete_all_entries!
if Rails.env.production?
raise "Mass deletion of entries not allowed in production"
end
filter = Net::LDAP::Filter.eq("objectClass", "*")
entries = ldap_client.search(base: @suffix, filter: filter, attributes: %w{dn})
entries.sort_by!{ |e| e.dn.length }.reverse!
entries.each do |e|
delete_entry e.dn, true
end
end
def fetch_users(args={})
if args[:ou]
treebase = "ou=#{args[:ou]},cn=users,#{@suffix}"
else
treebase = ldap_config["base"]
end
attributes = %w{dn cn uid mail admin}
filter = Net::LDAP::Filter.eq("uid", "*")
entries = ldap_client.search(base: treebase, filter: filter, attributes: attributes)
entries.sort_by! { |e| e.cn[0] }
entries = entries.collect do |e|
{
uid: e.uid.first,
mail: e.try(:mail) ? e.mail.first : nil,
admin: e.try(:admin) ? 'admin' : nil
# password: e.userpassword.first
}
end
end
def fetch_organizations
attributes = %w{dn ou description}
filter = Net::LDAP::Filter.eq("objectClass", "organizationalUnit")
# filter = Net::LDAP::Filter.eq("objectClass", "*")
treebase = "cn=users,#{@suffix}"
entries = ldap_client.search(base: treebase, filter: filter, attributes: attributes)
entries.sort_by! { |e| e.ou[0] }
entries = entries.collect do |e|
{
dn: e.dn,
ou: e.ou.first,
description: e.try(:description) ? e.description.first : nil,
}
end
end
def add_organization(ou, description, interactive=false)
dn = "ou=#{ou},cn=users,#{@suffix}"
aci = <<-EOS
(target="ldap:///cn=*,ou=#{ou},cn=users,#{@suffix}")(targetattr="cn || sn || uid || mail || userPassword || nsRole || objectClass") (version 3.0; acl "service-#{ou.gsub(".", "-")}-read-search"; allow (read,search) userdn="ldap:///uid=service,ou=#{ou},cn=applications,#{@suffix}";)
EOS
attrs = {
objectClass: ["top", "organizationalUnit"],
description: description,
ou: ou,
aci: aci
}
add_entry dn, attrs, interactive
end
def reset_directory!
if Rails.env.production?
raise "Resetting the directory not allowed in production"
end
delete_all_entries!
user_read_aci = <<-EOS
(target="ldap:///#{@suffix}")(targetattr="*") (version 3.0; acl "user-read-search-own-attributes"; allow (read,search) userdn="ldap:///self";)
EOS
add_entry @suffix, {
dc: "kosmos", objectClass: ["top", "domain"], aci: user_read_aci
}, true
add_entry "cn=users,#{@suffix}", {
cn: "users", objectClass: ["top", "organizationalRole"]
}, true
end
private
def ldap_client
ldap_client ||= Net::LDAP.new host: ldap_config['host'],
port: ldap_config['port'],
# TODO has to be :simple_tls if TLS is enabled
# 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

View File

@@ -17,6 +17,7 @@
<tr>
<th>Token</th>
<th>Accepted</th>
<th>Inviter</th>
<th>Invited user</th>
</tr>
</thead>
@@ -24,7 +25,8 @@
<% @invitations_used.each do |invitation| %>
<tr>
<td class="overflow-ellipsis font-mono"><%= invitation.token %></td>
<td><%= invitation.used_at.strftime("%Y-%m-%d") %></td>
<td><%= invitation.used_at.strftime("%Y-%m-%d (%H:%M UTC)") %></td>
<td><%= invitation.user.address %></td>
<td><%= User.find(invitation.invited_user_id).address %></td>
</tr>
<% end %>

View File

@@ -1,12 +1,12 @@
<%= render HeaderComponent.new(title: "Donations") %>
<%= render HeaderComponent.new(title: "Contributions") %>
<%= render MainSimpleComponent.new do %>
<%= render MainWithTabnavComponent.new(tabnav_partial: "shared/tabnav_contributions") do %>
<section>
<p class="mb-12">
Your financial contributions to the development and upkeep of Kosmos
software and services.
</p>
<% if @donations.any? %>
<p class="mb-12">
Your financial contributions to the development and upkeep of Kosmos
software and services.
</p>
<ul class="list-none">
<% @donations.each do |donation| %>
<li class="mb-8 grid gap-y-2 gap-x-8 grid-cols-2 items-center">
@@ -33,9 +33,19 @@
<% end %>
</ul>
<% else %>
<p class="text-gray-500">
No donations to show.
</p>
<div class="text-center">
<p class="mt-8 mb-12 inline-flex align-center items-center">
<%= image_tag("/img/illustrations/undraw_savings_re_eq4w.svg", class: 'h-48') %>
</p>
<h3>
No donations yet
</h3>
<p class="text-gray-500">
The donation process is not automated yet.<br>Please
<a href="https://wiki.kosmos.org/Main_Page#Community_.2F_Getting_in_touch_.2F_Getting_involved" class="ks-text-link" target="_blank">contact us</a>
if you'd like to contribute this way right now.
</p>
</div>
<% end %>
</section>
<% end %>

View File

@@ -0,0 +1,49 @@
<%= render HeaderComponent.new(title: "Contributions") %>
<%= render MainWithTabnavComponent.new(tabnav_partial: "shared/tabnav_contributions") do %>
<section>
<p class="mb-8">
Project contributions are how we develop and run all Kosmos software and
services. Everything we create and provide is free and open-source
software, even the page you're looking at right now!
</p>
<h3>Start contributing</h3>
<p>
Check out our
<a href="https://kosmos.org/projects/" target="_blank" class="ks-text-link">projects page</a>
for some (but not all) potential places that can use your help.
</p>
<p>
There's something to do for everyone, especially non-programmers! For
example, we need more help with graphics, UI/UX design, and
content/copywriting. We also need moderators for social media. And beta
testers for our software. The list doesn't end there.
</p>
<p>
A good way to get started is to join one of our
<a href="https://community.kosmos.org/t/kosmos-weekly-call/36" target="_blank" class="ks-text-link">weekly calls</a>
and introduce yourself. Alternatively, you can also ping us on any other
medium, or even just grab an open issue on
<a href="https://github.com/67P/" target="_blank" class="ks-text-link">GitHub</a>
or our
<a href="https://gitea.kosmos.org/kosmos/" target="_blank" class="ks-text-link">Gitea</a>
and dive right in (be sure to comment first, to prevent double efforts).
</p>
<p class="mb-8">
Last but not least, if you want to help by proposing new features or
services, head over to the
<a href="https://community.kosmos.org/" target="_blank" class="ks-text-link">community forums</a>,
where you can do just that.
</p>
<h3>Open Source Grants</h3>
<p>
Money coming in from financial contributions is first used to pay for our
bills. Additional funds are being paid out directly to our contributors,
including you, according to their rough share of contributions.
</p>
<p>
We have run two 6-month trials so far, with the next trial period
starting sometime in Q1 2023. Watch your email for notifications about it!
</p>
</section>
<% end %>

View File

@@ -25,7 +25,7 @@
</div>
<div>
<h3 class="mb-3.5">
<span class="text-yellow-500">🗲</span>
<%= render partial: "icons/zap", locals: { custom_class: "text-amber-500 h-4 w-4 inline" } %>
<%= link_to "Lightning Wallet", wallet_path, class: "ks-text-link" %>
</h3>
<p class="text-gray-500">

View File

@@ -2,7 +2,7 @@
<div id="error_explanation">
<ul>
<% resource.errors.full_messages.each do |message| %>
<li><%= message %></li>
<li class="text-red-600"><%= message %></li>
<% end %>
</ul>
</div>

View File

@@ -1 +0,0 @@
json.array! @donations, partial: "donations/donation", as: :donation

View File

@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-check"><polyline points="20 6 9 17 4 12"></polyline></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-check <%= custom_class %>"><polyline points="20 6 9 17 4 12"></polyline></svg>

Before

Width:  |  Height:  |  Size: 262 B

After

Width:  |  Height:  |  Size: 283 B

View File

@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-copy"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-copy <%= custom_class %>"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path></svg>

Before

Width:  |  Height:  |  Size: 351 B

After

Width:  |  Height:  |  Size: 372 B

View File

@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-user"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path><circle cx="12" cy="7" r="4"></circle></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-user <%= custom_class %>"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path><circle cx="12" cy="7" r="4"></circle></svg>

Before

Width:  |  Height:  |  Size: 313 B

After

Width:  |  Height:  |  Size: 334 B

View File

@@ -3,28 +3,41 @@
<%= render MainSimpleComponent.new do %>
<section>
<% if @invitations_unused.any? %>
<p>
<p class="mb-8">
Invite your friends to a Kosmos account by sharing an invitation URL with them:
</p>
<table class="mt-12">
<thead>
<tr>
<th>URL</th>
</tr>
</thead>
<tbody>
<% @invitations_unused.each do |invitation| %>
<tr>
<td class="font-mono"><%= invitation_url(invitation.token) %></td>
</tr>
<% end %>
</tbody>
</table>
<ul>
<% @invitations_unused.each do |invitation| %>
<li class="font-mono mb-1 flex gap-1 md:block"
data-controller="clipboard">
<input type="text" disabled class="md:w-3/4 flex-1"
value="<%= invitation_url(invitation.token) %>"
data-clipboard-target="source" />
<button id="copy-user-address" class="btn-md btn-icon btn-blue flex-none w-auto"
data-clipboard-target="trigger" data-action="clipboard#copy"
title="Copy to clipboard">
<span class="content-initial">
<%= render partial: "icons/copy", locals: { custom_class: "text-white h-4 w-4 inline" } %>
</span>
<span class="content-active hidden">
<%= render partial: "icons/check", locals: { custom_class: "text-white h-4 w-4 inline" } %>
</span>
</button>
</li>
<% end %>
</ul>
<% else %>
<p>
You do not have any invitations to give away yet. All good
things come in time.
</p>
<div class="text-center">
<p class="my-12 inline-flex align-center items-center">
<%= image_tag("/img/illustrations/undraw_loading_re_5axr.svg", class: 'h-48') %>
</p>
<h3>
No invitations available yet
</h3>
<p class="text-gray-500">
We will notify you, as soon as you can invite others.
</p>
</div>
<% end %>
</section>

View File

@@ -3,7 +3,7 @@
<%= render MainWithSidenavComponent.new(sidenav_partial: 'shared/sidenav_settings') do %>
<section>
<h3>Password</h3>
<p class="mb-12">Use the following button to request an email with a password reset link:</p>
<p class="mb-8">Use the following button to request an email with a password reset link:</p>
<p>
<%= form_with(url: settings_reset_password_path, method: :post) do %>
<%= submit_tag("Send me a password reset link", class: 'btn-md btn-gray w-full sm:w-auto') %>

View File

@@ -1 +0,0 @@
<h2>Settings</h2>

View File

@@ -0,0 +1,34 @@
<%= render HeaderComponent.new(title: "Settings") %>
<%= render MainWithSidenavComponent.new(sidenav_partial: 'shared/sidenav_settings') do %>
<section>
<h3>Profile</h3>
<p class="mb-1">
<%= label :user_address, 'User address', class: 'font-bold' %>
</p>
<p data-controller="clipboard" class="flex gap-1 mb-2 sm:block">
<input type="text" id="user_address" class="flex-1 sm:w-3/5"
value=<%= @user.address %> disabled="disabled"
data-clipboard-target="source" />
<button id="copy-user-address" class="btn-md btn-icon btn-blue flex-none w-auto"
data-clipboard-target="trigger" data-action="clipboard#copy"
title="Copy to clipboard">
<span class="content-initial">
<%= render partial: "icons/copy", locals: { custom_class: "text-white h-4 w-4 inline" } %>
</span>
<span class="content-active hidden">
<%= render partial: "icons/check", locals: { custom_class: "text-white h-4 w-4 inline" } %>
</span>
</button>
</p>
<p class="text-sm text-gray-500">
Your user address for Chat and Lightning Network.
</p>
<%# <%= form_for(@user, as: "profile", url: settings_profile_path) do |f| %>
<%# <p class="mt-8">
<%# <%= f.submit "Save changes", class: 'btn-md btn-blue w-full sm:w-auto' %>
<%# </p>
<%# <% end %>
</section>
<% end %>

View File

@@ -1,10 +1,10 @@
<%= link_to "Services", root_path,
class: main_nav_class(@current_section, :dashboard) %>
<%= link_to "Contributions", contributions_donations_path,
class: main_nav_class(@current_section, :contributions) %>
<%= link_to "Invitations", invitations_path,
class: main_nav_class(@current_section, :invitations) %>
<%= link_to "Donations", donations_path,
class: main_nav_class(@current_section, :contributions) %>
<%= link_to "Wallet", wallet_path,
class: main_nav_class(@current_section, :wallet) %>
<%= link_to "Settings", security_path,
class: main_nav_class(@current_section, :security) %>
<%= link_to "Settings", settings_profile_path,
class: main_nav_class(@current_section, :settings) %>

View File

@@ -1,9 +1,10 @@
<%= render SidenavLinkComponent.new(
name: "Account", path: "#", icon: "settings", disabled: true
name: "Profile", path: settings_profile_path, icon: "user",
active: current_page?(settings_profile_path)
) %>
<%= render SidenavLinkComponent.new(
name: "Password", path: security_path, icon: "key",
active: current_page?(security_path)
name: "Account", path: settings_account_path, icon: "key",
active: current_page?(settings_account_path)
) %>
<%= render SidenavLinkComponent.new(
name: "Security", path: "#", icon: "shield", disabled: true

View File

@@ -0,0 +1,12 @@
<div class="border-b border-gray-200">
<nav class="-mb-px flex" aria-label="Tabs">
<%= render TabnavLinkComponent.new(
name: "Donations", path: contributions_donations_path,
active: current_page?(contributions_donations_path)
) %>
<%= render TabnavLinkComponent.new(
name: "Projects", path: contributions_projects_path,
active: current_page?(contributions_projects_path)
) %>
</nav>
</div>

View File

@@ -0,0 +1,14 @@
<section>
<div class="border-b border-gray-200">
<nav class="-mb-px flex" aria-label="Tabs">
<%= render TabnavLinkComponent.new(
name: "Info", path: wallet_path,
active: current_page?(wallet_path)
) %>
<%= render TabnavLinkComponent.new(
name: "Transactions", path: wallet_transactions_path,
active: current_page?(wallet_transactions_path)
) %>
</nav>
</div>
</section>

View File

@@ -3,14 +3,7 @@
<%= render MainSimpleComponent.new do %>
<%= render WalletSummaryComponent.new(balance: @balance) %>
<section>
<div class="border-b border-gray-200">
<nav class="-mb-px flex" aria-label="Tabs">
<%= link_to "Info", wallet_path, class: "border-indigo-500 text-indigo-600 w-1/2 py-4 px-1 text-center border-b-2", "aria-current": "page" %>
<%= link_to "Transactions", wallet_transactions_path, class: "border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300 w-1/2 py-4 px-1 text-center border-b-2" %>
</nav>
</div>
</section>
<%= render partial: "shared/tabnav_wallet" %>
<section>
<h3>Lightning Address</h3>
@@ -33,8 +26,14 @@
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 w-full sm:w-auto">Copy setup code/URL</button>
<p data-controller="clipboard" class="my-6 text-center md:text-left">
<input type="text" disabled class="hidden" aria-hidden=true
value="<%= @wallet_url%>" data-clipboard-target="source" />
<button id="copy-setup-code" class="btn-md btn-blue w-full sm:w-auto"
data-action="clipboard#copy" data-clipboard-target="trigger">
<span class="content-initial">Copy setup code/URL</span>
<span class="content-active hidden">Copied ✔</span>
</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 w-full sm:w-auto">Show setup QR code</button>
<button id="hide-setup-code" class="hidden btn-md btn-blue w-full sm:w-auto">Hide setup QR code</button>
@@ -48,19 +47,6 @@
<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">
@@ -74,6 +60,32 @@
Choose "LNDHub (Bluewallet)" in the connect dialog and paste the setup
URL in the "LNDHub Export URI" field.
</p>
<h4 class="md:col-span-3 mt-4 mb:mt-0">
<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 mt-4 mb:mt-0">
<a href="https://zeusln.app" class="ks-text-link text-xl"
title="Zeus" target="_blank">
<%= image_tag("/img/logos/zeus.svg", class: 'h-16') %>
</a>
</h4>
<p class="md:col-span-4 mb-0 text-gray-500">
Android / iOS
</p>
<p class="md:col-span-5 mb-0">
On first launch, tap "Scan node config" and scan the setup QR code.
Add your Lightning address as a nickname, then "Save node config".
</p>
</div>
</section>
<% end %>
@@ -82,7 +94,6 @@
(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) {
@@ -100,16 +111,5 @@
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>

View File

@@ -3,14 +3,7 @@
<%= render MainSimpleComponent.new do %>
<%= render WalletSummaryComponent.new(balance: @balance) %>
<section>
<div class="border-b border-gray-200">
<nav class="-mb-px flex" aria-label="Tabs">
<%= link_to "Info", wallet_path, class: "border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300 w-1/2 py-4 px-1 text-center border-b-2" %>
<%= link_to "Transactions", wallet_transactions_path, class: "border-indigo-500 text-indigo-600 w-1/2 py-4 px-1 text-center border-b-2", "aria-current": "page" %>
</nav>
</div>
</section>
<%= render partial: "shared/tabnav_wallet" %>
<section>
<h3 class="hidden">Transactions</h3>

View File

@@ -1 +1 @@
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==
oDf4FcihlyfHQuf9SUwfr+UVA0kXoECtHV3vEwtBp2TktCWkWz7SPbSZ2cLT0+EknOKhuI9xutrs311YDU2EzqZba4fZ0+a6/ohVH4jUbk1XfiHZWBp4zh+9TZ5m5Tp2RxcXpdcWPY38mP4zHWFzobTOR/brLjuemozvh8MiBSPY4NN5NR6rbFo87auK6fvYO8ik/1Qwf6pQMoVDjcTh2983po1RU/gSevUmaYsmTTHcQ5T9O9wMIBc101iZyKhkAZG46a6eNYok8yqRm18AHWr+De2j7LlBaqYSz/BZA385RWhhqoeONArwyo3Az30Bv74VttUSJAPurkRg2wDF9t8A+cvf8CeYkZ6u398JLSJbZZ6YdQS5T4IcrnONRXtp3d/m+yw+XEzpluK3MvFbV02AhZk/xzkGK6xonhaTSh1ek9hXoYrUTBzu8HBzXwKjJMnvrAodldu++/rMwLsgVmFHqXC3dydVogatLev8z6ziuGkmeMAR1d9kzGBHM3FWgsWLD7j0Ug7MTMyNWioI3r6J2QTnxkyJGh3pKBlq8Fb/Q0ypERxOfSZVQQh2gB79RMEDIemdCmN5mCU3ojsxqAAip4v9C1BZMWPtom1sHLYQSd9Bh6i0nncrNEtZXcxe5Z8JCWQolHvvfoIF2rfJh2oXYLxNx5n/1fzaoSqBdLBgvsAMA0ZWfV1wa/5V/DCa9vaJjumzHYKcfCCYbVz9PjN9OUSfwrE3nWZu8Y0awsNgmeRQwI10j2+oYYSBp040I05Vj5FM7nCgHLpdupctwaH2/VlIq83OVI6VlbzxYEay7+R5ANmpiJ+vC466DzLv8LAeFOqh/XeNeOITUm9EzhGSgf98yuDc7vsi3gmc2g7atgQ3gpSje6vEfhigm3ukydaGfA==--IYQBMD+Tar1g+srm--01E5mujYFWvcpT8Qd9HxiA==

View File

@@ -1 +0,0 @@
2a8a17892dd9f41ea50c61310c83240b

View File

@@ -1 +0,0 @@
zdpQvlbfqXzaExLzw2LGZkXj97HH42jWZFSTlINpf/HlFr6NubPxLkVxeGsittJj5rm8yL+V21zPxp30Z7Q7R45qyCKFtevtVeqb+u1nZ/FsVfkwb/7wDW9scurgXw==--SB6C0aaNf8qPdteG--9hP+6tpsMnyiAVVvAIq+7w==

View File

@@ -1 +1 @@
xgPOFd8315z7lFtTR5/nD6WDBM2M6Grt/pmkCPdaqlw0WAmFKzbiRGFsXoUQ02JNzvT1/FVtBSsAcyK1Pdr1QQztlWC+/ywaflloMBS4//D8IEXvEgCK6uff5gcf1A==--WbFrw9advCJ4mqsK--HTVHZqO0ddG1toFpY0KKgQ==
IIjYiPSeZeMFhH8i8v8akXN4JrtGU+OsMQ8GAao/gVdesggriCBAQ8z+Vd0cmTf1SKYeT3OQDgygEekupr325P4eD9fZ+yi56EA/UMXQXMDVZAvZw7iwvKaOXpqisbWdJnomr1GXrHyR415Ce/Fxft3fgXDwMHJW2u+dDJgpE09uORnB9GXycFwHQmoIdXo=--iQ/Vcm0VcwHgUkwQ--tKHQW/45gM/s/NplqGPaxw==

View File

@@ -63,4 +63,7 @@ Rails.application.configure do
config.action_mailer.raise_delivery_errors = false
# Base URL to be used by email template link helpers
config.action_mailer.default_url_options = { host: "localhost:3000", protocol: "http" }
# Allow requests from any IP
config.web_console.whiny_requests = false
end

View File

@@ -3,6 +3,21 @@ require 'digest'
require 'securerandom'
# frozen_string_literal: true
# Create custom failure for turbo
class TurboFailureApp < Devise::FailureApp
def respond
if request_format == :turbo_stream
redirect
else
super
end
end
def skip_format?
%w(html turbo_stream */*).include? request_format.to_s
end
end
# Assuming you have not yet modified this file, each configuration option below
# is set to its default value. Note that some are commented out while others
# are not: uncommented lines are intended to protect your configuration from
@@ -44,6 +59,7 @@ Devise.setup do |config|
# ==> Controller configuration
# Configure the parent class to the devise controllers.
# config.parent_controller = 'DeviseController'
config.parent_controller = 'TurboController'
# ==> Mailer Configuration
# Configure the e-mail address which will be shown in Devise::Mailer,
@@ -289,6 +305,7 @@ Devise.setup do |config|
#
# The "*/*" below is required to match Internet Explorer requests.
# config.navigational_formats = ['*/*', :html]
config.navigational_formats = ['*/*', :html, :turbo_stream]
# The default HTTP method used to sign out a resource. Default is :delete.
config.sign_out_via = :get
@@ -302,10 +319,11 @@ Devise.setup do |config|
# If you want to use other strategies, that are not supported by Devise, or
# change the failure app, you can configure them inside the config.warden block.
#
# config.warden do |manager|
# manager.intercept_401 = false
# manager.default_strategies(scope: :user).unshift :some_external_strategy
# end
config.warden do |manager|
manager.failure_app = TurboFailureApp
# manager.intercept_401 = false
# manager.default_strategies(scope: :user).unshift :some_external_strategy
end
# ==> Mountable engine configurations
# When using Devise inside an engine, let's call it `MyEngine`, and this engine

View File

@@ -26,13 +26,13 @@ authorizations: &AUTHORIZATIONS
## Environment
development:
host: 192.168.56.5
port: 389
host: <%= ENV["LDAP_HOST"] || "localhost" %>
port: <%= ENV["LDAP_PORT"] || "389" %>
attribute: cn
base: ou=kosmos.org,cn=users,dc=kosmos,dc=org
base: <%= ENV["LDAP_BASE"] || "ou=kosmos.org,cn=users,dc=kosmos,dc=org" %>
admin_user: "cn=Directory Manager"
admin_password: localpass
# ssl: false
admin_password: <%= ENV["LDAP_ADMIN_PASSWORD"] %>
ssl: <%= ENV["LDAP_USE_TLS"] || "false" %>
# <<: *AUTHORIZATIONS
test:
@@ -46,11 +46,11 @@ test:
# <<: *AUTHORIZATIONS
production:
host: ldap.kosmos.org
port: 636
host: ldap.kosmos.local
port: 389
attribute: cn
base: ou=kosmos.org,cn=users,dc=kosmos,dc=org
admin_user: <%= Rails.application.credentials.ldap[:username] rescue nil %>
admin_password: <%= Rails.application.credentials.ldap[:password] rescue nil %>
ssl: simple_tls
# ssl: false
# <<: *AUTHORIZATIONS

View File

@@ -10,21 +10,31 @@ Rails.application.routes.draw do
match 'signup/:step', to: 'signup#steps', as: :signup_steps, via: [:get, :post]
post 'signup_validate', to: 'signup#validate'
get 'settings', to: 'settings#index'
post 'settings_reset_password', to: 'settings#reset_password'
namespace :settings do
get 'profile', to: 'profile#index'
post 'profile', to: 'profile#update'
get 'account', to: 'account#index'
post 'reset_password', to: 'account#reset_password'
end
get 'security', to: 'security#index'
namespace :contributions do
root to: 'donations#index'
get 'projects', to: 'projects#index'
resources :donations, only: ['index']
end
resources :invitations, only: ['index', 'show', 'create', 'destroy']
resources :donations
get 'wallet', to: 'wallet#index'
get 'wallet/transactions', to: 'wallet#transactions'
get 'lnurlpay/:address', to: 'lnurlpay#index', constraints: { address: /[^\/]+/}
get 'lnurlpay/:address/invoice', to: 'lnurlpay#invoice', constraints: { address: /[^\/]+/}
namespace :api do
get 'kredits/onchain_btc_balance', to: 'kredits#onchain_btc_balance'
end
namespace :admin do
root to: 'dashboard#index'
get 'invitations', to: 'invitations#index'

View File

@@ -2,25 +2,24 @@
# of editing this file, please use the migrations feature of Active Record to
# incrementally modify your database, and then regenerate this schema definition.
#
# This file is the source Rails uses to define your schema when running `rails
# db:schema:load`. When creating a new database, `rails db:schema:load` tends to
# This file is the source Rails uses to define your schema when running `bin/rails
# db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to
# be faster and is potentially less error prone than running all of your
# migrations from scratch. Old migrations may fail to apply correctly if those
# migrations use external dependencies or application code.
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 2021_11_20_010540) do
ActiveRecord::Schema[7.0].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.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.datetime "paid_at", precision: nil
t.index ["user_id"], name: "index_donations_on_user_id"
end
@@ -28,22 +27,24 @@ ActiveRecord::Schema.define(version: 2021_11_20_010540) do
t.string "token"
t.integer "user_id"
t.integer "invited_user_id"
t.datetime "used_at"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.datetime "used_at", precision: nil
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["invited_user_id"], name: "index_invitations_on_invited_user_id"
t.index ["user_id"], name: "index_invitations_on_user_id"
end
create_table "users", force: :cascade do |t|
t.string "cn"
t.string "ou"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "email", default: "", null: false
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "reset_password_sent_at", precision: nil
t.string "confirmation_token"
t.datetime "confirmed_at"
t.datetime "confirmation_sent_at"
t.datetime "confirmed_at", precision: nil
t.datetime "confirmation_sent_at", precision: nil
t.string "unconfirmed_email"
t.text "ln_login_ciphertext"
t.text "ln_password_ciphertext"

View File

@@ -1,7 +1,22 @@
# This file should contain all the record creation needed to seed the database with its default values.
# The data can then be loaded with the rails db:seed command (or created alongside the database with db:setup).
#
# Examples:
#
# movies = Movie.create([{ name: 'Star Wars' }, { name: 'Lord of the Rings' }])
# Character.create(name: 'Luke', movie: movies.first)
require 'sidekiq/testing'
ldap = LdapService.new
Sidekiq::Testing.inline! do
CreateAccount.call(
username: "admin", domain: "kosmos.org", email: "admin@example.com",
password: "admin is admin", confirmed: true
)
ldap.add_attribute "cn=admin,ou=kosmos.org,cn=users,dc=kosmos,dc=org", :admin, "true"
5.times do |n|
username = Faker::Name.unique.first_name.downcase
email = Faker::Internet.unique.email
CreateAccount.call(
username: username, domain: "kosmos.org", email: email,
password: "user is user", confirmed: true
)
end
end

34
docker-compose.yml Normal file
View File

@@ -0,0 +1,34 @@
services:
ldap:
image: 4teamwork/389ds:latest
volumes:
- ./tmp/389ds:/data
ports:
- "389:3389"
environment:
DS_DM_PASSWORD: passthebutter
SUFFIX_NAME: "dc=kosmos,dc=org"
# phpldapadmin:
# image: osixia/phpldapadmin:0.9.0
# ports:
# - "8389:80"
# environment:
# PHPLDAPADMIN_HTTPS: false
# PHPLDAPADMIN_LDAP_HOSTS: "#PYTHON2BASH:[{'ldap': [{'server': [{'tls': False}, {'port': 3389}]}, {'login': [{'bind_id': 'cn=Directory Manager'}, {'bind_pass': 'passthebutter'}]}]}]"
# PHPLDAPADMIN_LDAP_CLIENT_TLS: false
# web:
# build: .
# tty: true
# command: bash -c "sleep 5 && rm -f tmp/pids/server.pid && bin/dev"
# volumes:
# - .:/akkounts
# ports:
# - "3000:3000"
# environment:
# RAILS_ENV: development
# LDAP_HOST: ldap
# LDAP_PORT: 3389
# LDAP_ADMIN_PASSWORD: passthebutter
# LDAP_USE_TLS: "false"
# depends_on:
# - ldap

8
docker/entrypoint.sh Normal file
View File

@@ -0,0 +1,8 @@
#!/bin/bash
set -e
# Remove a potentially pre-existing server.pid for Rails.
rm -f /myapp/tmp/pids/server.pid
# Then exec the container's main process (what's set as CMD in the Dockerfile).
exec "$@"

28
lib/tasks/ldap.rake Normal file
View File

@@ -0,0 +1,28 @@
namespace :ldap do
desc "Reset the LDAP directory and set up base entries and default org"
task setup: :environment do |t, args|
ldap = LdapService.new
ldap.delete_entry "cn=admin_role,ou=kosmos.org,cn=users,dc=kosmos,dc=org", true
# Delete all existing entries and re-add base entries
ldap.reset_directory!
ldap.add_organization "kosmos.org", "Kosmos", true
# add admin role
ldap.add_entry "cn=admin_role,ou=kosmos.org,cn=users,dc=kosmos,dc=org", {
objectClass: %w{top LDAPsubentry nsRoleDefinition nsComplexRoleDefinition nsFilteredRoleDefinition},
cn: "admin_role",
nsRoleFilter: "(&(objectclass=person)(admin=true))",
description: "filtered role for admins"
}, true
end
desc "List user domains/organizations"
task list_organizations: :environment do |t, args|
ldap = LdapService.new
orgs = ldap.fetch_organizations
puts orgs.inspect
end
end

View File

@@ -5,4 +5,20 @@ namespace :lndhub do
CreateLndhubWalletJob.perform_later(user)
end
end
desc "List wallet balances"
task :balances => :environment do |t, args|
sum = 0
User.all.each do |user|
lndhub = Lndhub.new
auth_token = lndhub.authenticate(user)
data = lndhub.balance(auth_token)
balance = data["BTC"]["AvailableBalance"] rescue nil
if balance && balance > 0
sum += balance
puts "#{user.address}: #{balance} sats"
end
end
puts "--\nSum of user balances: #{sum} sats"
end
end

View File

@@ -2,14 +2,14 @@
"name": "akkounts",
"private": true,
"dependencies": {
"@tailwindcss/forms": "^0.4.0",
"autoprefixer": "^10.4.2",
"postcss": "^8.4.6",
"@tailwindcss/forms": "^0.5.3",
"autoprefixer": "^10.4.13",
"postcss": "^8.4.19",
"postcss-flexbugs-fixes": "^5.0.2",
"postcss-import": "^14.0.2",
"postcss-nested": "^5.0.6",
"postcss-preset-env": "^7.3.1",
"tailwindcss": "^3.0.22"
"postcss-import": "^15.0.1",
"postcss-nested": "^6.0.0",
"postcss-preset-env": "^7.8.3",
"tailwindcss": "^3.2.4"
},
"version": "0.4.0",
"scripts": {

View File

@@ -0,0 +1 @@
<svg data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" width="430.91406" height="559.70956" viewBox="0 0 430.91406 559.70956" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M689.29823,324.68445q0,4.785-.31006,9.49a143.75442,143.75442,0,0,1-13.46973,52.19c-.06006.14-.13037.27-.18994.4-.36035.76-.73047,1.52-1.11035,2.27a142.03868,142.03868,0,0,1-7.6499,13.5,144.462,144.462,0,0,1-118.56006,66.72l1.43018,82.24,18.6499-9.82,3.33008,6.33-21.83985,11.5,2.66992,152.74.02979,2.04-14.41992,1.21.02978-.05,4.54-246.18a144.17482,144.17482,0,0,1-102-44.38c-.90967-.94-1.81006-1.91-2.68994-2.87-.04-.04-.06982-.08-.1001-.11a144.76758,144.76758,0,0,1-26.33984-40.76c.14014.16.29.31.43017.47a144.642,144.642,0,0,1,68.57959-186.38c.5-.25,1.01026-.49,1.51026-.74a144.75207,144.75207,0,0,1,187.52978,56.93c.88037,1.48005,1.73047,2.99006,2.5503,4.51A143.85218,143.85218,0,0,1,689.29823,324.68445Z" transform="translate(-384.54297 -170.14522)" fill="#e5e5e5"/><circle cx="198.2848" cy="502.61836" r="43.06733" fill="#2f2e41"/><rect x="210.6027" y="532.22265" width="38.58356" height="13.08374" fill="#2f2e41"/><ellipse cx="249.45884" cy="534.4033" rx="4.08868" ry="10.90314" fill="#2f2e41"/><rect x="201.6027" y="531.22265" width="38.58356" height="13.08374" fill="#2f2e41"/><ellipse cx="240.45884" cy="533.4033" rx="4.08868" ry="10.90314" fill="#2f2e41"/><path d="M541.051,632.71229c-3.47748-15.5738,7.63866-31.31043,24.82866-35.14881s33.94421,5.67511,37.42169,21.2489-7.91492,21.31769-25.10486,25.156S544.5285,648.28608,541.051,632.71229Z" transform="translate(-384.54297 -170.14522)" fill="#14b8a6"/><path d="M599.38041,670.31119a10.75135,10.75135,0,0,1-10.33984-7.12305,1,1,0,0,1,1.896-.63672c1.51416,4.50782,6.69825,6.86524,11.55457,5.25342a9.60826,9.60826,0,0,0,5.57251-4.74756,8.23152,8.23152,0,0,0,.48547-6.33789,1,1,0,0,1,1.896-.63672,10.217,10.217,0,0,1-.59229,7.86817,11.62362,11.62362,0,0,1-6.73218,5.75244A11.87976,11.87976,0,0,1,599.38041,670.31119Z" transform="translate(-384.54297 -170.14522)" fill="#fff"/><path d="M618.56452,676.16463a9.57244,9.57244,0,1,1-17.04506,8.71737h0l-.00855-.01674c-2.40264-4.70921.91734-7.63227,5.62657-10.03485S616.162,671.45547,618.56452,676.16463Z" transform="translate(-384.54297 -170.14522)" fill="#fff"/><path d="M772.27559,716.2189h-381a1,1,0,0,1,0-2h381a1,1,0,0,1,0,2Z" transform="translate(-384.54297 -170.14522)" fill="#3f3d56"/><ellipse cx="567.22606" cy="706.64241" rx="7.50055" ry="23.89244" transform="translate(-543.03826 -6.10526) rotate(-14.4613)" fill="#2f2e41"/><path d="M645.50888,621.42349H629.12323a.77274.77274,0,0,1-.51881-1.3455l14.90017-13.49467h-13.7669a.77274.77274,0,0,1,0-1.54548h15.77119a.77275.77275,0,0,1,.51881,1.34551L631.12753,619.878h14.38135a.77274.77274,0,1,1,0,1.54548Z" transform="translate(-384.54297 -170.14522)" fill="#cbcbcb"/><path d="M666.37288,597.46853H649.98723a.77275.77275,0,0,1-.51881-1.34551l14.90017-13.49466h-13.7669a.77274.77274,0,0,1,0-1.54548h15.77119a.77274.77274,0,0,1,.51881,1.3455l-14.90016,13.49467h14.38135a.77274.77274,0,1,1,0,1.54548Z" transform="translate(-384.54297 -170.14522)" fill="#cbcbcb"/><path d="M657.1,571.19534H640.71434a.77274.77274,0,0,1-.51881-1.3455l14.90017-13.49467H641.3288a.77274.77274,0,0,1,0-1.54548H657.1a.77275.77275,0,0,1,.51881,1.34551l-14.90016,13.49466H657.1a.77274.77274,0,0,1,0,1.54548Z" transform="translate(-384.54297 -170.14522)" fill="#cbcbcb"/><path d="M770.66217,347.522,783.457,337.28854c-9.93976-1.09662-14.0238,4.32429-15.69525,8.615-7.76532-3.22446-16.21881,1.00136-16.21881,1.00136l25.6001,9.29375A19.37209,19.37209,0,0,0,770.66217,347.522Z" transform="translate(-384.54297 -170.14522)" fill="#3f3d56"/><path d="M403.66217,180.522,416.457,170.28854c-9.93976-1.09662-14.0238,4.32429-15.69525,8.615-7.76532-3.22446-16.21881,1.00136-16.21881,1.00136l25.6001,9.29375A19.37209,19.37209,0,0,0,403.66217,180.522Z" transform="translate(-384.54297 -170.14522)" fill="#3f3d56"/><path d="M802.66217,215.522,815.457,205.28854c-9.93976-1.09662-14.0238,4.32429-15.69525,8.615-7.76532-3.22446-16.21881,1.00136-16.21881,1.00136l25.6001,9.29375A19.37209,19.37209,0,0,0,802.66217,215.522Z" transform="translate(-384.54297 -170.14522)" fill="#3f3d56"/></svg>

After

Width:  |  Height:  |  Size: 4.1 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 14 KiB

64
public/img/logos/zeus.svg Normal file
View File

@@ -0,0 +1,64 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="330"
height="160"
viewBox="0 0 330 160"
fill="none"
version="1.1"
id="svg12"
sodipodi:docname="zeus.svg"
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview9"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
showgrid="false"
inkscape:zoom="2.3383653"
inkscape:cx="179.61266"
inkscape:cy="89.164854"
inkscape:window-width="1920"
inkscape:window-height="1011"
inkscape:window-x="0"
inkscape:window-y="32"
inkscape:window-maximized="1"
inkscape:current-layer="svg12" />
<defs
id="defs16" />
<g
id="g120"
transform="matrix(0.8832226,0,0,0.89599484,21.917939,12.35239)">
<path
d="m 215.456,79.5133 c 0,0 10.514,-46.5605 32.708,-76.34661 L 324,0 c 0,0 -36.958,34.6773 -47.661,53.4896 L 320.6,50.1974 c 0,0 -69.855,49.9466 -93.339,100.8026 0,0 -5.1,-41.857 18.857,-74.6534 z"
fill="#ffd93f"
id="path2" />
<path
d="m 121.235,19.9094 c 0,0 -13.033,5.6124 -27.9858,17.0879 -14.9532,11.4755 -23.2325,24.4873 -23.2325,24.4873 0,0 4.5332,0.3762 3.0851,2.6023 -1.4481,2.2262 -5.572,6.1767 -5.572,6.1767 0,0 7.5868,4.954 30.0007,14.705 22.4145,9.7511 25.6565,11.4442 25.9085,10.5662 0.283,-0.8779 10.577,1.7559 10.577,1.7559 0,0 -26.821,-15.2694 -40.2946,-27.3092 0,0 3.2425,-2.8219 11.3956,-5.6437 8.154,-2.8219 12.466,-3.6057 12.624,-4.609 0.157,-1.0033 -2.487,-1.4423 -2.487,-1.4423 l 9.444,-2.9159 c 0,0 -18.07,-0.6898 -37.2727,3.637 0,0 9.0664,-14.3913 18.6047,-21.6027 9.539,-7.2114 13.631,-9.124 13.789,-10.5036 0.157,-1.3795 -2.078,-2.2261 -2.078,-2.2261 0,0 0.535,-2.6964 3.494,-4.7658 z"
fill="white"
id="path4"
style="fill:#333333" />
<path
d="m 57.9283,43.0484 c 0,0 -11.2699,12.1653 -22.7602,29.9429 C 23.6777,90.769 8.69313,113.5 12.2819,115.789 c 3.5887,2.289 9.2552,1.443 9.2552,1.443 0,0 -0.7555,2.539 0.9129,2.759 1.6685,0.219 13.6939,-9.908 27.986,-15.364 14.2921,-5.4866 25.688,-8.5593 26.0028,-10.0329 0.3148,-1.505 -4.3758,-2.3829 -4.0925,-2.3829 0.2519,0 5.3517,-1.2855 5.7294,-2.9472 0.4093,-1.6618 -3.3054,-2.3829 -8.9089,-1.7245 -5.6035,0.6584 -30.7248,8.152 -30.7248,8.152 0,0 18.416,-33.047 28.6471,-46.5605 10.2311,-13.5135 13.568,-16.1472 13.5365,-18.1852 -0.0314,-2.038 -8.4367,-1.7871 -8.4367,-1.7871 0,0 -1.1018,-2.3202 -2.3295,-2.3516 -1.2277,-0.0313 -32.4247,1.411 -54.1147,5.6437 C -5.94522,36.6836 0.319361,36.9344 2.68038,38.9097 5.00993,40.9163 1.3897,44.961 1.3897,44.961 c 0,0 10.6403,-0.4389 10.4829,1.7558 -0.1574,2.1948 -3.46281,4.6717 -3.46281,4.6717 0,0 25.24721,-6.8978 49.51851,-8.3401 z"
fill="white"
id="path6"
style="fill:#333333" />
<path
d="m 130.585,21.8848 c 0,0 3.589,19.0004 4.753,35.5866 1.165,16.5861 0.063,31.3537 0.913,31.8554 0.819,0.5017 8.091,-2.5397 17.566,-1.505 9.476,1.0347 16.496,1.3482 17.283,0.4076 0.787,-0.9406 0.032,-3.1354 0.032,-3.1354 0,0 1.825,0.2822 2.172,-1.0033 0.346,-1.2855 -1.417,-2.665 -1.417,-2.665 0,0 0,-13.0433 -0.755,-28.877 -0.724,-15.8023 -5.604,-29.6606 -5.604,-29.6606 0,0 -3.211,53.2388 -4.124,56.4369 0,0 -8.751,-3.01 -15.488,-2.4143 0,0 -3.809,-21.6028 -6.957,-36.4645 -3.148,-14.8617 -4.03,-17.809 -4.785,-17.6522 -0.661,0.1254 -1.26,2.8845 -1.26,2.8845 z"
fill="white"
id="path8"
style="fill:#333333" />
<path
d="m 243.001,47.9712 c 0,0 -25.058,-14.1406 -41.208,-17.9658 -16.149,-3.8252 -19.203,-2.9786 -17.975,-2.0694 1.228,0.8779 4.187,2.5711 4.187,2.5711 0,0 -10.924,-0.2508 -9.948,1.0974 0.976,1.3482 4.093,11.5695 4.03,22.2925 -0.063,10.723 -0.063,18.0598 0.566,18.342 0.63,0.2822 6.202,-0.1881 6.202,-0.1881 0,0 -1.385,2.1634 -0.063,2.3829 1.322,0.2194 7.713,-1.1288 14.481,-0.6898 6.768,0.4389 9.885,1.2855 9.885,1.2855 0,0 0.661,14.7676 -3.526,25.6785 0,0 -31.795,-12.698 -32.866,-11.3185 -1.07,1.3482 0.819,4.3896 0.819,4.3896 l -12.277,4.9853 c 0,0 16.747,3.2606 30.567,8.7476 13.82,5.487 21.753,10.19 23.044,9.72 1.29,-0.471 3.462,-6.428 6.925,-24.4249 3.463,-17.9971 1.511,-26.8702 0.158,-27.9363 -3.022,-2.3515 -34.251,-1.0033 -34.251,-1.0033 0,0 1.417,-12.7297 0.283,-20.4113 0.032,-0.0314 1.354,-4.7658 50.967,4.515 z"
fill="white"
id="path10"
style="fill:#333333" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.6 KiB

View File

@@ -0,0 +1,43 @@
require 'rails_helper'
require 'webmock/rspec'
RSpec.describe "/api/kredits", type: :request do
describe "GET /onchain_btc_balance" do
before do
stub_request(:get, "http://btcpay.example.com/api/v1/stores/123456/payment-methods/onchain/BTC/wallet")
.to_return(status: 200, headers: {}, body: {
balance: 0.91108606,
unconfirmedBalance: 0,
confirmedBalance: 0.91108606
}.to_json)
end
it "returns a formatted result for the onchain wallet balance" do
get api_kredits_onchain_btc_balance_path
expect(response).to have_http_status(:ok)
res = JSON.parse(response.body)
expect(res["balance"]).to eq(0.91108606)
expect(res["unconfirmed_balance"]).to eq(0)
expect(res["confirmed_balance"]).to eq(0.91108606)
end
context "upstream request error" do
before do
stub_request(:get, "http://btcpay.example.com/api/v1/stores/123456/payment-methods/onchain/BTC/wallet")
.to_return(status: 500, headers: {}, body: "")
end
it "returns a formatted error" do
get api_kredits_onchain_btc_balance_path
expect(response).to have_http_status(:server_error)
res = JSON.parse(response.body)
expect(res["error"]).not_to be_nil
end
end
end
end

View File

837
yarn.lock

File diff suppressed because it is too large Load Diff