Compare commits
52 Commits
cdad7546fb
...
v0.5.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f08bb56a7a
|
||
| c1f275463e | |||
| 324809f77e | |||
|
|
f9b07bcb01
|
||
|
|
986eb5387c
|
||
| f76e2c2f14 | |||
|
|
22a7bbe6eb
|
||
| 18f4deb30f | |||
|
|
9f9bf6fd80
|
||
|
|
d2987da70a
|
||
|
|
6b7a80e23a
|
||
|
|
42b9b27561
|
||
|
|
c17c980b69
|
||
|
|
f199d5d12a
|
||
|
|
4b17afa93d
|
||
|
|
6d52af53ae
|
||
|
|
4c5ad67652
|
||
|
|
3437a756eb
|
||
| 0d9fc4aa74 | |||
| 82475161a9 | |||
|
|
fb3b9af3e5
|
||
|
|
b1a0268e6b
|
||
| e1e7d8f87d | |||
|
|
5b46f3adf5
|
||
|
|
a8a8fba14c
|
||
|
|
8a7016a30b
|
||
|
|
e2618de7c6
|
||
| 90680368fb | |||
|
|
8d90847896 | ||
|
|
8da297811b | ||
|
|
fa56d6b772 | ||
|
|
ca1221e9f3 | ||
|
|
295d486761 | ||
|
|
e00390d102 | ||
|
|
b947480190 | ||
|
|
fa07978aac | ||
|
|
e758e258a8 | ||
|
|
805733939c | ||
|
|
f050d010fd
|
||
|
|
95fac38b53
|
||
| cb80465297 | |||
|
|
c7550b4f64
|
||
| 341284aa99 | |||
|
|
b34d040ce3
|
||
| 1142a4e2d5 | |||
|
|
f2c7aa2f09
|
||
| cca44d7542 | |||
|
|
dc63506102
|
||
|
|
b87b9c2437
|
||
|
|
e580cc9991
|
||
|
|
68ab88c481
|
||
|
|
c7fe1bc3bc
|
20
.env.example
20
.env.example
@@ -1,16 +1,34 @@
|
||||
SMTP_SERVER=smtp.example.com
|
||||
SMTP_PORT=587
|
||||
SMTP_LOGIN=accounts
|
||||
SMTP_PASSWORD=123abc
|
||||
SMTP_FROM_ADDRESS=accounts@example.com
|
||||
SMTP_DOMAIN=example.com
|
||||
SMTP_AUTH_METHOD=plain
|
||||
SMTP_ENABLE_STARTTLS=auto
|
||||
|
||||
REDIS_URL='redis://localhost:6379/1'
|
||||
|
||||
LDAP_HOST=localhost
|
||||
LDAP_PORT=389
|
||||
LDAP_ADMIN_PASSWORD=passthebutter
|
||||
LDAP_SUFFIX="dc=kosmos,dc=org"
|
||||
LDAP_SUFFIX='dc=kosmos,dc=org'
|
||||
|
||||
WEBHOOKS_ALLOWED_IPS='10.1.1.163'
|
||||
|
||||
DISCOURSE_PUBLIC_URL='https://community.kosmos.org'
|
||||
GITEA_PUBLIC_URL='https://gitea.kosmos.org'
|
||||
MASTODON_PUBLIC_URL='https://kosmos.social'
|
||||
MEDIAWIKI_PUBLIC_URL='https://wiki.kosmos.org'
|
||||
|
||||
EJABBERD_ADMIN_URL='https://xmpp.kosmos.org/admin'
|
||||
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'
|
||||
LNDHUB_PUBLIC_KEY='0123d3be18617f39cf645851e3ba63f51fc13f0bb09e3bb25e6fd4de556486d946'
|
||||
LNDHUB_ADMIN_UI=true
|
||||
LNDHUB_PG_HOST=localhost
|
||||
LNDHUB_PG_PORT=5432
|
||||
|
||||
@@ -4,5 +4,6 @@ BTCPAY_API_URL='http://btcpay.example.com/api/v1'
|
||||
|
||||
LNDHUB_API_URL='http://localhost:3026'
|
||||
LNDHUB_PUBLIC_URL='https://lndhub.kosmos.org'
|
||||
LNDHUB_PUBLIC_KEY='024cd3be18617f39cf645851e3ba63f51fc13f0bb09e3bb25e6fd4de556486d946'
|
||||
|
||||
WEBHOOKS_ALLOWED_IPS='10.1.1.23'
|
||||
|
||||
13
.gitea/release-drafter.yml
Normal file
13
.gitea/release-drafter.yml
Normal file
@@ -0,0 +1,13 @@
|
||||
name-template: 'v$RESOLVED_VERSION'
|
||||
tag-template: 'v$RESOLVED_VERSION'
|
||||
version-resolver:
|
||||
major:
|
||||
labels:
|
||||
- 'release/major'
|
||||
minor:
|
||||
labels:
|
||||
- 'release/minor'
|
||||
patch:
|
||||
labels:
|
||||
- 'release/patch'
|
||||
default: patch
|
||||
11
.gitea/workflows/release_drafter.yml
Normal file
11
.gitea/workflows/release_drafter.yml
Normal file
@@ -0,0 +1,11 @@
|
||||
name: Release Drafter
|
||||
on:
|
||||
pull_request:
|
||||
types: [closed]
|
||||
jobs:
|
||||
release_drafter_job:
|
||||
name: Update release notes draft
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Release Drafter
|
||||
uses: https://github.com/raucao/gitea-release-drafter@dev
|
||||
15
Dockerfile
15
Dockerfile
@@ -1,8 +1,13 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
FROM ruby:2.7.6
|
||||
RUN apt-get update -qq && apt-get install -y curl ldap-utils
|
||||
|
||||
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
|
||||
|
||||
RUN apt-get update -qq && apt-get install -y --no-install-recommends curl \
|
||||
ldap-utils tini
|
||||
RUN curl -fsSL https://deb.nodesource.com/setup_lts.x | bash -
|
||||
RUN apt-get update && apt-get install -y nodejs
|
||||
|
||||
WORKDIR /akkounts
|
||||
COPY Gemfile /akkounts/Gemfile
|
||||
COPY Gemfile.lock /akkounts/Gemfile.lock
|
||||
@@ -12,11 +17,5 @@ RUN gem install foreman
|
||||
RUN npm install -g yarn
|
||||
RUN yarn install
|
||||
|
||||
# Add a script to be executed every time the container starts.
|
||||
COPY docker/entrypoint.sh /usr/bin/
|
||||
RUN chmod +x /usr/bin/entrypoint.sh
|
||||
ENTRYPOINT ["entrypoint.sh"]
|
||||
ENTRYPOINT ["/usr/bin/tini", "--"]
|
||||
EXPOSE 3000
|
||||
|
||||
# Configure the main process to run when running the image
|
||||
CMD ["bin", "dev"]
|
||||
|
||||
6
Gemfile
6
Gemfile
@@ -32,7 +32,7 @@ gem 'lockbox'
|
||||
|
||||
# Authentication
|
||||
gem 'warden'
|
||||
gem 'devise'
|
||||
gem 'devise', '~> 4.9.0'
|
||||
gem 'devise_ldap_authenticatable'
|
||||
gem 'net-ldap'
|
||||
|
||||
@@ -48,6 +48,10 @@ gem 'faraday'
|
||||
gem 'sidekiq', '< 7'
|
||||
gem 'sidekiq-scheduler'
|
||||
|
||||
# Monitoring
|
||||
gem "sentry-ruby"
|
||||
gem "sentry-rails"
|
||||
|
||||
group :development, :test do
|
||||
# Use sqlite3 as the database for Active Record
|
||||
gem 'sqlite3', '~> 1.4'
|
||||
|
||||
17
Gemfile.lock
17
Gemfile.lock
@@ -95,7 +95,7 @@ GEM
|
||||
activerecord (>= 5.a)
|
||||
database_cleaner-core (~> 2.0.0)
|
||||
database_cleaner-core (2.0.1)
|
||||
devise (4.8.1)
|
||||
devise (4.9.0)
|
||||
bcrypt (~> 3.0)
|
||||
orm_adapter (~> 0.1)
|
||||
railties (>= 4.1.0)
|
||||
@@ -226,9 +226,9 @@ GEM
|
||||
redis-client (0.11.2)
|
||||
connection_pool
|
||||
regexp_parser (2.6.1)
|
||||
responders (3.0.1)
|
||||
actionpack (>= 5.0)
|
||||
railties (>= 5.0)
|
||||
responders (3.1.0)
|
||||
actionpack (>= 5.2)
|
||||
railties (>= 5.2)
|
||||
rexml (3.2.5)
|
||||
rqrcode (2.1.2)
|
||||
chunky_png (~> 1.0)
|
||||
@@ -254,6 +254,11 @@ GEM
|
||||
ruby2_keywords (0.0.5)
|
||||
rufus-scheduler (3.8.2)
|
||||
fugit (~> 1.1, >= 1.1.6)
|
||||
sentry-rails (5.8.0)
|
||||
railties (>= 5.0)
|
||||
sentry-ruby (~> 5.8.0)
|
||||
sentry-ruby (5.8.0)
|
||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||
sidekiq (6.5.5)
|
||||
connection_pool (>= 2.2.2)
|
||||
rack (~> 2.0)
|
||||
@@ -315,7 +320,7 @@ DEPENDENCIES
|
||||
capybara
|
||||
cssbundling-rails
|
||||
database_cleaner
|
||||
devise
|
||||
devise (~> 4.9.0)
|
||||
devise_ldap_authenticatable
|
||||
dotenv-rails
|
||||
factory_bot_rails
|
||||
@@ -335,6 +340,8 @@ DEPENDENCIES
|
||||
rails-settings-cached (~> 2.8.3)
|
||||
rqrcode (~> 2.0)
|
||||
rspec-rails
|
||||
sentry-rails
|
||||
sentry-ruby
|
||||
sidekiq (< 7)
|
||||
sidekiq-scheduler
|
||||
sprockets-rails
|
||||
|
||||
@@ -14,7 +14,7 @@ so:
|
||||
|
||||
1. Make sure [Docker Compose is installed][1] and Docker is running (included in
|
||||
Docker Desktop)
|
||||
2. Uncomment the `web` section in `docker-compose.yml`
|
||||
2. Uncomment the `redis`, `web`, and `sidekiq` sections in `docker-compose.yml`
|
||||
3. Run `docker compose up` and wait until 389ds announces its successful start
|
||||
in the log output
|
||||
4. `docker-compose exec ldap dsconf localhost backend create --suffix="dc=kosmos,dc=org" --be-name="dev"`
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
@layer components {
|
||||
input[type=text], input[type=email], input[type=password],
|
||||
input[type=number], select, textarea {
|
||||
@apply mt-1 rounded-md bg-gray-100 focus:bg-white
|
||||
@apply rounded-md bg-gray-100 focus:bg-white
|
||||
border-transparent focus:border-transparent focus:ring-2
|
||||
focus:ring-blue-600 focus:ring-opacity-75;
|
||||
}
|
||||
@@ -10,6 +10,10 @@
|
||||
@apply inline-block;
|
||||
}
|
||||
|
||||
.field_with_errors input {
|
||||
@apply w-full bg-red-100;
|
||||
}
|
||||
|
||||
.error-msg {
|
||||
@apply text-red-700;
|
||||
}
|
||||
|
||||
@@ -5,10 +5,4 @@
|
||||
&:visited { @apply text-indigo-600; }
|
||||
&:active { @apply text-red-600; }
|
||||
}
|
||||
|
||||
.devise-links {
|
||||
a {
|
||||
@apply ks-text-link;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
13
app/components/form_elements/fieldset_component.html.erb
Normal file
13
app/components/form_elements/fieldset_component.html.erb
Normal file
@@ -0,0 +1,13 @@
|
||||
<%= tag.public_send(@tag, class: "mb-6 last:mb-0") do %>
|
||||
<label class="block">
|
||||
<p class="font-bold <%= @descripton.present? ? "mb-1" : "mb-2" %>">
|
||||
<%= @title %>
|
||||
</p>
|
||||
<% if @descripton.present? %>
|
||||
<p class="text-gray-500">
|
||||
<%= @descripton %>
|
||||
</p>
|
||||
<% end %>
|
||||
<%= content %>
|
||||
</label>
|
||||
<% end %>
|
||||
11
app/components/form_elements/fieldset_component.rb
Normal file
11
app/components/form_elements/fieldset_component.rb
Normal file
@@ -0,0 +1,11 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module FormElements
|
||||
class FieldsetComponent < ViewComponent::Base
|
||||
def initialize(tag: "li", title:, description: nil)
|
||||
@tag = tag
|
||||
@title = title
|
||||
@descripton = description
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,26 @@
|
||||
<%= tag.public_send @tag, class: "flex items-center justify-between mb-6 last:mb-0",
|
||||
data: @form.present? ? {
|
||||
controller: "settings--toggle",
|
||||
:'settings--toggle-switch-enabled-value' => @enabled.to_s
|
||||
} : nil do %>
|
||||
<div class="flex flex-col">
|
||||
<label class="font-bold mb-1"><%= @title %></label>
|
||||
<p class="text-gray-500"><%= @descripton %></p>
|
||||
</div>
|
||||
<div class="relative ml-4 inline-flex flex-shrink-0">
|
||||
<%= render FormElements::ToggleComponent.new(
|
||||
enabled: @enabled,
|
||||
input_enabled: @input_enabled,
|
||||
class_names: @form.present? ? "hidden" : nil,
|
||||
data: {
|
||||
:'settings--toggle-target' => "button",
|
||||
action: "settings--toggle#toggleSwitch"
|
||||
}) %>
|
||||
<% if @form.present? %>
|
||||
<%= @form.check_box @attribute, {
|
||||
checked: @enabled,
|
||||
data: { :'settings--toggle-target' => "checkbox" }
|
||||
}, "true", "false" %>
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
17
app/components/form_elements/fieldset_toggle_component.rb
Normal file
17
app/components/form_elements/fieldset_toggle_component.rb
Normal file
@@ -0,0 +1,17 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module FormElements
|
||||
class FieldsetToggleComponent < ViewComponent::Base
|
||||
def initialize(form: nil, attribute: nil, tag: "li", enabled: false,
|
||||
input_enabled: true, title:, description:)
|
||||
@form = form
|
||||
@attribute = attribute
|
||||
@tag = tag
|
||||
@enabled = enabled
|
||||
@input_enabled = input_enabled
|
||||
@title = title
|
||||
@descripton = description
|
||||
@button_text = @enabled ? "Switch off" : "Switch on"
|
||||
end
|
||||
end
|
||||
end
|
||||
15
app/components/form_elements/toggle_component.html.erb
Normal file
15
app/components/form_elements/toggle_component.html.erb
Normal file
@@ -0,0 +1,15 @@
|
||||
<%= button_tag type: "button", name: "toggle", data: @data,
|
||||
role: "switch", aria: { checked: @enabled.to_s },
|
||||
tabindex: @tabindex, disabled: !@input_enabled,
|
||||
class: "#{ @enabled ? 'bg-blue-600' : 'bg-gray-200' }
|
||||
#{ @class_names.present? ? @class_names : '' }
|
||||
relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer
|
||||
rounded-full border-2 border-transparent transition-colors
|
||||
duration-200 ease-in-out focus:outline-none focus:ring-2
|
||||
focus:ring-blue-600 focus:ring-offset-2" do %>
|
||||
<span class="sr-only"><%= @button_text %></span>
|
||||
<span aria-hidden="true" data-settings--toggle-target="switch"
|
||||
class="<%= @enabled ? 'translate-x-5' : 'translate-x-0' %>
|
||||
pointer-events-none inline-block h-5 w-5 transform rounded-full
|
||||
bg-white shadow ring-0 transition duration-200 ease-in-out"></span>
|
||||
<% end %>
|
||||
13
app/components/form_elements/toggle_component.rb
Normal file
13
app/components/form_elements/toggle_component.rb
Normal file
@@ -0,0 +1,13 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module FormElements
|
||||
class ToggleComponent < ViewComponent::Base
|
||||
def initialize(enabled:, input_enabled: true, data: nil, class_names: nil, tabindex: nil)
|
||||
@enabled = !!enabled
|
||||
@input_enabled = input_enabled
|
||||
@data = data
|
||||
@class_names = class_names
|
||||
@tabindex = tabindex
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,8 +1,9 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class SidenavLinkComponent < ViewComponent::Base
|
||||
def initialize(name:, path:, icon:, active: false, disabled: false)
|
||||
def initialize(name:, level: 1, path:, icon:, active: false, disabled: false)
|
||||
@name = name
|
||||
@level = level
|
||||
@path = path
|
||||
@icon = icon
|
||||
@active = active
|
||||
@@ -12,12 +13,15 @@ class SidenavLinkComponent < ViewComponent::Base
|
||||
end
|
||||
|
||||
def class_names_link(path)
|
||||
px = @level == 1 ? "px-4" : "pl-8 pr-4"
|
||||
base = "#{px} py-2 group border-l-4 flex items-center text-base font-medium"
|
||||
|
||||
if @active
|
||||
"bg-teal-50 border-teal-500 text-teal-700 hover:bg-teal-50 hover:text-teal-700 group border-l-4 px-4 py-2 flex items-center text-base font-medium"
|
||||
"#{base} bg-teal-50 border-teal-500 text-teal-700 hover:bg-teal-50 hover:text-teal-700"
|
||||
elsif @disabled
|
||||
"border-transparent text-gray-400 hover:bg-gray-50 group border-l-4 px-4 py-2 flex items-center text-base font-medium"
|
||||
"#{base} border-transparent text-gray-400 hover:bg-gray-50"
|
||||
else
|
||||
"border-transparent text-gray-900 hover:bg-gray-50 hover:text-gray-900 group border-l-4 px-4 py-2 flex items-center text-base font-medium"
|
||||
"#{base} border-transparent text-gray-900 hover:bg-gray-50 hover:text-gray-900"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ class Admin::DonationsController < Admin::BaseController
|
||||
end
|
||||
format.json { render :show, status: :created, location: @donation }
|
||||
else
|
||||
format.html { render :new }
|
||||
format.html { render :new, status: :unprocessable_entity }
|
||||
format.json { render json: @donation.errors, status: :unprocessable_entity }
|
||||
end
|
||||
end
|
||||
@@ -59,7 +59,7 @@ class Admin::DonationsController < Admin::BaseController
|
||||
end
|
||||
format.json { render :show, status: :ok, location: @donation }
|
||||
else
|
||||
format.html { render :edit }
|
||||
format.html { render :edit, status: :unprocessable_entity }
|
||||
format.json { render json: @donation.errors, status: :unprocessable_entity }
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,38 +1,12 @@
|
||||
class Admin::Settings::RegistrationsController < Admin::SettingsController
|
||||
|
||||
def index
|
||||
end
|
||||
|
||||
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
|
||||
update_settings
|
||||
|
||||
redirect_to admin_settings_registrations_path, flash: {
|
||||
success: "Settings saved"
|
||||
}
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def setting_params
|
||||
params.require(:setting).permit(:reserved_usernames)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -1,9 +1,19 @@
|
||||
class Admin::Settings::ServicesController < Admin::SettingsController
|
||||
|
||||
def index
|
||||
@service = params[:s]
|
||||
|
||||
if @service.blank?
|
||||
redirect_to admin_settings_services_path(params: { s: "discourse" })
|
||||
end
|
||||
end
|
||||
|
||||
def update
|
||||
end
|
||||
def create
|
||||
service = params.require(:service)
|
||||
|
||||
update_settings
|
||||
|
||||
redirect_to admin_settings_services_path(params: { s: service }), flash: {
|
||||
success: "Settings saved"
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
@@ -4,9 +4,37 @@ class Admin::SettingsController < Admin::BaseController
|
||||
def index
|
||||
end
|
||||
|
||||
def update_settings
|
||||
@errors = ActiveModel::Errors.new(Setting.new)
|
||||
changed_keys = []
|
||||
|
||||
setting_params.keys.each do |key|
|
||||
next if setting_params[key].nil? ||
|
||||
(Setting.send(key).to_s == setting_params[key].strip)
|
||||
changed_keys.push(key)
|
||||
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 and return
|
||||
end
|
||||
|
||||
changed_keys.each do |key|
|
||||
Setting.send("#{key}=", setting_params[key].strip)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_current_section
|
||||
@current_section = :settings
|
||||
end
|
||||
def set_current_section
|
||||
@current_section = :settings
|
||||
end
|
||||
|
||||
def setting_params
|
||||
params.require(:setting).permit(Setting.editable_keys.map(&:to_sym))
|
||||
end
|
||||
end
|
||||
|
||||
@@ -3,6 +3,18 @@ class ApplicationController < ActionController::Base
|
||||
render :text => exception, :status => 500
|
||||
end
|
||||
|
||||
before_action :sentry_set_user
|
||||
|
||||
def sentry_set_user
|
||||
return unless Setting.sentry_enabled
|
||||
|
||||
if user_signed_in?
|
||||
Sentry.set_user(id: current_user.id, username: current_user.cn)
|
||||
else
|
||||
Sentry.set_user({})
|
||||
end
|
||||
end
|
||||
|
||||
def require_user_signed_in
|
||||
unless user_signed_in?
|
||||
redirect_to welcome_path and return
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
class LnurlpayController < ApplicationController
|
||||
before_action :check_feature_enabled
|
||||
before_action :find_user_by_address
|
||||
|
||||
MIN_SATS = 10
|
||||
@@ -17,6 +18,20 @@ class LnurlpayController < ApplicationController
|
||||
}
|
||||
end
|
||||
|
||||
def keysend
|
||||
http_status :not_found and return unless Setting.lndhub_keysend_enabled?
|
||||
|
||||
render json: {
|
||||
status: "OK",
|
||||
tag: "keysend",
|
||||
pubkey: Setting.lndhub_public_key,
|
||||
customData: [{
|
||||
customKey: "696969",
|
||||
customValue: @user.ln_account
|
||||
}]
|
||||
}
|
||||
end
|
||||
|
||||
def invoice
|
||||
amount = params[:amount].to_i / 1000 # msats
|
||||
address = params[:address]
|
||||
@@ -72,4 +87,9 @@ class LnurlpayController < ApplicationController
|
||||
comment.length <= MAX_COMMENT_CHARS
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def check_feature_enabled
|
||||
http_status :not_found unless Setting.lndhub_enabled?
|
||||
end
|
||||
end
|
||||
|
||||
@@ -4,6 +4,10 @@ export default class extends Controller {
|
||||
static targets = ["buttons", "countdown"]
|
||||
|
||||
connect() {
|
||||
// Devise timeoutable ends up adding a second flash message without content
|
||||
// TODO investigate bug
|
||||
if (this.element.textContent.trim() == "true") return;
|
||||
|
||||
const timeoutSeconds = parseInt(this.data.get("timeout"));
|
||||
|
||||
setTimeout(() => {
|
||||
|
||||
30
app/javascript/controllers/settings/toggle_controller.js
Normal file
30
app/javascript/controllers/settings/toggle_controller.js
Normal file
@@ -0,0 +1,30 @@
|
||||
import { Controller } from "@hotwired/stimulus"
|
||||
|
||||
export default class extends Controller {
|
||||
static targets = [ "button", "switch", "checkbox" ]
|
||||
static values = { switchEnabled: Boolean }
|
||||
|
||||
connect () {
|
||||
this.buttonTarget.classList.remove("hidden")
|
||||
this.checkboxTarget.classList.add("hidden")
|
||||
}
|
||||
|
||||
toggleSwitch () {
|
||||
this.switchEnabledValue = !this.switchEnabledValue
|
||||
this.checkboxTarget.checked = this.switchEnabledValue
|
||||
|
||||
if (this.switchEnabledValue) {
|
||||
this.buttonTarget.setAttribute("aria-checked", "true");
|
||||
this.buttonTarget.classList.remove("bg-gray-200")
|
||||
this.buttonTarget.classList.add("bg-blue-600")
|
||||
this.switchTarget.classList.remove("translate-x-0")
|
||||
this.switchTarget.classList.add("translate-x-5")
|
||||
} else {
|
||||
this.buttonTarget.setAttribute("aria-checked", "false");
|
||||
this.buttonTarget.classList.remove("bg-blue-600")
|
||||
this.buttonTarget.classList.add("bg-gray-200")
|
||||
this.switchTarget.classList.remove("translate-x-5")
|
||||
this.switchTarget.classList.add("translate-x-0")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,12 +7,12 @@ class XmppExchangeContactsJob < ApplicationJob
|
||||
ejabberd.add_rosteritem({
|
||||
"localuser": username, "localhost": domain,
|
||||
"user": inviter.cn, "host": inviter.ou,
|
||||
"nick": inviter.cn, "group": "Friends", "subs": "both"
|
||||
"nick": inviter.cn, "group": Setting.ejabberd_buddy_roster, "subs": "both"
|
||||
})
|
||||
ejabberd.add_rosteritem({
|
||||
"localuser": inviter.cn, "localhost": inviter.ou,
|
||||
"user": username, "host": domain,
|
||||
"nick": username, "group": "Friends", "subs": "both"
|
||||
"nick": username, "group": Setting.ejabberd_buddy_roster, "subs": "both"
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
class ApplicationMailer < ActionMailer::Base
|
||||
default from: 'from@example.com'
|
||||
layout 'mailer'
|
||||
end
|
||||
|
||||
23
app/mailers/custom_mailer.rb
Normal file
23
app/mailers/custom_mailer.rb
Normal file
@@ -0,0 +1,23 @@
|
||||
# A custom mailer that can be used from the Rails console for one-off emails
|
||||
# today, and later connected from an admin panel mailing page.
|
||||
#
|
||||
# Assign any template variables you want to use:
|
||||
#
|
||||
# user = User.first
|
||||
#
|
||||
# Create the email body from a custom email template file:
|
||||
#
|
||||
# body = ERB.new(File.read('./tmp/mailer-1.txt.erb')).result binding
|
||||
#
|
||||
# Send email via Sidekiq:
|
||||
#
|
||||
# CustomMailer.with(user: user, subject: "Important announcement", body: body).custom_message.deliver_later
|
||||
#
|
||||
class CustomMailer < ApplicationMailer
|
||||
def custom_message
|
||||
@user = params[:user]
|
||||
@subject = params[:subject]
|
||||
@body = params[:body]
|
||||
mail(to: @user.email, subject: @subject)
|
||||
end
|
||||
end
|
||||
@@ -3,7 +3,9 @@ class Donation < ApplicationRecord
|
||||
belongs_to :user
|
||||
|
||||
# Validations
|
||||
validates_presence_of :user
|
||||
validates_presence_of :amount_sats
|
||||
validates_presence_of :paid_at
|
||||
|
||||
# Hooks
|
||||
# TODO before_create :store_fiat_value
|
||||
|
||||
@@ -2,10 +2,106 @@
|
||||
class Setting < RailsSettings::Base
|
||||
cache_prefix { "v1" }
|
||||
|
||||
#
|
||||
# Internal services
|
||||
#
|
||||
|
||||
field :redis_url, type: :string, readonly: true,
|
||||
default: ENV["REDIS_URL"] || "redis://localhost:6379/0"
|
||||
|
||||
#
|
||||
# Registrations
|
||||
#
|
||||
|
||||
field :reserved_usernames, type: :array, default: %w[
|
||||
account accounts 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
|
||||
#
|
||||
# Sentry
|
||||
#
|
||||
|
||||
field :sentry_enabled, type: :boolean, readonly: true,
|
||||
default: (ENV["SENTRY_DSN"].present?.to_s || false)
|
||||
|
||||
#
|
||||
# Discourse
|
||||
#
|
||||
|
||||
field :discourse_public_url, type: :string, readonly: true,
|
||||
default: ENV["DISCOURSE_PUBLIC_URL"].presence
|
||||
|
||||
field :discourse_enabled, type: :boolean,
|
||||
default: (ENV["DISCOURSE_PUBLIC_URL"].present?.to_s || false)
|
||||
|
||||
#
|
||||
# ejabberd
|
||||
#
|
||||
|
||||
field :ejabberd_enabled, type: :boolean,
|
||||
default: (ENV["EJABBERD_API_URL"].present?.to_s || false)
|
||||
|
||||
field :ejabberd_api_url, type: :string, readonly: true,
|
||||
default: ENV["EJABBERD_API_URL"].presence
|
||||
|
||||
field :ejabberd_admin_url, type: :string, readonly: true,
|
||||
default: ENV["EJABBERD_ADMIN_URL"].presence
|
||||
|
||||
field :ejabberd_buddy_roster, type: :string,
|
||||
default: "Buddies"
|
||||
|
||||
#
|
||||
# Gitea
|
||||
#
|
||||
|
||||
field :gitea_public_url, type: :string, readonly: true,
|
||||
default: ENV["GITEA_PUBLIC_URL"].presence
|
||||
|
||||
field :gitea_enabled, type: :boolean,
|
||||
default: (ENV["GITEA_PUBLIC_URL"].present?.to_s || false)
|
||||
|
||||
#
|
||||
# Lightning Network
|
||||
#
|
||||
|
||||
field :lndhub_api_url, type: :string, readonly: true,
|
||||
default: ENV["LNDHUB_API_URL"].presence
|
||||
|
||||
field :lndhub_enabled, type: :boolean,
|
||||
default: (ENV["LNDHUB_API_URL"].present?.to_s || false)
|
||||
|
||||
field :lndhub_admin_enabled, type: :boolean,
|
||||
default: (ENV["LNDHUB_ADMIN_UI"] || false)
|
||||
|
||||
field :lndhub_public_key, type: :string, readonly: true,
|
||||
default: (ENV["LNDHUB_PUBLIC_KEY"] || "")
|
||||
|
||||
field :lndhub_keysend_enabled, type: :boolean,
|
||||
default: -> { self.lndhub_public_key.present?.to_s || false }
|
||||
|
||||
#
|
||||
# Mastodon
|
||||
#
|
||||
|
||||
field :mastodon_public_url, type: :string, readonly: true,
|
||||
default: ENV["MASTODON_PUBLIC_URL"].presence
|
||||
|
||||
field :mastodon_enabled, type: :boolean,
|
||||
default: (ENV["MASTODON_PUBLIC_URL"].present?.to_s || false)
|
||||
|
||||
#
|
||||
# MediaWiki
|
||||
#
|
||||
|
||||
field :mediawiki_public_url, type: :string, readonly: true,
|
||||
default: ENV["MEDIAWIKI_PUBLIC_URL"].presence
|
||||
|
||||
field :mediawiki_enabled, type: :boolean,
|
||||
default: (ENV["MEDIAWIKI_PUBLIC_URL"].present?.to_s || false)
|
||||
|
||||
#
|
||||
# Nostr
|
||||
#
|
||||
|
||||
field :nostr_enabled, type: :boolean, default: true
|
||||
end
|
||||
|
||||
@@ -38,7 +38,9 @@ class User < ApplicationRecord
|
||||
devise :ldap_authenticatable,
|
||||
:confirmable,
|
||||
:recoverable,
|
||||
:validatable
|
||||
:validatable,
|
||||
:timeoutable,
|
||||
:rememberable
|
||||
|
||||
def ldap_before_save
|
||||
self.email = Devise::LDAP::Adapter.get_ldap_param(self.cn, "mail").first
|
||||
@@ -53,7 +55,18 @@ class User < ApplicationRecord
|
||||
end
|
||||
|
||||
def devise_after_confirmation
|
||||
enable_service %w[discourse gitea wiki xmpp]
|
||||
enable_service %w[ discourse ejabberd gitea mediawiki ]
|
||||
|
||||
#TODO enable in development when we have easy setup of ejabberd etc.
|
||||
return if Rails.env.development?
|
||||
|
||||
if inviter.present?
|
||||
exchange_xmpp_contact_with_inviter if Setting.ejabberd_enabled?
|
||||
end
|
||||
end
|
||||
|
||||
def send_devise_notification(notification, *args)
|
||||
devise_mailer.send(notification, self, *args).deliver_later
|
||||
end
|
||||
|
||||
def reset_password(new_password, new_password_confirmation)
|
||||
@@ -120,6 +133,12 @@ class User < ApplicationRecord
|
||||
ldap.delete_attribute(dn,:service)
|
||||
end
|
||||
|
||||
def exchange_xmpp_contact_with_inviter
|
||||
return unless inviter.services_enabled.include?("ejabberd") &&
|
||||
services_enabled.include?("ejabberd")
|
||||
XmppExchangeContactsJob.perform_later(inviter, self.cn, self.ou)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def ldap
|
||||
|
||||
@@ -11,11 +11,10 @@ class CreateAccount < ApplicationService
|
||||
def call
|
||||
user = create_user_in_database
|
||||
add_ldap_document
|
||||
create_lndhub_account(user)
|
||||
create_lndhub_account(user) if Setting.lndhub_enabled
|
||||
|
||||
if @invitation.present?
|
||||
update_invitation(user.id)
|
||||
exchange_xmpp_contacts
|
||||
end
|
||||
end
|
||||
|
||||
@@ -43,12 +42,6 @@ class CreateAccount < ApplicationService
|
||||
CreateLdapUserJob.perform_later(@username, @domain, @email, hashed_pw)
|
||||
end
|
||||
|
||||
def exchange_xmpp_contacts
|
||||
#TODO enable in development when we have easy setup of ejabberd etc.
|
||||
return if Rails.env.development?
|
||||
XmppExchangeContactsJob.perform_later(@invitation.user, @username, @domain)
|
||||
end
|
||||
|
||||
def create_lndhub_account(user)
|
||||
#TODO enable in development when we have a local lndhub (mock?) API
|
||||
return if Rails.env.development?
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
<%= form_with(url: url, model: donation, local: true) do |form| %>
|
||||
<% if donation.errors.any? %>
|
||||
<div id="error_explanation">
|
||||
<section id="error_explanation">
|
||||
<h3><%= pluralize(donation.errors.count, "error") %> prohibited this donation from being saved:</h3>
|
||||
<ul>
|
||||
<ul class="list-disc list-inside">
|
||||
<% donation.errors.full_messages.each do |message| %>
|
||||
<li><%= message %></li>
|
||||
<% end %>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
<% end %>
|
||||
|
||||
<div class="sm:w-1/2 grid grid-cols-2 items-center gap-y-2">
|
||||
<section 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, {} %>
|
||||
|
||||
@@ -28,9 +28,14 @@
|
||||
|
||||
<%= form.label :paid_at %>
|
||||
<%= form.text_field :paid_at %>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<p class="mt-8">
|
||||
<%= form.submit class: 'btn-md btn-blue' %>
|
||||
</p>
|
||||
<section>
|
||||
<p class="pt-6 border-t border-gray-200 text-right">
|
||||
<%= link_to 'Cancel',
|
||||
@donation.id.present? ? admin_donation_path(@donation) : admin_donations_path,
|
||||
class: 'btn-md btn-gray' %>
|
||||
<%= form.submit class: 'ml-2 btn-md btn-blue' %>
|
||||
</p>
|
||||
</section>
|
||||
<% end %>
|
||||
|
||||
@@ -2,8 +2,4 @@
|
||||
|
||||
<%= render MainSimpleComponent.new do %>
|
||||
<%= render 'form', donation: @donation, url: admin_donation_path(@donation) %>
|
||||
|
||||
<p class="mt-8">
|
||||
<%= link_to 'Cancel', admin_donation_path(@donation), class: 'btn-sm btn-gray' %>
|
||||
<p>
|
||||
<% end %>
|
||||
|
||||
@@ -2,8 +2,4 @@
|
||||
|
||||
<%= render MainSimpleComponent.new do %>
|
||||
<%= render 'form', donation: @donation, url: admin_donations_path %>
|
||||
|
||||
<p class="mt-8">
|
||||
<%= link_to 'Back', admin_donations_path, class: 'ks-text-link' %>
|
||||
</p>
|
||||
<% end %>
|
||||
|
||||
@@ -33,9 +33,9 @@
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<p>
|
||||
<%= link_to 'Edit', edit_admin_donation_path(@donation), class: 'btn-md btn-blue mr-1' %>
|
||||
<p class="pt-6 border-t border-gray-200 text-right">
|
||||
<%= link_to 'Back', admin_donations_path, class: 'btn-md btn-gray' %>
|
||||
<%= link_to 'Edit', edit_admin_donation_path(@donation), class: 'ml-2 btn-md btn-blue mr-1' %>
|
||||
</p>
|
||||
</section>
|
||||
<% end %>
|
||||
|
||||
7
app/views/admin/settings/_errors.html.erb
Normal file
7
app/views/admin/settings/_errors.html.erb
Normal file
@@ -0,0 +1,7 @@
|
||||
<section>
|
||||
<ul>
|
||||
<% errors.full_messages.each do |msg| %>
|
||||
<li><%= msg %></li>
|
||||
<% end %>
|
||||
</ul>
|
||||
</section>
|
||||
@@ -4,14 +4,9 @@
|
||||
<%= form_for(Setting.new, url: admin_settings_registrations_path) do |f| %>
|
||||
<section>
|
||||
<h3>Registrations</h3>
|
||||
|
||||
<% if @errors && @errors.any? %>
|
||||
<div>
|
||||
<ul>
|
||||
<% @errors.full_messages.each do |msg| %>
|
||||
<li><%= msg %></li>
|
||||
<% end %>
|
||||
</ul>
|
||||
</div>
|
||||
<%= render partial: "admin/settings/errors", locals: { errors: @errors } %>
|
||||
<% end %>
|
||||
|
||||
<label class="block">
|
||||
@@ -29,7 +24,7 @@
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<p class="pt-6 border-t border-gray-200">
|
||||
<p class="pt-6 border-t border-gray-200 text-right">
|
||||
<%= f.submit 'Save', class: "btn-md btn-blue w-full md:w-auto" %>
|
||||
</p>
|
||||
</section>
|
||||
|
||||
17
app/views/admin/settings/services/_discourse.html.erb
Normal file
17
app/views/admin/settings/services/_discourse.html.erb
Normal file
@@ -0,0 +1,17 @@
|
||||
<h3>Discourse</h3>
|
||||
<ul role="list">
|
||||
<%= render FormElements::FieldsetToggleComponent.new(
|
||||
form: f,
|
||||
attribute: :discourse_enabled,
|
||||
enabled: Setting.discourse_enabled?,
|
||||
title: "Enable Discourse integration",
|
||||
description: "Discourse configuration present and features enabled"
|
||||
) %>
|
||||
<% if Setting.discourse_enabled? %>
|
||||
<%= render FormElements::FieldsetComponent.new(title: "Public URL") do %>
|
||||
<%= f.text_field :discourse_public_url,
|
||||
value: Setting.discourse_public_url,
|
||||
class: "w-full", disabled: true %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</ul>
|
||||
30
app/views/admin/settings/services/_ejabberd.html.erb
Normal file
30
app/views/admin/settings/services/_ejabberd.html.erb
Normal file
@@ -0,0 +1,30 @@
|
||||
<h3>ejabberd (XMPP)</h3>
|
||||
<ul role="list">
|
||||
<%= render FormElements::FieldsetToggleComponent.new(
|
||||
form: f,
|
||||
attribute: :ejabberd_enabled,
|
||||
enabled: Setting.ejabberd_enabled?,
|
||||
title: "Enable ejabberd integration",
|
||||
description: "ejabberd configuration present and features enabled"
|
||||
) %>
|
||||
<% if Setting.ejabberd_enabled? %>
|
||||
<%= render FormElements::FieldsetComponent.new(title: "API URL") do %>
|
||||
<%= f.text_field :ejabberd_api_url,
|
||||
value: Setting.ejabberd_api_url,
|
||||
class: "w-full", disabled: true %>
|
||||
<% end %>
|
||||
<%= render FormElements::FieldsetComponent.new(title: "Admin URL") do %>
|
||||
<%= f.text_field :ejabberd_admin_url,
|
||||
value: Setting.ejabberd_admin_url,
|
||||
class: "w-full", disabled: true %>
|
||||
<% end %>
|
||||
<%= render FormElements::FieldsetComponent.new(
|
||||
title: "Contact roster name",
|
||||
description: "Used when exchanging contacts after signup from invitation"
|
||||
) do %>
|
||||
<%= f.text_field :ejabberd_buddy_roster,
|
||||
value: Setting.ejabberd_buddy_roster,
|
||||
class: "w-full" %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</ul>
|
||||
17
app/views/admin/settings/services/_gitea.html.erb
Normal file
17
app/views/admin/settings/services/_gitea.html.erb
Normal file
@@ -0,0 +1,17 @@
|
||||
<h3>Gitea</h3>
|
||||
<ul role="list">
|
||||
<%= render FormElements::FieldsetToggleComponent.new(
|
||||
form: f,
|
||||
attribute: :gitea_enabled,
|
||||
enabled: Setting.gitea_enabled?,
|
||||
title: "Enable Gitea integration",
|
||||
description: "Gitea configuration present and features enabled"
|
||||
) %>
|
||||
<% if Setting.gitea_enabled? %>
|
||||
<%= render FormElements::FieldsetComponent.new(title: "Public URL") do %>
|
||||
<%= f.text_field :gitea_public_url,
|
||||
value: Setting.gitea_public_url,
|
||||
class: "w-full", disabled: true %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</ul>
|
||||
38
app/views/admin/settings/services/_lndhub.html.erb
Normal file
38
app/views/admin/settings/services/_lndhub.html.erb
Normal file
@@ -0,0 +1,38 @@
|
||||
<h3>Lightning Network</h3>
|
||||
<ul role="list">
|
||||
<%= render FormElements::FieldsetToggleComponent.new(
|
||||
form: f,
|
||||
attribute: :lndhub_enabled,
|
||||
enabled: Setting.lndhub_enabled?,
|
||||
title: "Enable LNDHub integration",
|
||||
description: "LNDHub configuration present and wallet features enabled"
|
||||
) %>
|
||||
<% if Setting.lndhub_enabled? %>
|
||||
<%= render FormElements::FieldsetComponent.new(title: "API URL") do %>
|
||||
<%= f.text_field :lndhub_api_url,
|
||||
value: Setting.lndhub_api_url,
|
||||
class: "w-full", disabled: true %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<%= render FormElements::FieldsetToggleComponent.new(
|
||||
form: f,
|
||||
attribute: :lndhub_admin_enabled,
|
||||
enabled: Setting.lndhub_admin_enabled?,
|
||||
title: "Enable LNDHub admin panel",
|
||||
description: "LNDHub database configuration present and admin panel enabled"
|
||||
) %>
|
||||
<%= render FormElements::FieldsetToggleComponent.new(
|
||||
form: f,
|
||||
attribute: :lndhub_keysend_enabled,
|
||||
enabled: Setting.lndhub_keysend_enabled?,
|
||||
title: "Enable keysend payments",
|
||||
description: "Allow users to receive invoice-less payments to their Lightning Address"
|
||||
) %>
|
||||
<% if Setting.lndhub_keysend_enabled? %>
|
||||
<%= render FormElements::FieldsetComponent.new(title: "Public key", description: "The public key of the Lightning node used by LNDHub") do %>
|
||||
<%= f.text_field :lndhub_public_key,
|
||||
value: Setting.lndhub_public_key,
|
||||
class: "w-full", disabled: true %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</ul>
|
||||
17
app/views/admin/settings/services/_mastodon.html.erb
Normal file
17
app/views/admin/settings/services/_mastodon.html.erb
Normal file
@@ -0,0 +1,17 @@
|
||||
<h3>Mastodon</h3>
|
||||
<ul role="list">
|
||||
<%= render FormElements::FieldsetToggleComponent.new(
|
||||
form: f,
|
||||
attribute: :mastodon_enabled,
|
||||
enabled: Setting.mastodon_enabled?,
|
||||
title: "Enable Mastodon integration",
|
||||
description: "Mastodon configuration present and features enabled"
|
||||
) %>
|
||||
<% if Setting.mastodon_enabled? %>
|
||||
<%= render FormElements::FieldsetComponent.new(title: "Public URL") do %>
|
||||
<%= f.text_field :mastodon_public_url,
|
||||
value: Setting.mastodon_public_url,
|
||||
class: "w-full", disabled: true %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</ul>
|
||||
17
app/views/admin/settings/services/_mediawiki.html.erb
Normal file
17
app/views/admin/settings/services/_mediawiki.html.erb
Normal file
@@ -0,0 +1,17 @@
|
||||
<h3>MediaWiki</h3>
|
||||
<ul role="list">
|
||||
<%= render FormElements::FieldsetToggleComponent.new(
|
||||
form: f,
|
||||
attribute: :mediawiki_enabled,
|
||||
enabled: Setting.mediawiki_enabled?,
|
||||
title: "Enable MediaWiki integration",
|
||||
description: "MediaWiki configuration present and features enabled"
|
||||
) %>
|
||||
<% if Setting.mediawiki_enabled? %>
|
||||
<%= render FormElements::FieldsetComponent.new(title: "Public URL") do %>
|
||||
<%= f.text_field :mediawiki_public_url,
|
||||
value: Setting.mediawiki_public_url,
|
||||
class: "w-full", disabled: true %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</ul>
|
||||
10
app/views/admin/settings/services/_nostr.html.erb
Normal file
10
app/views/admin/settings/services/_nostr.html.erb
Normal file
@@ -0,0 +1,10 @@
|
||||
<h3>Nostr</h3>
|
||||
<ul role="list">
|
||||
<%= render FormElements::FieldsetToggleComponent.new(
|
||||
form: f,
|
||||
attribute: :nostr_enabled,
|
||||
enabled: Setting.nostr_enabled?,
|
||||
title: "Enable Nostr integration (experimental)",
|
||||
description: "Allow adding nostr pubkeys and resolve user addresses via NIP-05"
|
||||
) %>
|
||||
</ul>
|
||||
@@ -1,39 +1,23 @@
|
||||
<%= render HeaderComponent.new(title: "Settings") %>
|
||||
|
||||
<%= render MainWithSidenavComponent.new(sidenav_partial: 'shared/admin_sidenav_settings') do %>
|
||||
<section>
|
||||
<h3>Lightning Network</h3>
|
||||
<%= form_for(Setting.new, url: admin_settings_services_path) do |f| %>
|
||||
<% if @errors && @errors.any? %>
|
||||
<div>
|
||||
<ul>
|
||||
<% @errors.full_messages.each do |msg| %>
|
||||
<li><%= msg %></li>
|
||||
<% end %>
|
||||
</ul>
|
||||
</div>
|
||||
<% end %>
|
||||
<%= form_for(Setting.new, url: admin_settings_services_path) do |f| %>
|
||||
<%= hidden_field_tag :service, @service %>
|
||||
|
||||
<ul role="list" class="mt-2 divide-y divide-gray-200">
|
||||
<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">LNDHub configuration present and wallet features enabled</p>
|
||||
</div>
|
||||
<%= f.check_box :lndhub_enabled, checked: Setting.lndhub_enabled?,
|
||||
disabled: true,
|
||||
class: "relative ml-4 inline-flex flex-shrink-0" %>
|
||||
</li>
|
||||
<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">LNDHub database configuration present and admin panel enabled</p>
|
||||
</div>
|
||||
<%= f.check_box :lndhub_admin_enabled, checked: Setting.lndhub_admin_enabled?,
|
||||
disabled: true,
|
||||
class: "relative ml-4 inline-flex flex-shrink-0" %>
|
||||
</li>
|
||||
</ul>
|
||||
<% if @errors && @errors.any? %>
|
||||
<section>
|
||||
<%= render partial: "admin/settings/errors", locals: { errors: @errors } %>
|
||||
</section>
|
||||
<% end %>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<%= render partial: @service, locals: { f: f } %>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<p class="pt-6 border-t border-gray-200 text-right">
|
||||
<%= f.submit 'Save', class: "btn-md btn-blue w-full md:w-auto" %>
|
||||
</p>
|
||||
</section>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
@@ -65,28 +65,87 @@
|
||||
|
||||
<section>
|
||||
<h3>Services</h3>
|
||||
<table class="sm:w-1/4">
|
||||
<table class="divided">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Enabled</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<% if Setting.discourse_enabled %>
|
||||
<tr>
|
||||
<td>Discourse</td>
|
||||
<td><%= check_box_tag 'service_discourse', 'enabled', @services_enabled.include?("discourse"), disabled: true %></td>
|
||||
<td>
|
||||
<%= render FormElements::ToggleComponent.new(
|
||||
enabled: @services_enabled.include?("discourse"),
|
||||
input_enabled: false
|
||||
) %>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<%= link_to "Open profile", "#{Setting.discourse_public_url}/u/#{@user.cn}/summary", class: "btn-sm btn-gray" %>
|
||||
</td>
|
||||
</tr>
|
||||
<% end %>
|
||||
<% if Setting.gitea_enabled %>
|
||||
<tr>
|
||||
<td>Gitea</td>
|
||||
<td><%= check_box_tag 'service_gitea', 'enabled', @services_enabled.include?("gitea"), disabled: true %></td>
|
||||
<td>
|
||||
<%= render FormElements::ToggleComponent.new(
|
||||
enabled: @services_enabled.include?("gitea"),
|
||||
input_enabled: false
|
||||
) %>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<%= link_to "Open profile", "#{Setting.gitea_public_url}/#{@user.cn}", class: "btn-sm btn-gray" %>
|
||||
</td>
|
||||
</tr>
|
||||
<% end %>
|
||||
<% if Setting.mastodon_enabled %>
|
||||
<tr>
|
||||
<td>Mastodon</td>
|
||||
<td><%= check_box_tag 'service_mastodon', 'enabled', @services_enabled.include?("mastodon"), disabled: true %></td>
|
||||
<td>
|
||||
<%= render FormElements::ToggleComponent.new(
|
||||
enabled: @services_enabled.include?("mastodon"),
|
||||
input_enabled: false
|
||||
) %>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<%= link_to "Open profile", "#{Setting.mastodon_public_url}/@#{@user.cn}", class: "btn-sm btn-gray" %>
|
||||
</td>
|
||||
</tr>
|
||||
<% end %>
|
||||
<% if Setting.mediawiki_enabled %>
|
||||
<tr>
|
||||
<td>Wiki</td>
|
||||
<td><%= check_box_tag 'service_wiki', 'enabled', @services_enabled.include?("wiki"), disabled: true %></td>
|
||||
<td>MediaWiki</td>
|
||||
<td>
|
||||
<%= render FormElements::ToggleComponent.new(
|
||||
enabled: @services_enabled.include?("mediawiki"),
|
||||
input_enabled: false
|
||||
) %>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<%= link_to "Open profile", "#{Setting.mediawiki_public_url}/Special:Contributions/#{@user.cn}", class: "btn-sm btn-gray" %>
|
||||
</td>
|
||||
</tr>
|
||||
<% end %>
|
||||
<% if Setting.ejabberd_enabled %>
|
||||
<tr>
|
||||
<td>XMPP</td>
|
||||
<td><%= check_box_tag 'service_xmpp', 'enabled', @services_enabled.include?("xmpp"), disabled: true %></td>
|
||||
<td>XMPP (ejabberd)</td>
|
||||
<td>
|
||||
<%= render FormElements::ToggleComponent.new(
|
||||
enabled: @services_enabled.include?("ejabberd"),
|
||||
input_enabled: false
|
||||
) %>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<% if Setting.ejabberd_admin_url.present? %>
|
||||
<%= link_to "Open profile", "#{Setting.ejabberd_admin_url}/server/#{@user.ou}/user/#{@user.cn}/", class: "btn-sm btn-gray" %>
|
||||
<% end %>
|
||||
</td>
|
||||
</tr>
|
||||
<% end %>
|
||||
</tbody>
|
||||
</table>
|
||||
</section>
|
||||
|
||||
1
app/views/custom_mailer/custom_message.text.erb
Normal file
1
app/views/custom_mailer/custom_message.text.erb
Normal file
@@ -0,0 +1 @@
|
||||
<%= @body %>
|
||||
@@ -6,7 +6,7 @@
|
||||
<%= form_for(resource, as: resource_name, url: confirmation_path(resource_name), html: { method: :post }) do |f| %>
|
||||
<%= render "devise/shared/error_messages", resource: resource %>
|
||||
<p>
|
||||
<%= f.label :email, 'Email address', class: 'block mb-1 w-full' %>
|
||||
<%= f.label :email, 'Email address', class: 'block mb-2 font-bold' %>
|
||||
<%= f.email_field :email,
|
||||
required: true, autofocus: true, autocomplete: "email",
|
||||
value: (resource.pending_reconfirmation? ? resource.unconfirmed_email : resource.email),
|
||||
@@ -14,7 +14,7 @@
|
||||
</p>
|
||||
<p class="mt-8">
|
||||
<%= f.submit "Resend confirmation link",
|
||||
class: 'btn-md btn-blue w-full sm:w-auto' %>
|
||||
class: 'btn-md btn-blue w-full' %>
|
||||
</p>
|
||||
<% end %>
|
||||
|
||||
|
||||
@@ -5,19 +5,21 @@
|
||||
|
||||
<%= form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :post }) do |f| %>
|
||||
<%= render "devise/shared/error_messages", resource: resource %>
|
||||
<div class="mb-6">
|
||||
<%= f.label :cn, 'User', class: 'block mb-2 font-bold' %>
|
||||
<p class="flex gap-2 items-center">
|
||||
<%= f.text_field :cn, autofocus: true, autocomplete: "username",
|
||||
required: true, class: "relative grow"%>
|
||||
<span class="relative shrink-0 text-gray-500">@ kosmos.org</span>
|
||||
</p>
|
||||
</div>
|
||||
<p>
|
||||
<%= f.label :cn, 'User', class: 'block' %>
|
||||
<%= f.text_field :cn, autofocus: true, autocomplete: "username",
|
||||
required: true, class: "w-full md:w-3/5"%>
|
||||
<span class="ml-1 text-gray-500">@ kosmos.org</span>
|
||||
</p>
|
||||
<p>
|
||||
<%= f.label :email, 'Email address', class: 'block' %>
|
||||
<%= f.label :email, 'Email address', class: 'block mb-2 font-bold' %>
|
||||
<%= f.email_field :email, autocomplete: "email", required: true,
|
||||
class: "w-full md:w-3/5"%>
|
||||
class: "w-full"%>
|
||||
</p>
|
||||
<p class="mt-8">
|
||||
<%= f.submit "Send me a reset link", class: 'btn-md btn-blue w-full sm:w-auto' %>
|
||||
<%= f.submit "Send me a reset link", class: 'btn-md btn-blue w-full' %>
|
||||
</p>
|
||||
<% end %>
|
||||
|
||||
|
||||
@@ -3,21 +3,47 @@
|
||||
<%= render MainCompactComponent.new do %>
|
||||
<%= form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| %>
|
||||
<%= render "devise/shared/error_messages", resource: resource %>
|
||||
<p>
|
||||
<%= f.label :cn, 'User', class: 'block' %>
|
||||
<%= f.text_field :cn, autofocus: true, autocomplete: "username",
|
||||
class: "w-full md:w-3/5"%>
|
||||
<span class="ml-1 text-gray-500">@ kosmos.org</span>
|
||||
</p>
|
||||
<p>
|
||||
<%= f.label :password, class: 'block' %>
|
||||
<div class="mb-6">
|
||||
<%= f.label :cn, 'User', class: 'block mb-2 font-bold' %>
|
||||
<p class="flex gap-2 items-center">
|
||||
<%= f.text_field :cn, autofocus: true, autocomplete: "username",
|
||||
required: true, class: "relative grow", tabindex: "1" %>
|
||||
<span class="relative shrink-0 text-gray-500">@ kosmos.org</span>
|
||||
</p>
|
||||
</div>
|
||||
<p class="mb-8">
|
||||
<%= f.label :password, class: 'block mb-2 font-bold' %>
|
||||
<%= f.password_field :password, autocomplete: "current-password",
|
||||
class: "w-full md:w-3/5"%>
|
||||
required: true, class: "w-full", tabindex: "2" %>
|
||||
</p>
|
||||
<p class="mt-8">
|
||||
<%= f.submit "Log in", class: 'btn-md btn-blue w-full sm:w-auto' %>
|
||||
|
||||
<%= tag.div class: "flex items-center mb-8 gap-x-3", data: {
|
||||
controller: "settings--toggle",
|
||||
:'settings--toggle-switch-enabled-value' => "false"
|
||||
} do %>
|
||||
<div class="relative inline-flex flex-shrink-0">
|
||||
<%= render FormElements::ToggleComponent.new(
|
||||
enabled: false, input_enabled: true, class_names: "hidden",
|
||||
tabindex: "3", data: {
|
||||
:'settings--toggle-target' => "button",
|
||||
action: "settings--toggle#toggleSwitch"
|
||||
}) %>
|
||||
<%= f.check_box :remember_me, {
|
||||
checked: false,
|
||||
data: { :'settings--toggle-target' => "checkbox" }
|
||||
}, "true", "false" %>
|
||||
</div>
|
||||
<%= f.label :remember_me,
|
||||
class: "text-gray-500 flex flex-col",
|
||||
data: { action: "click->settings--toggle#toggleSwitch" } %>
|
||||
<p class="grow text-sm text-right">
|
||||
<%= link_to "Forgot your password?", new_password_path(resource_name),
|
||||
class: "text-gray-500 underline" %><br />
|
||||
</p>
|
||||
<% end %>
|
||||
|
||||
<p>
|
||||
<%= f.submit "Log in", class: 'btn-md btn-blue w-full', tabindex: "4" %>
|
||||
</p>
|
||||
<% end %>
|
||||
|
||||
<%= render "devise/shared/links" %>
|
||||
<% end %>
|
||||
|
||||
@@ -1,25 +1,29 @@
|
||||
<div class="devise-links mt-8 text-sm">
|
||||
<%- if controller_name != 'sessions' %>
|
||||
<p class="mb-1.5">
|
||||
<%= link_to "Log in", new_session_path(resource_name) %><br />
|
||||
<p class="mb-2">
|
||||
<%= link_to "Log in", new_session_path(resource_name),
|
||||
class: "text-gray-500 underline" %>
|
||||
</p>
|
||||
<% end %>
|
||||
|
||||
<%- if devise_mapping.recoverable? && controller_name != 'passwords' && controller_name != 'registrations' %>
|
||||
<p class="mb-1.5">
|
||||
<%= link_to "Forgot your password?", new_password_path(resource_name) %><br />
|
||||
<p class="mb-2">
|
||||
<%= link_to "Forgot your password?", new_password_path(resource_name),
|
||||
class: "text-gray-500 underline" %>
|
||||
</p>
|
||||
<% end %>
|
||||
|
||||
<%- if devise_mapping.confirmable? && controller_name != 'confirmations' %>
|
||||
<p class="mb-1.5">
|
||||
<%= link_to "Didn't receive confirmation instructions?", new_confirmation_path(resource_name) %><br />
|
||||
<%- if devise_mapping.confirmable? && !controller_name.match(/^(confirmations|sessions)$/) %>
|
||||
<p class="mb-2">
|
||||
<%= link_to "Didn't receive confirmation instructions?", new_confirmation_path(resource_name),
|
||||
class: "text-gray-500 underline" %>
|
||||
</p>
|
||||
<% end %>
|
||||
|
||||
<%- if devise_mapping.lockable? && resource_class.unlock_strategy_enabled?(:email) && controller_name != 'unlocks' %>
|
||||
<p class="mb-1.5">
|
||||
<%= link_to "Didn't receive unlock instructions?", new_unlock_path(resource_name) %><br />
|
||||
<p class="mb-2">
|
||||
<%= link_to "Didn't receive unlock instructions?", new_unlock_path(resource_name),
|
||||
class: "text-gray-500 underline" %>
|
||||
</p>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-x"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-x <%= custom_class %>"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 299 B After Width: | Height: | Size: 320 B |
@@ -6,14 +6,13 @@
|
||||
<p class="mb-8">
|
||||
Invite your friends to a Kosmos account by sharing an invitation URL with them:
|
||||
</p>
|
||||
<ul>
|
||||
<ul class="md:w-3/4">
|
||||
<% @invitations_unused.each do |invitation| %>
|
||||
<li class="font-mono mb-1 flex gap-1 md:block"
|
||||
data-controller="clipboard">
|
||||
<input type="text" disabled class="md:w-3/4 flex-1"
|
||||
<li class="font-mono mb-2 flex gap-1" data-controller="clipboard">
|
||||
<input type="text" disabled class="relative grow"
|
||||
value="<%= invitation_url(invitation.token) %>"
|
||||
data-clipboard-target="source" />
|
||||
<button id="copy-user-address" class="btn-md btn-icon btn-blue flex-none w-auto"
|
||||
<button id="copy-user-address" class="btn-md btn-icon btn-blue shrink-0 w-auto"
|
||||
data-clipboard-target="trigger" data-action="clipboard#copy"
|
||||
title="Copy to clipboard">
|
||||
<span class="content-initial">
|
||||
|
||||
@@ -2,9 +2,7 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<style>
|
||||
/* Email styles need to be inline */
|
||||
</style>
|
||||
<style></style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
@@ -1,6 +1,16 @@
|
||||
<%= render HeaderComponent.new(title: "Settings") %>
|
||||
|
||||
<%= render MainWithSidenavComponent.new(sidenav_partial: 'shared/sidenav_settings') do %>
|
||||
<section>
|
||||
<h3>E-Mail</h3>
|
||||
<p class="mb-2">
|
||||
<%= label :email, 'Address', class: 'font-bold' %>
|
||||
</p>
|
||||
<p class="flex gap-1 mb-2 sm:w-3/5">
|
||||
<input type="text" id="email" class="grow"
|
||||
value=<%= current_user.email %> disabled="disabled" />
|
||||
</p>
|
||||
</section>
|
||||
<section>
|
||||
<h3>Password</h3>
|
||||
<p class="mb-8">Use the following button to request an email with a password reset link:</p>
|
||||
|
||||
@@ -3,14 +3,14 @@
|
||||
<%= render MainWithSidenavComponent.new(sidenav_partial: 'shared/sidenav_settings') do %>
|
||||
<section>
|
||||
<h3>Profile</h3>
|
||||
<p class="mb-1">
|
||||
<p class="mb-2">
|
||||
<%= label :user_address, 'User address', class: 'font-bold' %>
|
||||
</p>
|
||||
<p data-controller="clipboard" class="flex gap-1 mb-2 sm:block">
|
||||
<input type="text" id="user_address" class="flex-1 sm:w-3/5"
|
||||
<p data-controller="clipboard" class="flex gap-1 mb-2 sm:w-3/5">
|
||||
<input type="text" id="user_address" class="grow"
|
||||
value=<%= @user.address %> disabled="disabled"
|
||||
data-clipboard-target="source" />
|
||||
<button id="copy-user-address" class="btn-md btn-icon btn-blue flex-none w-auto"
|
||||
<button id="copy-user-address" class="btn-md btn-icon btn-blue shrink-0"
|
||||
data-clipboard-target="trigger" data-action="clipboard#copy"
|
||||
title="Copy to clipboard">
|
||||
<span class="content-initial">
|
||||
|
||||
@@ -6,6 +6,9 @@
|
||||
name: "Services", path: admin_settings_services_path, icon: "grid",
|
||||
active: current_page?(admin_settings_services_path)
|
||||
) %>
|
||||
<% if current_page?(admin_settings_services_path) %>
|
||||
<%= render partial: "shared/admin_sidenav_settings_services" %>
|
||||
<% end %>
|
||||
<%= render SidenavLinkComponent.new(
|
||||
name: "Security", path: "#", icon: "shield", disabled: true
|
||||
) %>
|
||||
|
||||
49
app/views/shared/_admin_sidenav_settings_services.html.erb
Normal file
49
app/views/shared/_admin_sidenav_settings_services.html.erb
Normal file
@@ -0,0 +1,49 @@
|
||||
<%= render SidenavLinkComponent.new(
|
||||
level: 2,
|
||||
name: "Discourse",
|
||||
path: admin_settings_services_path(params: { s: "discourse" }),
|
||||
icon: Setting.discourse_enabled? ? "check" : "x",
|
||||
active: current_page?(admin_settings_services_path(params: { s: "discourse" })),
|
||||
) %>
|
||||
<%= render SidenavLinkComponent.new(
|
||||
level: 2,
|
||||
name: "ejabberd",
|
||||
path: admin_settings_services_path(params: { s: "ejabberd" }),
|
||||
icon: Setting.ejabberd_enabled? ? "check" : "x",
|
||||
active: current_page?(admin_settings_services_path(params: { s: "ejabberd" })),
|
||||
) %>
|
||||
<%= render SidenavLinkComponent.new(
|
||||
level: 2,
|
||||
name: "Gitea",
|
||||
path: admin_settings_services_path(params: { s: "gitea" }),
|
||||
icon: Setting.gitea_enabled? ? "check" : "x",
|
||||
active: current_page?(admin_settings_services_path(params: { s: "gitea" })),
|
||||
) %>
|
||||
<%= render SidenavLinkComponent.new(
|
||||
level: 2,
|
||||
name: "LNDHub",
|
||||
path: admin_settings_services_path(params: { s: "lndhub" }),
|
||||
icon: Setting.lndhub_enabled? ? "check" : "x",
|
||||
active: current_page?(admin_settings_services_path(params: { s: "lndhub" })),
|
||||
) %>
|
||||
<%= render SidenavLinkComponent.new(
|
||||
level: 2,
|
||||
name: "Mastodon",
|
||||
path: admin_settings_services_path(params: { s: "mastodon" }),
|
||||
icon: Setting.mastodon_enabled? ? "check" : "x",
|
||||
active: current_page?(admin_settings_services_path(params: { s: "mastodon" })),
|
||||
) %>
|
||||
<%= render SidenavLinkComponent.new(
|
||||
level: 2,
|
||||
name: "MediaWiki",
|
||||
path: admin_settings_services_path(params: { s: "mediawiki" }),
|
||||
icon: Setting.mediawiki_enabled? ? "check" : "x",
|
||||
active: current_page?(admin_settings_services_path(params: { s: "mediawiki" })),
|
||||
) %>
|
||||
<%= render SidenavLinkComponent.new(
|
||||
level: 2,
|
||||
name: "Nostr",
|
||||
path: admin_settings_services_path(params: { s: "nostr" }),
|
||||
icon: Setting.nostr_enabled? ? "check" : "x",
|
||||
active: current_page?(admin_settings_services_path(params: { s: "nostr" })),
|
||||
) %>
|
||||
@@ -11,6 +11,6 @@
|
||||
</p>
|
||||
<p class="mt-12">
|
||||
<%= link_to "Get started", signup_steps_path(1),
|
||||
class: "btn-md btn-blue block w-full md:inline-block sm:w-auto" %>
|
||||
class: "btn-md btn-blue block w-full" %>
|
||||
</p>
|
||||
<% end %>
|
||||
|
||||
@@ -5,19 +5,20 @@
|
||||
when 1 %>
|
||||
<h2>Choose a username</h2>
|
||||
<%= form_for @user, :url => signup_validate_url do |f| %>
|
||||
<p>
|
||||
<%= f.label :cn, 'Username', class: 'hidden' %>
|
||||
<%= f.text_field :cn, autofocus: true, autocomplete: "username",
|
||||
class: 'text-xl w-full md:w-3/5 mb-1' %>
|
||||
<span class="text-base md:text-xl text-gray-500 ml-1">@</span>
|
||||
<span class="text-base md:text-xl text-gray-500">kosmos.org</span>
|
||||
</p>
|
||||
<div class="mb-6">
|
||||
<p class="flex gap-2 items-center">
|
||||
<%= f.text_field :cn, autofocus: true, autocomplete: "username",
|
||||
required: true, class: "relative grow text-xl"%>
|
||||
<span class="relative shrink-0 text-gray-500 md:text-xl">
|
||||
@ kosmos.org
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
<% if @validation_error.present? %>
|
||||
<p class="error-msg">Username <%= @validation_error %></p>
|
||||
<% end %>
|
||||
<p class="mt-12">
|
||||
<%= f.submit "Continue",
|
||||
class: "btn-md btn-blue block w-full md:inline-block sm:w-auto" %>
|
||||
<%= f.submit "Continue", class: "btn-md btn-blue block w-full" %>
|
||||
</p>
|
||||
<% end %>
|
||||
|
||||
@@ -27,14 +28,13 @@
|
||||
<p>
|
||||
<%= f.label :email, 'Email address', class: 'hidden' %>
|
||||
<%= f.email_field :email, autofocus: true, autocomplete: 'email',
|
||||
class: 'text-xl w-full' %>
|
||||
required: true, class: 'text-xl w-full' %>
|
||||
</p>
|
||||
<% if @validation_error.present? %>
|
||||
<p class="error-msg">Email <%= @validation_error %></p>
|
||||
<% end %>
|
||||
<p class="mt-12">
|
||||
<%= f.submit "Continue",
|
||||
class: "btn-md btn-blue block w-full md:inline-block sm:w-auto" %>
|
||||
<%= f.submit "Continue", class: "btn-md btn-blue block w-full" %>
|
||||
</p>
|
||||
<% end %>
|
||||
|
||||
@@ -44,8 +44,7 @@
|
||||
<%= form_for @user, :url => signup_validate_url do |f| %>
|
||||
<p>
|
||||
<%= f.label :password, 'Password', class: 'hidden' %>
|
||||
<%= f.password_field :password, autofocus: true,
|
||||
class: 'text-xl w-full' %>
|
||||
<%= f.password_field :password, autofocus: true, class: 'text-xl w-full' %>
|
||||
</p>
|
||||
<% if @validation_error.present? %>
|
||||
<p class="error-msg">Password <%= @validation_error %></p>
|
||||
@@ -55,8 +54,7 @@
|
||||
and Privacy Policy. Don't worry, they will be excellent!
|
||||
</p>
|
||||
<p class="mt-8">
|
||||
<%= f.submit "Create account",
|
||||
class: "btn-md btn-blue block w-full sm:inline-block sm:w-auto" %>
|
||||
<%= f.submit "Create account", class: "btn-md btn-blue block w-full" %>
|
||||
</p>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
@@ -8,7 +8,7 @@ require "active_record/railtie"
|
||||
# require "active_storage/engine"
|
||||
require "action_controller/railtie"
|
||||
require "action_mailer/railtie"
|
||||
# require "action_mailbox/engine"
|
||||
require "action_mailbox/engine"
|
||||
# require "action_text/engine"
|
||||
require "action_view/railtie"
|
||||
require "action_cable/engine"
|
||||
|
||||
@@ -57,10 +57,14 @@ Rails.application.configure do
|
||||
# routes, locales, etc. This feature depends on the listen gem.
|
||||
config.file_watcher = ActiveSupport::EventedFileUpdateChecker
|
||||
|
||||
config.action_mailer.default_options = {
|
||||
from: "accounts@localhost"
|
||||
}
|
||||
|
||||
# Don't actually send emails, cache them for viewing via letter opener
|
||||
config.action_mailer.delivery_method = :letter_opener
|
||||
# Don't care if the mailer can't send
|
||||
config.action_mailer.raise_delivery_errors = false
|
||||
config.action_mailer.raise_delivery_errors = true
|
||||
# Base URL to be used by email template link helpers
|
||||
config.action_mailer.default_url_options = { host: "localhost:3000", protocol: "http" }
|
||||
|
||||
|
||||
@@ -57,28 +57,53 @@ Rails.application.configure do
|
||||
# config.active_job.queue_adapter = :resque
|
||||
# config.active_job.queue_name_prefix = "akkounts_production"
|
||||
|
||||
config.action_mailer.perform_caching = false
|
||||
# E-mail settings, adapted from https://github.com/mastodon/mastodon
|
||||
|
||||
config.action_mailer.delivery_method = :smtp
|
||||
outgoing_email_address = ENV.fetch('SMTP_FROM_ADDRESS', 'accounts@localhost')
|
||||
outgoing_email_domain = Mail::Address.new(outgoing_email_address).domain
|
||||
|
||||
config.action_mailer.default_options = {
|
||||
from: outgoing_email_address,
|
||||
message_id: -> { "<#{Mail.random_tag}@#{outgoing_email_domain}>" },
|
||||
}
|
||||
|
||||
config.action_mailer.default_options[:reply_to] = ENV['SMTP_REPLY_TO'] if ENV['SMTP_REPLY_TO'].present?
|
||||
config.action_mailer.default_options[:return_path] = ENV['SMTP_RETURN_PATH'] if ENV['SMTP_RETURN_PATH'].present?
|
||||
|
||||
enable_starttls = nil
|
||||
enable_starttls_auto = nil
|
||||
|
||||
case ENV['SMTP_ENABLE_STARTTLS']
|
||||
when 'always'
|
||||
enable_starttls = true
|
||||
when 'never'
|
||||
enable_starttls = false
|
||||
when 'auto'
|
||||
enable_starttls_auto = true
|
||||
else
|
||||
enable_starttls_auto = ENV['SMTP_ENABLE_STARTTLS_AUTO'] != 'false'
|
||||
end
|
||||
|
||||
config.action_mailer.smtp_settings = {
|
||||
address: "mail.gandi.net",
|
||||
port: "587",
|
||||
authentication: "plain",
|
||||
enable_starttls_auto: true,
|
||||
user_name: Rails.application.credentials.smtp[:username],
|
||||
password: Rails.application.credentials.smtp[:password]
|
||||
port: ENV['SMTP_PORT'],
|
||||
address: ENV['SMTP_SERVER'],
|
||||
user_name: ENV['SMTP_LOGIN'].presence,
|
||||
password: ENV['SMTP_PASSWORD'].presence,
|
||||
domain: ENV['SMTP_DOMAIN'] || ENV['LOCAL_DOMAIN'],
|
||||
authentication: ENV['SMTP_AUTH_METHOD'] == 'none' ? nil : ENV['SMTP_AUTH_METHOD'] || :plain,
|
||||
ca_file: ENV['SMTP_CA_FILE'].presence || '/etc/ssl/certs/ca-certificates.crt',
|
||||
openssl_verify_mode: ENV['SMTP_OPENSSL_VERIFY_MODE'],
|
||||
enable_starttls: enable_starttls,
|
||||
enable_starttls_auto: enable_starttls_auto,
|
||||
tls: ENV['SMTP_TLS'].presence && ENV['SMTP_TLS'] == 'true',
|
||||
ssl: ENV['SMTP_SSL'].presence && ENV['SMTP_SSL'] == 'true',
|
||||
}
|
||||
|
||||
config.action_mailer.default_url_options = {
|
||||
host: "accounts.kosmos.org",
|
||||
protocol: "https",
|
||||
from: "accounts@kosmos.org"
|
||||
}
|
||||
config.action_mailer.delivery_method = ENV.fetch('SMTP_DELIVERY_METHOD', 'smtp').to_sym
|
||||
|
||||
# Ignore bad email addresses and do not raise email delivery errors.
|
||||
# Set this to true and configure the email server for immediate delivery to raise delivery errors.
|
||||
# config.action_mailer.raise_delivery_errors = false
|
||||
config.action_mailer.raise_delivery_errors = true
|
||||
|
||||
# Enable locale fallbacks for I18n (makes lookups for any locale fall back to
|
||||
# the I18n.default_locale when a translation cannot be found).
|
||||
|
||||
@@ -3,30 +3,12 @@ require 'digest'
|
||||
require 'securerandom'
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Create custom failure for turbo
|
||||
class TurboFailureApp < Devise::FailureApp
|
||||
def respond
|
||||
if request_format == :turbo_stream
|
||||
redirect
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
def skip_format?
|
||||
%w(html turbo_stream */*).include? request_format.to_s
|
||||
end
|
||||
end
|
||||
|
||||
# Assuming you have not yet modified this file, each configuration option below
|
||||
# is set to its default value. Note that some are commented out while others
|
||||
# are not: uncommented lines are intended to protect your configuration from
|
||||
# breaking changes in upgrades (i.e., in the event that future versions of
|
||||
# Devise change the default values for those options).
|
||||
#
|
||||
# Use this hook to configure devise mailer, warden hooks and so forth.
|
||||
# Many of these configuration options can be set straight in your model.
|
||||
Devise.setup do |config|
|
||||
# Hotwire/Turbo
|
||||
config.responder.error_status = :unprocessable_entity
|
||||
config.responder.redirect_status = :see_other
|
||||
|
||||
# ==> LDAP Configuration
|
||||
config.ldap_logger = true
|
||||
config.ldap_create_user = true
|
||||
@@ -59,7 +41,6 @@ Devise.setup do |config|
|
||||
# ==> Controller configuration
|
||||
# Configure the parent class to the devise controllers.
|
||||
# config.parent_controller = 'DeviseController'
|
||||
config.parent_controller = 'TurboController'
|
||||
|
||||
# ==> Mailer Configuration
|
||||
# Configure the e-mail address which will be shown in Devise::Mailer,
|
||||
@@ -205,13 +186,13 @@ Devise.setup do |config|
|
||||
|
||||
# ==> Configuration for :rememberable
|
||||
# The time the user will be remembered without asking for credentials again.
|
||||
# config.remember_for = 2.weeks
|
||||
config.remember_for = 2.weeks
|
||||
|
||||
# Invalidates all the remember me tokens when the user signs out.
|
||||
config.expire_all_remember_me_on_sign_out = true
|
||||
|
||||
# If true, extends the user's remember period when remembered via cookie.
|
||||
# config.extend_remember_period = false
|
||||
config.extend_remember_period = true
|
||||
|
||||
# Options to be passed to the created cookie. For instance, you can set
|
||||
# secure: true in order to force SSL only cookies.
|
||||
@@ -229,7 +210,7 @@ Devise.setup do |config|
|
||||
# ==> Configuration for :timeoutable
|
||||
# The time you want to timeout the user session without activity. After this
|
||||
# time the user will be asked for credentials again. Default is 30 minutes.
|
||||
# config.timeout_in = 30.minutes
|
||||
config.timeout_in = 30.minutes
|
||||
|
||||
# ==> Configuration for :lockable
|
||||
# Defines which strategy will be used to lock an account.
|
||||
@@ -319,11 +300,10 @@ Devise.setup do |config|
|
||||
# If you want to use other strategies, that are not supported by Devise, or
|
||||
# change the failure app, you can configure them inside the config.warden block.
|
||||
#
|
||||
config.warden do |manager|
|
||||
manager.failure_app = TurboFailureApp
|
||||
# config.warden do |manager|
|
||||
# manager.intercept_401 = false
|
||||
# manager.default_strategies(scope: :user).unshift :some_external_strategy
|
||||
end
|
||||
# end
|
||||
|
||||
# ==> Mountable engine configurations
|
||||
# When using Devise inside an engine, let's call it `MyEngine`, and this engine
|
||||
@@ -339,13 +319,6 @@ Devise.setup do |config|
|
||||
# so you need to do it manually. For the users scope, it would be:
|
||||
# config.omniauth_path_prefix = '/my_engine/users/auth'
|
||||
|
||||
# ==> Turbolinks configuration
|
||||
# If your app is using Turbolinks, Turbolinks::Controller needs to be included to make redirection work correctly:
|
||||
#
|
||||
# ActiveSupport.on_load(:devise_failure_app) do
|
||||
# include Turbolinks::Controller
|
||||
# end
|
||||
|
||||
# ==> Configuration for :registerable
|
||||
|
||||
# When set to false, does not sign a user in automatically after their password is
|
||||
|
||||
9
config/initializers/sentry.rb
Normal file
9
config/initializers/sentry.rb
Normal file
@@ -0,0 +1,9 @@
|
||||
if ENV["SENTRY_DSN"].present?
|
||||
Sentry.init do |config|
|
||||
config.dsn = ENV["SENTRY_DSN"]
|
||||
config.breadcrumbs_logger = [:active_support_logger, :http_logger]
|
||||
config.traces_sampler = lambda do |context|
|
||||
true
|
||||
end
|
||||
end
|
||||
end
|
||||
5
config/initializers/sidekiq.rb
Normal file
5
config/initializers/sidekiq.rb
Normal file
@@ -0,0 +1,5 @@
|
||||
require_relative "../../app/models/setting"
|
||||
|
||||
Sidekiq.configure_server do |config|
|
||||
config.redis = { url: Setting.redis_url }
|
||||
end
|
||||
@@ -1,7 +1,7 @@
|
||||
require 'sidekiq/web'
|
||||
|
||||
Rails.application.routes.draw do
|
||||
devise_for :users, :controllers => { :confirmations => "users/confirmations" }
|
||||
devise_for :users, controllers: { confirmations: "users/confirmations" }
|
||||
|
||||
get 'welcome', to: 'welcome#index'
|
||||
get 'check_your_email', to: 'welcome#check_your_email'
|
||||
@@ -28,8 +28,12 @@ Rails.application.routes.draw do
|
||||
get 'wallet', to: 'wallet#index'
|
||||
get 'wallet/transactions', to: 'wallet#transactions'
|
||||
|
||||
get 'lnurlpay/:address', to: 'lnurlpay#index', constraints: { address: /[^\/]+/}
|
||||
get 'lnurlpay/:address/invoice', to: 'lnurlpay#invoice', constraints: { address: /[^\/]+/}
|
||||
get 'lnurlpay/:address', to: 'lnurlpay#index',
|
||||
as: 'lightning_address', constraints: { address: /[^\/]+/}
|
||||
get 'lnurlpay/:address/invoice', to: 'lnurlpay#invoice',
|
||||
as: 'lnurlpay_invoice', constraints: { address: /[^\/]+/}
|
||||
get 'keysend/:address', to: 'lnurlpay#keysend',
|
||||
as: 'lightning_address_keysend', constraints: { address: /[^\/]+/}
|
||||
|
||||
post 'webhooks/lndhub', to: 'webhooks#lndhub'
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
:concurrency: 2
|
||||
:queues:
|
||||
- default
|
||||
- mailers
|
||||
|
||||
7
config/storage.yml
Normal file
7
config/storage.yml
Normal file
@@ -0,0 +1,7 @@
|
||||
test:
|
||||
service: Disk
|
||||
root: <%= Rails.root.join("tmp/storage") %>
|
||||
|
||||
local:
|
||||
service: Disk
|
||||
root: <%= Rails.root.join("storage") %>
|
||||
@@ -0,0 +1,6 @@
|
||||
class AddRememberCreatedAtToUsers < ActiveRecord::Migration[7.0]
|
||||
def change
|
||||
add_column :users, :remember_created_at, :datetime
|
||||
add_column :users, :remember_token, :string
|
||||
end
|
||||
end
|
||||
@@ -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_02_23_115536) do
|
||||
ActiveRecord::Schema[7.0].define(version: 2023_03_19_101128) do
|
||||
create_table "donations", force: :cascade do |t|
|
||||
t.integer "user_id"
|
||||
t.integer "amount_sats"
|
||||
@@ -57,6 +57,8 @@ ActiveRecord::Schema[7.0].define(version: 2023_02_23_115536) do
|
||||
t.text "ln_login_ciphertext"
|
||||
t.text "ln_password_ciphertext"
|
||||
t.string "ln_account"
|
||||
t.datetime "remember_created_at"
|
||||
t.string "remember_token"
|
||||
t.index ["email"], name: "index_users_on_email", unique: true
|
||||
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
|
||||
end
|
||||
|
||||
@@ -3,11 +3,67 @@ services:
|
||||
image: 4teamwork/389ds:latest
|
||||
volumes:
|
||||
- ./tmp/389ds:/data
|
||||
networks:
|
||||
- external_network
|
||||
- internal_network
|
||||
ports:
|
||||
- "389:3389"
|
||||
environment:
|
||||
DS_DM_PASSWORD: passthebutter
|
||||
SUFFIX_NAME: "dc=kosmos,dc=org"
|
||||
|
||||
# redis:
|
||||
# restart: always
|
||||
# image: redis:7-alpine
|
||||
# networks:
|
||||
# - internal_network
|
||||
# healthcheck:
|
||||
# test: ['CMD', 'redis-cli', 'ping']
|
||||
# volumes:
|
||||
# - ./tmp/redis:/data
|
||||
|
||||
# web:
|
||||
# build: .
|
||||
# tty: true
|
||||
# command: bash -c "rm -f /akkounts/tmp/pids/server.pid; bin/dev"
|
||||
# volumes:
|
||||
# - .:/akkounts
|
||||
# networks:
|
||||
# - external_network
|
||||
# - internal_network
|
||||
# ports:
|
||||
# - "3000:3000"
|
||||
# environment:
|
||||
# RAILS_ENV: development
|
||||
# REDIS_URL: redis://redis:6379/0
|
||||
# LDAP_HOST: ldap
|
||||
# LDAP_PORT: 3389
|
||||
# LDAP_ADMIN_PASSWORD: passthebutter
|
||||
# LDAP_USE_TLS: "false"
|
||||
# depends_on:
|
||||
# - ldap
|
||||
# - redis
|
||||
|
||||
# sidekiq:
|
||||
# build: .
|
||||
# command: bash -c "bundle exec sidekiq -C config/sidekiq.yml"
|
||||
# volumes:
|
||||
# - .:/akkounts
|
||||
# networks:
|
||||
# - internal_network
|
||||
# environment:
|
||||
# RAILS_ENV: development
|
||||
# REDIS_URL: redis://redis:6379/0
|
||||
# LDAP_HOST: ldap
|
||||
# LDAP_PORT: 3389
|
||||
# LDAP_ADMIN_PASSWORD: passthebutter
|
||||
# LDAP_USE_TLS: "false"
|
||||
# LAUNCHY_DRY_RUN: true
|
||||
# BROWSER: /dev/null
|
||||
# depends_on:
|
||||
# - ldap
|
||||
# - redis
|
||||
|
||||
# phpldapadmin:
|
||||
# image: osixia/phpldapadmin:0.9.0
|
||||
# ports:
|
||||
@@ -16,19 +72,8 @@ services:
|
||||
# PHPLDAPADMIN_HTTPS: false
|
||||
# PHPLDAPADMIN_LDAP_HOSTS: "#PYTHON2BASH:[{'ldap': [{'server': [{'tls': False}, {'port': 3389}]}, {'login': [{'bind_id': 'cn=Directory Manager'}, {'bind_pass': 'passthebutter'}]}]}]"
|
||||
# PHPLDAPADMIN_LDAP_CLIENT_TLS: false
|
||||
# web:
|
||||
# build: .
|
||||
# tty: true
|
||||
# command: bash -c "sleep 5 && rm -f tmp/pids/server.pid && bin/dev"
|
||||
# volumes:
|
||||
# - .:/akkounts
|
||||
# ports:
|
||||
# - "3000:3000"
|
||||
# environment:
|
||||
# RAILS_ENV: development
|
||||
# LDAP_HOST: ldap
|
||||
# LDAP_PORT: 3389
|
||||
# LDAP_ADMIN_PASSWORD: passthebutter
|
||||
# LDAP_USE_TLS: "false"
|
||||
# depends_on:
|
||||
# - ldap
|
||||
|
||||
networks:
|
||||
external_network:
|
||||
internal_network:
|
||||
internal: true
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# Remove a potentially pre-existing server.pid for Rails.
|
||||
rm -f /myapp/tmp/pids/server.pid
|
||||
|
||||
# Then exec the container's main process (what's set as CMD in the Dockerfile).
|
||||
exec "$@"
|
||||
@@ -11,7 +11,7 @@
|
||||
"postcss-preset-env": "^7.8.3",
|
||||
"tailwindcss": "^3.2.4"
|
||||
},
|
||||
"version": "0.4.0",
|
||||
"version": "0.5.0",
|
||||
"scripts": {
|
||||
"build:css:tailwind": "tailwindcss --postcss -i ./app/assets/stylesheets/application.tailwind.css -o ./app/assets/builds/application.css",
|
||||
"build:css": "yarn run build:css:tailwind"
|
||||
|
||||
@@ -18,4 +18,33 @@ RSpec.describe 'Admin/global settings', type: :feature do
|
||||
click_button "Save"
|
||||
expect(Setting.reserved_usernames).to eq(['Kosmos', 'Kredits'])
|
||||
end
|
||||
|
||||
describe "Service settings" do
|
||||
scenario "Opening service settings shows page for first service" do
|
||||
visit admin_settings_services_path
|
||||
|
||||
expect(current_url).to eq(admin_settings_services_url(params: { s: "discourse" }))
|
||||
end
|
||||
|
||||
scenario "View ejabberd settings" do
|
||||
visit admin_settings_services_path(params: { s: "ejabberd" })
|
||||
|
||||
expect(page).to have_content("Enable ejabberd integration")
|
||||
expect(page).to have_field("API URL",
|
||||
with: "http://xmpp.example.com/api",
|
||||
disabled: true)
|
||||
end
|
||||
|
||||
scenario "Disable ejabberd integration" do
|
||||
visit admin_settings_services_path(params: { s: "ejabberd" })
|
||||
expect(page).to have_checked_field("setting[ejabberd_enabled]")
|
||||
|
||||
uncheck "setting[ejabberd_enabled]"
|
||||
click_button "Save"
|
||||
|
||||
expect(current_url).to eq(admin_settings_services_url(params: { s: "ejabberd" }))
|
||||
expect(page).to_not have_checked_field("setting[ejabberd_enabled]")
|
||||
expect(page).to_not have_field("API URL", disabled: true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -17,9 +17,9 @@ RSpec.describe XmppExchangeContactsJob, type: :job do
|
||||
perform_enqueued_jobs { job }
|
||||
|
||||
expect(WebMock).to have_requested(:post, "http://xmpp.example.com/api/add_rosteritem")
|
||||
.with { |req| req.body == '{"localuser":"isaacnewton","localhost":"kosmos.org","user":"willherschel","host":"kosmos.org","nick":"willherschel","group":"Friends","subs":"both"}' }
|
||||
.with { |req| req.body == '{"localuser":"isaacnewton","localhost":"kosmos.org","user":"willherschel","host":"kosmos.org","nick":"willherschel","group":"Buddies","subs":"both"}' }
|
||||
expect(WebMock).to have_requested(:post, "http://xmpp.example.com/api/add_rosteritem")
|
||||
.with { |req| req.body == '{"localuser":"willherschel","localhost":"kosmos.org","user":"isaacnewton","host":"kosmos.org","nick":"isaacnewton","group":"Friends","subs":"both"}' }
|
||||
.with { |req| req.body == '{"localuser":"willherschel","localhost":"kosmos.org","user":"isaacnewton","host":"kosmos.org","nick":"isaacnewton","group":"Buddies","subs":"both"}' }
|
||||
end
|
||||
|
||||
after do
|
||||
|
||||
@@ -101,4 +101,52 @@ RSpec.describe User, type: :model do
|
||||
end
|
||||
end
|
||||
|
||||
describe "#exchange_xmpp_contact_with_inviter" do
|
||||
include ActiveJob::TestHelper
|
||||
|
||||
let(:user) { create :user, cn: "willherschel", ou: "kosmos.org" }
|
||||
let(:guest) { create :user, id: 2, cn: "isaacnewton", ou: "kosmos.org", email: "newt@example.com" }
|
||||
|
||||
before do
|
||||
Invitation.create! user: user, invited_user_id: guest.id, used_at: DateTime.now
|
||||
allow_any_instance_of(User).to receive(:services_enabled).and_return(%w[ ejabberd ])
|
||||
end
|
||||
|
||||
it "enqueues a job to exchange XMPP contacts between inviter and invitee" do
|
||||
guest.send(:exchange_xmpp_contact_with_inviter)
|
||||
|
||||
expect(enqueued_jobs.size).to eq(1)
|
||||
args = enqueued_jobs.first['arguments']
|
||||
expect(args[0]['_aj_globalid']).to match('gid://akkounts/User')
|
||||
expect(args[1]).to eq('isaacnewton')
|
||||
expect(args[2]).to eq('kosmos.org')
|
||||
end
|
||||
|
||||
after do
|
||||
clear_enqueued_jobs
|
||||
end
|
||||
end
|
||||
|
||||
describe "#devise_after_confirmation" do
|
||||
let(:user) { create :user, cn: "willherschel", ou: "kosmos.org" }
|
||||
|
||||
it "enables default services" do
|
||||
expect(user).to receive(:enable_service).with(%w[ discourse ejabberd gitea mediawiki ])
|
||||
user.send(:devise_after_confirmation)
|
||||
end
|
||||
|
||||
context "for invited user with ejabberd enabled" do
|
||||
let(:guest) { create :user, id: 2, cn: "isaacnewton", ou: "kosmos.org", email: "newt@example.com" }
|
||||
|
||||
before do
|
||||
Invitation.create! user: user, invited_user_id: guest.id, used_at: DateTime.now
|
||||
allow_any_instance_of(User).to receive(:enable_service).and_return(true)
|
||||
end
|
||||
|
||||
it "exchanges XMPP contacts with the inviter" do
|
||||
expect(guest).to receive(:exchange_xmpp_contact_with_inviter)
|
||||
guest.send(:devise_after_confirmation)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
110
spec/requests/lnurlpay_spec.rb
Normal file
110
spec/requests/lnurlpay_spec.rb
Normal file
@@ -0,0 +1,110 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe "/lnurlpay", type: :request do
|
||||
|
||||
context "Non-existent user/address" do
|
||||
describe "GET /lnurlpay/:address" do
|
||||
it "returns a 404" do
|
||||
get lightning_address_path(address: "csw@kosmos.org")
|
||||
expect(response).to have_http_status(:not_found)
|
||||
end
|
||||
end
|
||||
|
||||
describe "GET /lnurlpay/:address/invoice" do
|
||||
it "returns a 404" do
|
||||
get lnurlpay_invoice_path(address: "csw@kosmos.org", params: { amount: 5000 })
|
||||
expect(response).to have_http_status(:not_found)
|
||||
end
|
||||
end
|
||||
|
||||
describe "GET /keysend/:address/" do
|
||||
it "returns a 404" do
|
||||
get lightning_address_keysend_path(address: "csw@kosmos.org")
|
||||
expect(response).to have_http_status(:not_found)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "Valid user/address" do
|
||||
let(:user) { create :user, cn: 'satoshi', ou: 'kosmos.org', ln_account: 'abcdefg123456' }
|
||||
|
||||
before do
|
||||
login_as user, :scope => :user
|
||||
end
|
||||
|
||||
describe "GET /lnurlpay/:address" do
|
||||
it "returns a formatted Lightning Address response" do
|
||||
get lightning_address_path(address: "satoshi@kosmos.org")
|
||||
|
||||
expect(response).to have_http_status(:ok)
|
||||
|
||||
res = JSON.parse(response.body)
|
||||
expect(res["status"]).to eq('OK')
|
||||
expect(res["tag"]).to eq('payRequest')
|
||||
expect(res["callback"]).to match(lnurlpay_invoice_path('satoshi@kosmos.org'))
|
||||
expect(res["minSendable"]).to be_a(Integer)
|
||||
expect(res["maxSendable"]).to be_a(Integer)
|
||||
expect(res["commentAllowed"]).to be_a(Integer)
|
||||
end
|
||||
end
|
||||
|
||||
describe "GET /lnurlpay/:address/invoice" do
|
||||
before do
|
||||
allow_any_instance_of(User).to receive(:ln_create_invoice).and_return("lnbc50u1p3lwgknpp52g78gqya5euvzjc53fc6hkmlm2rfjhcd305tcmc0g9gaestav48sdq4gdhkven9v5sx6mmwv4ujzcqzpgxqyz5vqsp5skkz4jlqr6tkvv2g9739ygrjupc4rkqd94mc7dfpj3pgx3f6w7qs9qyyssq7mf3fzcuxlmkr9nqatcch3u8uf4gjyawe052tejz8e9fqxu4pncqk3qklt8g6ylpshg09xyjquyrgtc72vcw5cp0dzcf406apyua7dgpnfn7an")
|
||||
end
|
||||
|
||||
it "returns a formatted lnurlpay response" do
|
||||
get lnurlpay_invoice_path(address: "satoshi@kosmos.org", params: {
|
||||
amount: 50000, comment: "Coffee time!"
|
||||
})
|
||||
|
||||
expect(response).to have_http_status(:ok)
|
||||
|
||||
res = JSON.parse(response.body)
|
||||
expect(res["status"]).to eq('OK')
|
||||
expect(res["successAction"]["tag"]).to eq('message')
|
||||
expect(res["successAction"]["message"]).to match('Thank you')
|
||||
expect(res["pr"]).to eq("lnbc50u1p3lwgknpp52g78gqya5euvzjc53fc6hkmlm2rfjhcd305tcmc0g9gaestav48sdq4gdhkven9v5sx6mmwv4ujzcqzpgxqyz5vqsp5skkz4jlqr6tkvv2g9739ygrjupc4rkqd94mc7dfpj3pgx3f6w7qs9qyyssq7mf3fzcuxlmkr9nqatcch3u8uf4gjyawe052tejz8e9fqxu4pncqk3qklt8g6ylpshg09xyjquyrgtc72vcw5cp0dzcf406apyua7dgpnfn7an")
|
||||
end
|
||||
|
||||
context "amount too low" do
|
||||
it "returns an error" do
|
||||
get lnurlpay_invoice_path(address: "satoshi@kosmos.org", params: {
|
||||
amount: 5000, comment: "Coffee time!"
|
||||
})
|
||||
expect(response).to have_http_status(:ok)
|
||||
res = JSON.parse(response.body)
|
||||
expect(res["status"]).to eq('ERROR')
|
||||
expect(res["reason"]).to eq('Invalid amount')
|
||||
end
|
||||
end
|
||||
|
||||
context "comment too long" do
|
||||
it "returns an error" do
|
||||
get lnurlpay_invoice_path(address: "satoshi@kosmos.org", params: {
|
||||
amount: 5000000, comment: "Coffee time is the best time, so here's some money for you to get some. May I suggest to sample some Pacamara beans from El Salvador?"
|
||||
})
|
||||
expect(response).to have_http_status(:ok)
|
||||
res = JSON.parse(response.body)
|
||||
expect(res["status"]).to eq('ERROR')
|
||||
expect(res["reason"]).to eq('Comment too long')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "GET /keysend/:address/" do
|
||||
it "returns a formatted Lightning Address keysend response" do
|
||||
get lightning_address_keysend_path(address: "satoshi@kosmos.org")
|
||||
|
||||
expect(response).to have_http_status(:ok)
|
||||
|
||||
res = JSON.parse(response.body)
|
||||
expect(res["status"]).to eq('OK')
|
||||
expect(res["tag"]).to eq('keysend')
|
||||
expect(res["pubkey"]).to eq(Setting.lndhub_public_key)
|
||||
expect(res["customData"][0]["customKey"]).to eq('696969')
|
||||
expect(res["customData"][0]["customValue"]).to eq('abcdefg123456')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -65,34 +65,6 @@ RSpec.describe CreateAccount, type: :model do
|
||||
end
|
||||
end
|
||||
|
||||
describe "#exchange_xmpp_contacts" do
|
||||
include ActiveJob::TestHelper
|
||||
|
||||
let(:inviter) { create :user, cn: "willherschel", ou: "kosmos.org" }
|
||||
let(:invitation) { create :invitation, user: inviter }
|
||||
let(:service) { CreateAccount.new(
|
||||
username: 'isaacnewton',
|
||||
email: 'isaacnewton@example.com',
|
||||
password: 'bright-ideas-in-autumn',
|
||||
invitation: invitation
|
||||
)}
|
||||
|
||||
it "enqueues a job to exchange XMPP contacts between inviter and invitee" do
|
||||
service.send(:exchange_xmpp_contacts)
|
||||
|
||||
expect(enqueued_jobs.size).to eq(1)
|
||||
|
||||
args = enqueued_jobs.first['arguments']
|
||||
expect(args[0]['_aj_globalid']).to match('gid://akkounts/User')
|
||||
expect(args[1]).to eq('isaacnewton')
|
||||
expect(args[2]).to eq('kosmos.org')
|
||||
end
|
||||
|
||||
after do
|
||||
clear_enqueued_jobs
|
||||
end
|
||||
end
|
||||
|
||||
describe "#create_lndhub_account" do
|
||||
include ActiveJob::TestHelper
|
||||
|
||||
|
||||
Reference in New Issue
Block a user