From 09d99ce9c283315e9eb6c1ed9894c49f4fbb25e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A2u=20Cao?= Date: Fri, 10 Feb 2023 11:37:27 +0800 Subject: [PATCH 01/20] Increase size of current balance --- app/components/wallet_summary_component.html.erb | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/app/components/wallet_summary_component.html.erb b/app/components/wallet_summary_component.html.erb index b02e0d8..dc510de 100644 --- a/app/components/wallet_summary_component.html.erb +++ b/app/components/wallet_summary_component.html.erb @@ -7,10 +7,14 @@

<% if @balance %> - <%= number_with_delimiter @balance %> sats
+ <%= number_with_delimiter @balance %> + sats +
Available balance <% else %> - n/a sats
+ n/a + sats +
Balance unavailable <% end %>

From dd482d7f2e7ed03bb3c5f8aeca6f81e221b52654 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A2u=20Cao?= Date: Fri, 10 Feb 2023 13:12:36 +0800 Subject: [PATCH 02/20] Add LndHub db/models, and quick stats for admin views --- .../quickstats_container_component.html.erb | 3 ++ .../quickstats_container_component.rb | 4 ++ .../quickstats_item_component.html.erb | 18 +++++++ app/components/quickstats_item_component.rb | 13 +++++ app/controllers/admin/donations_controller.rb | 6 ++- .../admin/invitations_controller.rb | 7 ++- app/controllers/admin/lightning_controller.rb | 12 +++++ app/models/lndhub_account.rb | 21 ++++++++ app/models/lndhub_account_ledger.rb | 3 ++ app/models/lndhub_base.rb | 4 ++ app/models/lndhub_user.rb | 15 ++++++ app/models/user.rb | 5 ++ app/views/admin/dashboard/index.html.erb | 11 +++-- app/views/admin/donations/index.html.erb | 34 ++++++++++--- app/views/admin/invitations/index.html.erb | 26 +++++++--- app/views/admin/lightning/index.html.erb | 48 +++++++++++++++++++ app/views/settings/account/index.html.erb | 8 ++-- app/views/shared/_admin_nav.html.erb | 6 ++- config/database.yml | 41 +++++++++++----- config/routes.rb | 3 +- .../illustrations/undraw_vault_re_s4my.svg | 1 + .../quickstats_container_component_spec.rb | 15 ++++++ .../quickstats_item_component_spec.rb | 15 ++++++ 23 files changed, 282 insertions(+), 37 deletions(-) create mode 100644 app/components/quickstats_container_component.html.erb create mode 100644 app/components/quickstats_container_component.rb create mode 100644 app/components/quickstats_item_component.html.erb create mode 100644 app/components/quickstats_item_component.rb create mode 100644 app/controllers/admin/lightning_controller.rb create mode 100644 app/models/lndhub_account.rb create mode 100644 app/models/lndhub_account_ledger.rb create mode 100644 app/models/lndhub_base.rb create mode 100644 app/models/lndhub_user.rb create mode 100644 app/views/admin/lightning/index.html.erb create mode 100644 public/img/illustrations/undraw_vault_re_s4my.svg create mode 100644 spec/components/quickstats_container_component_spec.rb create mode 100644 spec/components/quickstats_item_component_spec.rb diff --git a/app/components/quickstats_container_component.html.erb b/app/components/quickstats_container_component.html.erb new file mode 100644 index 0000000..466f09c --- /dev/null +++ b/app/components/quickstats_container_component.html.erb @@ -0,0 +1,3 @@ +
+ <%= content %> +
diff --git a/app/components/quickstats_container_component.rb b/app/components/quickstats_container_component.rb new file mode 100644 index 0000000..ac229b7 --- /dev/null +++ b/app/components/quickstats_container_component.rb @@ -0,0 +1,4 @@ +# frozen_string_literal: true + +class QuickstatsContainerComponent < ViewComponent::Base +end diff --git a/app/components/quickstats_item_component.html.erb b/app/components/quickstats_item_component.html.erb new file mode 100644 index 0000000..4d27fb6 --- /dev/null +++ b/app/components/quickstats_item_component.html.erb @@ -0,0 +1,18 @@ +
+
+ <%= @title %> +
+
+ <% if @type == :number %> + <%= number_with_delimiter @value %> + <% else %> + <%= @value %> + <% end %> + <% if @unit %> + <%= @unit %> + <% end %> + <% if @meta %> + <%= @meta %> + <% end %> +
+
diff --git a/app/components/quickstats_item_component.rb b/app/components/quickstats_item_component.rb new file mode 100644 index 0000000..a93b79e --- /dev/null +++ b/app/components/quickstats_item_component.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +class QuickstatsItemComponent < ViewComponent::Base + def initialize(type:, title:, value:, unit: nil, meta: nil, icon_name: nil, icon_color_class: nil) + @type = type + @title = title + @value = value + @unit = unit + @meta = meta + @icon_name = icon_name + @icon_color_class = icon_color_class + end +end diff --git a/app/controllers/admin/donations_controller.rb b/app/controllers/admin/donations_controller.rb index e6c37cc..84d98d9 100644 --- a/app/controllers/admin/donations_controller.rb +++ b/app/controllers/admin/donations_controller.rb @@ -5,7 +5,11 @@ class Admin::DonationsController < Admin::BaseController # GET /donations # GET /donations.json def index - @donations = Donation.all + @donations = Donation.all.order('created_at desc') + @stats = { + overall_sats: @donations.all.sum("amount_sats"), + donor_count: Donation.distinct.count(:user_id) + } end # GET /donations/1 diff --git a/app/controllers/admin/invitations_controller.rb b/app/controllers/admin/invitations_controller.rb index 7bfa7be..ca9b407 100644 --- a/app/controllers/admin/invitations_controller.rb +++ b/app/controllers/admin/invitations_controller.rb @@ -1,8 +1,11 @@ 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') + @stats = { + available: Invitation.unused.count, + accepted: @invitations_used.length, + users_with_referrals: Invitation.used.distinct.count(:user_id) + } end end diff --git a/app/controllers/admin/lightning_controller.rb b/app/controllers/admin/lightning_controller.rb new file mode 100644 index 0000000..bd7ca99 --- /dev/null +++ b/app/controllers/admin/lightning_controller.rb @@ -0,0 +1,12 @@ +class Admin::LightningController < Admin::BaseController + def index + @current_section = :lightning + + @users = User.pluck(:cn, :ou, :ln_account) + @accounts = LndhubAccount.with_balances.order(balance: :desc).to_a + + @ln = {} + @ln[:current_balance] = LndhubAccount.current.joins(:ledgers).sum("account_ledgers.amount") + @ln[:users_with_sats] = @accounts.length + end +end diff --git a/app/models/lndhub_account.rb b/app/models/lndhub_account.rb new file mode 100644 index 0000000..b5eb437 --- /dev/null +++ b/app/models/lndhub_account.rb @@ -0,0 +1,21 @@ +class LndhubAccount < LndhubBase + self.table_name = "accounts" + self.inheritance_column = :_type_disabled + + has_many :ledgers, class_name: "LndhubAccountLedger", + foreign_key: "account_id" + + belongs_to :user, class_name: "LndhubUser", + foreign_key: "user_id" + + scope :current, -> { where(type: "current") } + scope :outgoing, -> { where(type: "outgoing") } + scope :incoming, -> { where(type: "incoming") } + scope :fees, -> { where(type: "fees") } + + scope :with_balances, -> { + current.joins(:user).joins(:ledgers) + .group("accounts.id", "users.login") + .select("accounts.id, users.login, SUM(account_ledgers.amount) AS balance") + } +end diff --git a/app/models/lndhub_account_ledger.rb b/app/models/lndhub_account_ledger.rb new file mode 100644 index 0000000..6858275 --- /dev/null +++ b/app/models/lndhub_account_ledger.rb @@ -0,0 +1,3 @@ +class LndhubAccountLedger < LndhubBase + self.table_name = "account_ledgers" +end diff --git a/app/models/lndhub_base.rb b/app/models/lndhub_base.rb new file mode 100644 index 0000000..d8a75ea --- /dev/null +++ b/app/models/lndhub_base.rb @@ -0,0 +1,4 @@ +class LndhubBase < ActiveRecord::Base + self.abstract_class = true + establish_connection :lndhub +end diff --git a/app/models/lndhub_user.rb b/app/models/lndhub_user.rb new file mode 100644 index 0000000..e5645c5 --- /dev/null +++ b/app/models/lndhub_user.rb @@ -0,0 +1,15 @@ +class LndhubUser < LndhubBase + self.table_name = "users" + self.inheritance_column = :_type_disabled + + has_many :accounts, class_name: "LndhubAccount", + foreign_key: "user_id" + + belongs_to :user, class_name: "User", + primary_key: "ln_account", + foreign_key: "login" + + def balance + accounts.current.first.ledgers.sum("account_ledgers.amount") + end +end diff --git a/app/models/user.rb b/app/models/user.rb index 660d53c..e9237c2 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -5,6 +5,11 @@ class User < ApplicationRecord has_many :invitations, dependent: :destroy has_many :donations, dependent: :nullify + has_one :lndhub_user, class_name: "LndhubUser", inverse_of: "user", + primary_key: "ln_account", foreign_key: "login" + + has_many :accounts, through: :lndhub_user + validates_uniqueness_of :cn validates_length_of :cn, :minimum => 3 validates_uniqueness_of :email diff --git a/app/views/admin/dashboard/index.html.erb b/app/views/admin/dashboard/index.html.erb index bb66283..49d9e30 100644 --- a/app/views/admin/dashboard/index.html.erb +++ b/app/views/admin/dashboard/index.html.erb @@ -1,7 +1,12 @@ <%= render HeaderComponent.new(title: "Admin Panel") %> <%= render MainSimpleComponent.new do %> -

