252 Commits

Author SHA1 Message Date
raucao 28b31e63f9 Merge pull request 'Update Docker image in CI' (#75) from chore/ci_image_upgrade into feature/docker_compose
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
Reviewed-on: #75
2022-12-06 09:23:05 +00:00
Râu Cao efafd38f68 Update Docker image in CI
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)
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
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
continuous-integration/drone/push Build is passing
2022-09-16 11:20:29 +02:00
raucao 87027b514b Merge pull request 'Update gems' (#72) from chore/update_gems into master
continuous-integration/drone/push Build is passing
Reviewed-on: #72
2022-07-27 13:47:33 +00:00
Râu Cao 16ad621365 Update gems
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
raucao 33e87d6472 Merge pull request 'Add BTCPay service, Kredits API' (#71) from feature/community_funds_balance into master
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
basti 03dc6c7a9c Log unexpected kredits API errors
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2022-05-24 13:42:00 +02:00
basti 897b5bf4ea Specify whole API base URL in config
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2022-05-23 22:49:39 +02:00
basti caea2d0121 Add kredits API with wallet balance endpoint
continuous-integration/drone/push Build is passing
2022-05-23 22:47:08 +02:00
basti e1ff5c479e Initial BTCPay integration 2022-05-23 21:35:03 +02:00
basti 9b3386de30 Update credentials 2022-05-23 18:49:37 +02:00
basti f2287c1186 Remove separate development credentials files 2022-05-23 18:49:22 +02:00
raucao b29197cf4e Merge pull request 'Various UI improvements' (#70) from feature/ui_improvements into master
continuous-integration/drone/push Build is passing
Reviewed-on: #70
2022-04-28 13:05:10 +00:00
basti 5c48055ac8 Use feather icon for wallet on dashboard
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
fixes #51
2022-04-28 15:01:20 +02:00
basti 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
raucao fbf163740a Merge pull request 'Replace the LDAP production config for the new server' (#69) from chore/new_ldap_server into master
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
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
raucao 1f57bbd9c2 Merge pull request 'Add admin task to list LndHub balances' (#68) from feature/list_lndhub_balances into master
continuous-integration/drone/push Build is passing
Reviewed-on: #68
2022-04-18 08:41:40 +00:00
basti 2a2793ae44 Print sum of user balances
continuous-integration/drone/pr Build is passing
2022-04-12 16:05:46 +02:00
basti 8773bf5f9e Slow down LndHub auth requests in task 2022-04-12 15:42:44 +02:00
basti d9970c126a List balances of LndHub accounts 2022-04-12 15:36:45 +02:00
basti 4e0d4bf86d 0.4.0
continuous-integration/drone/push Build is passing
2022-03-17 14:59:07 -06:00
basti 333bcbfe7e Remove Sass dependency
continuous-integration/drone/push Build is passing
2022-03-17 13:30:10 -06:00
raucao 875af6d14c Merge pull request 'Add transaction history view to wallet' (#66) from feature/wallet_history into master
continuous-integration/drone/push Build is passing
Reviewed-on: #66
2022-03-17 19:28:58 +00:00
raucao 8f87a03060 Merge pull request 'Finish Tailwind migration' (#67) from chore/finish_tailwind_migration into feature/wallet_history
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
Reviewed-on: #67
2022-03-17 19:27:52 +00:00
basti 7838fe5f34 Remove legacy CSS build from task
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2022-03-17 13:26:36 -06:00
basti 512798d122 Port last remaining styles from legacy to Tailwind
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is failing
2022-03-17 13:24:13 -06:00
basti 384c28aaaa Build PRs for all branches
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2022-03-17 13:06:33 -06:00
basti 8e5d6dabdc Port most remaining legacy styles to Tailwind
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2022-03-11 10:15:09 -06:00
basti ade9261c2c Remove obsolete CSS 2022-03-11 09:52:11 -06:00
basti bd2a161306 Add tab menu to wallet pages
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2022-03-02 19:18:28 -06:00
basti 78c243c985 Add wallet transactions
continuous-integration/drone/push Build is passing
2022-03-02 18:43:22 -06:00
basti cf62bfc5c2 WIP Add wallet transactions route, view
continuous-integration/drone/push Build is passing
Adds a new component for the wallet summary as well, and makes the
component tests work with RSpec.
2022-03-02 15:31:39 -06:00
basti 10f179a095 Port shared CSS for tables to Tailwind 2022-03-02 15:30:50 -06:00
basti f7d0a0ba85 0.3.0
continuous-integration/drone/push Build is failing
2022-03-02 10:41:54 -06:00
raucao 83e4dfa18f Merge pull request 'Allow comments for LNURL-PAY invoices' (#65) from feature/lnurlp_memos into master
continuous-integration/drone/push Build is passing
Reviewed-on: #65
2022-03-02 14:13:40 +00:00
basti 4c70600d1f Re-add description_hash
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
Necessary for lnurlpay-enabled wallets
2022-03-01 13:53:22 -06:00
basti 9903683536 Remove desc hash, always add memo to invoices
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2022-03-01 13:26:44 -06:00
basti 4c51b9c966 Allow comments for LNURL-PAY invoices
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
Allows senders to add a short message to payments, which will be stored
as invoice memo by LND/LndHub.
2022-03-01 11:20:23 -06:00
raucao 6790e8383d Merge pull request 'Redesign layout and navigation' (#64) from feature/new_layout into master
continuous-integration/drone/push Build is passing
Reviewed-on: #64
2022-02-26 15:45:12 +00:00
basti ed886d8182 Introduce sidebar nav components, settings nav
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2022-02-24 18:56:07 -06:00
basti ca940ec35d Consolidate some styles
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2022-02-24 17:24:59 -06:00
basti 5751c0338a Nicer buttons on small screens
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2022-02-24 13:59:51 -06:00
basti b9ec363f36 Remove caveat from README 2022-02-24 13:59:15 -06:00
basti 417768a30c Fix specs, markup
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2022-02-23 18:27:33 -06:00
basti 9824dcd2c6 Remove unused specs
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is failing
2022-02-23 18:17:43 -06:00
basti 5a784b5fa6 Improve devise views
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is failing
2022-02-23 18:16:14 -06:00
basti f36f6866a7 Port signup to new layout
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is failing
2022-02-23 18:07:54 -06:00
basti 1fecfe57de Fix status views 2022-02-23 17:50:16 -06:00
basti 3165714957 Implement proper mobile navigation
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is failing
2022-02-23 14:16:51 -06:00
basti 4ccf43cf4a Layout classes 2022-02-23 12:13:14 -06:00
basti c0e79918ea Fix confirm dialog missing
continuous-integration/drone/push Build is passing
2022-02-21 11:20:58 -06:00
basti 2b00eebb73 Fix delete link, remove obsolete notice
continuous-integration/drone/push Build is passing
2022-02-21 11:19:07 -06:00
basti 86cdb1202b Port check-email screen to new layout 2022-02-21 11:09:57 -06:00
basti 6a469d6a75 Allow empty values for fiat conversion 2022-02-21 11:09:44 -06:00
basti 7d66b75216 Improve notifications, fix styles not being added
continuous-integration/drone/push Build is passing
Based on https://petr.codes/blog/rails/modern-rails-flash-messages/part-3/
2022-02-21 11:03:43 -06:00
basti 8102fa1230 WIP Add notification component for flash messages
continuous-integration/drone/push Build is passing
2022-02-20 17:22:49 -06:00
basti 835152c656 Introduce ViewComponent
continuous-integration/drone/push Build is passing
https://viewcomponent.org
2022-02-20 16:53:11 -06:00
basti 7c5bd9aa34 Improve focused field style
continuous-integration/drone/push Build is passing
2022-02-20 12:54:16 -06:00
basti b329b557c4 Add compact layout for content, port sign-in screens
continuous-integration/drone/push Build is passing
2022-02-20 12:48:11 -06:00
basti 2e301c3019 Port admin to new layout
continuous-integration/drone/push Build is passing
2022-02-20 11:22:06 -06:00
basti 4f2b35ccb9 WIP New app layout
continuous-integration/drone/push Build is passing
2022-02-19 22:46:12 -06:00
raucao a2889705ed Merge pull request 'Fix sign out link' (#62) from bugfix/signout into master
continuous-integration/drone/push Build is passing
Reviewed-on: #62
2022-02-19 18:16:00 +00:00
basti 7cb0111449 Fix sign out link
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
The correct HTML attribute to send a DELETE request would be
`data-turbo-method`, but then it still fails with JS turned off, which
is unnecessary.

fixes #61
2022-02-19 12:12:32 -06:00
raucao 773ea24c5d Merge pull request 'Switch from Webpacker to cssbundling-rails, upgrade Tailwind CSS to version 3' (#59) from dev/cssbundling into master
continuous-integration/drone/push Build is passing
Reviewed-on: #59
Reviewed-by: bumi <bumi@noreply.kosmos.org>
2022-02-17 14:45:18 +00:00
basti cd3e4161b8 Update dev command in README
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2022-02-16 10:46:04 -06:00
basti 5a658ce580 Update RSpec syntax/usage
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
Fixes a deprecation warning
2022-02-16 10:14:32 -06:00
basti 6e9b38f04b Fix deprecation warning from Rails
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2022-02-16 09:41:07 -06:00
basti a71a9dfad0 Remove unused helper specs
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2022-02-16 09:38:40 -06:00
basti 1c4e444c0b Adjust bundle options in CI
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2022-02-16 09:25:47 -06:00
basti 565a3c3276 Fix broken name
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is failing
2022-02-12 15:34:18 -06:00
basti 9fdbf27a60 Use rake tasks in CI
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is failing
2022-02-12 15:29:04 -06:00
basti 1a9b47ceee Losing the battle
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is failing
2022-02-12 15:22:57 -06:00
basti 908809bc48 Remove bundler version requirement
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is failing
2022-02-12 15:11:34 -06:00
basti 9636671d57 Use rspec binstub in CI
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is failing
2022-02-12 15:10:23 -06:00
basti 51cddd94f5 Add rspec binstub 2022-02-12 15:09:56 -06:00
basti 123e7aa2a1 Update Gemfile 2022-02-12 15:09:41 -06:00
basti 3596955642 Don't use deprecated bundler flags 2022-02-12 15:09:19 -06:00
basti 562b16cf89 Update Rails CI Docker image
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is failing
2022-02-12 15:02:20 -06:00
basti 830c634f88 Explicitly install dev and test gems
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is failing
2022-02-12 14:55:30 -06:00
basti 2a793e9201 Define RAILS_ENV in CI
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is failing
2022-02-12 14:53:23 -06:00
basti e571ed9429 Use vanilla Yarn to build CSS in CI
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is failing
2022-02-12 14:43:51 -06:00
basti a67f3e466b Remove bootsnap
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is failing
2022-02-12 14:41:47 -06:00
basti ff3013f917 Remove all remains of Webpack
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is failing
2022-02-12 14:30:31 -06:00
basti 0fa6c1a211 Don't pin bootsnap version
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is failing
2022-02-12 14:26:14 -06:00
basti 30b2646b85 Fix rake command
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is failing
2022-02-12 14:22:53 -06:00
basti f8b86b0a22 Remove obsolete gems
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is failing
2022-02-12 14:21:21 -06:00
raucao b71a2fa643 Merge pull request 'Upgrade Rails to 7.0.2, use native JS bundling' (#60) from dev/upgrade_rails into dev/cssbundling
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is failing
Reviewed-on: #60
2022-02-12 20:13:42 +00:00
basti eda1f3999f Update validation message in spec
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
They seem to have shortened the default message.
2022-02-12 14:10:04 -06:00
basti c06e58a0fb Use new lockbox method
The old one conflicts with Rails' own new ActiveRecord encryption
feature.
2022-02-12 14:04:41 -06:00
basti c33637003e Upgrade to Rails 7, new JS build setup 2022-02-12 13:55:56 -06:00
basti 836bd0a977 Build CSS bundles in CI
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is failing
2022-02-12 13:55:30 -06:00
basti 8578fbdad9 Build legacy CSS via cssbundling as well
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is failing
Add vanilla Sass builds that are also handled by cssbundling-rails.
2022-02-12 13:52:45 -06:00
basti 878eac083c Move legacy (S)CSS files to legacy folder
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is failing
2022-02-12 12:43:37 -06:00
basti 05da7f5dac Bump package version
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is failing
2022-02-12 10:01:19 -06:00
basti 87e3b1a76c Sign Drone config 2022-02-12 09:34:36 -06:00
basti 32f02cc18a Switch from Webpacker to cssbundling-rails, upgrade Tailwind 2022-02-11 17:23:31 -06:00
basti 1b17cfb396 Fix typo
continuous-integration/drone/push Build is passing
2022-02-03 11:32:41 -06:00
raucao e5aa5a665c Merge pull request 'Fix LNURL pay amount validation' (#58) from bugfix/fix-max-receivable-amount into master
continuous-integration/drone/push Build is passing
Reviewed-on: #58
2022-02-03 17:13:20 +00:00
bumi d37b68a6e5 Fix LNURL pay amount validation
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
We allow receiving of more than 100 sats and less than 1M sats
2022-02-03 17:32:18 +01:00
basti 56936916ff Move SVG images to public folder
continuous-integration/drone/push Build is passing
Wasn't working in production
2022-01-12 19:37:12 -06:00
basti c93a460cff Bump style version
continuous-integration/drone/push Build is passing
Triggers rebuild
2022-01-12 18:52:32 -06:00
raucao f5ceda35c1 Merge pull request 'Add more content/help to wallet page' (#57) from feature/wallet_page_content into master
continuous-integration/drone/push Build is passing
Reviewed-on: #57
2022-01-13 00:48:22 +00:00
basti eb0439d6dc Improve Blue Wallet instructions
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2022-01-12 18:46:14 -06:00
basti c3dde3506e Add more content/help to wallet page
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
* Lighting Address info
* Improve explanation for wallet apps, add Alby
2022-01-10 13:37:04 -06:00
raucao f22ffe373c Merge pull request 'Fix exception during signup' (#56) from bugfix/signup_lndhub into master
continuous-integration/drone/push Build is passing
Reviewed-on: #56
2022-01-10 15:31:10 +00:00
basti bc20e89617 Fix exception during signup
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2022-01-10 09:28:12 -06:00
raucao 0f0f296a5e Merge pull request 'Add button for copying lndhub setup code' (#55) from feature/37-copy_setup_code into master
continuous-integration/drone/push Build is passing
Reviewed-on: #55
2021-12-16 14:23:13 +00:00
basti 78aea5d608 Use Tailwind classes to hide/show elements
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2021-12-16 15:18:37 +01:00
basti f1d3e3d8ec Add button for copying lndhub setup code
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
closes #37
2021-12-15 14:54:44 +01:00
raucao 2706c76890 Merge pull request 'Improve admin LDAP user index' (#53) from feature/improve_admin_ldap_page into master
continuous-integration/drone/push Build is passing
Reviewed-on: #53
2021-11-30 14:38:05 +00:00
raucao 17f5eb56cd Merge pull request 'Show sats instead of BTC on donation page, refactor CSS' (#54) from feature/45-sats_everywhere into master
continuous-integration/drone/push Build is running
Reviewed-on: #54
2021-11-30 14:37:45 +00:00
raucao aa6b677b13 Merge pull request 'Improve task for generating invitations' (#52) from feature/improve_invitation_generation into master
continuous-integration/drone/push Build is passing
Reviewed-on: #52
2021-11-28 20:44:06 +00:00
basti 9abdab2274 Show sats instead of BTC on donation page, refactor CSS
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
* Show sats instead of BTC on donation page
* Use number delimiters on both donation page and wallet page
* Refactor donation page CSS into Tailwind directives
2021-11-28 11:53:20 -06:00
basti dd49d1208f Remove feature list from README
continuous-integration/drone/push Build is passing
Not overly useful or impressive IMO.
2021-11-28 11:16:49 -06:00
basti db9118cb7c Improve admin LDAP user index
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
* Show which domain the current list is for
* Render text links as such
2021-11-28 11:11:41 -06:00
basti 89913ba60b Improve task for generating invitations
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
Turn the argument into the target number of unused invitations for each
user, thus not generating more invitations for users who already have a
sufficient amount of unused ones.
2021-11-28 10:40:09 -06:00
basti 8cf631fd94 Add preconfigured lockbox credentials for development
continuous-integration/drone/push Build is passing
2021-11-26 13:05:26 -06:00
raucao d0b359039b Merge pull request 'Wrap global JS into IIFE' (#50) from bugfix/41-turbolinks into master
continuous-integration/drone/push Build is passing
Reviewed-on: #50
2021-11-26 19:04:34 +00:00
basti 84cf523049 Wrap global JS into IIFE
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
fixes #41
2021-11-26 13:01:39 -06:00
raucao a7390ba00b Merge pull request 'Fixes/improvements for lnurl-pay' (#49) from feature/lnurlp_improvements into master
continuous-integration/drone/push Build is passing
Reviewed-on: #49
2021-11-26 17:37:37 +00:00
basti 67d148d117 Lower the minimum receivable via lnurlp
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2021-11-26 11:34:51 -06:00
basti 83ad6f4eef Update README
continuous-integration/drone/push Build is passing
2021-11-25 19:24:56 -06:00
basti 2e31268698 Change description and success message for lnurlp
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
closes #46
2021-11-25 19:14:43 -06:00
basti f3b22c02ef Set correct min/max amounts for lnurlp
fixes #47
2021-11-25 19:14:17 -06:00
raucao dbe65b4b5a Merge pull request 'Add lndhub and lockbox configs for test environment' (#48) from bugfix/lndhub_specs into master
continuous-integration/drone/push Build is passing
Reviewed-on: #48
2021-11-26 00:55:18 +00:00
basti 2871fc0f53 Add lockbox credentials for test env
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2021-11-25 18:51:40 -06:00
basti 968689a512 Add lndhub config to test environment
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is failing
2021-11-25 18:38:54 -06:00
basti ab29f618f4 Update README
continuous-integration/drone/push Build is failing
2021-11-24 11:11:36 -06:00
raucao 94975a1b30 Merge pull request 'Add Tailwind info to README' (#44) from bugfix/35-tailwind-2 into master
continuous-integration/drone/push Build is failing
Reviewed-on: #44
2021-11-24 17:10:26 +00:00
basti cd8880d9dc Add Tailwind info to README
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
2021-11-24 11:03:42 -06:00
basti f59182b9c1 Second try, triggering asset compilation
continuous-integration/drone/push Build is failing
2021-11-24 10:48:29 -06:00
basti 941cb4a571 Minor CSS tweak
continuous-integration/drone/push Build is failing
2021-11-24 10:45:40 -06:00
basti f534898d8b Try triggering asset compilation
continuous-integration/drone/push Build is failing
2021-11-24 10:42:40 -06:00
raucao 18c7c54403 Merge pull request 'Various UI improvements' (#43) from ui/misc into master
continuous-integration/drone/push Build is failing
Reviewed-on: #43
2021-11-24 02:41:55 +00:00
basti 12a9d4674b Fix a couple of oversights
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
2021-11-23 20:40:42 -06:00
basti 1af8e068c5 Add the wallet to the dashboard as a service
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
2021-11-23 20:09:30 -06:00
basti 669b163814 Make the wallet balance look a bit nicer 2021-11-23 20:08:54 -06:00
basti 46c7affd1f Add explanatory intro to invitations page 2021-11-23 20:08:33 -06:00
basti 7ab107b689 Hide headings
The main nav entry above is enough context.
2021-11-23 20:07:57 -06:00
raucao 5aee1a4100 Merge pull request 'Fix main nav on small screens' (#42) from bugfix/38-navbar into master
continuous-integration/drone/push Build is failing
Reviewed-on: #42
2021-11-23 22:02:36 +00:00
basti 1578fb9976 Fix main nav on small screens
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
closes #38
2021-11-23 16:00:32 -06:00
raucao 8e64a7cf78 Merge pull request 'Fix invoice amount for lnurlp payments' (#40) from bugfix/lnurlp_amounts into master
continuous-integration/drone/push Build is failing
Reviewed-on: #40
2021-11-23 21:29:33 +00:00
basti 8b5bd66598 Fix invoice amount for lnurlp payments
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
Incoming amount is msats, but we create invoices with sats.
2021-11-23 15:28:16 -06:00
raucao ac8552362c Merge pull request 'Adjust tailwind purge config' (#39) from bugfix/35-tailwind into master
continuous-integration/drone/push Build is failing
Reviewed-on: #39
2021-11-23 21:27:22 +00:00
basti 99c86c42c5 Adjust tailwind purge config
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
2021-11-23 15:15:10 -06:00
basti d0267cb760 Update README
continuous-integration/drone/push Build is failing
2021-11-22 17:05:46 -06:00
raucao 25ddab9241 Merge pull request 'Add LndHub wallets' (#33) from feature/lndhub into master
continuous-integration/drone/push Build is failing
Reviewed-on: #33
2021-11-22 23:04:19 +00:00
basti bf76ac55ee Do not allow comments for lnurlp senders
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
2021-11-22 17:03:18 -06:00
basti 40e5c3609e Remove obsolete files
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
2021-11-22 16:29:23 -06:00
basti 1078c034ad Remove obsolete comment
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
2021-11-22 16:26:05 -06:00
basti bfa38ad7b2 Adjust spec for new development config
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
2021-11-22 16:24:42 -06:00
basti 4f20cd0d0a Add Rake task for generating wallets
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
2021-11-22 16:22:53 -06:00
basti e2ee33a1da Configure LndHub for production
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
2021-11-22 16:19:16 -06:00
basti 8662a4c8c1 Don't overwrite existing lndhub wallet credentials
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is failing
2021-11-22 15:51:30 -06:00
basti dbc811b840 Add LndHub service, lnurl-pay endpoints
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is failing
Enables the lnurl-pay payment workflow
2021-11-22 15:41:05 -06:00
basti 884070a3cb Show available balance on wallet page
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
2021-11-21 16:47:55 -06:00
basti 3c350155de Formatting
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
2021-11-21 15:34:24 -06:00
basti 21c6ebc137 Fix small issue with turbolinks
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
The JS was run again, and failed to assign variables using `const` then.
2021-11-21 13:27:55 -06:00
basti 0a1052fcb7 Add wallet page
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is failing
With setup code to connect Blue Wallet to Kosmos account wallets
2021-11-20 16:13:43 -06:00
basti f94227f9f3 Create LndHub accounts 2021-11-19 20:10:36 -06:00
raucao 088961dfec Merge pull request 'Switch to shared Kosmos font(s)' (#30) from feature/webfonts_open-sans into master
continuous-integration/drone/push Build is passing
Reviewed-on: #30
2021-02-25 17:11:39 +00:00
basti 31cf353d3a Load remote fonts before other stylesheets
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
They won't apply when loaded afterwards.
2021-02-25 18:04:27 +01:00
basti 4eb40abc9c Import webfont from shared URL
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2021-02-19 15:45:22 +01:00
basti 682c78c7c3 Move headline styles to tailwind base
continuous-integration/drone/push Build is passing
2021-02-19 15:11:53 +01:00
basti f9726ad9be Use Open Sans as default font for everything 2021-02-19 15:02:07 +01:00
basti 89188f5081 Don't purge CSS in base and component layers
continuous-integration/drone/push Build is passing
The production build was purging input[type=text] styles, because it
couldn't find it in the Rails templates. Change config, so it only
purges utility classes.
2021-02-10 16:37:34 +01:00
raucao 6a6ff84ff2 Merge pull request 'Add Tailwind CSS, migrate most of the styles' (#27) from feature/tailwind-css into master
continuous-integration/drone/push Build is passing
Reviewed-on: #27
2021-02-10 14:29:06 +00:00
basti b6949acc96 Style forms, migrate more styles to Tailwind
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2021-02-09 20:32:10 +01:00
basti 814633034f WIP Add Tailwind CSS
continuous-integration/drone/push Build is passing
2021-02-09 02:05:31 +01:00
raucao 260dedb6cf Merge pull request 'Set up async workers/jobs via Sidekiq' (#26) from feature/sidekiq into master
continuous-integration/drone/push Build is passing
Reviewed-on: #26
2021-02-03 18:12:48 +00:00
basti 656c887811 Add missing hook to spec
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2021-02-03 19:11:43 +01:00
basti 7e9af716ac Make them colors pop
continuous-integration/drone/push Build is passing
2021-02-03 13:37:44 +01:00
basti 58cc6811f9 Move XMPP contacts exchange to background job
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2021-02-03 13:16:47 +01:00
basti 8ad85636d9 Create LDAP users asynchronously
continuous-integration/drone/push Build is passing
2021-02-02 21:16:24 +01:00
basti 35e2c8cd30 Add Sidekiq, configure admin access to Web UI
continuous-integration/drone/push Build is passing
2021-02-02 11:44:17 +01:00
raucao 4526c941b8 Merge pull request 'Add invitations page to admin panel' (#24) from feature/admin_invitations into master
continuous-integration/drone/push Build is passing
Reviewed-on: #24
2021-02-01 22:53:31 +00:00
raucao 4f5ebd5330 Merge pull request 'Add cosmic background to header' (#25) from ui/kosmic_sky into master
continuous-integration/drone/push Build is passing
Reviewed-on: #25
2021-02-01 22:53:15 +00:00
basti d7e4c6f3ae Add cosmic background to header
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
Going back to space, where we belong.
2021-02-01 23:10:54 +01:00
basti 14caefe2d1 Replace yellow menu highlight with blue color 2021-02-01 22:49:42 +01:00
basti 0110f27ada Add invitation stats
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
Show some stats about unused invitations and active inviters
2021-02-01 22:35:30 +01:00
basti dc7cf107c2 New admin page for invitations 2021-02-01 21:53:18 +01:00
raucao 4fbfaadb44 Merge pull request 'Various UI improvements' (#23) from ux/various_design_changes into master
continuous-integration/drone/push Build is passing
Reviewed-on: #23
2021-02-01 18:33:06 +00:00
basti a01cb9ae21 Adjust site header in admin, signup layouts
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2021-02-01 18:58:34 +01:00
basti 698e4381c2 Improve table styles
continuous-integration/drone/push Build is passing
* Nicer table headers
* Hide invitation IDs on small screens
2021-02-01 18:53:48 +01:00
basti 8997349186 Move password change to new Security tab
continuous-integration/drone/push Build is passing
2021-02-01 18:39:51 +01:00
basti 92bfc33bf0 Remove bottom border from last section on page 2021-02-01 18:24:01 +01:00
basti c6eb21faad Change site name to "Account", add comet icon
continuous-integration/drone/push Build is passing
... and remove the "beta" tag.
2021-02-01 18:17:26 +01:00
raucao 2d9bc90b16 Merge pull request 'Use new .local domain for Postgres in production' (#22) from chore/postgres_hostname into master
continuous-integration/drone/push Build is passing
Reviewed-on: #22
2021-01-23 14:04:45 +00:00
basti a0c579e319 Use new .local domain for Postgres in production
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2021-01-23 15:03:16 +01:00
basti f289ee9365 Switch menu items
continuous-integration/drone/push Build is passing
2020-12-29 11:09:04 +01:00
raucao 46a7345ce9 Merge pull request 'Add main navigation bar' (#20) from feature/main_nav into master
continuous-integration/drone/push Build is passing
Reviewed-on: #20
2020-12-29 10:04:42 +00:00
basti e12d02a988 Fix spec
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
Content changed
2020-12-29 11:02:24 +01:00
raucao 5e8618f25a Merge pull request 'Add admin layout with admin navigation' (#21) from feature/admin_layout into feature/main_nav
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is failing
Reviewed-on: #21
2020-12-29 09:58:57 +00:00
basti 2bdf08a523 Add admin layout with admin navigation
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
And remove the hacky link list from the dashboard.
2020-12-28 09:32:04 +01:00
basti 9ddd36c414 Add missing section markup
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2020-12-27 14:21:16 +01:00
basti 9372ea7343 Add small-screen layout for main navigation 2020-12-27 14:14:53 +01:00
basti c62ce00184 Add main navigation bar
Make donations and invitations accessible to everyone
2020-12-27 14:03:40 +01:00
basti 4d8cd740ba Argh
continuous-integration/drone/push Build is passing
2020-12-22 17:15:46 +01:00
basti 9858572a2f Remove useless bundler version requirement
continuous-integration/drone/push Build is passing
2020-12-22 17:03:14 +01:00
basti 51edf55ae9 Use zerotier for connecting to postgres
continuous-integration/drone/push Build is passing
2020-12-22 12:24:18 +01:00
raucao 75485ce8e9 Merge pull request 'Update postgres master host' (#19) from chore/update_postgres_host into master
continuous-integration/drone/push Build is passing
Reviewed-on: #19
2020-12-22 10:42:51 +00:00
basti fcbfcc4007 Update postgres master host
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2020-12-22 11:41:40 +01:00
basti cdcb7b3aef Update README
continuous-integration/drone/push Build is passing
2020-12-21 15:49:58 +01:00
raucao bcf5172956 Merge pull request 'Add basic donation records' (#18) from feature/donation_records into master
continuous-integration/drone/push Build is passing
Reviewed-on: #18
2020-12-21 14:46:50 +00:00
basti 26c6c5a3b2 Nullify donation owners when related record destroyed
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2020-12-21 13:59:46 +01:00
basti 4a65573934 Format numbers on admin donations page
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
And fix the wrong unit display in the user donations list.
2020-12-19 14:59:16 +01:00
basti 5e2d5c3b28 Add paid_at date to donations
continuous-integration/drone/push Build is passing
2020-12-19 13:28:47 +01:00
basti 2f70bae523 Format and style user donations 2020-12-19 13:16:04 +01:00
basti 40f3e8327a Basic donation records
Adds donation model/table and basic manual management in the admin
panel, as well as basic listing of users' own donations.
2020-12-17 21:56:51 +01:00
basti f3d6e29e4e Remove time from used invitations list
Date is enough.
2020-12-17 17:02:30 +01:00
raucao 8903ae2624 Merge pull request 'Fix XMPP API POST request' (#17) from bugfix/faraday_post into master
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
Reviewed-on: #17
2020-12-13 13:17:57 +00:00
basti 26e9073674 Fix XMPP API POST request
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
Faraday does not turn hashes into JSON by itself apparently.
2020-12-13 14:07:25 +01:00
raucao 73a89c2601 Merge pull request 'Add missing port number to ejabberd API base URL' (#16) from bugfix/ejabberd_http_port into master
continuous-integration/drone/push Build is passing
Reviewed-on: #16
2020-12-13 12:57:34 +00:00
basti 7d4dee17b7 Add missing port number to ejabberd API base URL
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2020-12-13 13:54:33 +01:00
raucao 602ca6ee94 Merge pull request 'Exchange XMPP contacts when invitee signs up' (#13) from feature/automatic_xmpp_roster into master
continuous-integration/drone/push Build is passing
Reviewed-on: #13
2020-12-09 20:52:14 +00:00
basti 69fc1ca57e Add production dotenv config
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2020-12-08 20:34:13 +01:00
basti ee72a32c7e Exchange XMPP contacts when invitee signs up
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2020-12-08 19:16:08 +01:00
basti 8a0d89ef60 Add webmock gem 2020-12-08 18:16:41 +01:00
basti 54af949c7d Add faraday for HTTP requests 2020-12-08 18:16:41 +01:00
basti 6dac732a7f Move invitation invalidation to service 2020-12-08 17:52:53 +01:00
basti e8c1a6066a Move user db creation to service 2020-12-08 17:39:54 +01:00
raucao 44fadb12d6 Merge pull request 'Update link to Chat service' (#11) from chore/update_service_link into master
continuous-integration/drone/push Build is passing
Reviewed-on: #11
2020-12-04 15:18:49 +00:00
basti 533452469b Update link to Chat service
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
The wiki page has been moved and improved for new users.
2020-12-04 16:15:17 +01:00
raucao efe168b205 Merge pull request 'Sign up for new account via invitation' (#9) from feature/signup_from_invite into master
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
Reviewed-on: #9
2020-12-04 12:30:28 +00:00
basti 5b6d6bbd00 Explain ApplicationService
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2020-12-04 13:29:07 +01:00
basti 458b585cdb Check off invitation signup feature in README
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2020-12-03 15:14:13 +01:00
basti f651289410 Add mailer host config for test
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
So Devise can build URLs in confirmation emails
2020-12-03 14:59:21 +01:00
basti 7ca91cf882 Don't run caching steps on CI when not master or PR
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is failing
2020-12-03 14:56:11 +01:00
basti 022094ce51 Add feature spec for whole signup process
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is failing
2020-12-03 14:50:02 +01:00
basti 2a2b0a90dc Validate email address properly 2020-12-03 14:49:37 +01:00
basti e44535daee Don't use deprecated method 2020-12-03 14:49:16 +01:00
basti c8ccb418b2 Add indexes for invitations table 2020-12-03 14:49:02 +01:00
basti a792d66c90 Show unused invitations list 2020-12-03 14:48:43 +01:00
basti f7e48ad3a6 Accept non-existing terms
Legal how does it work
2020-12-03 14:47:57 +01:00
basti 8a7d809b92 Add scopes for invitations 2020-12-03 14:04:58 +01:00
basti b8e75c7c4a Re-order services on dashboard
continuous-integration/drone/push Build is passing
Docs and forums are more important for most users than Gitea and Drone.
2020-12-03 13:50:07 +01:00
basti 7a58babd8e Remove obsolete argument
continuous-integration/drone/push Build is passing
2020-12-03 13:48:23 +01:00
basti ba31ed559a Improve wording
continuous-integration/drone/push Build is passing
2020-12-03 00:53:43 +01:00
basti 9cebfd3f58 Signup steps with validation 2020-12-03 00:53:25 +01:00
basti 7aadb5cb51 Require valid invitation to start sign-up process
continuous-integration/drone/push Build is passing
2020-12-02 19:20:01 +01:00
basti 69b99711e5 Remove fixtures, configure factory generation
continuous-integration/drone/push Build is passing
2020-12-02 15:40:41 +01:00
basti e5fe843814 Add task for generating invitations
continuous-integration/drone/push Build is passing
2020-12-02 15:23:18 +01:00
basti d7fbda0855 Add basic invitations 2020-12-02 15:22:58 +01:00
basti 18df8fe449 Add account creation service 2020-11-29 17:31:08 +01:00
464 changed files with 4653 additions and 8364 deletions
+24 -8
View File
@@ -1,3 +1,4 @@
---
kind: pipeline kind: pipeline
type: docker type: docker
name: CI build name: CI build
@@ -11,16 +12,23 @@ steps:
settings: settings:
restore: true restore: true
mount: mount:
- vendor - ./vendor
- name: rspec
image: guildeducation/rails:2.7.1-12.19.0
commands:
- bundle install --jobs=3 --retry=3 --deployment
- yarn install
- bundle exec rspec
when: when:
branch: branch:
- master - master
- name: rspec
image: guildeducation/rails:2.7.2-14.20.0
environment:
RAILS_ENV: test
commands:
- bundle config unset deployment
- bundle config set cache_all 'true'
- bundle config set cache_path 'vendor/cache'
- bundle config set with 'development test'
- bundle install --jobs=3 --retry=3
- yarn install
- rake css:build
- rake spec
- name: rebuild-cache - name: rebuild-cache
image: drillster/drone-volume-cache image: drillster/drone-volume-cache
volumes: volumes:
@@ -29,9 +37,17 @@ steps:
settings: settings:
rebuild: true rebuild: true
mount: mount:
- vendor - ./vendor
when:
branch:
- master
volumes: volumes:
- name: cache - name: cache
host: host:
path: /var/lib/drone/tmp path: /var/lib/drone/tmp
---
kind: signature
hmac: f9a8cf97f6596625721365f6238f6f298aa5a7a4de10c3fb61c57202ae9d1ee1
...
+10 -7
View File
@@ -1,8 +1,11 @@
LDAP_HOST=192.168.33.10 LDAP_HOST=localhost
LDAP_PORT=389 LDAP_PORT=389
# LDAP_ADMIN_PASSWORD=passthebutter
# Production LDAP server: LDAP_SUFFIX="dc=kosmos,dc=org"
#
# LDAP_HOST=ldap.kosmos.org EJABBERD_API_URL='https://xmpp.kosmos.org/api'
# LDAP_PORT=636
# LDAP_USE_TLS=true BTCPAY_API_URL='http://localhost:23001/api/v1'
LNDHUB_API_URL='http://localhost:3023'
LNDHUB_PUBLIC_URL='https://lndhub.kosmos.org'
+4
View File
@@ -0,0 +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'
+4
View File
@@ -0,0 +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'
+6
View File
@@ -39,3 +39,9 @@ yarn-debug.log*
# Ignore local dotenv config file # Ignore local dotenv config file
.env .env
# Ignore redis dumps from sidekiq
dump.rdb
/app/assets/builds/*
!/app/assets/builds/.keep
+1 -1
View File
@@ -1 +1 @@
2.6.1 2.7.2
+21
View File
@@ -0,0 +1,21 @@
# syntax=docker/dockerfile:1
FROM ruby:2.7
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 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"]
+31 -16
View File
@@ -2,15 +2,21 @@ source 'https://rubygems.org'
git_source(:github) { |repo| "https://github.com/#{repo}.git" } git_source(:github) { |repo| "https://github.com/#{repo}.git" }
# Bundle edge Rails instead: gem 'rails', github: 'rails/rails' # Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
gem 'rails', '~> 6.0.3', '>= 6.0.3.4' gem 'rails', '~> 7.0.2'
# Use Puma as the app server # Use Puma as the app server
gem 'puma', '~> 4.1' gem 'puma', '~> 4.1'
# Use SCSS for stylesheets # View components
gem 'sass-rails', '>= 6' gem "view_component"
# Transpile app-like JavaScript. Read more: https://github.com/rails/webpacker # Separate dependency since Rails 7.0
gem 'webpacker', '~> 4.0' gem 'sprockets-rails'
# Turbolinks makes navigating your web application faster. Read more: https://github.com/turbolinks/turbolinks # Allows custom JS build tasks to integrate with the asset pipeline
gem 'turbolinks', '~> 5' gem 'cssbundling-rails'
# Use JavaScript with ESM import maps [https://github.com/rails/importmap-rails]
gem "importmap-rails"
# Hotwire's SPA-like page accelerator [https://turbo.hotwired.dev]
gem "turbo-rails"
# Hotwire's modest JavaScript framework [https://stimulus.hotwired.dev]
gem "stimulus-rails"
# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder # Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
gem 'jbuilder', '~> 2.7' gem 'jbuilder', '~> 2.7'
# Use Redis adapter to run Action Cable in production # Use Redis adapter to run Action Cable in production
@@ -18,39 +24,48 @@ gem 'jbuilder', '~> 2.7'
# Use Active Model has_secure_password # Use Active Model has_secure_password
# gem 'bcrypt', '~> 3.1.7' # gem 'bcrypt', '~> 3.1.7'
# Reduces boot times through caching; required in config/boot.rb # Configuration
gem 'bootsnap', '>= 1.4.2', require: false gem 'dotenv-rails'
gem 'dotenv-rails', groups: [:development, :test] # 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'
# Background/scheduled jobs
gem 'sidekiq', '< 7'
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'
# Call 'byebug' anywhere in the code to stop execution and get a debugger console gem 'rspec-rails'
gem 'byebug', platforms: [:mri, :mingw, :x64_mingw] gem "byebug", "~> 11.1"
end end
group :development do group :development do
# Access an interactive console on exception pages or by calling 'console' anywhere in the code. # Access an interactive console on exception pages or by calling 'console' anywhere in the code.
gem 'web-console', '>= 3.3.0' gem 'web-console', '>= 3.3.0'
gem 'listen', '~> 3.2' gem 'listen', '~> 3.2'
# Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
gem 'spring'
gem 'spring-watcher-listen', '~> 2.0.0'
gem 'letter_opener' gem 'letter_opener'
gem 'letter_opener_web' gem 'letter_opener_web'
end end
group :test do group :test do
gem 'rspec-rails'
gem 'factory_bot_rails' gem 'factory_bot_rails'
gem 'capybara' gem 'capybara'
gem 'database_cleaner' gem 'database_cleaner'
gem 'webmock'
end end
group :production do group :production do
+245 -177
View File
@@ -1,81 +1,101 @@
GEM GEM
remote: https://rubygems.org/ remote: https://rubygems.org/
specs: specs:
actioncable (6.0.3.4) actioncable (7.0.4)
actionpack (= 6.0.3.4) actionpack (= 7.0.4)
activesupport (= 7.0.4)
nio4r (~> 2.0) nio4r (~> 2.0)
websocket-driver (>= 0.6.1) websocket-driver (>= 0.6.1)
actionmailbox (6.0.3.4) actionmailbox (7.0.4)
actionpack (= 6.0.3.4) actionpack (= 7.0.4)
activejob (= 6.0.3.4) activejob (= 7.0.4)
activerecord (= 6.0.3.4) activerecord (= 7.0.4)
activestorage (= 6.0.3.4) activestorage (= 7.0.4)
activesupport (= 6.0.3.4) activesupport (= 7.0.4)
mail (>= 2.7.1) mail (>= 2.7.1)
actionmailer (6.0.3.4) net-imap
actionpack (= 6.0.3.4) net-pop
actionview (= 6.0.3.4) net-smtp
activejob (= 6.0.3.4) 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) mail (~> 2.5, >= 2.5.4)
net-imap
net-pop
net-smtp
rails-dom-testing (~> 2.0) rails-dom-testing (~> 2.0)
actionpack (6.0.3.4) actionpack (7.0.4)
actionview (= 6.0.3.4) actionview (= 7.0.4)
activesupport (= 6.0.3.4) activesupport (= 7.0.4)
rack (~> 2.0, >= 2.0.8) rack (~> 2.0, >= 2.2.0)
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 (7.0.4)
actionpack (= 6.0.3.4) actionpack (= 7.0.4)
activerecord (= 6.0.3.4) activerecord (= 7.0.4)
activestorage (= 6.0.3.4) activestorage (= 7.0.4)
activesupport (= 6.0.3.4) activesupport (= 7.0.4)
globalid (>= 0.6.0)
nokogiri (>= 1.8.5) nokogiri (>= 1.8.5)
actionview (6.0.3.4) actionview (7.0.4)
activesupport (= 6.0.3.4) activesupport (= 7.0.4)
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 (7.0.4)
activesupport (= 6.0.3.4) activesupport (= 7.0.4)
globalid (>= 0.3.6) globalid (>= 0.3.6)
activemodel (6.0.3.4) activemodel (7.0.4)
activesupport (= 6.0.3.4) activesupport (= 7.0.4)
activerecord (6.0.3.4) activerecord (7.0.4)
activemodel (= 6.0.3.4) activemodel (= 7.0.4)
activesupport (= 6.0.3.4) activesupport (= 7.0.4)
activestorage (6.0.3.4) activestorage (7.0.4)
actionpack (= 6.0.3.4) actionpack (= 7.0.4)
activejob (= 6.0.3.4) activejob (= 7.0.4)
activerecord (= 6.0.3.4) activerecord (= 7.0.4)
marcel (~> 0.3.1) activesupport (= 7.0.4)
activesupport (6.0.3.4) marcel (~> 1.0)
mini_mime (>= 1.1.0)
activesupport (7.0.4)
concurrent-ruby (~> 1.0, >= 1.0.2) concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 0.7, < 2) i18n (>= 1.6, < 2)
minitest (~> 5.1) minitest (>= 5.1)
tzinfo (~> 1.1) tzinfo (~> 2.0)
zeitwerk (~> 2.2, >= 2.2.2) addressable (2.8.1)
addressable (2.7.0) public_suffix (>= 2.0.2, < 6.0)
public_suffix (>= 2.0.2, < 5.0) bcrypt (3.1.18)
bcrypt (3.1.16)
bindex (0.8.1) bindex (0.8.1)
bootsnap (1.5.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.38.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)
concurrent-ruby (1.1.10)
connection_pool (2.3.0)
crack (0.4.5)
rexml
crass (1.0.6) crass (1.0.6)
database_cleaner (1.8.5) cssbundling-rails (1.1.1)
devise (4.7.3) railties (>= 6.0.0)
database_cleaner (2.0.1)
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.1)
bcrypt (~> 3.0) bcrypt (~> 3.0)
orm_adapter (~> 0.1) orm_adapter (~> 0.1)
railties (>= 4.1.0) railties (>= 4.1.0)
@@ -84,191 +104,239 @@ GEM
devise_ldap_authenticatable (0.8.7) devise_ldap_authenticatable (0.8.7)
devise (>= 3.4.1) devise (>= 3.4.1)
net-ldap (>= 0.16.0) net-ldap (>= 0.16.0)
diff-lcs (1.4.4) diff-lcs (1.5.0)
dotenv (2.7.2) dotenv (2.8.1)
dotenv-rails (2.7.2) dotenv-rails (2.8.1)
dotenv (= 2.7.2) dotenv (= 2.8.1)
railties (>= 3.2, < 6.1)
erubi (1.9.0)
factory_bot (6.1.0)
activesupport (>= 5.0.0)
factory_bot_rails (6.1.0)
factory_bot (~> 6.1.0)
railties (>= 5.0.0)
ffi (1.13.1)
globalid (0.4.2)
activesupport (>= 4.2.0)
i18n (1.8.5)
concurrent-ruby (~> 1.0)
jbuilder (2.10.1)
activesupport (>= 5.0.0)
launchy (2.4.3)
addressable (~> 2.3)
letter_opener (1.7.0)
launchy (~> 2.2)
letter_opener_web (1.3.4)
actionmailer (>= 3.2)
letter_opener (~> 1.0)
railties (>= 3.2) railties (>= 3.2)
listen (3.2.1) erubi (1.11.0)
et-orbi (1.2.7)
tzinfo
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.7.1)
faraday-net_http (>= 2.0, < 3.1)
ruby2_keywords (>= 0.0.4)
faraday-net_http (3.0.2)
ffi (1.15.5)
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.12.0)
concurrent-ruby (~> 1.0)
importmap-rails (1.1.5)
actionpack (>= 6.0.0)
railties (>= 6.0.0)
jbuilder (2.11.5)
actionview (>= 5.0.0)
activesupport (>= 5.0.0)
launchy (2.5.0)
addressable (~> 2.7)
letter_opener (1.8.1)
launchy (>= 2.2, < 3)
letter_opener_web (2.0.0)
actionmailer (>= 5.2)
letter_opener (~> 1.7)
railties (>= 5.2)
rexml
listen (3.7.1)
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 (1.1.0)
loofah (2.19.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) mini_portile2 (2.8.0)
mini_portile2 (2.4.0) minitest (5.16.3)
minitest (5.14.2) net-imap (0.3.1)
msgpack (1.3.3) net-protocol
net-ldap (0.16.3) net-ldap (0.17.1)
nio4r (2.5.4) net-pop (0.1.2)
nokogiri (1.10.10) net-protocol
mini_portile2 (~> 2.4.0) net-protocol (0.1.3)
timeout
net-smtp (0.3.3)
net-protocol
nio4r (2.5.8)
nokogiri (1.13.9)
mini_portile2 (~> 2.8.0)
racc (~> 1.4)
nokogiri (1.13.9-x86_64-linux)
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 (5.0.0)
puma (4.3.6) puma (4.3.12)
nio4r (~> 2.0) nio4r (~> 2.0)
rack (2.2.3) raabro (1.4.0)
rack-proxy (0.6.5) racc (1.6.0)
rack rack (2.2.4)
rack-test (1.1.0) rack-test (2.0.2)
rack (>= 1.0, < 3) rack (>= 1.3)
rails (6.0.3.4) rails (7.0.4)
actioncable (= 6.0.3.4) actioncable (= 7.0.4)
actionmailbox (= 6.0.3.4) actionmailbox (= 7.0.4)
actionmailer (= 6.0.3.4) actionmailer (= 7.0.4)
actionpack (= 6.0.3.4) actionpack (= 7.0.4)
actiontext (= 6.0.3.4) actiontext (= 7.0.4)
actionview (= 6.0.3.4) actionview (= 7.0.4)
activejob (= 6.0.3.4) activejob (= 7.0.4)
activemodel (= 6.0.3.4) activemodel (= 7.0.4)
activerecord (= 6.0.3.4) activerecord (= 7.0.4)
activestorage (= 6.0.3.4) activestorage (= 7.0.4)
activesupport (= 6.0.3.4) activesupport (= 7.0.4)
bundler (>= 1.3.0) bundler (>= 1.15.0)
railties (= 6.0.3.4) railties (= 7.0.4)
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.3)
loofah (~> 2.3) loofah (~> 2.3)
railties (6.0.3.4) railties (7.0.4)
actionpack (= 6.0.3.4) actionpack (= 7.0.4)
activesupport (= 6.0.3.4) activesupport (= 7.0.4)
method_source method_source
rake (>= 0.8.7) rake (>= 12.2)
thor (>= 0.20.3, < 2.0) thor (~> 1.0)
rake (13.0.1) zeitwerk (~> 2.5)
rb-fsevent (0.10.4) rake (13.0.6)
rb-fsevent (0.11.2)
rb-inotify (0.10.1) rb-inotify (0.10.1)
ffi (~> 1.0) ffi (~> 1.0)
regexp_parser (1.8.2) 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) 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)
rspec-support (~> 3.10.0) rqrcode (2.1.2)
rspec-expectations (3.10.0) chunky_png (~> 1.0)
rqrcode_core (~> 1.0)
rqrcode_core (1.2.0)
rspec-core (3.12.0)
rspec-support (~> 3.12.0)
rspec-expectations (3.12.0)
diff-lcs (>= 1.2.0, < 2.0) diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.10.0) rspec-support (~> 3.12.0)
rspec-mocks (3.10.0) rspec-mocks (3.12.0)
diff-lcs (>= 1.2.0, < 2.0) diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.10.0) rspec-support (~> 3.12.0)
rspec-rails (4.0.1) rspec-rails (6.0.1)
actionpack (>= 4.2) actionpack (>= 6.1)
activesupport (>= 4.2) activesupport (>= 6.1)
railties (>= 4.2) railties (>= 6.1)
rspec-core (~> 3.9) rspec-core (~> 3.11)
rspec-expectations (~> 3.9) rspec-expectations (~> 3.11)
rspec-mocks (~> 3.9) rspec-mocks (~> 3.11)
rspec-support (~> 3.9) rspec-support (~> 3.11)
rspec-support (3.10.0) rspec-support (3.12.0)
sass-rails (6.0.0) ruby2_keywords (0.0.5)
sassc-rails (~> 2.1, >= 2.1.1) rufus-scheduler (3.8.2)
sassc (2.4.0) fugit (~> 1.1, >= 1.1.6)
ffi (~> 1.9) sidekiq (6.5.5)
sassc-rails (2.1.2) connection_pool (>= 2.2.2)
railties (>= 4.0.0) rack (~> 2.0)
sassc (>= 2.0) redis (>= 4.5.0)
sprockets (> 3.0) sidekiq-scheduler (4.0.3)
sprockets-rails redis (>= 4.2.0)
tilt rufus-scheduler (~> 3.2)
spring (2.1.1) sidekiq (>= 4, < 7)
spring-watcher-listen (2.0.1) tilt (>= 1.4.0)
listen (>= 2.7, < 4.0) sprockets (4.1.1)
spring (>= 1.2, < 3.0)
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.2)
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.5.4)
thor (1.0.1) mini_portile2 (~> 2.8.0)
thread_safe (0.3.6) sqlite3 (1.5.4-x86_64-linux)
tilt (2.0.10) stimulus-rails (1.2.1)
turbolinks (5.2.1) railties (>= 6.0.0)
turbolinks-source (~> 5.2) thor (1.2.1)
turbolinks-source (5.2.0) tilt (2.0.11)
tzinfo (1.2.7) timeout (0.3.0)
thread_safe (~> 0.1) turbo-rails (1.3.2)
actionpack (>= 6.0.0)
activejob (>= 6.0.0)
railties (>= 6.0.0)
tzinfo (2.0.5)
concurrent-ruby (~> 1.0)
view_component (2.78.0)
activesupport (>= 5.0.0, < 8.0)
concurrent-ruby (~> 1.0)
method_source (~> 1.0)
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)
webpacker (4.3.0) webmock (3.18.1)
activesupport (>= 4.2) addressable (>= 2.8.0)
rack-proxy (>= 0.6.1) crack (>= 0.3.2)
railties (>= 4.2) hashdiff (>= 0.4.0, < 2.0.0)
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.6.6)
PLATFORMS PLATFORMS
ruby ruby
x86_64-linux
DEPENDENCIES DEPENDENCIES
bootsnap (>= 1.4.2) byebug (~> 11.1)
byebug
capybara capybara
cssbundling-rails
database_cleaner database_cleaner
devise devise
devise_ldap_authenticatable devise_ldap_authenticatable
dotenv-rails dotenv-rails
factory_bot_rails factory_bot_rails
faraday
importmap-rails
jbuilder (~> 2.7) jbuilder (~> 2.7)
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 (~> 7.0.2)
rqrcode (~> 2.0)
rspec-rails rspec-rails
sass-rails (>= 6) sidekiq (< 7)
spring sidekiq-scheduler
spring-watcher-listen (~> 2.0.0) sprockets-rails
sqlite3 (~> 1.4) sqlite3 (~> 1.4)
turbolinks (~> 5) stimulus-rails
turbo-rails
tzinfo-data tzinfo-data
view_component
warden warden
web-console (>= 3.3.0) web-console (>= 3.3.0)
webpacker (~> 4.0) webmock
BUNDLED WITH BUNDLED WITH
2.0.2 2.3.7
+2
View File
@@ -0,0 +1,2 @@
web: bin/rails server -b 0.0.0.0 -p 3000
css: yarn build:css --watch
+40 -21
View File
@@ -1,25 +1,10 @@
[![Build Status](https://drone.kosmos.org/api/badges/kosmos/akkounts/status.svg)](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
* [ ] List my donations
* [ ] Invite new users from your account
* [ ] Sign up for a new account via invite
* [ ] 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
@@ -34,24 +19,53 @@ Setting up local database (SQLite):
bundle exec rails db:create bundle exec rails db:create
bundle exec rails db:migrate bundle exec rails db:migrate
Running the dev server: Running the dev server and auto-building CSS files on change:
bundle exec rails server bin/dev
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
### Docker (Compose)
There is a working Dockr 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`.
### LDAP server ### LDAP server
TODO make it easy to run a local Kosmos LDAP server for development, without See the previous section for quickly spinning up an LDAP server with Docker (or
manual LDIF imports etc. (or provide a staging instance) edit your environment configuration to use an existing one).
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:seed
The seeds task will first delete any existing entries in the directory tree
("dc=kosmos,dc=org"), and then create our example/development entries.
## Documentation ## Documentation
* [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/)
### Testing ### Testing
* [RSpec](https://rspec.info/documentation/) * [RSpec](https://rspec.info/documentation/)
@@ -62,6 +76,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/)
View File
+2 -1
View File
@@ -1,2 +1,3 @@
//= link_tree ../images //= link_tree ../images
//= link_directory ../stylesheets .css //= link_tree ../../javascript .js
//= link_tree ../builds
-33
View File
@@ -1,33 +0,0 @@
$breakpoints-max: (
small: 600px,
medium: 960px,
large: 1280px
);
$breakpoints-min: (
small: 601px,
medium: 961px,
large: 1281px
);
@mixin media-max($screen-size) {
@if map-has-key($breakpoints-max, $screen-size) {
@media (max-width: map-get($breakpoints-max, $screen-size)) {
@content;
}
} @else {
// Debugging
@warn "'#{$screen-size}' has not been declared as a breakpoint."
}
}
@mixin media-min($screen-size) {
@if map-has-key($breakpoints-min, $screen-size) {
@media (min-width: map-get($breakpoints-min, $screen-size)) {
@content;
}
} @else {
// Debugging
@warn "'#{$screen-size}' has not been declared as a breakpoint."
}
}
-13
View File
@@ -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;
}
-15
View File
@@ -1,15 +0,0 @@
/*
* This is a manifest file that'll be compiled into application.css, which will include all the files
* listed below.
*
* Any CSS and SCSS file within this directory, lib/assets/stylesheets, or any plugin's
* vendor/assets/stylesheets directory can be referenced here using a relative path.
*
* You're free to add application-wide styles to this file and they'll appear at the bottom of the
* compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
* files in this directory. Styles in this file should be added after the last require_* statement.
* It is generally better to create a new file per style scope.
*
*= require_tree .
*= require_self
*/
@@ -0,0 +1,10 @@
@import "tailwindcss/base";
@import "tailwindcss/components";
@import "tailwindcss/utilities";
@import "components/base";
@import "components/buttons";
@import "components/forms";
@import "components/links";
@import "components/notifications";
@import "components/tables";
@@ -0,0 +1,46 @@
@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');
}
body#admin {
background-image: linear-gradient(35deg, rgba(255,0,255,0.2) 0, rgba(153,12,14,0.9) 100%), url('/img/bg-1.jpg');
}
h1 {
@apply text-3xl uppercase;
}
h2 {
@apply text-2xl mb-8;
}
h3 {
@apply text-xl mb-6;
}
main section {
@apply pt-8 sm:pt-12;
}
main section:first-of-type {
@apply pt-0;
}
main p {
@apply mb-4 leading-6;
}
main ul {
@apply mb-6;
}
main ul li {
@apply leading-6;
}
}
@@ -0,0 +1,31 @@
@layer components {
.btn {
@apply font-semibold rounded-md leading-none cursor-pointer text-center
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;
}
}
@@ -0,0 +1,16 @@
@layer components {
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-transparent focus:ring-2
focus:ring-blue-600 focus:ring-opacity-75;
}
.field_with_errors {
@apply inline-block;
}
.error-msg {
@apply text-red-700;
}
}
@@ -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;
}
}
}
@@ -0,0 +1,39 @@
@layer components {
@keyframes notification-countdown {
from { width: 100%; }
to { width: 0; }
}
.notification-enter {
@apply transform ease-out duration-300 transition;
}
.notification-enter-from {
@apply translate-y-2 opacity-0;
}
.notification-enter-to {
@apply translate-y-0 opacity-100;
}
@screen sm {
.notification-enter-from {
@apply translate-y-0 translate-x-2;
}
.notification-enter-to {
@apply translate-x-0;
}
}
.notification-leave {
@apply transition ease-in duration-100;
}
.notification-leave-from {
@apply opacity-100;
}
.notification-leave-to {
@apply opacity-0;
}
}
@@ -0,0 +1,22 @@
@layer components {
table {
@apply w-full;
}
table thead tr {
@apply text-left;
}
table th {
@apply pb-3.5 text-sm font-normal uppercase text-gray-500;
}
table th:not(:last-of-type),
table td:not(:last-of-type) {
@apply pr-2;
}
table td {
@apply py-2;
}
}
-20
View File
@@ -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;
}
-173
View File
@@ -1,173 +0,0 @@
@import "mediaqueries";
$content-width: 800px;
$content-max-width: 100%;
body {
}
#wrapper {
width: 100%;
text-align: center;
> header {
margin: 0 auto;
padding: 4rem 0;
text-align: center;
background: #0d4f99;
background: linear-gradient(35deg, #8955a0 0, #0d4f99 100%);
@include media-max(small) {
padding: 3rem 0;
}
h1 {
font-size: 1.8rem;
color: #fff;
span.project-name {
display: none;
// font-size: .5em;
// text-transform: none;
// vertical-align: super;
}
span.beta {
font-size: .5em;
font-style: italic;
text-transform: none;
vertical-align: super;
}
span.bolt {
color: #ffd000;
}
}
p.current-user {
margin-top: 2rem;
color: rgba(255,255,255,0.6);
@include media-max(small) {
font-size: 0.85rem;
}
strong {
font-weight: 400;
color: #fff;
// color: #ffd000;
// color: #ccff40;
}
}
a {
color: rgba(255,255,255,0.6);
transition: color 0.1s linear;
&:hover, &:active {
color: #fff;
}
}
}
}
.flash-msg {
width: 100%;
text-align: center;
padding: 2rem 0;
&.notice {
background: #efffc4;
}
&.alert {
background: #fff4c2;
}
}
main {
width: $content-width;
max-width: $content-max-width;
margin: 4rem auto;
text-align: left;
@include media-max(medium) {
max-width: 90%;
}
@include media-max(small) {
margin: 3rem auto;
}
h2, h3 {
margin-bottom: 1.5em;
}
h2 {
font-size: 1.5rem;
}
h3 {
font-size: 1.25rem;
}
p {
line-height: 1.5rem;
margin-bottom: 1rem;
&.notice {
text-align: center;
}
}
ul {
margin-bottom: 1.5rem;
li {
line-height: 1.5rem;
}
}
th, td {
line-height: 1.5rem;
padding-right: 1rem;
}
section {
border-bottom: 1px dotted #ccc;
padding-bottom: 4rem;
margin-bottom: 4rem;
@include media-max(small) {
padding-bottom: 3rem;
margin-bottom: 3rem;
}
}
}
.grid {
display: grid;
&.services {
grid-template-columns: 1fr 1fr 1fr;
grid-row-gap: 1rem;
grid-column-gap: 2rem;
@include media-max(small) {
grid-template-columns: 1fr;
}
margin-top: 3rem;
h3 {
margin-bottom: 1rem;
}
.grid-item {
p {
color: #888;
font-size: 0.85rem;
}
}
}
}
@@ -0,0 +1,7 @@
<header class="py-10">
<div class="max-w-xl mx-auto px-4 sm:px-6 lg:px-8">
<h1 class="text-3xl font-bold text-white text-center">
<%= @title %>
</h1>
</div>
</header>
@@ -0,0 +1,7 @@
# frozen_string_literal: true
class HeaderCompactComponent < ViewComponent::Base
def initialize(title:)
@title = title
end
end
+7
View File
@@ -0,0 +1,7 @@
<header class="py-10">
<div class="max-w-6xl mx-auto px-4 sm:px-6 lg:px-8">
<h1 class="text-3xl font-bold text-white">
<%= @title %>
</h1>
</div>
</header>
+7
View File
@@ -0,0 +1,7 @@
# frozen_string_literal: true
class HeaderComponent < ViewComponent::Base
def initialize(title:)
@title = title
end
end
@@ -0,0 +1,5 @@
<main class="w-full max-w-xl mx-auto pb-12 px-4 sm:px-6 lg:px-8">
<div class="bg-white rounded-lg shadow px-6 sm:px-12 py-8 sm:py-12">
<%= content %>
</div>
</main>
+5
View File
@@ -0,0 +1,5 @@
# frozen_string_literal: true
class MainCompactComponent < ViewComponent::Base
end
@@ -0,0 +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">
<%= content %>
</div>
</main>
+5
View File
@@ -0,0 +1,5 @@
# frozen_string_literal: true
class MainSimpleComponent < ViewComponent::Base
end
@@ -0,0 +1,15 @@
<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">
<aside class="py-6 sm:py-8 lg:col-span-3">
<nav class="space-y-1">
<%= render partial: @sidenav_partial %>
</nav>
</aside>
<div class="lg:col-span-9 px-6 sm:px-12 py-8 sm:pt-10 sm:pb-12">
<%= content %>
</div>
</div>
</div>
</main>
@@ -0,0 +1,7 @@
# frozen_string_literal: true
class MainWithSidenavComponent < ViewComponent::Base
def initialize(sidenav_partial:)
@sidenav_partial = sidenav_partial
end
end
@@ -0,0 +1,49 @@
<div class="flash-msg <%= @type %> hidden max-w-sm w-full bg-white shadow-lg rounded-lg pointer-events-auto mt-4"
data-turbo="false"
data-notification-action-url="<%= @data.dig(:action, :url) %>"
data-notification-action-method="<%= @data.dig(:action, :method) %>"
data-notification-timeout="<%= @data[:timeout] %>"
data-controller="notification">
<div class="rounded-lg shadow-xs overflow-hidden">
<div class="p-4">
<div class="flex items-start">
<div class="flex-shrink-0">
<span class="inline-block h-6 w-6 <%= @icon_color_class %>">
<%= render "icons/#{@icon_name}" %>
</span>
</div>
<div class="ml-3 w-0 flex-1 pt-0.5">
<p class="text-sm leading-5 font-medium text-gray-900">
<%= @data[:title] %>
</p>
<% if @data[:body].present? %>
<p class="mt-1 text-sm leading-5 text-gray-500">
<%= @data[:body] %>
</p>
<% end %>
<% if @data[:action].present? %>
<div class="mt-2" data-notification-target="buttons">
<a data-turbo-frame="_top" <% if @data.dig(:action, :method) == 'get' %> href="<%= @data.dig(:action, :url) %>" <% else %> href="#" data-action="notification#run" <% end %> class="text-sm leading-5 font-medium text-indigo-600 hover:text-indigo-500 focus:outline-none focus:underline transition ease-in-out duration-150">
<%= @data.dig(:action, :name) %>
</a>
<button data-action="notification#close" class="ml-6 text-sm leading-5 font-medium text-gray-700 hover:text-gray-500 focus:outline-none focus:underline transition ease-in-out duration-150">
<%= t('.dismiss') %>
</button>
</div>
<% end %>
</div>
<div class="ml-4 flex-shrink-0 flex">
<button class="inline-flex text-gray-400 focus:outline-none focus:text-gray-500 transition ease-in-out duration-150" data-action="notification#close">
<!-- Heroicon name: solid/x -->
<svg class="h-5 w-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd" />
</svg>
</button>
</div>
</div>
</div>
<% if @data[:countdown] %>
<div class="bg-indigo-600 rounded-lg h-1 w-0" data-notification-target="countdown"></div>
<% end %>
</div>
</div>
+54
View File
@@ -0,0 +1,54 @@
# frozen_string_literal: true
# @param type [String] Classic notification type `error`, `alert` and `info` + custom `success`
# @param data [String, Hash] `String` for backward compatibility,
# `Hash` for the new functionality `{title: '', body: '', timeout: 5, countdown: false, action: { url: '', method: '', name: ''}}`.
# The `title` attribute for `Hash` is mandatory.
class NotificationComponent < ViewComponent::Base
def initialize(type:, data:)
@type = type
@data = prepare_data(data)
@icon_name = icon_name
@icon_color_class = icon_color_class
@data[:timeout] ||= 5
@data[:action][:method] ||= "get" if @data[:action]
end
private
def prepare_data(data)
case data
when Hash
data
else
{ title: data }
end
end
def icon_name
case @type
when 'success'
'check-circle'
when 'error'
'alert-octagon'
when 'alert'
'alert-octagon'
else
'info'
end
end
def icon_color_class
case @type
when 'success'
'text-emerald-500'
when 'error'
'text-rose-600'
when 'alert'
'text-rose-600'
else
'text-gray-400'
end
end
end
@@ -0,0 +1,4 @@
<%= link_to @path, class: @link_class do %>
<%= render partial: "icons/#{@icon}", locals: { custom_class: @icon_class } %>
<span class="truncate"><%= @name %></span>
<% end %>
+33
View File
@@ -0,0 +1,33 @@
# frozen_string_literal: true
class SidenavLinkComponent < ViewComponent::Base
def initialize(name:, path:, icon:, active: false, disabled: false)
@name = name
@path = path
@icon = icon
@active = active
@disabled = disabled
@link_class = class_names_link(path)
@icon_class = class_names_icon(path)
end
def class_names_link(path)
if @active
"bg-teal-50 border-teal-500 text-teal-700 hover:bg-teal-50 hover:text-teal-700 group border-l-4 px-4 py-2 flex items-center text-base font-medium"
elsif @disabled
"border-transparent text-gray-400 hover:bg-gray-50 group border-l-4 px-4 py-2 flex items-center text-base font-medium"
else
"border-transparent text-gray-900 hover:bg-gray-50 hover:text-gray-900 group border-l-4 px-4 py-2 flex items-center text-base font-medium"
end
end
def class_names_icon(path)
if @active
"text-teal-500 group-hover:text-teal-500 flex-shrink-0 -ml-1 mr-3 h-6 w-6"
elsif @disabled
"text-gray-300 group-hover:text-gray-300 flex-shrink-0 -ml-1 mr-3 h-6 w-6"
else
"text-gray-400 group-hover:text-gray-500 flex-shrink-0 -ml-1 mr-3 h-6 w-6"
end
end
end
@@ -0,0 +1,18 @@
<section class="w-full grid grid-cols-1 md:grid-cols-12 md:mb-0">
<div class="md:col-span-8">
<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>
@@ -0,0 +1,8 @@
# frozen_string_literal: true
class WalletSummaryComponent < ViewComponent::Base
def initialize(balance:)
@balance = balance
end
end
+5
View File
@@ -2,5 +2,10 @@ class Admin::BaseController < ApplicationController
before_action :authenticate_user! before_action :authenticate_user!
before_action :authorize_admin before_action :authorize_admin
before_action :set_context
def set_context
@context = :admin
end
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
@@ -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
@@ -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
+5
View File
@@ -0,0 +1,5 @@
class Api::BaseController < ApplicationController
layout false
end
+13
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
@@ -9,6 +9,12 @@ class ApplicationController < ActionController::Base
end end
end end
def require_user_signed_out
if user_signed_in?
redirect_to root_path and return
end
end
def authorize_admin def authorize_admin
http_status :forbidden unless current_user.is_admin? http_status :forbidden unless current_user.is_admin?
end end
+1
View File
@@ -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
View 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
+48
View File
@@ -0,0 +1,48 @@
class InvitationsController < ApplicationController
before_action :require_user_signed_in, except: ["show"]
before_action :require_user_signed_out, only: ["show"]
# GET /invitations
def index
@invitations_unused = current_user.invitations.unused
@invitations_used = current_user.invitations.used
@current_section = :invitations
end
# GET /invitations/a-random-invitation-token
def show
token = session[:invitation_token] = params[:id]
if Invitation.where(token: token, used_at: nil).exists?
redirect_to signup_path and return
else
flash.now[:alert] = "This invitation either doesn't exist or has already been used."
http_status :unauthorized
end
end
# POST /invitations
def create
@invitation = Invitation.new(user: current_user)
respond_to do |format|
if @invitation.save
format.html { redirect_to @invitation, notice: 'Invitation was successfully created.' }
format.json { render :show, status: :created, location: @invitation }
else
format.html { render :new }
format.json { render json: @invitation.errors, status: :unprocessable_entity }
end
end
end
# DELETE /invitations/1
def destroy
@invitation = current_user.invitations.find(params[:id])
@invitation.destroy
respond_to do |format|
format.html { redirect_to invitations_url }
format.json { head :no_content }
end
end
end
+75
View File
@@ -0,0 +1,75 @@
class LnurlpayController < ApplicationController
before_action :find_user_by_address
MIN_SATS = 10
MAX_SATS = 1_000_000
MAX_COMMENT_CHARS = 100
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: MAX_COMMENT_CHARS
}
end
def invoice
amount = params[:amount].to_i / 1000 # msats
address = params[:address]
comment = params[:comment] || ""
if !valid_amount?(amount)
render json: { status: "ERROR", reason: "Invalid amount" }
return
end
if !valid_comment?(comment)
render json: { status: "ERROR", reason: "Comment too long" }
return
end
memo = "Sats for #{address}"
memo = "#{memo}: \"#{comment}\"" if comment.present?
payment_request = @user.ln_create_invoice({
amount: amount, # we create invoices in sats
memo: memo,
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
def valid_comment?(comment)
comment.length <= MAX_COMMENT_CHARS
end
end
+7
View File
@@ -0,0 +1,7 @@
class SecurityController < ApplicationController
before_action :require_user_signed_in
def index
@current_section = :security
end
end
+111
View File
@@ -0,0 +1,111 @@
class SignupController < ApplicationController
before_action :require_user_signed_out
before_action :require_invitation
before_action :set_invitation
before_action :set_new_user, only: ["steps", "validate"]
before_action :set_context
def index
@invited_by_name = @invitation.user.address
end
def steps
@step = params[:step].to_i
http_status :not_found unless [1,2,3].include?(@step)
@validation_error = session[:validation_error]
end
def validate
session[:validation_error] = nil
case user_params.keys.first
when "cn"
@user.cn = user_params[:cn]
@user.valid?
session[:new_user] = @user
if @user.errors[:cn].present?
session[:validation_error] = @user.errors[:cn].first # Store user including validation errors
redirect_to signup_steps_path(1) and return
else
redirect_to signup_steps_path(2) and return
end
when "email"
@user.email = user_params[:email]
@user.valid?
session[:new_user] = @user
if @user.errors[:email].present?
session[:validation_error] = @user.errors[:email].first # Store user including validation errors
redirect_to signup_steps_path(2) and return
else
redirect_to signup_steps_path(3) and return
end
when "password"
@user.password = user_params[:password]
@user.password_confirmation = user_params[:password]
@user.valid?
session[:new_user] = @user
if @user.errors[:password].present?
session[:validation_error] = @user.errors[:password].first # Store user including validation errors
redirect_to signup_steps_path(3) and return
else
complete_signup
msg = "Almost done! We have sent you an email to confirm your address."
redirect_to(check_your_email_path, notice: msg) and return
end
end
end
private
def user_params
params.require(:user).permit(:cn, :email, :password)
end
def require_invitation
if session[:invitation_token].blank?
flash.now[:alert] = "You need an invitation to sign up for an account."
http_status :unauthorized
elsif !valid_invitation?(session[:invitation_token])
flash.now[:alert] = "This invitation either doesn't exist or has already been used."
http_status :unauthorized
end
@invitation = Invitation.find_by(token: session[:invitation_token])
end
def valid_invitation?(token)
Invitation.where(token: session[:invitation_token], used_at: nil).exists?
end
def set_invitation
@invitation = Invitation.find_by(token: session[:invitation_token])
end
def set_new_user
if session[:new_user].present?
@user = User.new(session[:new_user])
else
@user = User.new(ou: "kosmos.org")
end
end
def complete_signup
session[:new_user] = nil
session[:validation_error] = nil
CreateAccount.call(
username: @user.cn,
domain: "kosmos.org",
email: @user.email,
password: @user.password,
invitation: @invitation
)
end
def set_context
@context = :signup
end
end
+81
View File
@@ -0,0 +1,81 @@
require "rqrcode"
class WalletController < ApplicationController
before_action :require_user_signed_in
before_action :authenticate_with_lndhub
before_action :set_current_section
before_action :fetch_balance
def index
@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'
}
)
end
def transactions
@transactions = fetch_transactions
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 set_current_section
@current_section = :wallet
end
def fetch_balance
lndhub = Lndhub.new
data = lndhub.balance @ln_auth_token
@balance = data["BTC"]["AvailableBalance"] rescue nil
end
def fetch_transactions
lndhub = Lndhub.new
txs = lndhub.gettxs @ln_auth_token
invoices = lndhub.getuserinvoices(@ln_auth_token).select{|i| i["ispaid"]}
process_transactions(txs + invoices)
end
def process_transactions(txs)
txs.collect do |tx|
if tx["type"] == "bitcoind_tx"
tx["amount_sats"] = (tx["amount"] * 100000000).to_i
tx["datetime"] = Time.at(tx["time"].to_i)
tx["title"] = "Received"
tx["description"] = "On-chain topup"
tx["received"] = true
else
tx["amount_sats"] = tx["value"] || tx["amt"]
tx["datetime"] = Time.at(tx["timestamp"].to_i)
tx["title"] = tx["type"] == "paid_invoice" ? "Sent" : "Received"
tx["description"] = tx["memo"] || tx["description"]
tx["received"] = tx["type"] == "user_invoice"
end
end
txs.sort{ |a,b| b["datetime"] <=> a["datetime"] }
end
end
+12
View File
@@ -1,2 +1,14 @@
module ApplicationHelper module ApplicationHelper
def sats_to_btc(sats)
sats.to_f / 100000000
end
def main_nav_class(current_section, link_to_section)
if current_section == link_to_section
"bg-gray-900/50 text-white px-3 py-2 rounded-md font-medium text-base md:text-sm block md:inline-block"
else
"text-gray-300 hover:bg-gray-900/30 hover:text-white active:bg-gray-900/30 active:text-white px-3 py-2 rounded-md font-medium text-base md:text-sm block md:inline-block"
end
end
end end
+2
View File
@@ -0,0 +1,2 @@
module DonationsHelper
end
+2
View File
@@ -0,0 +1,2 @@
module InvitationsHelper
end
+2
View File
@@ -0,0 +1,2 @@
module LnurlpayHelper
end
+2
View File
@@ -0,0 +1,2 @@
module SignupHelper
end
+2
View File
@@ -0,0 +1,2 @@
module WalletHelper
end
+3
View File
@@ -0,0 +1,3 @@
// Configure your import map in config/importmap.rb. Read more: https://github.com/rails/importmap-rails
import "@hotwired/turbo-rails"
import "controllers"
-6
View File
@@ -1,6 +0,0 @@
// Action Cable provides the framework to deal with WebSockets in Rails.
// You can generate new channels where WebSocket features live using the `rails generate channel` command.
import { createConsumer } from "@rails/actioncable"
export default createConsumer()
-5
View File
@@ -1,5 +0,0 @@
// Load all the channels within this directory and all subdirectories.
// Channel files must be named *_channel.js.
const channels = require.context('.', true, /_channel\.js$/)
channels.keys().forEach(channels)
@@ -0,0 +1,9 @@
import { Application } from "@hotwired/stimulus"
const application = Application.start()
// Configure Stimulus development experience
application.debug = false
window.Stimulus = application
export { application }
+11
View File
@@ -0,0 +1,11 @@
// Import and register all your controllers from the importmap under controllers/*
import { application } from "controllers/application"
// Eager load all controllers defined in the import map under controllers/**/*_controller
import { eagerLoadControllersFrom } from "@hotwired/stimulus-loading"
eagerLoadControllersFrom("controllers", application)
// Lazy load controllers as they appear in the DOM (remember not to preload controllers in import map!)
// import { lazyLoadControllersFrom } from "@hotwired/stimulus-loading"
// lazyLoadControllersFrom("controllers", application)
@@ -0,0 +1,110 @@
import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
static targets = ["buttons", "countdown"]
connect() {
const timeoutSeconds = parseInt(this.data.get("timeout"));
setTimeout(() => {
this.element.classList.remove('hidden');
this.element.classList.add('notification-enter', 'notification-enter-from');
// Trigger transition
setTimeout(() => {
this.element.classList.add('notification-enter-to');
}, 100);
// Trigger countdown
if (this.hasCountdownTarget) {
this.countdownTarget.style.animation = 'notification-countdown linear ' + timeoutSeconds + 's';
}
}, 500);
this.timeoutId = setTimeout(() => {
this.close();
}, timeoutSeconds * 1000 + 500);
}
run(e) {
e.preventDefault();
this.stop();
let _this = this;
this.buttonsTarget.innerHTML = '<span class="text-sm leading-5 font-medium text-grey-700">Processing...</span>';
// Call the action
fetch(this.data.get("action-url"), {
method: this.data.get("action-method").toUpperCase(),
dataType: 'script',
credentials: "include",
headers: {
"X-CSRF-Token": this.csrfToken
},
})
.then(response => {
if (!response.ok) {
throw Error(response.statusText);
}
return response;
})
.then(response => response.json())
.then(data => {
// Set new content
_this.buttonsTarget.innerHTML = '<span class="text-sm leading-5 font-medium text-green-700">' + data.message + '</span>';
// Remove hidden class and display the record
if (data.inline) {
document.getElementById(data.dom_id).classList.toggle('hidden');
}
// Close
setTimeout(() => {
if (data.inline) {
// Just close the notification
_this.close();
} else {
// Reload the page using Turbo
window.Turbo.visit(window.location.toString(), {action: 'replace'})
}
}, 1000);
})
.catch(error => {
console.log(error);
_this.buttonsTarget.innerHTML = '<span class="text-sm leading-5 font-medium text-red-700">Error!</span>';
setTimeout(() => {
_this.close();
}, 1000);
});
}
stop() {
clearTimeout(this.timeoutId)
this.timeoutId = null
}
continue() {
this.timeoutId = setTimeout(() => {
this.close();
}, parseInt(this.data.get("timeout")));
}
close() {
this.element.classList.remove('notification-enter', 'notification-enter-from', 'notification-enter-to');
this.element.classList.add('notification-leave', 'notification-leave-from')
// Trigger transition
setTimeout(() => {
this.element.classList.add('notification-leave-to');
}, 100);
// Remove element after transition
setTimeout(() => {
this.element.remove();
}, 300);
}
get csrfToken() {
const element = document.head.querySelector('meta[name="csrf-token"]')
return element.getAttribute("content")
}
}
@@ -0,0 +1,31 @@
import { Controller } from "@hotwired/stimulus"
function show (element) {
element.classList.add('block');
element.classList.remove('hidden');
}
function hide (element) {
element.classList.remove('block');
element.classList.add('hidden');
}
export default class extends Controller {
static targets = [ 'mobileMenu', 'iconMobileMenuOpen', 'iconMobileMenuClose' ];
connect() {
this.mobileMenuTarget.classList.add('hidden');
}
toggleMobileNav() {
if (this.mobileMenuTarget.classList.contains('hidden')) {
show(this.mobileMenuTarget);
show(this.iconMobileMenuCloseTarget);
hide(this.iconMobileMenuOpenTarget);
} else {
hide(this.mobileMenuTarget);
hide(this.iconMobileMenuCloseTarget);
show(this.iconMobileMenuOpenTarget);
}
}
}
-16
View File
@@ -1,16 +0,0 @@
// This file is automatically compiled by Webpack, along with any other files
// present in this directory. You're encouraged to place your actual application logic in
// a relevant structure within app/javascript and only use these pack files to reference
// that code so it'll be compiled.
require("@rails/ujs").start()
require("turbolinks").start()
require("channels")
// 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' %>)
// or the `imagePath` JavaScript helper below.
//
// const images = require.context('../images', true)
// const imagePath = (name) => images(name, true)
+32
View 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
View 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
View 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
+15
View File
@@ -0,0 +1,15 @@
require 'mail'
module EmailValidatable
extend ActiveSupport::Concern
class EmailValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
begin
a = Mail::Address.new(value)
rescue Mail::Field::ParseError
record.errors[attribute] << (options[:message] || "is not a valid address")
end
end
end
end
+13
View 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
+20
View File
@@ -0,0 +1,20 @@
class Invitation < ApplicationRecord
# Relations
belongs_to :user
# Validations
validates_presence_of :user
# Hooks
before_create :generate_token
# Scopes
scope :unused, -> { where(used_at: nil) }
scope :used, -> { where.not(used_at: nil) }
private
def generate_token
self.token = SecureRandom.hex(8)
end
end
+29
View File
@@ -1,4 +1,18 @@
class User < ApplicationRecord class User < ApplicationRecord
include EmailValidatable
# Relations
has_many :invitations, dependent: :destroy
has_many :donations, dependent: :nullify
validates_uniqueness_of :cn
validates_length_of :cn, :minimum => 3
validates_uniqueness_of :email
validates :email, email: true
lockbox_encrypts :ln_login
lockbox_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,
@@ -33,4 +47,19 @@ class User < ApplicationRecord
false false
end end
end end
def address
"#{self.cn}@#{self.ou}"
end
def valid_attribute?(attribute_name)
self.valid?
self.errors[attribute_name].blank?
end
def ln_create_invoice(payload)
lndhub = Lndhub.new
lndhub.authenticate self
lndhub.addinvoice payload
end
end end
+7
View File
@@ -0,0 +1,7 @@
class ApplicationService
# This enables executing a service's `#call` method directly via
# `MyService.call(args)`, without creating a class instance it first.
def self.call(*args, &block)
new(*args, &block).call
end
end
+32
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
+66
View File
@@ -0,0 +1,66 @@
class CreateAccount < ApplicationService
def initialize(args)
@username = args[:username]
@domain = args[:ou] || "kosmos.org"
@email = args[:email]
@password = args[:password]
@invitation = args[:invitation]
end
def call
user = create_user_in_database
add_ldap_document
create_lndhub_wallet(user)
if @invitation.present?
update_invitation(user.id)
exchange_xmpp_contacts
end
end
private
def create_user_in_database
User.create!(
cn: @username,
ou: @domain,
email: @email,
password: @password,
password_confirmation: @password
)
end
def update_invitation(user_id)
@invitation.update! invited_user_id: user_id, used_at: DateTime.now
end
# TODO move to confirmation
def add_ldap_document
hashed_pw = Devise.ldap_auth_password_builder.call(@password)
CreateLdapUserJob.perform_later(@username, @domain, @email, hashed_pw)
end
def exchange_xmpp_contacts
#TODO enable in development when we have easy setup of ejabberd etc.
return if Rails.env.development?
ExchangeXmppContactsJob.perform_later(@invitation.user, @username, @domain)
end
def create_lndhub_wallet(user)
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
+20
View File
@@ -0,0 +1,20 @@
class EjabberdApiClient
def initialize
@base_url = ENV["EJABBERD_API_URL"]
end
def post(endpoint, payload)
res = Faraday.post("#{@base_url}/#{endpoint}", payload.to_json,
"Content-Type" => "application/json")
if res.status != 200
Rails.logger.error "[ejabberd] API request failed:"
Rails.logger.error res.body
#TODO add some kind of exception tracking/notifications
end
end
def add_rosteritem(payload)
post "add_rosteritem", payload
end
end
+111
View File
@@ -0,0 +1,111 @@
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 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
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
+66
View File
@@ -0,0 +1,66 @@
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 gettxs(user_token)
get "gettxs", user_token || auth_token
end
def getuserinvoices(user_token)
get "getuserinvoices", user_token || auth_token
end
def addinvoice(payload)
invoice = post "addinvoice", {
amt: payload[:amount],
memo: payload[:memo],
description_hash: payload[:description_hash]
}
invoice["payment_request"]
end
end
+7 -7
View File
@@ -1,7 +1,7 @@
<h2>Admin Panel</h2> <%= render HeaderComponent.new(title: "Admin Panel") %>
<p>
Ohai there, admin human. <%= render MainSimpleComponent.new do %>
</p> <p class="text-center">
<p> With great power comes great responsibility.
<%= link_to 'LDAP users', admin_ldap_users_path %> </p>
</p> <% end %>
@@ -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
View 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 %>
+12
View File
@@ -0,0 +1,12 @@
<%= render HeaderComponent.new(title: "Donations") %>
<%= render MainSimpleComponent.new do %>
<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>
<% end %>
+44
View File
@@ -0,0 +1,44 @@
<%= render HeaderComponent.new(title: "Donations") %>
<%= render MainSimpleComponent.new do %>
<% if @donations.any? %>
<table>
<thead>
<tr>
<th>User</th>
<th class="text-right">Amount BTC</th>
<th class="text-right">in EUR</th>
<th class="text-right">in USD</th>
<th class="pl-2">Public name</th>
<th>Date</th>
<th colspan="3"></th>
</tr>
</thead>
<tbody>
<% @donations.each do |donation| %>
<tr>
<td><%= donation.user.address %></td>
<td class="text-right"><%= sats_to_btc donation.amount_sats %></td>
<td class="text-right"><% if donation.amount_eur.present? %><%= number_to_currency donation.amount_eur / 100, unit: "" %><% end %></td>
<td class="text-right"><% if donation.amount_usd.present? %><%= number_to_currency donation.amount_usd / 100, unit: "" %><% end %></td>
<td class="pl-2"><%= 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',
data: { turbo_method: :delete, turbo_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>
<% end %>
@@ -0,0 +1 @@
json.array! @donations, partial: "donations/donation", as: :donation
+11
View File
@@ -0,0 +1,11 @@
<%= render HeaderComponent.new(title: "Donations") %>
<%= render MainSimpleComponent.new do %>
<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>
<% end %>
+38
View File
@@ -0,0 +1,38 @@
<%= render HeaderComponent.new(title: "Donations") %>
<%= render MainSimpleComponent.new do %>
<p>
<strong>User:</strong>
<%= @donation.user.address %>
</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>
<% end %>
@@ -0,0 +1 @@
json.partial! "donations/donation", donation: @donation
@@ -0,0 +1,35 @@
<%= render HeaderComponent.new(title: "Invitations") %>
<%= render MainSimpleComponent.new do %>
<section>
<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 font-mono"><%= 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 %>
+14 -7
View File
@@ -1,11 +1,17 @@
<h2>LDAP users</h2> <%= render HeaderComponent.new(title: "LDAP Users: #{@ou}") %>
<ul> <%= render MainSimpleComponent.new do %>
<li><%= link_to 'kosmos.org', admin_ldap_users_path %></li> <h3 class="hidden">Domains</h3>
<li><%= link_to '5apps.com', admin_ldap_users_path(ou: '5apps.com') %></li> <ul class="mb-10">
</ul> <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>
<table> <table>
<thead> <thead>
<tr> <tr>
<th>UID</th> <th>UID</th>
@@ -24,4 +30,5 @@
</tr> </tr>
<% end %> <% end %>
</tbody> </tbody>
</table> </table>
<% end %>
+50 -37
View File
@@ -1,48 +1,61 @@
<section> <%= render HeaderComponent.new(title: "Services") %>
<h2>Services</h2>
<%= render MainSimpleComponent.new do %>
<section>
<p> <p>
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 grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-6 services mt-12">
<div class="grid-item chat"> <div>
<h3><%= link_to "Chat", "https://wiki.kosmos.org/Services:XMPP" %></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 gitea"> <div>
<h3><%= link_to "Gitea", "https://gitea.kosmos.org" %></h3> <h3 class="mb-3.5">
<p> <%= link_to "Discourse", "https://community.kosmos.org", class: "ks-text-link" %>
Code hosting and collaboration for software projects </h3>
</p> <p class="text-gray-500">
</div>
<div class="grid-item gitea">
<h3><%= link_to "Drone CI", "https://drone.kosmos.org" %></h3>
<p>
Continuous integration for software projects, tied to our Gitea
</p>
</div>
<div class="grid-item wiki">
<h3><%= link_to "Wiki", "https://wiki.kosmos.org" %></h3>
<p>
Kosmos documentation and knowledge base
</p>
</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> <div>
</section> <h3 class="mb-3.5">
<%= render partial: "icons/zap", locals: { custom_class: "text-amber-500 h-4 w-4 inline" } %>
<section> <%= link_to "Lightning Wallet", wallet_path, class: "ks-text-link" %>
<h3>Password change</h3> </h3>
<p> <p class="text-gray-500">
<%= form_with(url: settings_reset_password_path, method: :post) do %> Send and receive sats over the Bitcoin Lightning Network
<%= submit_tag("Send me a password reset link") %>
<% end %>
</p> </p>
</section> </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
</p>
</div>
<div>
<h3 class="mb-3.5">
<%= link_to "Drone CI", "https://drone.kosmos.org", class: "ks-text-link" %>
</h3>
<p class="text-gray-500">
Continuous integration for software projects on Gitea
</p>
</div>
</div>
</section>
<% end %>
+16 -14
View File
@@ -1,20 +1,22 @@
<h2>Resend confirmation instructions</h2> <%= render HeaderCompactComponent.new(title: "Log in") %>
<%= form_for(resource, as: resource_name, url: confirmation_path(resource_name), html: { method: :post }) do |f| %> <%= render MainCompactComponent.new do %>
<h2>Resend confirmation instructions</h2>
<%= 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 %>
<div class="field">
<p> <p>
<%= f.label :email, 'Email address' %><br /> <%= f.label :email, 'Email address', class: 'block mb-1 w-full' %>
<%= f.email_field :email, required: true, autofocus: true, autocomplete: "email", value: (resource.pending_reconfirmation? ? resource.unconfirmed_email : resource.email) %> <%= f.email_field :email,
required: true, autofocus: true, autocomplete: "email",
value: (resource.pending_reconfirmation? ? resource.unconfirmed_email : resource.email),
class: "w-full" %>
</p> </p>
</div> <p class="mt-8">
<%= f.submit "Resend confirmation link",
class: 'btn-md btn-blue w-full sm:w-auto' %>
</p>
<% end %>
<div class="actions"> <%= render "devise/shared/links" %>
<p>
<%= f.submit "Resend confirmation instructions" %>
</p>
</div>
<% end %> <% end %>
<%= render "devise/shared/links" %>
+23 -17
View File
@@ -1,25 +1,31 @@
<h2>Change your password</h2> <%= render HeaderCompactComponent.new(title: "Log in") %>
<%= form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :put }) do |f| %> <%= render MainCompactComponent.new do %>
<h2>Change your password</h2>
<%= form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :put }) do |f| %>
<%= 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">
<%= f.submit "Change my password", class: 'btn-md btn-blue' %>
</p>
<% end %>
<div class="actions"> <%= render "devise/shared/links" %>
<%= f.submit "Change my password" %>
</div>
<% end %> <% end %>
<%= render "devise/shared/links" %>
+17 -19
View File
@@ -1,27 +1,25 @@
<h2>Forgot your password?</h2> <%= render HeaderCompactComponent.new(title: "Log in") %>
<%= form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :post }) do |f| %> <%= render MainCompactComponent.new do %>
<h2>Forgot your password?</h2>
<%= 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 %>
<div class="field">
<p> <p>
<%= f.label :cn, 'User' %><br /> <%= f.label :cn, 'User', class: 'block' %>
<%= f.text_field :cn, autofocus: true, autocomplete: "username", required: true %> @ kosmos.org <%= f.text_field :cn, autofocus: true, autocomplete: "username",
required: true, class: "w-full md:w-3/5"%>
<span class="ml-1 text-gray-500">@ kosmos.org</span>
</p> </p>
</div>
<div class="field">
<p> <p>
<%= f.label :email, 'Email address' %><br /> <%= f.label :email, 'Email address', class: 'block' %>
<%= f.email_field :email, autocomplete: "email", required: true %> <%= f.email_field :email, autocomplete: "email", required: true,
class: "w-full md:w-3/5"%>
</p> </p>
</div> <p class="mt-8">
<%= f.submit "Send me a reset link", class: 'btn-md btn-blue w-full sm:w-auto' %>
</p>
<% end %>
<div class="actions"> <%= render "devise/shared/links" %>
<p>
<%= f.submit "Send me reset password instructions" %>
</p>
</div>
<% end %> <% end %>
<%= render "devise/shared/links" %>
+3 -3
View File
@@ -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>
+19 -21
View File
@@ -1,25 +1,23 @@
<h2>Log in</h2> <%= render HeaderCompactComponent.new(title: "Log in") %>
<%= form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| %> <%= render MainCompactComponent.new do %>
<%= 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>
<%= f.label :cn, 'User', class: 'block' %>
<%= f.text_field :cn, autofocus: true, autocomplete: "username",
class: "w-full md:w-3/5"%>
<span class="ml-1 text-gray-500">@ kosmos.org</span>
</p>
<p>
<%= f.label :password, class: 'block' %>
<%= f.password_field :password, autocomplete: "current-password",
class: "w-full md:w-3/5"%>
</p>
<p class="mt-8">
<%= f.submit "Log in", class: 'btn-md btn-blue w-full sm:w-auto' %>
</p>
<% end %>
<div class="field"> <%= render "devise/shared/links" %>
<p>
<%= f.label :cn, 'User' %><br />
<%= f.text_field :cn, autofocus: true, autocomplete: "username" %> @ kosmos.org
</p>
</div>
<div class="field">
<p>
<%= f.label :password %><br />
<%= f.password_field :password, autocomplete: "current-password" %>
</p>
</div>
<div class="actions">
<p>
<%= f.submit "Log in" %>
</p>
</div>
<% end %> <% end %>
<%= render "devise/shared/links" %>

Some files were not shown because too many files have changed in this diff Show More