@@ -11,7 +30,7 @@
in USD
Public name
Date
-
+
@@ -23,11 +42,13 @@
<% 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?' } %>
+
<% 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
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 %>
- Domains
-
-
- <%= 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 %>
+
-
-
-
- UID
- E-Mail
- Admin
-
-
-
-
- <% @entries.each do |entry| %>
-
- <%= entry[:uid] %>
- <%= entry[:mail] %>
- <%= entry[:admin] %>
-
-
- <% end %>
-
-
+ <% if @orgs.length > 1 %>
+
+ Domains
+
+ <% @orgs.each do |org| %>
+
+ <%= link_to org[:ou], admin_ldap_users_path(ou: org[:ou]), class: "ks-text-link" %>
+
+ <% end %>
+
+
+ <% end %>
+
+
+
+
+
+ UID
+ E-Mail
+ Admin
+
+
+
+
+ <% @entries.each do |entry| %>
+
+ <%= entry[:uid] %>
+ <%= entry[:mail] %>
+ <%= entry[:admin] %>
+
+
+ <% end %>
+
+
+
<% end %>
diff --git a/app/views/admin/lightning/index.html.erb b/app/views/admin/lightning/index.html.erb
new file mode 100644
index 0000000..2c099cb
--- /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
+
+
+
+ LN Account
+ User
+ Balance
+
+
+
+ <% @accounts.each do |account| %>
+
+
+ <%= account.login %>
+
+
+ <% if user = @users.find{ |u| u[2] == account.login } %>
+ <%= "#{user[0]}@#{user[1]}" %>
+ <% end %>
+
+ <%= number_with_delimiter account.balance.to_i.to_s %>
+
+ <% end %>
+
+
+
+<% 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..2131e41
--- /dev/null
+++ b/app/views/admin/settings/registrations/index.html.erb
@@ -0,0 +1,37 @@
+<%= render HeaderComponent.new(title: "Settings") %>
+
+<%= render MainWithSidenavComponent.new(sidenav_partial: 'shared/admin_sidenav_settings') do %>
+ <%= form_for(Setting.new, url: admin_settings_registrations_path) do |f| %>
+
+ Registrations
+ <% if @errors && @errors.any? %>
+
+
+ <% @errors.full_messages.each do |msg| %>
+ <%= msg %>
+ <% end %>
+
+
+ <% end %>
+
+
+ Reserved usernames
+
+ These usernames cannot be registered as accounts:
+
+ <%= f.text_area :reserved_usernames,
+ value: Setting.reserved_usernames.join("\n"),
+ class: "h-44 mb-2" %>
+
+ One username per line
+
+
+
+
+
+
+ <%= f.submit 'Save', class: "btn-md btn-blue w-full md:w-auto" %>
+
+
+ <% 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 %>
+
+
+
+
+
Enable LNDHub integration
+
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" %>
+
+
+
+
Enable LNDHub admin panel
+
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/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..dd8c8b8 100644
--- a/app/views/shared/_admin_nav.html.erb
+++ b/app/views/shared/_admin_nav.html.erb
@@ -1,8 +1,14 @@
<%= 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) %>
+<% if Setting.lndhub_admin_enabled? %>
+ <%= 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/database.yml b/config/database.yml
index 10df6c5..7f9bef7 100644
--- a/config/database.yml
+++ b/config/database.yml
@@ -1,30 +1,49 @@
-# 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:
- <<: *default
- database: db/development.sqlite3
+ primary:
+ <<: *default
+ database: db/development.sqlite3
+ lndhub:
+ <<: *default
+ adapter: postgresql
+ database_tasks: false
+ host: <%= ENV["LNDHUB_PG_HOST"] || 'localhost' %>
+ port: <%= ENV["LNDHUB_PG_PORT"] || 5432 %>
+ database: <%= ENV["LNDHUB_PG_DATABASE"] || 'lndhub' %>
+ username: <%= ENV["LNDHUB_PG_USERNAME"] || 'lndhub' %>
+ password: <%= ENV["LNDHUB_PG_PASSWORD"] %>
# Warning: The database defined as "test" will be erased and
# 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
+ 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"] %>
diff --git a/config/routes.rb b/config/routes.rb
index 5ed99bd..7559393 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -39,9 +39,15 @@ 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'
+
+ namespace :settings do
+ resources 'registrations', only: ['index', 'create']
+ resources 'services', only: ['index', 'create']
+ end
end
authenticate :user, ->(user) { user.is_admin? } do
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"
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/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
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
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
diff --git a/spec/features/signup_spec.rb b/spec/features/signup_spec.rb
index 8b730c2..9959661 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")
@@ -103,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