- With great power comes great responsibility. -

+
+

+ <%= image_tag("/img/illustrations/undraw_vault_re_s4my.svg", class: 'h-48') %> +

+

+ With great power comes great responsibility. +

+
<% end %> diff --git a/app/views/admin/donations/index.html.erb b/app/views/admin/donations/index.html.erb index baf51cb..0f57949 100644 --- a/app/views/admin/donations/index.html.erb +++ b/app/views/admin/donations/index.html.erb @@ -1,7 +1,26 @@ <%= render HeaderComponent.new(title: "Donations") %> <%= render MainSimpleComponent.new do %> +
+ <%= render QuickstatsContainerComponent.new do %> + <%= render QuickstatsItemComponent.new( + type: :number, + title: 'Overall', + value: @stats[:overall_sats], + unit: 'sats' + ) %> + <%= render QuickstatsItemComponent.new( + type: :number, + title: 'Donors', + value: @stats[:donor_count], + meta: "/ #{User.count} users" + ) %> + <% end %> +
+ +
<% if @donations.any? %> +

Recent Donations

@@ -11,7 +30,7 @@ - + @@ -23,11 +42,13 @@ - - - - + + <% end %> @@ -37,6 +58,7 @@ No donations yet.

<% end %> +

<%= link_to 'Record an out-of-system donation', new_admin_donation_path, class: 'btn-md btn-gray' %> diff --git a/app/views/admin/invitations/index.html.erb b/app/views/admin/invitations/index.html.erb index 4bad1fb..1bc5517 100644 --- a/app/views/admin/invitations/index.html.erb +++ b/app/views/admin/invitations/index.html.erb @@ -2,16 +2,28 @@ <%= render MainSimpleComponent.new do %>

-

- There are currently <%= @invitations_unused_count %> - unused invitations available to existing users. - <%= @users_with_referrals_count %> users have successfully - invited new users. -

+ <%= render QuickstatsContainerComponent.new do %> + <%= render QuickstatsItemComponent.new( + type: :number, + title: 'Available', + value: @stats[:available], + ) %> + <%= render QuickstatsItemComponent.new( + type: :number, + title: 'Accepted', + value: @stats[:accepted], + ) %> + <%= render QuickstatsItemComponent.new( + type: :number, + title: 'Users with referrals', + value: @stats[:users_with_referrals], + meta: "/ #{User.count}" + ) %> + <% end %>
<% if @invitations_used.any? %>
-

Accepted (<%= @invitations_used.length %>)

+

Recently Accepted

in USD Public name Date
<% if donation.amount_eur.present? %><%= number_to_currency donation.amount_eur / 100, unit: "" %><% end %> <% if donation.amount_usd.present? %><%= number_to_currency donation.amount_usd / 100, unit: "" %><% end %> <%= donation.public_name %><%= donation.paid_at ? donation.paid_at.strftime("%Y-%m-%d") : "" %><%= link_to 'Show', admin_donation_path(donation), class: 'btn btn-sm btn-gray' %><%= link_to 'Edit', edit_admin_donation_path(donation), class: 'btn btn-sm btn-gray' %><%= link_to 'Destroy', admin_donation_path(donation), class: 'btn btn-sm btn-red', - data: { turbo_method: :delete, turbo_confirm: 'Are you sure?' } %><%= donation.paid_at ? donation.paid_at.strftime("%Y-%m-%d (%H:%M UTC)") : "" %> + <%= link_to 'Show', admin_donation_path(donation), class: 'btn btn-sm btn-gray' %> + <%= link_to 'Edit', edit_admin_donation_path(donation), class: 'btn btn-sm btn-gray' %> + <%= link_to 'Destroy', admin_donation_path(donation), class: 'btn btn-sm btn-red', + data: { turbo_method: :delete, turbo_confirm: 'Are you sure?' } %> +
diff --git a/app/views/admin/lightning/index.html.erb b/app/views/admin/lightning/index.html.erb new file mode 100644 index 0000000..902186c --- /dev/null +++ b/app/views/admin/lightning/index.html.erb @@ -0,0 +1,48 @@ +<%= render HeaderComponent.new(title: "Lightning Network") %> + +<%= render MainSimpleComponent.new do %> +
+ <%= render QuickstatsContainerComponent.new do %> + <%= render QuickstatsItemComponent.new( + type: :number, + title: 'Current user balance', + value: @ln[:current_balance], + unit: 'sats' + ) %> + <%= render QuickstatsItemComponent.new( + type: :number, + title: 'Users with sats', + value: @ln[:users_with_sats], + meta: "/ #{User.count}" + ) %> + <% end %> +
+ +
+

Accounts

+
+ + + + + + + + + <% @accounts.each do |account| %> + + + + + + <% end %> + +
LN AccountUserBalance
+ <%= account.login[0, 8] %>...<%= account.login[12, 19] %> + + <% if user = @users.find{ |u| u[2] == account.login } %> + <%= "#{user[0]}@#{user[1]}" %> + <% end %> + <%= number_with_delimiter account.balance.to_i.to_s %>
+
+<% end %> diff --git a/app/views/settings/account/index.html.erb b/app/views/settings/account/index.html.erb index 708a174..279d37d 100644 --- a/app/views/settings/account/index.html.erb +++ b/app/views/settings/account/index.html.erb @@ -4,10 +4,10 @@

Password

Use the following button to request an email with a password reset link:

-

- <%= form_with(url: settings_reset_password_path, method: :post) do %> + <%= form_with(url: settings_reset_password_path, method: :post) do %> +

<%= submit_tag("Send me a password reset link", class: 'btn-md btn-gray w-full sm:w-auto') %> - <% end %> -

+

