Compare commits
55 Commits
dfb12b8f62
...
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 | |||
| cdad7546fb | |||
| feb7833533 | |||
|
|
6c2a97e7e5
|
||
|
|
dc63506102
|
||
|
|
b87b9c2437
|
||
|
|
e580cc9991
|
||
|
|
68ab88c481
|
||
|
|
c7fe1bc3bc
|
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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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"`
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
@import "components/base";
|
||||
@import "components/buttons";
|
||||
@import "components/dashboard_services";
|
||||
@import "components/forms";
|
||||
@import "components/links";
|
||||
@import "components/notifications";
|
||||
|
||||
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%);
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -0,0 +1 @@
|
||||
<%= @body %>
|
||||
@@ -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 %>
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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"
|
||||
|
||||
8
public/img/logos/icon_discourse.svg
Normal file
@@ -0,0 +1,8 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -1 104 106">
|
||||
<path fill="#231f20" d="M51.87 0C23.71 0 0 22.83 0 51v52.81l51.86-.05c28.16 0 51-23.71 51-51.87S80 0 51.87 0Z"/>
|
||||
<path fill="#fff9ae" d="M52.37 19.74a31.62 31.62 0 0 0-27.79 46.67l-5.72 18.4 20.54-4.64a31.61 31.61 0 1 0 13-60.43Z"/>
|
||||
<path fill="#00aeef" d="M77.45 32.12a31.6 31.6 0 0 1-38.05 48l-20.54 4.7 20.91-2.47a31.6 31.6 0 0 0 37.68-50.23Z"/>
|
||||
<path fill="#00a94f" d="M71.63 26.29A31.6 31.6 0 0 1 38.8 78l-19.94 6.82 20.54-4.65a31.6 31.6 0 0 0 32.23-53.88Z"/>
|
||||
<path fill="#f15d22" d="M26.47 67.11a31.61 31.61 0 0 1 51-35 31.61 31.61 0 0 0-52.89 34.3l-5.72 18.4Z"/>
|
||||
<path fill="#e31b23" d="M24.58 66.41a31.61 31.61 0 0 1 47.05-40.12 31.61 31.61 0 0 0-49 39.63l-3.76 18.9Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 761 B |
9
public/img/logos/icon_droneci.svg
Normal file
@@ -0,0 +1,9 @@
|
||||
<svg width="33" height="34" viewBox="0 0 33 34" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M32.8125 17.2932C32.8125 19.6071 32.3208 21.8488 31.4779 23.8735L25.6476 17.944C26.35 16.787 26.7012 15.4131 26.7012 13.9669C26.7715 9.84522 23.47 6.44662 19.3958 6.44662C17.9207 6.44662 16.586 6.88048 15.4621 7.60359L10.0533 2.10799C12.0904 1.16795 14.2679 0.661774 16.6562 0.661774C25.5773 0.661774 32.8125 8.10976 32.8125 17.2932ZM7.80543 3.40958L13.6357 9.41135C12.6523 10.7129 12.0904 12.3038 12.0904 14.0392C12.0904 18.2332 15.3918 21.6318 19.466 21.6318C21.1519 21.6318 22.6973 21.0534 23.9617 20.041L30.2134 26.4767C27.2632 30.9599 22.3461 33.9246 16.6562 33.9246C7.73519 33.9246 0.5 26.4767 0.5 17.2932C0.5 11.436 3.38003 6.37431 7.80543 3.40958ZM19.466 18.7394C22.2056 18.7394 24.3832 16.4978 24.3832 13.6777C24.3832 10.8576 22.2056 8.61594 19.466 8.61594C16.7265 8.61594 14.5489 10.8576 14.5489 13.6777C14.5489 16.4978 16.7265 18.7394 19.466 18.7394Z" fill="url(#paint0_linear)"/>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear" x1="6.88952" y1="1.73016" x2="27.8689" y2="33.2773" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#73DFE7"/>
|
||||
<stop offset="1" stop-color="#0095F7"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
BIN
public/img/logos/icon_gitea.png
Normal file
|
After Width: | Height: | Size: 45 KiB |
1
public/img/logos/icon_lightning.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 24 24" fill="none" stroke="#fbbf24" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-zap"><polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"></polygon></svg>
|
||||
|
After Width: | Height: | Size: 279 B |
48
public/img/logos/icon_mastodon.svg
Normal file
@@ -0,0 +1,48 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
width="79"
|
||||
height="79"
|
||||
viewBox="0 0 79 75"
|
||||
version="1.1"
|
||||
id="svg7"
|
||||
sodipodi:docname="icon_mastodon.svg"
|
||||
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs11" />
|
||||
<sodipodi:namedview
|
||||
id="namedview9"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="true"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
showgrid="false"
|
||||
inkscape:zoom="10.329114"
|
||||
inkscape:cx="34.465686"
|
||||
inkscape:cy="39.548407"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1011"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="32"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg7" />
|
||||
<symbol
|
||||
id="logo-symbol-icon">
|
||||
<path
|
||||
d="M74.7135 16.6043C73.6199 8.54587 66.5351 2.19527 58.1366 0.964691C56.7196 0.756754 51.351 0 38.9148 0H38.822C26.3824 0 23.7135 0.756754 22.2966 0.964691C14.1319 2.16118 6.67571 7.86752 4.86669 16.0214C3.99657 20.0369 3.90371 24.4888 4.06535 28.5726C4.29578 34.4289 4.34049 40.275 4.877 46.1075C5.24791 49.9817 5.89495 53.8251 6.81328 57.6088C8.53288 64.5968 15.4938 70.4122 22.3138 72.7848C29.6155 75.259 37.468 75.6697 44.9919 73.971C45.8196 73.7801 46.6381 73.5586 47.4475 73.3063C49.2737 72.7302 51.4164 72.086 52.9915 70.9542C53.0131 70.9384 53.0308 70.9178 53.0433 70.8942C53.0558 70.8706 53.0628 70.8445 53.0637 70.8179V65.1661C53.0634 65.1412 53.0574 65.1167 53.0462 65.0944C53.035 65.0721 53.0189 65.0525 52.9992 65.0371C52.9794 65.0218 52.9564 65.011 52.9318 65.0056C52.9073 65.0002 52.8819 65.0003 52.8574 65.0059C48.0369 66.1472 43.0971 66.7193 38.141 66.7103C29.6118 66.7103 27.3178 62.6981 26.6609 61.0278C26.1329 59.5842 25.7976 58.0784 25.6636 56.5486C25.6622 56.5229 25.667 56.4973 25.6775 56.4738C25.688 56.4502 25.7039 56.4295 25.724 56.4132C25.7441 56.397 25.7678 56.3856 25.7931 56.3801C25.8185 56.3746 25.8448 56.3751 25.8699 56.3816C30.6101 57.5151 35.4693 58.0873 40.3455 58.086C41.5183 58.086 42.6876 58.086 43.8604 58.0553C48.7647 57.919 53.9339 57.6701 58.7591 56.7361C58.8794 56.7123 58.9998 56.6918 59.103 56.6611C66.7139 55.2124 73.9569 50.665 74.6929 39.1501C74.7204 38.6967 74.7892 34.4016 74.7892 33.9312C74.7926 32.3325 75.3085 22.5901 74.7135 16.6043ZM62.9996 45.3371H54.9966V25.9069C54.9966 21.8163 53.277 19.7302 49.7793 19.7302C45.9343 19.7302 44.0083 22.1981 44.0083 27.0727V37.7082H36.0534V27.0727C36.0534 22.1981 34.124 19.7302 30.279 19.7302C26.8019 19.7302 25.0651 21.8163 25.0617 25.9069V45.3371H17.0656V25.3172C17.0656 21.2266 18.1191 17.9769 20.2262 15.568C22.3998 13.1648 25.2509 11.9308 28.7898 11.9308C32.8859 11.9308 35.9812 13.492 38.0447 16.6111L40.036 19.9245L42.0308 16.6111C44.0943 13.492 47.1896 11.9308 51.2788 11.9308C54.8143 11.9308 57.6654 13.1648 59.8459 15.568C61.9529 17.9746 63.0065 21.2243 63.0065 25.3172L62.9996 45.3371Z"
|
||||
fill="currentColor"
|
||||
id="path2"
|
||||
style="fill:#563acc;fill-opacity:1" />
|
||||
</symbol>
|
||||
<use
|
||||
xlink:href="#logo-symbol-icon"
|
||||
style="color:#fff;fill:#563acc;fill-opacity:1"
|
||||
id="use5" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.4 KiB |
1
public/img/logos/icon_mediawiki.svg
Normal file
|
After Width: | Height: | Size: 10 KiB |
114
public/img/logos/icon_xmpp.svg
Normal file
@@ -0,0 +1,114 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Generator: Adobe Illustrator 13.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 14948) -->
|
||||
|
||||
<svg
|
||||
version="1.1"
|
||||
xml:space="preserve"
|
||||
viewBox="0 0 174.98412 127.21901"
|
||||
width="174.98412"
|
||||
height="127.21901"
|
||||
x="0px"
|
||||
y="0px"
|
||||
enable-background="new 0 0 200 200"
|
||||
id="svg36"
|
||||
sodipodi:docname="icon_xmpp.svg"
|
||||
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><defs
|
||||
id="defs40" /><sodipodi:namedview
|
||||
id="namedview38"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
showgrid="false"
|
||||
inkscape:zoom="4.08"
|
||||
inkscape:cx="86.519608"
|
||||
inkscape:cy="84.681373"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1011"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="32"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg36" />
|
||||
|
||||
<linearGradient
|
||||
id="SVGID_right_"
|
||||
y2="1.279e-13"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x2="-1073.2"
|
||||
gradientTransform="translate(1183.188)"
|
||||
y1="126.85"
|
||||
x1="-1073.2">
|
||||
<stop
|
||||
stop-color="#1b3967"
|
||||
offset=".011"
|
||||
id="stop2" />
|
||||
<stop
|
||||
stop-color="#13b5ea"
|
||||
offset=".467"
|
||||
id="stop4" />
|
||||
<stop
|
||||
stop-color="#002b5c"
|
||||
offset=".9945"
|
||||
id="stop6" />
|
||||
</linearGradient>
|
||||
|
||||
<linearGradient
|
||||
id="SVGID_left_"
|
||||
y2="1.279e-13"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x2="-1073.2"
|
||||
gradientTransform="matrix(-1,0,0,1,-1008.204,-10e-4)"
|
||||
y1="126.85"
|
||||
x1="-1073.2">
|
||||
<stop
|
||||
stop-color="#1b3967"
|
||||
offset=".011"
|
||||
id="stop9" />
|
||||
<stop
|
||||
stop-color="#13b5ea"
|
||||
offset=".467"
|
||||
id="stop11" />
|
||||
<stop
|
||||
stop-color="#002b5c"
|
||||
offset=".9945"
|
||||
id="stop13" />
|
||||
</linearGradient>
|
||||
|
||||
<path
|
||||
d="m 138.38912,14.189001 c 0.077,1.313 -1.787,0.968 -1.787,2.293 0,38.551 -46.558,97.366009 -91.687985,108.730009 v 1.639 C 104.86713,121.33101 173.47412,59.051001 174.98412,0.001 l -36.599,14.189001 z"
|
||||
style="fill:url(#SVGID_right_)"
|
||||
id="path16" />
|
||||
<path
|
||||
d="m 120.25712,18.932001 c 0.076,1.313 0.12,2.63 0.12,3.957 0,38.551 -30.69898,90.497009 -75.826985,101.860009 v 1.639 c 59.044005,-2.79 105.809995,-63.024009 105.809995,-109.200009 0,-2.375 -0.128,-4.729 -0.371,-7.056 l -29.73,8.798 z"
|
||||
style="fill:#e96d1f"
|
||||
id="path18" />
|
||||
<path
|
||||
d="m 150.27512,9.583001 -7.61699,2.722 c 0.041,0.962 0.066,2.254 0.066,3.225 0,41.219 -37.271,98.204009 -87.271995,107.120009 -3.24501,1.088 -7.53801,2.077 -10.932,2.931 v 1.638 C 109.77413,121.65901 155.62013,55.353001 150.28012,9.579001 Z"
|
||||
style="fill:#d9541e"
|
||||
id="path20" />
|
||||
|
||||
<path
|
||||
d="m 36.595,14.188001 c -0.077,1.313 1.787,0.968 1.787,2.293 0,38.551 46.558007,97.366009 91.68799,108.730009 v 1.639 C 70.117,121.33001 1.51,59.050001 0,0 l 36.599,14.189001 z"
|
||||
style="fill:url(#SVGID_left_)"
|
||||
id="path22" />
|
||||
<path
|
||||
d="m 54.727,18.931001 c -0.076,1.313 -0.12,2.63 -0.12,3.957 0,38.551 30.698995,90.497009 75.82699,101.860009 v 1.639 C 71.39,123.59701 24.624,63.363001 24.624,17.187001 c 0,-2.375 0.128,-4.729 0.371,-7.056 l 29.73,8.798 z"
|
||||
style="fill:#a0ce67"
|
||||
id="path24" />
|
||||
<path
|
||||
d="m 24.709,9.582001 7.617,2.722 c -0.041,0.962 -0.066,2.254 -0.066,3.225 0,41.219 37.271,98.204009 87.27199,107.120009 3.245,1.088 7.538,2.077 10.932,2.931 v 1.638 C 65.21,121.65801 19.364,55.352001 24.704,9.578001 Z"
|
||||
style="fill:#439639"
|
||||
id="path26" />
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.6 KiB |
@@ -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
@@ -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
|
||||
|
||||
|
||||