Merge branch 'master' into feature/ln_address_keysend
This commit is contained in:
@@ -4,7 +4,9 @@
|
||||
|
||||
@import "components/base";
|
||||
@import "components/buttons";
|
||||
@import "components/dashboard_services";
|
||||
@import "components/forms";
|
||||
@import "components/links";
|
||||
@import "components/notifications";
|
||||
@import "components/pagination";
|
||||
@import "components/tables";
|
||||
|
||||
@@ -36,10 +36,18 @@
|
||||
@apply mb-4 leading-6;
|
||||
}
|
||||
|
||||
main p:last-child {
|
||||
@apply mb-0;
|
||||
}
|
||||
|
||||
main ul {
|
||||
@apply mb-6;
|
||||
}
|
||||
|
||||
main ul:last-child {
|
||||
@apply mb-0;
|
||||
}
|
||||
|
||||
main ul li {
|
||||
@apply leading-6;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
@layer components {
|
||||
.btn {
|
||||
@apply font-semibold rounded-md leading-none cursor-pointer text-center
|
||||
@apply inline-block font-semibold rounded-md leading-none cursor-pointer text-center
|
||||
transition-colors duration-75 focus:outline-none focus:ring-4;
|
||||
}
|
||||
|
||||
|
||||
5
app/assets/stylesheets/components/dashboard_services.css
Normal file
5
app/assets/stylesheets/components/dashboard_services.css
Normal file
@@ -0,0 +1,5 @@
|
||||
@layer components {
|
||||
.services > div > a {
|
||||
background-image: linear-gradient(110deg, rgba(255,255,255,0.99) 0, rgba(255,255,255,0.88) 100%);
|
||||
}
|
||||
}
|
||||
45
app/assets/stylesheets/components/pagination.css
Normal file
45
app/assets/stylesheets/components/pagination.css
Normal file
@@ -0,0 +1,45 @@
|
||||
@layer components {
|
||||
.pagy-nav.pagination {
|
||||
@apply isolate inline-flex -space-x-px rounded-md shadow-sm;
|
||||
}
|
||||
|
||||
.pagy-nav .page:not(.prev):not(.next) {
|
||||
@apply hidden sm:inline-block;
|
||||
}
|
||||
|
||||
.pagy-nav .page.next a {
|
||||
@apply relative inline-flex items-center rounded-r-md border
|
||||
border-gray-300 bg-white px-3 py-2 text-sm font-medium
|
||||
text-gray-500 hover:bg-gray-100 focus:z-20;
|
||||
}
|
||||
|
||||
.pagy-nav .page.prev a {
|
||||
@apply relative inline-flex items-center rounded-l-md border
|
||||
border-gray-300 bg-white px-3 py-2 text-sm font-medium
|
||||
text-gray-500 hover:bg-gray-100 focus:z-20;
|
||||
}
|
||||
|
||||
.pagy-nav .page.next.disabled {
|
||||
@apply relative inline-flex items-center rounded-r-md border
|
||||
border-gray-300 bg-gray-100 px-3 py-2 text-sm font-medium
|
||||
text-gray-400 focus:z-20;
|
||||
}
|
||||
|
||||
.pagy-nav .page.prev.disabled {
|
||||
@apply relative inline-flex items-center rounded-l-md border
|
||||
border-gray-300 bg-gray-100 px-3 py-2 text-sm font-medium
|
||||
text-gray-400 focus:z-20;
|
||||
}
|
||||
|
||||
.pagy-nav .page a, .page.gap {
|
||||
@apply bg-white border-gray-300 text-gray-500 hover:bg-gray-100 relative
|
||||
inline-flex items-center border px-4 py-2 text-sm font-medium
|
||||
focus:z-20;
|
||||
}
|
||||
|
||||
.pagy-nav .page.active {
|
||||
@apply z-10 border-indigo-500 bg-indigo-50 text-indigo-600 relative
|
||||
inline-flex items-center border px-4 py-2 text-sm font-medium
|
||||
focus:z-20;
|
||||
}
|
||||
}
|
||||
@@ -7,16 +7,30 @@
|
||||
@apply text-left;
|
||||
}
|
||||
|
||||
table th {
|
||||
table thead th {
|
||||
@apply pb-3.5 text-sm font-normal uppercase text-gray-500;
|
||||
}
|
||||
|
||||
table tbody th {
|
||||
@apply text-left font-normal text-gray-500;
|
||||
}
|
||||
|
||||
table th:not(:last-of-type),
|
||||
table td:not(:last-of-type) {
|
||||
@apply pr-2;
|
||||
}
|
||||
|
||||
table td {
|
||||
table td, tbody th {
|
||||
@apply py-2;
|
||||
}
|
||||
|
||||
table.divided {
|
||||
@apply divide-y divide-gray-300;
|
||||
}
|
||||
table.divided tbody {
|
||||
@apply divide-y divide-gray-200;
|
||||
}
|
||||
table.divided td, table.divided tbody th {
|
||||
@apply py-3;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
class Admin::BaseController < ApplicationController
|
||||
include Pagy::Backend
|
||||
|
||||
before_action :authenticate_user!
|
||||
before_action :authorize_admin
|
||||
@@ -7,5 +8,4 @@ class Admin::BaseController < ApplicationController
|
||||
def set_context
|
||||
@context = :admin
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -5,7 +5,8 @@ class Admin::DonationsController < Admin::BaseController
|
||||
# GET /donations
|
||||
# GET /donations.json
|
||||
def index
|
||||
@donations = Donation.all.order('created_at desc')
|
||||
@pagy, @donations = pagy(Donation.all.order('created_at desc'))
|
||||
|
||||
@stats = {
|
||||
overall_sats: @donations.all.sum("amount_sats"),
|
||||
donor_count: Donation.distinct.count(:user_id)
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
class Admin::InvitationsController < Admin::BaseController
|
||||
def index
|
||||
@current_section = :invitations
|
||||
@invitations_used = Invitation.used.order('used_at desc')
|
||||
@pagy, @invitations_used = pagy(Invitation.used.order('used_at desc'))
|
||||
|
||||
@stats = {
|
||||
available: Invitation.unused.count,
|
||||
accepted: @invitations_used.length,
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
class Admin::LdapUsersController < Admin::BaseController
|
||||
before_action :set_current_section
|
||||
|
||||
def index
|
||||
ldap = LdapService.new
|
||||
@ou = params[:ou] || "kosmos.org"
|
||||
@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 set_current_section
|
||||
@current_section = :ldap_users
|
||||
end
|
||||
end
|
||||
35
app/controllers/admin/users_controller.rb
Normal file
35
app/controllers/admin/users_controller.rb
Normal file
@@ -0,0 +1,35 @@
|
||||
class Admin::UsersController < Admin::BaseController
|
||||
before_action :set_user, only: [:show]
|
||||
before_action :set_current_section
|
||||
|
||||
def index
|
||||
ldap = LdapService.new
|
||||
@ou = params[:ou] || "kosmos.org"
|
||||
@orgs = ldap.fetch_organizations
|
||||
@pagy, @users = pagy(User.where(ou: @ou).order(cn: :asc))
|
||||
|
||||
@stats = {
|
||||
users_confirmed: User.where(ou: @ou).confirmed.count,
|
||||
users_pending: User.where(ou: @ou).pending.count
|
||||
}
|
||||
end
|
||||
|
||||
def show
|
||||
if Setting.lndhub_admin_enabled?
|
||||
@lndhub_user = @user.lndhub_user
|
||||
end
|
||||
|
||||
@services_enabled = @user.services_enabled
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_user
|
||||
address = params[:address].split("@")
|
||||
@user = User.where(cn: address.first, ou: address.last).first
|
||||
end
|
||||
|
||||
def set_current_section
|
||||
@current_section = :users
|
||||
end
|
||||
end
|
||||
17
app/controllers/users/confirmations_controller.rb
Normal file
17
app/controllers/users/confirmations_controller.rb
Normal file
@@ -0,0 +1,17 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Users::ConfirmationsController < Devise::ConfirmationsController
|
||||
# GET /resource/confirmation?confirmation_token=abcdef
|
||||
def show
|
||||
self.resource = resource_class.confirm_by_token(params[:confirmation_token])
|
||||
yield resource if block_given?
|
||||
|
||||
if resource.errors.empty?
|
||||
set_flash_message!(:success, :confirmed)
|
||||
resource.devise_after_confirmation
|
||||
respond_with_navigational(resource){ redirect_to after_confirmation_path_for(resource_name, resource) }
|
||||
else
|
||||
respond_with_navigational(resource.errors, status: :unprocessable_entity){ render :new }
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -7,7 +7,7 @@ class WalletController < ApplicationController
|
||||
before_action :fetch_balance
|
||||
|
||||
def index
|
||||
@wallet_url = "lndhub://#{current_user.ln_login}:#{current_user.ln_password}@#{ENV['LNDHUB_PUBLIC_URL']}"
|
||||
@wallet_url = "lndhub://#{current_user.ln_account}:#{current_user.ln_password}@#{ENV['LNDHUB_PUBLIC_URL']}"
|
||||
|
||||
qrcode = RQRCode::QRCode.new(@wallet_url)
|
||||
@svg = qrcode.as_svg(
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
module ApplicationHelper
|
||||
include Pagy::Frontend
|
||||
|
||||
def sats_to_btc(sats)
|
||||
sats.to_f / 100000000
|
||||
end
|
||||
@@ -10,5 +12,10 @@ module ApplicationHelper
|
||||
"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
|
||||
|
||||
# Colors available: gray, red, yellow, green, blue, purple, pink
|
||||
# (Add more colors by adding classes to the safelist in tailwind.config.js)
|
||||
def badge(text, color)
|
||||
tag.span text, class: "inline-flex items-center rounded-full bg-#{color}-100 px-2.5 py-0.5 text-xs font-medium text-#{color}-800"
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
module LdapUsersHelper
|
||||
end
|
||||
2
app/helpers/users_helper.rb
Normal file
2
app/helpers/users_helper.rb
Normal file
@@ -0,0 +1,2 @@
|
||||
module UsersHelper
|
||||
end
|
||||
@@ -2,13 +2,12 @@ class CreateLndhubAccountJob < ApplicationJob
|
||||
queue_as :default
|
||||
|
||||
def perform(user)
|
||||
return if user.ln_login.present? && user.ln_password.present?
|
||||
return if user.ln_account.present? && user.ln_password.present?
|
||||
|
||||
lndhub = LndhubV2.new
|
||||
credentials = lndhub.create_account
|
||||
|
||||
user.update! ln_account: credentials["login"],
|
||||
ln_login: credentials["login"], # TODO remove when production is migrated
|
||||
ln_password: credentials["password"]
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
class Invitation < ApplicationRecord
|
||||
# Relations
|
||||
belongs_to :user
|
||||
belongs_to :invitee, class_name: "User", foreign_key: 'invited_user_id', optional: true
|
||||
|
||||
# Validations
|
||||
validates_presence_of :user
|
||||
|
||||
@@ -10,6 +10,18 @@ class LndhubUser < LndhubBase
|
||||
foreign_key: "login"
|
||||
|
||||
def balance
|
||||
accounts.current.first.ledgers.sum("account_ledgers.amount")
|
||||
accounts.current.first.ledgers.sum("account_ledgers.amount").to_i.abs
|
||||
end
|
||||
|
||||
def sum_outgoing
|
||||
accounts.outgoing.first.ledgers.sum("account_ledgers.amount").to_i.abs
|
||||
end
|
||||
|
||||
def sum_incoming
|
||||
accounts.incoming.first.ledgers.sum("account_ledgers.amount").to_i.abs
|
||||
end
|
||||
|
||||
def sum_fees
|
||||
accounts.fees.first.ledgers.sum("account_ledgers.amount").to_i.abs
|
||||
end
|
||||
end
|
||||
|
||||
@@ -7,7 +7,7 @@ class Setting < RailsSettings::Base
|
||||
#
|
||||
|
||||
field :reserved_usernames, type: :array, default: %w[
|
||||
account accounts admin donations mail webmaster support
|
||||
account accounts donations mail webmaster support
|
||||
]
|
||||
|
||||
#
|
||||
|
||||
@@ -3,6 +3,10 @@ class User < ApplicationRecord
|
||||
|
||||
# Relations
|
||||
has_many :invitations, dependent: :destroy
|
||||
has_one :invitation, inverse_of: :invitee, foreign_key: 'invited_user_id'
|
||||
has_one :inviter, through: :invitation, source: :user
|
||||
has_many :invitees, through: :invitations
|
||||
|
||||
has_many :donations, dependent: :nullify
|
||||
|
||||
has_one :lndhub_user, class_name: "LndhubUser", inverse_of: "user",
|
||||
@@ -27,8 +31,7 @@ class User < ApplicationRecord
|
||||
scope :confirmed, -> { where.not(confirmed_at: nil) }
|
||||
scope :pending, -> { where(confirmed_at: nil) }
|
||||
|
||||
lockbox_encrypts :ln_login
|
||||
lockbox_encrypts :ln_password
|
||||
has_encrypted :ln_login, :ln_password
|
||||
|
||||
# Include default devise modules. Others available are:
|
||||
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
|
||||
@@ -39,9 +42,9 @@ class User < ApplicationRecord
|
||||
|
||||
def ldap_before_save
|
||||
self.email = Devise::LDAP::Adapter.get_ldap_param(self.cn, "mail").first
|
||||
|
||||
dn = Devise::LDAP::Adapter.get_ldap_param(self.cn, "dn")
|
||||
self.ou = dn.split(',').select{|e| e[0..1] == "ou"}.first.delete_prefix("ou=")
|
||||
self.ou = dn.split(',')
|
||||
.select{|e| e[0..1] == "ou"}.first
|
||||
.delete_prefix("ou=")
|
||||
|
||||
if self.confirmed_at.blank? && self.confirmation_token.blank?
|
||||
# User had an account with a trusted email address before akkounts was a thing
|
||||
@@ -49,6 +52,10 @@ class User < ApplicationRecord
|
||||
end
|
||||
end
|
||||
|
||||
def devise_after_confirmation
|
||||
enable_service %w[discourse gitea wiki xmpp]
|
||||
end
|
||||
|
||||
def reset_password(new_password, new_password_confirmation)
|
||||
self.password = new_password
|
||||
self.password_confirmation = new_password_confirmation
|
||||
@@ -81,4 +88,42 @@ class User < ApplicationRecord
|
||||
lndhub.authenticate self
|
||||
lndhub.addinvoice payload
|
||||
end
|
||||
|
||||
def dn
|
||||
return @dn if defined?(@dn)
|
||||
@dn = Devise::LDAP::Adapter.get_dn(self.cn)
|
||||
end
|
||||
|
||||
def ldap_entry
|
||||
ldap.fetch_users(uid: self.cn, ou: self.ou).first
|
||||
end
|
||||
|
||||
def services_enabled
|
||||
ldap_entry[:service] || []
|
||||
end
|
||||
|
||||
def enable_service(service)
|
||||
current_services = services_enabled
|
||||
new_services = Array(service).map(&:to_s)
|
||||
services = (current_services + new_services).uniq
|
||||
ldap.replace_attribute(dn, :service, services)
|
||||
end
|
||||
|
||||
def disable_service(service)
|
||||
current_services = services_enabled
|
||||
disabled_services = Array(service).map(&:to_s)
|
||||
services = (current_services - disabled_services).uniq
|
||||
ldap.replace_attribute(dn, :service, services)
|
||||
end
|
||||
|
||||
def disable_all_services
|
||||
ldap.delete_attribute(dn,:service)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def ldap
|
||||
return @ldap_service if defined?(@ldap_service)
|
||||
@ldap_service = LdapService.new
|
||||
end
|
||||
end
|
||||
|
||||
@@ -3,6 +3,18 @@ class LdapService < ApplicationService
|
||||
@suffix = ENV["LDAP_SUFFIX"] || "dc=kosmos,dc=org"
|
||||
end
|
||||
|
||||
def add_attribute(dn, attr, values)
|
||||
ldap_client.add_attribute dn, attr, values
|
||||
end
|
||||
|
||||
def replace_attribute(dn, attr, values)
|
||||
ldap_client.replace_attribute dn, attr, values
|
||||
end
|
||||
|
||||
def delete_attribute(dn, attr)
|
||||
ldap_client.delete_attribute dn, attr
|
||||
end
|
||||
|
||||
def add_entry(dn, attrs, interactive=false)
|
||||
puts "Adding entry: #{dn}" if interactive
|
||||
res = ldap_client.add dn: dn, attributes: attrs
|
||||
@@ -10,10 +22,6 @@ class LdapService < ApplicationService
|
||||
res
|
||||
end
|
||||
|
||||
def add_attribute(dn, attr, value)
|
||||
ldap_client.add_attribute dn, attr, value
|
||||
end
|
||||
|
||||
def delete_entry(dn, interactive=false)
|
||||
puts "Deleting entry: #{dn}" if interactive
|
||||
res = ldap_client.delete dn: dn
|
||||
@@ -42,18 +50,17 @@ class LdapService < ApplicationService
|
||||
treebase = ldap_config["base"]
|
||||
end
|
||||
|
||||
attributes = %w{dn cn uid mail admin}
|
||||
filter = Net::LDAP::Filter.eq("uid", "*")
|
||||
attributes = %w{dn cn uid mail admin service}
|
||||
filter = Net::LDAP::Filter.eq("uid", args[: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
|
||||
admin: e.try(:admin) ? 'admin' : nil,
|
||||
service: e.try(:service)
|
||||
}
|
||||
end
|
||||
end
|
||||
@@ -131,5 +138,4 @@ class LdapService < ApplicationService
|
||||
def ldap_config
|
||||
ldap_config ||= YAML.load(ERB.new(File.read("#{Rails.root}/config/ldap.yml")).result)[Rails.env]
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -2,7 +2,7 @@ class Lndhub
|
||||
attr_accessor :auth_token
|
||||
|
||||
def initialize
|
||||
@base_url = ENV["LNDHUB_LEGACY_API_URL"]
|
||||
@base_url = ENV["LNDHUB_API_URL"]
|
||||
end
|
||||
|
||||
def post(endpoint, payload)
|
||||
@@ -42,7 +42,7 @@ class Lndhub
|
||||
end
|
||||
|
||||
def authenticate(user)
|
||||
credentials = post "auth?type=auth", { login: user.ln_login, password: user.ln_password }
|
||||
credentials = post "auth?type=auth", { login: user.ln_account, password: user.ln_password }
|
||||
self.auth_token = credentials["access_token"]
|
||||
self.auth_token
|
||||
end
|
||||
|
||||
@@ -39,7 +39,7 @@ class LndhubV2
|
||||
end
|
||||
|
||||
def authenticate(user)
|
||||
credentials = post "auth?type=auth", { login: user.ln_login, password: user.ln_password }
|
||||
credentials = post "auth?type=auth", { login: user.ln_account, password: user.ln_password }
|
||||
self.auth_token = credentials["access_token"]
|
||||
self.auth_token
|
||||
end
|
||||
|
||||
@@ -10,46 +10,24 @@
|
||||
</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="sm:w-1/2 grid grid-cols-2 items-center gap-y-2">
|
||||
<%= form.label :user_id %>
|
||||
<%= form.collection_select :user_id, User.where(ou: "kosmos.org").order(:cn), :id, :cn, {} %>
|
||||
|
||||
<div class="field">
|
||||
<p>
|
||||
<%= form.label :amount_sats, "Amount BTC (sats)" %>
|
||||
<%= form.number_field :amount_sats %>
|
||||
</p>
|
||||
</div>
|
||||
<%= form.label :amount_sats, "Amount BTC (sats)" %>
|
||||
<%= form.number_field :amount_sats %>
|
||||
|
||||
<div class="field">
|
||||
<p>
|
||||
<%= form.label :amount_eur, "Amount EUR (cents)" %>
|
||||
<%= form.number_field :amount_eur %>
|
||||
</p>
|
||||
</div>
|
||||
<%= form.label :amount_eur, "Amount EUR (cents)" %>
|
||||
<%= form.number_field :amount_eur %>
|
||||
|
||||
<div class="field">
|
||||
<p>
|
||||
<%= form.label :amount_usd, "Amount USD (cents)"%>
|
||||
<%= form.number_field :amount_usd %>
|
||||
</p>
|
||||
</div>
|
||||
<%= form.label :amount_usd, "Amount USD (cents)"%>
|
||||
<%= form.number_field :amount_usd %>
|
||||
|
||||
<div class="field">
|
||||
<p>
|
||||
<%= form.label :public_name %>
|
||||
<%= form.text_field :public_name %>
|
||||
</p>
|
||||
</div>
|
||||
<%= form.label :public_name %>
|
||||
<%= form.text_field :public_name %>
|
||||
|
||||
<div class="field">
|
||||
<p>
|
||||
<%= form.label :paid_at %>
|
||||
<%= form.text_field :paid_at %>
|
||||
</p>
|
||||
<%= form.label :paid_at %>
|
||||
<%= form.text_field :paid_at %>
|
||||
</div>
|
||||
|
||||
<p class="mt-8">
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
<%= render HeaderComponent.new(title: "Donations") %>
|
||||
<%= render HeaderComponent.new(title: "Donation ##{@donation.id}") %>
|
||||
|
||||
<%= 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' %>
|
||||
<%= link_to 'Cancel', admin_donation_path(@donation), class: 'btn-sm btn-gray' %>
|
||||
<p>
|
||||
<% end %>
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<section>
|
||||
<% if @donations.any? %>
|
||||
<h3>Recent Donations</h3>
|
||||
<table>
|
||||
<table class="divided mb-8">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>User</th>
|
||||
@@ -33,11 +33,10 @@
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
<% @donations.each do |donation| %>
|
||||
<tr>
|
||||
<td><%= donation.user.address %></td>
|
||||
<td><%= link_to donation.user.address, admin_user_path(donation.user.address), class: 'ks-text-link' %></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>
|
||||
@@ -53,6 +52,7 @@
|
||||
<% end %>
|
||||
</tbody>
|
||||
</table>
|
||||
<%== pagy_nav @pagy %>
|
||||
<% else %>
|
||||
<p>
|
||||
No donations yet.
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
<%= render HeaderComponent.new(title: "Donations") %>
|
||||
<%= render HeaderComponent.new(title: "Add Donation") %>
|
||||
|
||||
<%= render MainSimpleComponent.new do %>
|
||||
<h2>New Donation</h2>
|
||||
|
||||
<%= render 'form', donation: @donation, url: admin_donations_path %>
|
||||
|
||||
<p class="mt-8">
|
||||
|
||||
@@ -1,38 +1,41 @@
|
||||
<%= render HeaderComponent.new(title: "Donations") %>
|
||||
<%= render HeaderComponent.new(title: "Donation ##{@donation.id}") %>
|
||||
|
||||
<%= render MainSimpleComponent.new do %>
|
||||
<p>
|
||||
<strong>User:</strong>
|
||||
<%= @donation.user.address %>
|
||||
</p>
|
||||
<section>
|
||||
<table class="w-1/2 divided">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>User</th>
|
||||
<td><%= link_to @donation.user.address, admin_user_path(@donation.user.address), class: 'ks-text-link' %></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Amount sats</th>
|
||||
<td><%= @donation.amount_sats %></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Amount EUR</th>
|
||||
<td><%= @donation.amount_eur %></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Amount USD</th>
|
||||
<td><%= @donation.amount_usd %></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Public name</th>
|
||||
<td><%= @donation.public_name %></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Date</th>
|
||||
<td><%= @donation.paid_at.strftime("%Y-%m-%d (%H:%M UTC)") %></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</section>
|
||||
|
||||
<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>
|
||||
<section>
|
||||
<p>
|
||||
<%= link_to 'Edit', edit_admin_donation_path(@donation), class: 'btn-md btn-blue mr-1' %>
|
||||
<%= link_to 'Back', admin_donations_path, class: 'btn-md btn-gray' %>
|
||||
</p>
|
||||
</section>
|
||||
<% end %>
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<% if @invitations_used.any? %>
|
||||
<section>
|
||||
<h3>Recently Accepted</h3>
|
||||
<table>
|
||||
<table class="divided mb-8">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Token</th>
|
||||
@@ -38,12 +38,13 @@
|
||||
<tr>
|
||||
<td class="overflow-ellipsis font-mono"><%= invitation.token %></td>
|
||||
<td><%= invitation.used_at.strftime("%Y-%m-%d (%H:%M UTC)") %></td>
|
||||
<td><%= invitation.user.address %></td>
|
||||
<td><%= User.find(invitation.invited_user_id).address %></td>
|
||||
<td><%= link_to invitation.user.address, admin_user_path(invitation.user.address), class: "ks-text-link" %></td>
|
||||
<td><%= link_to invitation.invitee.address, admin_user_path(invitation.invitee.address), class: "ks-text-link" %></td>
|
||||
</tr>
|
||||
<% end %>
|
||||
</tbody>
|
||||
</table>
|
||||
<%== pagy_nav @pagy %>
|
||||
</section>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
<section>
|
||||
<h3>Accounts</h3>
|
||||
<table>
|
||||
<table class="divided">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>LN Account</th>
|
||||
@@ -36,7 +36,7 @@
|
||||
</td>
|
||||
<td>
|
||||
<% if user = @users.find{ |u| u[2] == account.login } %>
|
||||
<%= "#{user[0]}@#{user[1]}" %>
|
||||
<%= link_to "#{user[0]}@#{user[1]}", admin_user_path("#{user[0]}@#{user[1]}"), class: "ks-text-link" %>
|
||||
<% end %>
|
||||
</td>
|
||||
<td><%= number_with_delimiter account.balance.to_i.to_s %></td>
|
||||
|
||||
@@ -22,14 +22,14 @@
|
||||
<%= f.text_area :reserved_usernames,
|
||||
value: Setting.reserved_usernames.join("\n"),
|
||||
class: "h-44 mb-2" %>
|
||||
<p class="mb-0 text-sm text-gray-500">
|
||||
<p class="text-sm text-gray-500">
|
||||
One username per line
|
||||
</p>
|
||||
</label>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<p class="mb-0 pt-6 border-t border-gray-200">
|
||||
<p class="pt-6 border-t border-gray-200">
|
||||
<%= f.submit 'Save', class: "btn-md btn-blue w-full md:w-auto" %>
|
||||
</p>
|
||||
</section>
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
<li class="flex items-center justify-between py-6">
|
||||
<div class="flex flex-col">
|
||||
<label class="font-bold mb-1">Enable LNDHub integration</label>
|
||||
<p class="text-gray-500 mb-0">LNDHub configuration present and wallet features enabled</p>
|
||||
<p class="text-gray-500">LNDHub configuration present and wallet features enabled</p>
|
||||
</div>
|
||||
<%= f.check_box :lndhub_enabled, checked: Setting.lndhub_enabled?,
|
||||
disabled: true,
|
||||
@@ -27,7 +27,7 @@
|
||||
<li class="flex items-center justify-between py-6">
|
||||
<div class="flex flex-col">
|
||||
<label class="font-bold mb-1">Enable LNDHub admin panel</label>
|
||||
<p class="text-gray-500 mb-0">LNDHub database configuration present and admin panel enabled</p>
|
||||
<p class="text-gray-500">LNDHub database configuration present and admin panel enabled</p>
|
||||
</div>
|
||||
<%= f.check_box :lndhub_admin_enabled, checked: Setting.lndhub_admin_enabled?,
|
||||
disabled: true,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<%= render HeaderComponent.new(title: "LDAP Users: #{@ou}") %>
|
||||
<%= render HeaderComponent.new(title: "Users: #{@ou}") %>
|
||||
|
||||
<%= render MainSimpleComponent.new do %>
|
||||
<section>
|
||||
@@ -22,7 +22,7 @@
|
||||
<ul>
|
||||
<% @orgs.each do |org| %>
|
||||
<li class="inline-block">
|
||||
<%= link_to org[:ou], admin_ldap_users_path(ou: org[:ou]), class: "ks-text-link" %>
|
||||
<%= link_to org[:ou], admin_users_path(ou: org[:ou]), class: "ks-text-link" %>
|
||||
</li>
|
||||
<% end %>
|
||||
</ul>
|
||||
@@ -30,25 +30,25 @@
|
||||
<% end %>
|
||||
|
||||
<section>
|
||||
<table>
|
||||
<table class="divided mb-8">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>UID</th>
|
||||
<th>E-Mail</th>
|
||||
<th>Admin</th>
|
||||
<th>Status</th>
|
||||
<th>Roles</th>
|
||||
<!-- <th>Password</th> -->
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<% @entries.each do |entry| %>
|
||||
<% @users.each do |user| %>
|
||||
<tr>
|
||||
<td><%= entry[:uid] %></td>
|
||||
<td><%= entry[:mail] %></td>
|
||||
<td><%= entry[:admin] %></td>
|
||||
<!-- <td><%= entry[:password] %></td> -->
|
||||
<td><%= link_to(user.cn, admin_user_path(user.address), class: 'ks-text-link') %></td>
|
||||
<td><%= user.confirmed_at.nil? ? badge("pending", :yellow) : "" %></td>
|
||||
<td><%= user.is_admin? ? badge("admin", :red) : "" %></td>
|
||||
</tr>
|
||||
<% end %>
|
||||
</tbody>
|
||||
</table>
|
||||
<%== pagy_nav @pagy %>
|
||||
</section>
|
||||
<% end %>
|
||||
123
app/views/admin/users/show.html.erb
Normal file
123
app/views/admin/users/show.html.erb
Normal file
@@ -0,0 +1,123 @@
|
||||
<%= render HeaderComponent.new(title: "User: #{@user.address}") %>
|
||||
|
||||
<%= render MainSimpleComponent.new do %>
|
||||
<div class="mb-12 sm:flex sm:flex-row sm:gap-x-8">
|
||||
<section class="sm:flex-1">
|
||||
<h3>Account</h3>
|
||||
<table class="divided">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>Created at</th>
|
||||
<td><%= @user.created_at.strftime("%Y-%m-%d (%H:%M UTC)") %></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Confirmed at</th>
|
||||
<td>
|
||||
<% if @user.confirmed_at %>
|
||||
<%= @user.confirmed_at.strftime("%Y-%m-%d (%H:%M UTC)") %>
|
||||
<% else %>
|
||||
<%= badge "pending", :yellow %>
|
||||
<% end %>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Email</th>
|
||||
<td><%= @user.email %></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Roles</th>
|
||||
<td><%= @user.is_admin? ? badge("admin", :red) : "—" %></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Invited by</th>
|
||||
<td>
|
||||
<% if @user.inviter %>
|
||||
<%= link_to @user.inviter.address, admin_user_path(@user.inviter.address), class: 'ks-text-link' %>
|
||||
<% else %>—<% end %>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Invitations available</th>
|
||||
<td>
|
||||
<%= @user.invitations.count %>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="align-top">Invited users</th>
|
||||
<td class="align-top">
|
||||
<% if @user.invitees.length > 0 %>
|
||||
<ul class="mb-0">
|
||||
<% @user.invitees.order(cn: :asc).each do |invitee| %>
|
||||
<li class="leading-none mb-2 last:mb-0"><%= link_to invitee.address, admin_user_path(invitee.address), class: 'ks-text-link' %></li>
|
||||
<% end %>
|
||||
</ul>
|
||||
<% else %>—<% end %>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</section>
|
||||
|
||||
<section class="sm:flex-1 sm:pt-0">
|
||||
<!-- <h3>Actions</h3> -->
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<section>
|
||||
<h3>Services</h3>
|
||||
<table class="sm:w-1/4">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Discourse</td>
|
||||
<td><%= check_box_tag 'service_discourse', 'enabled', @services_enabled.include?("discourse"), disabled: true %></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Gitea</td>
|
||||
<td><%= check_box_tag 'service_gitea', 'enabled', @services_enabled.include?("gitea"), disabled: true %></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Mastodon</td>
|
||||
<td><%= check_box_tag 'service_mastodon', 'enabled', @services_enabled.include?("mastodon"), disabled: true %></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Wiki</td>
|
||||
<td><%= check_box_tag 'service_wiki', 'enabled', @services_enabled.include?("wiki"), disabled: true %></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>XMPP</td>
|
||||
<td><%= check_box_tag 'service_xmpp', 'enabled', @services_enabled.include?("xmpp"), disabled: true %></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</section>
|
||||
|
||||
<% if Setting.lndhub_admin_enabled? && @user.confirmed? %>
|
||||
<section>
|
||||
<h3>LndHub</h3>
|
||||
<% if @lndhub_user %>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Account</th>
|
||||
<th>Balance</th>
|
||||
<th>Incoming</th>
|
||||
<th>Outgoing</th>
|
||||
<th>Fees</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><%= @user.ln_account %></td>
|
||||
<td><%= number_with_delimiter @lndhub_user.balance %> sats</td>
|
||||
<td><%= number_with_delimiter @lndhub_user.sum_incoming %> sats</td>
|
||||
<td><%= number_with_delimiter @lndhub_user.sum_outgoing %> sats</td>
|
||||
<td><%= number_with_delimiter @lndhub_user.sum_fees %> sats</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<% else %>
|
||||
<p>No LndHub user found for account <strong class="font-mono"><%= @user.ln_account %></strong>.
|
||||
<% end %>
|
||||
</section>
|
||||
<% end %>
|
||||
<% end %>
|
||||
@@ -2,60 +2,87 @@
|
||||
|
||||
<%= render MainSimpleComponent.new do %>
|
||||
<section>
|
||||
<p>
|
||||
<p class="mb-8">
|
||||
Your Kosmos account and password currently give you access to these
|
||||
services:
|
||||
</p>
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-6 services mt-12">
|
||||
<div>
|
||||
<h3 class="mb-3.5">
|
||||
<%= 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)
|
||||
</p>
|
||||
<div class="services grid grid-cols-1 sm:grid-cols-2 gap-4 sm:gap-6">
|
||||
<div class="border border-gray-300 rounded-md hover:border-gray-400
|
||||
bg-cover bg-[center_top_-50px] bg-no-repeat
|
||||
bg-[url(/img/logos/icon_xmpp.svg)]">
|
||||
<%= link_to "https://wiki.kosmos.org/Services:Chat",
|
||||
class: "block h-full px-6 py-6 rounded-md" do %>
|
||||
<h3 class="mb-3.5">Chat</h3>
|
||||
<p class="text-gray-600">
|
||||
Federated chat rooms and instant messaging
|
||||
</p>
|
||||
<% end %>
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="mb-3.5">
|
||||
<%= link_to "Discourse", "https://community.kosmos.org", class: "ks-text-link" %>
|
||||
</h3>
|
||||
<p class="text-gray-500">
|
||||
Kosmos community forums and user support/help site
|
||||
</p>
|
||||
<div class="border border-gray-300 rounded-md hover:border-gray-400
|
||||
bg-[length:95%] bg-center bg-no-repeat
|
||||
bg-[url(/img/logos/icon_discourse.svg)]">
|
||||
<%= link_to "https://community.kosmos.org",
|
||||
class: "block h-full px-6 py-6 rounded-md" do %>
|
||||
<h3 class="mb-3.5">Discourse</h3>
|
||||
<p class="text-gray-600">
|
||||
Kosmos community forums and user support/help site
|
||||
</p>
|
||||
<% end %>
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="mb-3.5">
|
||||
<%= render partial: "icons/zap", locals: { custom_class: "text-amber-500 h-4 w-4 inline" } %>
|
||||
<%= link_to "Lightning Wallet", wallet_path, class: "ks-text-link" %>
|
||||
</h3>
|
||||
<p class="text-gray-500">
|
||||
Send and receive sats over the Bitcoin Lightning Network
|
||||
</p>
|
||||
<div class="border border-gray-300 rounded-md hover:border-gray-400
|
||||
bg-cover bg-[center_top_-20px] bg-no-repeat
|
||||
bg-[url(/img/logos/icon_mediawiki.svg)]">
|
||||
<%= link_to "https://wiki.kosmos.org",
|
||||
class: "block h-full px-6 py-6 rounded-md" do %>
|
||||
<h3 class="mb-3.5">Wiki</h3>
|
||||
<p class="text-gray-600">
|
||||
Kosmos documentation and knowledge base
|
||||
</p>
|
||||
<% end %>
|
||||
</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 class="border border-gray-300 rounded-md hover:border-gray-400
|
||||
bg-cover bg-center sm:bg-[center_top_-140px] bg-no-repeat
|
||||
bg-[url(/img/logos/icon_lightning.svg)]">
|
||||
<%= link_to wallet_path,
|
||||
class: "block h-full px-6 py-6 rounded-md" do %>
|
||||
<h3 class="mb-3.5">Wallet</h3>
|
||||
<p class="text-gray-600">
|
||||
Send and receive sats over the Bitcoin Lightning Network
|
||||
</p>
|
||||
<% end %>
|
||||
</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 class="border border-gray-300 rounded-md hover:border-gray-400
|
||||
bg-cover bg-center bg-no-repeat
|
||||
bg-[url(/img/logos/icon_gitea.png)]">
|
||||
<%= link_to "https://gitea.kosmos.org",
|
||||
class: "block h-full px-6 py-6 rounded-md" do %>
|
||||
<h3 class="mb-3.5">Gitea</h3>
|
||||
<p class="text-gray-600">
|
||||
Code hosting and collaboration for software projects
|
||||
</p>
|
||||
<% end %>
|
||||
</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 class="border border-gray-300 rounded-md hover:border-gray-400
|
||||
bg-cover bg-[center_top_-70px] bg-no-repeat
|
||||
bg-[url(/img/logos/icon_droneci.svg)]">
|
||||
<%= link_to "https://drone.kosmos.org",
|
||||
class: "block h-full px-6 py-6 rounded-md" do %>
|
||||
<h3 class="mb-3.5">Drone CI</h3>
|
||||
<p class="text-gray-600">
|
||||
Continuous integration for software projects on Gitea
|
||||
</p>
|
||||
<% end %>
|
||||
</div>
|
||||
<!-- <div class="border border-gray-300 rounded-md hover:border-gray-400 -->
|
||||
<!-- bg-[length:80%] bg-[right_top_-30px] bg-no-repeat -->
|
||||
<!-- bg-[url(/img/logos/icon_mastodon.svg)]"> -->
|
||||
<!-- <%= link_to "https://kosmos.social", class: "block h-full px-6 py-6 rounded-md" do %> -->
|
||||
<!-- <h3 class="mb-3.5">Mastodon</h3> -->
|
||||
<!-- <p class="text-gray-400"> -->
|
||||
<!-- Your account on the Open Social Web -->
|
||||
<!-- </p> -->
|
||||
<!-- <% end %> -->
|
||||
<!-- </div> -->
|
||||
</div>
|
||||
</section>
|
||||
<% end %>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<%= 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 "Users", admin_users_path,
|
||||
class: main_nav_class(@current_section, :users) %>
|
||||
<%= link_to "Invitations", admin_invitations_path,
|
||||
class: main_nav_class(@current_section, :invitations) %>
|
||||
<%= link_to "Donations", admin_donations_path,
|
||||
|
||||
Reference in New Issue
Block a user