+ <% end %>
<% end %> diff --git a/app/views/shared/_admin_nav.html.erb b/app/views/shared/_admin_nav.html.erb index 881cd93..b7ef06e 100644 --- a/app/views/shared/_admin_nav.html.erb +++ b/app/views/shared/_admin_nav.html.erb @@ -1,8 +1,10 @@ <%= link_to "Dashboard", admin_root_path, class: main_nav_class(@current_section, :dashboard) %> +<%= link_to "Users", admin_ldap_users_path, + class: main_nav_class(@current_section, :ldap_users) %> <%= link_to "Invitations", admin_invitations_path, class: main_nav_class(@current_section, :invitations) %> <%= link_to "Donations", admin_donations_path, class: main_nav_class(@current_section, :donations) %> -<%= link_to "LDAP Users", admin_ldap_users_path, - class: main_nav_class(@current_section, :ldap_users) %> +<%= link_to "Lightning", admin_lightning_path, + class: main_nav_class(@current_section, :lightning) %> diff --git a/config/database.yml b/config/database.yml index 10df6c5..49b5c31 100644 --- a/config/database.yml +++ b/config/database.yml @@ -10,21 +10,40 @@ default: &default timeout: 5000 development: - <<: *default - database: db/development.sqlite3 + primary: + <<: *default + database: db/development.sqlite3 + # lndhub: + # <<: *default + # database: db/lndhub.sqlite3 # Warning: The database defined as "test" will be erased and # re-generated from your development database when you run "rake". # Do not set this db to the same as development or production. test: - <<: *default - database: db/test.sqlite3 + primary: + <<: *default + database: db/test.sqlite3 + lndhub: + <<: *default + database_tasks: false + database: db/test.lndhub.sqlite3 production: - <<: *default - adapter: postgresql - database: akkounts - port: 5432 - host: <%= Rails.application.credentials.postgres[:host] rescue nil %> - username: <%= Rails.application.credentials.postgres[:username] rescue nil %> - password: <%= Rails.application.credentials.postgres[:password] rescue nil %> + primary: + <<: *default + adapter: postgresql + database: akkounts + port: 5432 + host: <%= Rails.application.credentials.postgres[:host] rescue nil %> + username: <%= Rails.application.credentials.postgres[:username] rescue nil %> + password: <%= Rails.application.credentials.postgres[:password] rescue nil %> + lndhub: + <<: *default + adapter: postgresql + database_tasks: false + database: lndhub + port: 5432 + host: <%= Rails.application.credentials.postgres[:host] rescue nil %> + username: <%= Rails.application.credentials.postgres[:username] rescue nil %> + password: <%= Rails.application.credentials.postgres[:password] rescue nil %> diff --git a/config/routes.rb b/config/routes.rb index 5ed99bd..00475c5 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -39,9 +39,10 @@ Rails.application.routes.draw do namespace :admin do root to: 'dashboard#index' - get 'invitations', to: 'invitations#index' get 'ldap_users', to: 'ldap_users#index' + get 'invitations', to: 'invitations#index' resources :donations + get 'lightning', to: 'lightning#index' end authenticate :user, ->(user) { user.is_admin? } do diff --git a/public/img/illustrations/undraw_vault_re_s4my.svg b/public/img/illustrations/undraw_vault_re_s4my.svg new file mode 100644 index 0000000..408326c --- /dev/null +++ b/public/img/illustrations/undraw_vault_re_s4my.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/spec/components/quickstats_container_component_spec.rb b/spec/components/quickstats_container_component_spec.rb new file mode 100644 index 0000000..9ac0406 --- /dev/null +++ b/spec/components/quickstats_container_component_spec.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +require "rails_helper" + +RSpec.describe QuickstatsContainerComponent, type: :component do + pending "add some examples to (or delete) #{__FILE__}" + + # it "renders something useful" do + # expect( + # render_inline(described_class.new(attr: "value")) { "Hello, components!" }.css("p").to_html + # ).to include( + # "Hello, components!" + # ) + # end +end diff --git a/spec/components/quickstats_item_component_spec.rb b/spec/components/quickstats_item_component_spec.rb new file mode 100644 index 0000000..c886c37 --- /dev/null +++ b/spec/components/quickstats_item_component_spec.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +require "rails_helper" + +RSpec.describe QuickstatsItemComponent, type: :component do + pending "add some examples to (or delete) #{__FILE__}" + + # it "renders something useful" do + # expect( + # render_inline(described_class.new(attr: "value")) { "Hello, components!" }.css("p").to_html + # ).to include( + # "Hello, components!" + # ) + # end +end From d9e767298bae4eb018455a92c1b73c3a713e0884 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A2u=20Cao?= Date: Mon, 13 Feb 2023 16:32:28 +0800 Subject: [PATCH 03/20] Refactor admin users page, add quick stats --- .../admin/ldap_users_controller.rb | 39 ++-------- app/models/user.rb | 3 + app/views/admin/ldap_users/index.html.erb | 78 ++++++++++++------- .../wallet_summary_component_spec.rb | 2 +- 4 files changed, 60 insertions(+), 62 deletions(-) diff --git a/app/controllers/admin/ldap_users_controller.rb b/app/controllers/admin/ldap_users_controller.rb index 2e2c3dd..5109041 100644 --- a/app/controllers/admin/ldap_users_controller.rb +++ b/app/controllers/admin/ldap_users_controller.rb @@ -2,43 +2,18 @@ class Admin::LdapUsersController < Admin::BaseController before_action :set_current_section def index - attributes = %w{dn cn uid mail admin} - filter = Net::LDAP::Filter.eq("uid", "*") - + ldap = LdapService.new @ou = params[:ou] || "kosmos.org" - treebase = "ou=#{@ou},cn=users,dc=kosmos,dc=org" - - 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 - # ldap_client.get_operation_result + @orgs = ldap.fetch_organizations + @entries = ldap.fetch_users(ou: @ou) + @stats = { + users_confirmed: User.where(ou: @ou).confirmed.count, + users_pending: User.where(ou: @ou).pending.count + } end private - 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 - def set_current_section @current_section = :ldap_users end diff --git a/app/models/user.rb b/app/models/user.rb index e9237c2..40d5f20 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -15,6 +15,9 @@ class User < ApplicationRecord validates_uniqueness_of :email validates :email, email: true + scope :confirmed, -> { where.not(confirmed_at: nil) } + scope :pending, -> { where(confirmed_at: nil) } + lockbox_encrypts :ln_login lockbox_encrypts :ln_password diff --git a/app/views/admin/ldap_users/index.html.erb b/app/views/admin/ldap_users/index.html.erb index 85637b0..e39bd6f 100644 --- a/app/views/admin/ldap_users/index.html.erb +++ b/app/views/admin/ldap_users/index.html.erb @@ -1,34 +1,54 @@ <%= render HeaderComponent.new(title: "LDAP Users: #{@ou}") %> <%= render MainSimpleComponent.new do %> - -
    -
  • - <%= link_to 'kosmos.org', admin_ldap_users_path, class: "ks-text-link" %> -
  • -
  • - <%= link_to '5apps.com', admin_ldap_users_path(ou: '5apps.com'), class: "ks-text-link" %> -
  • -
+
+ <%= render QuickstatsContainerComponent.new do %> + <%= render QuickstatsItemComponent.new( + type: :number, + title: 'Confirmed', + value: @stats[:users_confirmed], + ) %> + <%= render QuickstatsItemComponent.new( + type: :number, + title: 'Pending', + value: @stats[:users_pending], + ) %> + <% end %> +
- - - - - - - - - - - <% @entries.each do |entry| %> - - - - - - - <% end %> - -
UIDE-MailAdmin
<%= entry[:uid] %><%= entry[:mail] %><%= entry[:admin] %>
+ <% if @orgs.length > 1 %> +
+ +
    + <% @orgs.each do |org| %> +
  • + <%= link_to org[:ou], admin_ldap_users_path(ou: org[:ou]), class: "ks-text-link" %> +
  • + <% end %> +
+
+ <% end %> + +
+ + + + + + + + + + + <% @entries.each do |entry| %> + + + + + + + <% end %> + +
UIDE-MailAdmin
<%= entry[:uid] %><%= entry[:mail] %><%= entry[:admin] %>
+
<% end %> diff --git a/spec/components/wallet_summary_component_spec.rb b/spec/components/wallet_summary_component_spec.rb index 7acdb04..a253964 100644 --- a/spec/components/wallet_summary_component_spec.rb +++ b/spec/components/wallet_summary_component_spec.rb @@ -5,7 +5,7 @@ RSpec.describe WalletSummaryComponent, type: :component do expect( render_inline(described_class.new(balance: 2301000)) {}.css("section").to_html ).to include( - "2,301,000 sats" + "2,301,000" ) end end From 43f133ebd74849e57c3a6b397be2fd8c2d80985b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A2u=20Cao?= Date: Mon, 13 Feb 2023 21:56:32 +0800 Subject: [PATCH 04/20] Add config for lndhub postgres/admin --- .env.example | 10 ++++++++-- .env.production | 9 --------- config/database.yml | 22 ++++++++++++++-------- 3 files changed, 22 insertions(+), 19 deletions(-) delete mode 100644 .env.production diff --git a/.env.example b/.env.example index cd29496..c63d0bb 100644 --- a/.env.example +++ b/.env.example @@ -3,11 +3,17 @@ LDAP_PORT=389 LDAP_ADMIN_PASSWORD=passthebutter LDAP_SUFFIX="dc=kosmos,dc=org" +WEBHOOKS_ALLOWED_IPS='10.1.1.163' + EJABBERD_API_URL='https://xmpp.kosmos.org/api' BTCPAY_API_URL='http://localhost:23001/api/v1' LNDHUB_API_URL='http://localhost:3023' LNDHUB_PUBLIC_URL='https://lndhub.kosmos.org' - -WEBHOOKS_ALLOWED_IPS='10.1.1.163' +LNDHUB_ADMIN_UI=true +LNDHUB_PG_HOST=localhost +LNDHUB_PG_PORT=5432 +LNDHUB_PG_DATABASE=lndhub +LNDHUB_PG_USERNAME=lndhub +LNDHUB_PG_PASSWORD='' diff --git a/.env.production b/.env.production deleted file mode 100644 index d99e4fe..0000000 --- a/.env.production +++ /dev/null @@ -1,9 +0,0 @@ -EJABBERD_API_URL='https://xmpp.kosmos.org:5443/api' - -BTCPAY_API_URL='http://10.1.1.163:23001/api/v1' - -LNDHUB_LEGACY_API_URL='http://10.1.1.163:3026' -LNDHUB_API_URL='http://10.1.1.163:3026' -LNDHUB_PUBLIC_URL='https://lndhub.kosmos.org' - -WEBHOOKS_ALLOWED_IPS='10.1.1.163' diff --git a/config/database.yml b/config/database.yml index 49b5c31..bac8f50 100644 --- a/config/database.yml +++ b/config/database.yml @@ -13,9 +13,15 @@ development: primary: <<: *default database: db/development.sqlite3 - # lndhub: - # <<: *default - # database: db/lndhub.sqlite3 + lndhub: + <<: *default + adapter: postgresql + database_tasks: false + host: <%= ENV.fetch("LNDHUB_PG_HOST") { 'localhost' } %> + port: <%= ENV.fetch("LNDHUB_PG_PORT") { 5432 } %> + database: <%= ENV.fetch("LNDHUB_PG_DATABASE") { 'lndhub' } %> + username: <%= ENV.fetch("LNDHUB_PG_USERNAME") { 'lndhub' } %> + password: <%= ENV.fetch("LNDHUB_PG_PASSWORD") %> # Warning: The database defined as "test" will be erased and # re-generated from your development database when you run "rake". @@ -42,8 +48,8 @@ production: <<: *default adapter: postgresql database_tasks: false - database: lndhub - port: 5432 - host: <%= Rails.application.credentials.postgres[:host] rescue nil %> - username: <%= Rails.application.credentials.postgres[:username] rescue nil %> - password: <%= Rails.application.credentials.postgres[:password] rescue nil %> + host: <%= ENV.fetch("LNDHUB_PG_HOST") { 'localhost' } %> + port: <%= ENV.fetch("LNDHUB_PG_PORT") { 5432 } %> + database: <%= ENV.fetch("LNDHUB_PG_DATABASE") { 'lndhub' } %> + username: <%= ENV.fetch("LNDHUB_PG_USERNAME") { 'lndhub' } %> + password: <%= ENV.fetch("LNDHUB_PG_PASSWORD") %> From b4c9b31ce759e18331ad030d60961d7a24dc0848 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A2u=20Cao?= Date: Mon, 13 Feb 2023 21:57:06 +0800 Subject: [PATCH 05/20] Disable lndhub admin UI when not configured --- app/controllers/admin/lightning_controller.rb | 9 +++++++++ app/views/shared/_admin_nav.html.erb | 6 ++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/app/controllers/admin/lightning_controller.rb b/app/controllers/admin/lightning_controller.rb index bd7ca99..10e6267 100644 --- a/app/controllers/admin/lightning_controller.rb +++ b/app/controllers/admin/lightning_controller.rb @@ -1,4 +1,6 @@ class Admin::LightningController < Admin::BaseController + before_action :check_feature_enabled + def index @current_section = :lightning @@ -9,4 +11,11 @@ class Admin::LightningController < Admin::BaseController @ln[:current_balance] = LndhubAccount.current.joins(:ledgers).sum("account_ledgers.amount") @ln[:users_with_sats] = @accounts.length end + + def check_feature_enabled + if ENV["LNDHUB_ADMIN_UI"].empty? + flash[:alert] = "Lightning Admin UI not enabled" + redirect_to admin_root_path and return + end + end end diff --git a/app/views/shared/_admin_nav.html.erb b/app/views/shared/_admin_nav.html.erb index b7ef06e..00a0fe8 100644 --- a/app/views/shared/_admin_nav.html.erb +++ b/app/views/shared/_admin_nav.html.erb @@ -6,5 +6,7 @@ class: main_nav_class(@current_section, :invitations) %> <%= link_to "Donations", admin_donations_path, class: main_nav_class(@current_section, :donations) %> -<%= link_to "Lightning", admin_lightning_path, - class: main_nav_class(@current_section, :lightning) %> +<% if ENV["LNDHUB_ADMIN_UI"] %> + <%= link_to "Lightning", admin_lightning_path, + class: main_nav_class(@current_section, :lightning) %> +<% end %> From 959449a3f49214b6377c9671f85ac251bebff050 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A2u=20Cao?= Date: Mon, 13 Feb 2023 22:10:54 +0800 Subject: [PATCH 06/20] Add default empty password --- config/database.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/database.yml b/config/database.yml index bac8f50..6c84728 100644 --- a/config/database.yml +++ b/config/database.yml @@ -52,4 +52,4 @@ production: port: <%= ENV.fetch("LNDHUB_PG_PORT") { 5432 } %> database: <%= ENV.fetch("LNDHUB_PG_DATABASE") { 'lndhub' } %> username: <%= ENV.fetch("LNDHUB_PG_USERNAME") { 'lndhub' } %> - password: <%= ENV.fetch("LNDHUB_PG_PASSWORD") %> + password: <%= ENV.fetch("LNDHUB_PG_PASSWORD") { '' } %> From bdc03a718138d0a4a6e6041ed22ea9a95de95675 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A2u=20Cao?= Date: Tue, 14 Feb 2023 12:55:03 +0800 Subject: [PATCH 07/20] bundle exec rspec --- .drone.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.drone.yml b/.drone.yml index ed52d05..8694c49 100644 --- a/.drone.yml +++ b/.drone.yml @@ -28,7 +28,7 @@ steps: - bundle install --jobs=3 --retry=3 - yarn install - rake css:build - - rake spec + - bundle exec rspec - name: rebuild-cache image: drillster/drone-volume-cache volumes: From e2294c40296e91d87e0750484dddc17e19f45401 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A2u=20Cao?= Date: Mon, 13 Feb 2023 21:56:32 +0800 Subject: [PATCH 08/20] Add config for lndhub postgres/admin --- .env.example | 10 ++++++++-- .env.production | 9 --------- config/database.yml | 22 ++++++++++++++-------- 3 files changed, 22 insertions(+), 19 deletions(-) delete mode 100644 .env.production diff --git a/.env.example b/.env.example index cd29496..c63d0bb 100644 --- a/.env.example +++ b/.env.example @@ -3,11 +3,17 @@ LDAP_PORT=389 LDAP_ADMIN_PASSWORD=passthebutter LDAP_SUFFIX="dc=kosmos,dc=org" +WEBHOOKS_ALLOWED_IPS='10.1.1.163' + EJABBERD_API_URL='https://xmpp.kosmos.org/api' BTCPAY_API_URL='http://localhost:23001/api/v1' LNDHUB_API_URL='http://localhost:3023' LNDHUB_PUBLIC_URL='https://lndhub.kosmos.org' - -WEBHOOKS_ALLOWED_IPS='10.1.1.163' +LNDHUB_ADMIN_UI=true +LNDHUB_PG_HOST=localhost +LNDHUB_PG_PORT=5432 +LNDHUB_PG_DATABASE=lndhub +LNDHUB_PG_USERNAME=lndhub +LNDHUB_PG_PASSWORD='' diff --git a/.env.production b/.env.production deleted file mode 100644 index d99e4fe..0000000 --- a/.env.production +++ /dev/null @@ -1,9 +0,0 @@ -EJABBERD_API_URL='https://xmpp.kosmos.org:5443/api' - -BTCPAY_API_URL='http://10.1.1.163:23001/api/v1' - -LNDHUB_LEGACY_API_URL='http://10.1.1.163:3026' -LNDHUB_API_URL='http://10.1.1.163:3026' -LNDHUB_PUBLIC_URL='https://lndhub.kosmos.org' - -WEBHOOKS_ALLOWED_IPS='10.1.1.163' diff --git a/config/database.yml b/config/database.yml index 49b5c31..bac8f50 100644 --- a/config/database.yml +++ b/config/database.yml @@ -13,9 +13,15 @@ development: primary: <<: *default database: db/development.sqlite3 - # lndhub: - # <<: *default - # database: db/lndhub.sqlite3 + lndhub: + <<: *default + adapter: postgresql + database_tasks: false + host: <%= ENV.fetch("LNDHUB_PG_HOST") { 'localhost' } %> + port: <%= ENV.fetch("LNDHUB_PG_PORT") { 5432 } %> + database: <%= ENV.fetch("LNDHUB_PG_DATABASE") { 'lndhub' } %> + username: <%= ENV.fetch("LNDHUB_PG_USERNAME") { 'lndhub' } %> + password: <%= ENV.fetch("LNDHUB_PG_PASSWORD") %> # Warning: The database defined as "test" will be erased and # re-generated from your development database when you run "rake". @@ -42,8 +48,8 @@ production: <<: *default adapter: postgresql database_tasks: false - database: lndhub - port: 5432 - host: <%= Rails.application.credentials.postgres[:host] rescue nil %> - username: <%= Rails.application.credentials.postgres[:username] rescue nil %> - password: <%= Rails.application.credentials.postgres[:password] rescue nil %> + host: <%= ENV.fetch("LNDHUB_PG_HOST") { 'localhost' } %> + port: <%= ENV.fetch("LNDHUB_PG_PORT") { 5432 } %> + database: <%= ENV.fetch("LNDHUB_PG_DATABASE") { 'lndhub' } %> + username: <%= ENV.fetch("LNDHUB_PG_USERNAME") { 'lndhub' } %> + password: <%= ENV.fetch("LNDHUB_PG_PASSWORD") %> From c44ce61e258d4382bf82a1d018bd0cacd279db01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A2u=20Cao?= Date: Tue, 14 Feb 2023 13:06:18 +0800 Subject: [PATCH 09/20] Remove empty tests --- .../quickstats_container_component_spec.rb | 15 --------------- spec/components/quickstats_item_component_spec.rb | 15 --------------- 2 files changed, 30 deletions(-) delete mode 100644 spec/components/quickstats_container_component_spec.rb delete mode 100644 spec/components/quickstats_item_component_spec.rb diff --git a/spec/components/quickstats_container_component_spec.rb b/spec/components/quickstats_container_component_spec.rb deleted file mode 100644 index 9ac0406..0000000 --- a/spec/components/quickstats_container_component_spec.rb +++ /dev/null @@ -1,15 +0,0 @@ -# frozen_string_literal: true - -require "rails_helper" - -RSpec.describe QuickstatsContainerComponent, type: :component do - pending "add some examples to (or delete) #{__FILE__}" - - # it "renders something useful" do - # expect( - # render_inline(described_class.new(attr: "value")) { "Hello, components!" }.css("p").to_html - # ).to include( - # "Hello, components!" - # ) - # end -end diff --git a/spec/components/quickstats_item_component_spec.rb b/spec/components/quickstats_item_component_spec.rb deleted file mode 100644 index c886c37..0000000 --- a/spec/components/quickstats_item_component_spec.rb +++ /dev/null @@ -1,15 +0,0 @@ -# frozen_string_literal: true - -require "rails_helper" - -RSpec.describe QuickstatsItemComponent, type: :component do - pending "add some examples to (or delete) #{__FILE__}" - - # it "renders something useful" do - # expect( - # render_inline(described_class.new(attr: "value")) { "Hello, components!" }.css("p").to_html - # ).to include( - # "Hello, components!" - # ) - # end -end From c2e09091325cd007eeb0bb6e6ec00430b47a774b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A2u=20Cao?= Date: Tue, 14 Feb 2023 13:16:10 +0800 Subject: [PATCH 10/20] Use plain hash for ENV vars --- config/database.yml | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/config/database.yml b/config/database.yml index bac8f50..7f9bef7 100644 --- a/config/database.yml +++ b/config/database.yml @@ -1,12 +1,6 @@ -# SQLite. Versions 3.8.0 and up are supported. -# gem install sqlite3 -# -# Ensure the SQLite 3 gem is defined in your Gemfile -# gem 'sqlite3' -# default: &default adapter: sqlite3 - pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> + pool: <%= ENV["DB_POOL"] || ENV['MAX_THREADS'] || 5 %> timeout: 5000 development: @@ -17,11 +11,11 @@ development: <<: *default adapter: postgresql database_tasks: false - host: <%= ENV.fetch("LNDHUB_PG_HOST") { 'localhost' } %> - port: <%= ENV.fetch("LNDHUB_PG_PORT") { 5432 } %> - database: <%= ENV.fetch("LNDHUB_PG_DATABASE") { 'lndhub' } %> - username: <%= ENV.fetch("LNDHUB_PG_USERNAME") { 'lndhub' } %> - password: <%= ENV.fetch("LNDHUB_PG_PASSWORD") %> + host: <%= ENV["LNDHUB_PG_HOST"] || 'localhost' %> + port: <%= ENV["LNDHUB_PG_PORT"] || 5432 %> + database: <%= ENV["LNDHUB_PG_DATABASE"] || 'lndhub' %> + username: <%= ENV["LNDHUB_PG_USERNAME"] || 'lndhub' %> + password: <%= ENV["LNDHUB_PG_PASSWORD"] %> # Warning: The database defined as "test" will be erased and # re-generated from your development database when you run "rake". @@ -48,8 +42,8 @@ production: <<: *default adapter: postgresql database_tasks: false - host: <%= ENV.fetch("LNDHUB_PG_HOST") { 'localhost' } %> - port: <%= ENV.fetch("LNDHUB_PG_PORT") { 5432 } %> - database: <%= ENV.fetch("LNDHUB_PG_DATABASE") { 'lndhub' } %> - username: <%= ENV.fetch("LNDHUB_PG_USERNAME") { 'lndhub' } %> - password: <%= ENV.fetch("LNDHUB_PG_PASSWORD") %> + host: <%= ENV["LNDHUB_PG_HOST"] || 'localhost' %> + port: <%= ENV["LNDHUB_PG_PORT"] || 5432 %> + database: <%= ENV["LNDHUB_PG_DATABASE"] || 'lndhub' %> + username: <%= ENV["LNDHUB_PG_USERNAME"] || 'lndhub' %> + password: <%= ENV["LNDHUB_PG_PASSWORD"] %> From 7280a4c0230225fddf19c41747d96c53c2c0314c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A2u=20Cao?= Date: Thu, 16 Feb 2023 23:40:17 +0800 Subject: [PATCH 11/20] Order invitations by date on user invitations page --- app/controllers/invitations_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/invitations_controller.rb b/app/controllers/invitations_controller.rb index 147cb6a..2bc9d80 100644 --- a/app/controllers/invitations_controller.rb +++ b/app/controllers/invitations_controller.rb @@ -5,7 +5,7 @@ class InvitationsController < ApplicationController # GET /invitations def index @invitations_unused = current_user.invitations.unused - @invitations_used = current_user.invitations.used + @invitations_used = current_user.invitations.used.order('used_at desc') @current_section = :invitations end From cd7b05e2ff912d041e05ef7bdd1deb54197f4fd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A2u=20Cao?= Date: Fri, 17 Feb 2023 17:07:42 +0800 Subject: [PATCH 12/20] Add rails-settings-cached, use for initial feature flags --- Gemfile | 1 + Gemfile.lock | 4 ++++ app/controllers/admin/lightning_controller.rb | 2 +- app/models/setting.rb | 7 +++++++ app/views/shared/_admin_nav.html.erb | 2 +- db/migrate/20230217084310_create_settings.rb | 15 +++++++++++++++ db/schema.rb | 10 +++++++++- 7 files changed, 38 insertions(+), 3 deletions(-) create mode 100644 app/models/setting.rb create mode 100644 db/migrate/20230217084310_create_settings.rb diff --git a/Gemfile b/Gemfile index 8824e1b..169f39e 100644 --- a/Gemfile +++ b/Gemfile @@ -38,6 +38,7 @@ gem 'net-ldap' # Utilities gem "rqrcode", "~> 2.0" +gem 'rails-settings-cached', '~> 2.8.3' # HTTP requests gem 'faraday' diff --git a/Gemfile.lock b/Gemfile.lock index d80ab81..5a658b5 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -206,6 +206,9 @@ GEM nokogiri (>= 1.6) rails-html-sanitizer (1.4.3) loofah (~> 2.3) + rails-settings-cached (2.8.3) + activerecord (>= 5.0.0) + railties (>= 5.0.0) railties (7.0.4) actionpack (= 7.0.4) activesupport (= 7.0.4) @@ -327,6 +330,7 @@ DEPENDENCIES pg (~> 1.2.3) puma (~> 4.1) rails (~> 7.0.2) + rails-settings-cached (~> 2.8.3) rqrcode (~> 2.0) rspec-rails sidekiq (< 7) diff --git a/app/controllers/admin/lightning_controller.rb b/app/controllers/admin/lightning_controller.rb index 10e6267..2b3dfe7 100644 --- a/app/controllers/admin/lightning_controller.rb +++ b/app/controllers/admin/lightning_controller.rb @@ -13,7 +13,7 @@ class Admin::LightningController < Admin::BaseController end def check_feature_enabled - if ENV["LNDHUB_ADMIN_UI"].empty? + if !Setting.lndhub_admin_enabled? flash[:alert] = "Lightning Admin UI not enabled" redirect_to admin_root_path and return end diff --git a/app/models/setting.rb b/app/models/setting.rb new file mode 100644 index 0000000..2a80940 --- /dev/null +++ b/app/models/setting.rb @@ -0,0 +1,7 @@ +# RailsSettings Model +class Setting < RailsSettings::Base + cache_prefix { "v1" } + + field :lndhub_enabled, default: (ENV["LNDHUB_API_URL"].present?.to_s || "false"), type: :boolean + field :lndhub_admin_enabled, default: (ENV["LNDHUB_ADMIN_UI"] || "false"), type: :boolean +end diff --git a/app/views/shared/_admin_nav.html.erb b/app/views/shared/_admin_nav.html.erb index 00a0fe8..dfba699 100644 --- a/app/views/shared/_admin_nav.html.erb +++ b/app/views/shared/_admin_nav.html.erb @@ -6,7 +6,7 @@ class: main_nav_class(@current_section, :invitations) %> <%= link_to "Donations", admin_donations_path, class: main_nav_class(@current_section, :donations) %> -<% if ENV["LNDHUB_ADMIN_UI"] %> +<% if Setting.lndhub_admin_enabled? %> <%= link_to "Lightning", admin_lightning_path, class: main_nav_class(@current_section, :lightning) %> <% end %> diff --git a/db/migrate/20230217084310_create_settings.rb b/db/migrate/20230217084310_create_settings.rb new file mode 100644 index 0000000..6fc5a0c --- /dev/null +++ b/db/migrate/20230217084310_create_settings.rb @@ -0,0 +1,15 @@ +class CreateSettings < ActiveRecord::Migration[7.0] + def self.up + create_table :settings do |t| + t.string :var, null: false + t.text :value, null: true + t.timestamps + end + + add_index :settings, %i(var), unique: true + end + + def self.down + drop_table :settings + end +end diff --git a/db/schema.rb b/db/schema.rb index 486c465..6b3cf56 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.0].define(version: 2023_01_11_113139) do +ActiveRecord::Schema[7.0].define(version: 2023_02_17_084310) do create_table "donations", force: :cascade do |t| t.integer "user_id" t.integer "amount_sats" @@ -34,6 +34,14 @@ ActiveRecord::Schema[7.0].define(version: 2023_01_11_113139) do t.index ["user_id"], name: "index_invitations_on_user_id" end + create_table "settings", force: :cascade do |t| + t.string "var", null: false + t.text "value" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["var"], name: "index_settings_on_var", unique: true + end + create_table "users", force: :cascade do |t| t.string "cn" t.string "ou" From 084835f06a6f139667d9f6bb445659df512f080a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A2u=20Cao?= Date: Fri, 17 Feb 2023 20:32:29 +0800 Subject: [PATCH 13/20] WIP Add admin settings pages, reserved username config Prototyping settings forms --- app/assets/stylesheets/components/forms.css | 2 +- .../settings/registrations_controller.rb | 9 +++++ .../admin/settings/services_controller.rb | 9 +++++ app/controllers/admin/settings_controller.rb | 12 ++++++ app/models/setting.rb | 10 +++++ .../settings/registrations/index.html.erb | 31 +++++++++++++++ .../admin/settings/services/index.html.erb | 39 +++++++++++++++++++ app/views/icons/_grid.html.erb | 2 +- app/views/shared/_admin_nav.html.erb | 2 + .../shared/_admin_sidenav_settings.html.erb | 11 ++++++ config/routes.rb | 5 +++ 11 files changed, 130 insertions(+), 2 deletions(-) create mode 100644 app/controllers/admin/settings/registrations_controller.rb create mode 100644 app/controllers/admin/settings/services_controller.rb create mode 100644 app/controllers/admin/settings_controller.rb create mode 100644 app/views/admin/settings/registrations/index.html.erb create mode 100644 app/views/admin/settings/services/index.html.erb create mode 100644 app/views/shared/_admin_sidenav_settings.html.erb diff --git a/app/assets/stylesheets/components/forms.css b/app/assets/stylesheets/components/forms.css index 6c68083..61c7b1e 100644 --- a/app/assets/stylesheets/components/forms.css +++ b/app/assets/stylesheets/components/forms.css @@ -1,6 +1,6 @@ @layer components { input[type=text], input[type=email], input[type=password], - input[type=number], select { + input[type=number], select, textarea { @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; diff --git a/app/controllers/admin/settings/registrations_controller.rb b/app/controllers/admin/settings/registrations_controller.rb new file mode 100644 index 0000000..1abca45 --- /dev/null +++ b/app/controllers/admin/settings/registrations_controller.rb @@ -0,0 +1,9 @@ +class Admin::Settings::RegistrationsController < Admin::SettingsController + + def index + end + + def update + end + +end diff --git a/app/controllers/admin/settings/services_controller.rb b/app/controllers/admin/settings/services_controller.rb new file mode 100644 index 0000000..ebdad4e --- /dev/null +++ b/app/controllers/admin/settings/services_controller.rb @@ -0,0 +1,9 @@ +class Admin::Settings::ServicesController < Admin::SettingsController + + def index + end + + def update + end + +end diff --git a/app/controllers/admin/settings_controller.rb b/app/controllers/admin/settings_controller.rb new file mode 100644 index 0000000..8353f9b --- /dev/null +++ b/app/controllers/admin/settings_controller.rb @@ -0,0 +1,12 @@ +class Admin::SettingsController < Admin::BaseController + before_action :set_current_section + + def index + end + + private + + def set_current_section + @current_section = :settings + end +end diff --git a/app/models/setting.rb b/app/models/setting.rb index 2a80940..16a8590 100644 --- a/app/models/setting.rb +++ b/app/models/setting.rb @@ -2,6 +2,16 @@ class Setting < RailsSettings::Base cache_prefix { "v1" } + field :reserved_usernames, type: :array, default: %w[ + account + accounts + admin + donations + mail + webmaster + support + ] + field :lndhub_enabled, default: (ENV["LNDHUB_API_URL"].present?.to_s || "false"), type: :boolean field :lndhub_admin_enabled, default: (ENV["LNDHUB_ADMIN_UI"] || "false"), type: :boolean end diff --git a/app/views/admin/settings/registrations/index.html.erb b/app/views/admin/settings/registrations/index.html.erb new file mode 100644 index 0000000..2a327e1 --- /dev/null +++ b/app/views/admin/settings/registrations/index.html.erb @@ -0,0 +1,31 @@ +<%= render HeaderComponent.new(title: "Settings") %> + +<%= render MainWithSidenavComponent.new(sidenav_partial: 'shared/admin_sidenav_settings') do %> +
+

Registrations

+ <%= form_for(Setting.new, url: admin_settings_services_path) do |f| %> + <% if @errors && @errors.any? %> +
+
    + <% @errors.full_messages.each do |msg| %> +
  • <%= msg %>
  • + <% end %> +
+
+ <% end %> + + + <% end %> +
+<% end %> diff --git a/app/views/admin/settings/services/index.html.erb b/app/views/admin/settings/services/index.html.erb new file mode 100644 index 0000000..c0dad64 --- /dev/null +++ b/app/views/admin/settings/services/index.html.erb @@ -0,0 +1,39 @@ +<%= render HeaderComponent.new(title: "Settings") %> + +<%= render MainWithSidenavComponent.new(sidenav_partial: 'shared/admin_sidenav_settings') do %> +
+

Lightning Network

+ <%= form_for(Setting.new, url: admin_settings_services_path) do |f| %> + <% if @errors && @errors.any? %> +
+
    + <% @errors.full_messages.each do |msg| %> +
  • <%= msg %>
  • + <% end %> +
+
+ <% end %> + +
    +
  • +
    + +

    LNDHub configuration present and wallet features enabled

    +
    + <%= f.check_box :lndhub_enabled, checked: Setting.lndhub_enabled?, + disabled: true, + class: "relative ml-4 inline-flex flex-shrink-0" %> +
  • +
  • +
    + +

    LNDHub database configuration present and admin panel enabled

    +
    + <%= f.check_box :lndhub_admin_enabled, checked: Setting.lndhub_admin_enabled?, + disabled: true, + class: "relative ml-4 inline-flex flex-shrink-0" %> +
  • +
+ <% end %> +
+<% end %> diff --git a/app/views/icons/_grid.html.erb b/app/views/icons/_grid.html.erb index 8ef2e9d..57f9632 100644 --- a/app/views/icons/_grid.html.erb +++ b/app/views/icons/_grid.html.erb @@ -1 +1 @@ - \ No newline at end of file + diff --git a/app/views/shared/_admin_nav.html.erb b/app/views/shared/_admin_nav.html.erb index dfba699..dd8c8b8 100644 --- a/app/views/shared/_admin_nav.html.erb +++ b/app/views/shared/_admin_nav.html.erb @@ -10,3 +10,5 @@ <%= link_to "Lightning", admin_lightning_path, class: main_nav_class(@current_section, :lightning) %> <% end %> +<%= link_to "Settings", admin_settings_registrations_path, + class: main_nav_class(@current_section, :settings) %> diff --git a/app/views/shared/_admin_sidenav_settings.html.erb b/app/views/shared/_admin_sidenav_settings.html.erb new file mode 100644 index 0000000..d675242 --- /dev/null +++ b/app/views/shared/_admin_sidenav_settings.html.erb @@ -0,0 +1,11 @@ +<%= render SidenavLinkComponent.new( + name: "Registrations", path: admin_settings_registrations_path, icon: "user", + active: current_page?(admin_settings_registrations_path) +) %> +<%= render SidenavLinkComponent.new( + name: "Services", path: admin_settings_services_path, icon: "grid", + active: current_page?(admin_settings_services_path) +) %> +<%= render SidenavLinkComponent.new( + name: "Security", path: "#", icon: "shield", disabled: true +) %> diff --git a/config/routes.rb b/config/routes.rb index 00475c5..eb118cf 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -43,6 +43,11 @@ Rails.application.routes.draw do get 'invitations', to: 'invitations#index' resources :donations get 'lightning', to: 'lightning#index' + + namespace :settings do + resources 'registrations', only: ['index', 'update'] + resources 'services', only: ['index', 'update'] + end end authenticate :user, ->(user) { user.is_admin? } do From b7bf957dd257c80781fad9d70ec500f820e3c688 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A2u=20Cao?= Date: Fri, 17 Feb 2023 21:36:30 +0800 Subject: [PATCH 14/20] Update registration settings --- .../settings/registrations_controller.rb | 31 ++++++++++++++++++- app/models/setting.rb | 8 +---- .../settings/registrations/index.html.erb | 20 +++++++----- config/routes.rb | 4 +-- 4 files changed, 46 insertions(+), 17 deletions(-) diff --git a/app/controllers/admin/settings/registrations_controller.rb b/app/controllers/admin/settings/registrations_controller.rb index 1abca45..3f2019e 100644 --- a/app/controllers/admin/settings/registrations_controller.rb +++ b/app/controllers/admin/settings/registrations_controller.rb @@ -3,7 +3,36 @@ class Admin::Settings::RegistrationsController < Admin::SettingsController def index end - def update + def create + @errors = ActiveModel::Errors.new(Setting.new) + + setting_params.keys.each do |key| + next if setting_params[key].nil? + + setting = Setting.new(var: key) + setting.value = setting_params[key].strip + unless setting.valid? + @errors.merge!(setting.errors) + end + end + + if @errors.any? + render :index + end + + setting_params.keys.each do |key| + Setting.send("#{key}=", setting_params[key].strip) unless setting_params[key].nil? + end + + redirect_to admin_settings_registrations_path, flash: { + success: "Settings saved" + } + end + + private + + def setting_params + params.require(:setting).permit(:reserved_usernames) end end diff --git a/app/models/setting.rb b/app/models/setting.rb index 16a8590..fb4dae5 100644 --- a/app/models/setting.rb +++ b/app/models/setting.rb @@ -3,13 +3,7 @@ class Setting < RailsSettings::Base cache_prefix { "v1" } field :reserved_usernames, type: :array, default: %w[ - account - accounts - admin - donations - mail - webmaster - support + account accounts admin donations mail webmaster support ] field :lndhub_enabled, default: (ENV["LNDHUB_API_URL"].present?.to_s || "false"), type: :boolean diff --git a/app/views/admin/settings/registrations/index.html.erb b/app/views/admin/settings/registrations/index.html.erb index 2a327e1..2131e41 100644 --- a/app/views/admin/settings/registrations/index.html.erb +++ b/app/views/admin/settings/registrations/index.html.erb @@ -1,9 +1,9 @@ <%= render HeaderComponent.new(title: "Settings") %> <%= render MainWithSidenavComponent.new(sidenav_partial: 'shared/admin_sidenav_settings') do %> -
-

Registrations

- <%= form_for(Setting.new, url: admin_settings_services_path) do |f| %> + <%= form_for(Setting.new, url: admin_settings_registrations_path) do |f| %> +
+

Registrations

<% if @errors && @errors.any? %>
    @@ -14,7 +14,7 @@
<% end %> -
+
+ +
+

+ <%= f.submit 'Save', class: "btn-md btn-blue w-full md:w-auto" %> +

+
+ <% end %> <% end %> diff --git a/config/routes.rb b/config/routes.rb index eb118cf..7559393 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -45,8 +45,8 @@ Rails.application.routes.draw do get 'lightning', to: 'lightning#index' namespace :settings do - resources 'registrations', only: ['index', 'update'] - resources 'services', only: ['index', 'update'] + resources 'registrations', only: ['index', 'create'] + resources 'services', only: ['index', 'create'] end end From 346e36e16023f8600158073277124a1f3ea44c77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A2u=20Cao?= Date: Sat, 18 Feb 2023 10:07:54 +0800 Subject: [PATCH 15/20] Use success notices where appropriate --- app/controllers/admin/donations_controller.rb | 17 ++++++++++++++--- app/controllers/invitations_controller.rb | 5 ++++- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/app/controllers/admin/donations_controller.rb b/app/controllers/admin/donations_controller.rb index 84d98d9..ffbae14 100644 --- a/app/controllers/admin/donations_controller.rb +++ b/app/controllers/admin/donations_controller.rb @@ -33,7 +33,11 @@ class Admin::DonationsController < Admin::BaseController respond_to do |format| if @donation.save - format.html { redirect_to admin_donation_url(@donation), notice: 'Donation was successfully created.' } + format.html do + redirect_to admin_donation_url(@donation), flash: { + success: 'Donation was successfully created.' + } + end format.json { render :show, status: :created, location: @donation } else format.html { render :new } @@ -47,7 +51,11 @@ class Admin::DonationsController < Admin::BaseController 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.html do + redirect_to admin_donation_url(@donation), flash: { + success: 'Donation was successfully updated.' + } + end format.json { render :show, status: :ok, location: @donation } else format.html { render :edit } @@ -61,7 +69,10 @@ class Admin::DonationsController < Admin::BaseController def destroy @donation.destroy respond_to do |format| - format.html { redirect_to admin_donations_url, notice: 'Donation was successfully destroyed.' } + format.html do redirect_to admin_donations_url, flash: { + success: 'Donation was successfully destroyed.' + } + end format.json { head :no_content } end end diff --git a/app/controllers/invitations_controller.rb b/app/controllers/invitations_controller.rb index 2bc9d80..3bb038a 100644 --- a/app/controllers/invitations_controller.rb +++ b/app/controllers/invitations_controller.rb @@ -27,7 +27,10 @@ class InvitationsController < ApplicationController respond_to do |format| if @invitation.save - format.html { redirect_to @invitation, notice: 'Invitation was successfully created.' } + format.html do redirect_to @invitation, flash: { + success: 'Invitation was successfully created.' + } + end format.json { render :show, status: :created, location: @invitation } else format.html { render :new } From 6e884b789af792d03ef15276c78f8daf84cb1466 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A2u=20Cao?= Date: Sat, 18 Feb 2023 10:08:49 +0800 Subject: [PATCH 16/20] Show full lightning account ID/login No use in hiding it, because it will be public through Lightning Address, but can be useful for copypasta. --- app/views/admin/lightning/index.html.erb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/admin/lightning/index.html.erb b/app/views/admin/lightning/index.html.erb index 902186c..2c099cb 100644 --- a/app/views/admin/lightning/index.html.erb +++ b/app/views/admin/lightning/index.html.erb @@ -32,7 +32,7 @@ <% @accounts.each do |account| %> - <%= account.login[0, 8] %>...<%= account.login[12, 19] %> + <%= account.login %> <% if user = @users.find{ |u| u[2] == account.login } %> From 25a0723166ec6d2f80cad3fddb4518eb87c563c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A2u=20Cao?= Date: Sun, 19 Feb 2023 11:55:53 +0800 Subject: [PATCH 17/20] Make admin flag persist for subsequent calls in spec --- spec/features/admin/dashboard_spec.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/spec/features/admin/dashboard_spec.rb b/spec/features/admin/dashboard_spec.rb index 3120d5b..f0e7edb 100644 --- a/spec/features/admin/dashboard_spec.rb +++ b/spec/features/admin/dashboard_spec.rb @@ -4,7 +4,9 @@ RSpec.describe 'Admin dashboard', type: :feature do let(:user) { create :user } before do - allow(user).to receive(:is_admin?).and_return(true) + allow(Devise::LDAP::Adapter).to receive(:get_ldap_param) + .with(user.cn, :admin).and_return(["true"]) + login_as user, :scope => :user end From df3ec9f90a4b646ea0093351e404033e921a162a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A2u=20Cao?= Date: Sun, 19 Feb 2023 11:56:16 +0800 Subject: [PATCH 18/20] Add spec for updating reserved usernames setting --- spec/features/admin/settings_spec.rb | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 spec/features/admin/settings_spec.rb diff --git a/spec/features/admin/settings_spec.rb b/spec/features/admin/settings_spec.rb new file mode 100644 index 0000000..bd586c7 --- /dev/null +++ b/spec/features/admin/settings_spec.rb @@ -0,0 +1,21 @@ +require 'rails_helper' + +RSpec.describe 'Admin/global settings', type: :feature do + let(:user) { create :user } + + before do + allow(Devise::LDAP::Adapter).to receive(:get_ldap_param) + .with(user.cn, :admin).and_return(["true"]) + + login_as user, :scope => :user + end + + scenario 'Update reserved usernames' do + visit admin_settings_registrations_path + expect(Setting.reserved_usernames).not_to include(['Kosmos', 'Kredits']) + + fill_in 'Reserved usernames', with: "Kosmos\nKredits" + click_button "Save" + expect(Setting.reserved_usernames).to eq(['Kosmos', 'Kredits']) + end +end From 9407c7a94dbf363cb5f4469df905167edd5f7a2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A2u=20Cao?= Date: Sun, 19 Feb 2023 12:04:24 +0800 Subject: [PATCH 19/20] Add username format restrictions --- app/models/user.rb | 7 +++++++ spec/features/signup_spec.rb | 6 ++++++ 2 files changed, 13 insertions(+) diff --git a/app/models/user.rb b/app/models/user.rb index 40d5f20..4f49b81 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -12,6 +12,13 @@ class User < ApplicationRecord validates_uniqueness_of :cn validates_length_of :cn, :minimum => 3 + validates_format_of :cn, with: /\A([a-z0-9\-])*\z/, + if: Proc.new{ |u| u.cn.present? }, + message: "is invalid. Please use only letters, numbers and -" + validates_format_of :cn, without: /\A-/, + if: Proc.new{ |u| u.cn.present? }, + message: "is invalid. Usernames need to start with a letter." + validates_uniqueness_of :email validates :email, email: true diff --git a/spec/features/signup_spec.rb b/spec/features/signup_spec.rb index 8b730c2..9dff0df 100644 --- a/spec/features/signup_spec.rb +++ b/spec/features/signup_spec.rb @@ -71,6 +71,12 @@ RSpec.describe "Signup", type: :feature do fill_in "user_cn", with: "t" click_button "Continue" expect(page).to have_content("Username is too short") + fill_in "user_cn", with: "-tony" + click_button "Continue" + expect(page).to have_content("Username is invalid") + fill_in "user_cn", with: "$atoshi" + click_button "Continue" + expect(page).to have_content("Username is invalid") fill_in "user_cn", with: "jimmy" click_button "Continue" expect(page).to have_content("Username has already been taken") From f3f967f9f7f73a98a6f1f2cd4ddabf96bd075e03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A2u=20Cao?= Date: Sun, 19 Feb 2023 12:10:26 +0800 Subject: [PATCH 20/20] Prevent signups with reserved usernames closes #12 --- app/models/user.rb | 2 ++ spec/features/signup_spec.rb | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/app/models/user.rb b/app/models/user.rb index 4f49b81..f12c8ce 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -18,6 +18,8 @@ class User < ApplicationRecord validates_format_of :cn, without: /\A-/, if: Proc.new{ |u| u.cn.present? }, message: "is invalid. Usernames need to start with a letter." + validates_format_of :cn, without: /\A(#{Setting.reserved_usernames.join('|')})\z/i, + message: "has already been taken" validates_uniqueness_of :email validates :email, email: true diff --git a/spec/features/signup_spec.rb b/spec/features/signup_spec.rb index 9dff0df..9959661 100644 --- a/spec/features/signup_spec.rb +++ b/spec/features/signup_spec.rb @@ -109,5 +109,11 @@ RSpec.describe "Signup", type: :feature do expect(page).to have_content("confirm your address") end end + + scenario "Reserved usernames" do + fill_in "user_cn", with: "accounts" + click_button "Continue" + expect(page).to have_content("Username has already been taken") + end end end