Compare commits
36 Commits
32dff9c67f
...
v0.10.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
8f7994d82e
|
|||
|
a7d0e71ab6
|
|||
|
27d9f73c61
|
|||
|
ed3de8b16f
|
|||
|
d7b4c67953
|
|||
| 7489d4a32f | |||
|
ac77e5b7c1
|
|||
|
e544c28105
|
|||
|
4909dac5c2
|
|||
| 3cf4348695 | |||
|
af3da0a26c
|
|||
|
2d32320c7d
|
|||
|
fc2bec6246
|
|||
|
5addd25186
|
|||
|
215d178e69
|
|||
|
5474bf66e7
|
|||
|
ef2a37e2bf
|
|||
|
0e3180602c
|
|||
|
15e2f9b962
|
|||
|
4ae10c9b53
|
|||
| 45137e0cfe | |||
|
717fe93104
|
|||
|
fdac789ccb
|
|||
|
9355dab6b6
|
|||
|
f3676949d2
|
|||
|
79952b73c5
|
|||
| 17c419403e | |||
|
6d06312a5c
|
|||
|
acb399b0b7
|
|||
|
bf20b6467e
|
|||
|
b91d90d75c
|
|||
|
3284bbf6ca
|
|||
|
171b84ee81
|
|||
|
54b01dd282
|
|||
|
e08ea64f47
|
|||
|
8cc2c9554f
|
13
Dockerfile
@@ -1,18 +1,11 @@
|
|||||||
# syntax=docker/dockerfile:1
|
# syntax=docker/dockerfile:1
|
||||||
FROM debian:bullseye-slim as base
|
FROM ruby:3.3.4
|
||||||
|
|
||||||
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
|
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
|
||||||
|
|
||||||
# TODO Remove when upstream Ruby works properly on Apple silicon
|
RUN apt-get update -qq && apt-get install -y --no-install-recommends curl \
|
||||||
RUN apt update && apt install -y build-essential wget autoconf libpq-dev pkg-config
|
ldap-utils tini libvips
|
||||||
RUN wget https://github.com/postmodern/ruby-install/releases/download/v0.9.3/ruby-install-0.9.3.tar.gz \
|
|
||||||
&& tar -xzvf ruby-install-0.9.3.tar.gz \
|
|
||||||
&& cd ruby-install-0.9.3/ \
|
|
||||||
&& make install
|
|
||||||
RUN ruby-install -p https://github.com/ruby/ruby/pull/9371.diff ruby 3.3.0
|
|
||||||
ENV PATH="/opt/rubies/ruby-3.3.0/bin:${PATH}"
|
|
||||||
|
|
||||||
RUN apt-get install -y --no-install-recommends curl ldap-utils tini libvips
|
|
||||||
RUN curl -fsSL https://deb.nodesource.com/setup_lts.x | bash -
|
RUN curl -fsSL https://deb.nodesource.com/setup_lts.x | bash -
|
||||||
RUN apt-get update && apt-get install -y nodejs
|
RUN apt-get update && apt-get install -y nodejs
|
||||||
|
|
||||||
|
|||||||
2
Gemfile
@@ -61,7 +61,7 @@ gem "sentry-rails"
|
|||||||
# Services
|
# Services
|
||||||
gem 'discourse_api'
|
gem 'discourse_api'
|
||||||
gem "lnurl"
|
gem "lnurl"
|
||||||
gem 'manifique'
|
gem 'manifique', '~> 1.1.0'
|
||||||
gem 'nostr', '~> 0.6.0'
|
gem 'nostr', '~> 0.6.0'
|
||||||
|
|
||||||
group :development, :test do
|
group :development, :test do
|
||||||
|
|||||||
@@ -245,7 +245,7 @@ GEM
|
|||||||
net-imap
|
net-imap
|
||||||
net-pop
|
net-pop
|
||||||
net-smtp
|
net-smtp
|
||||||
manifique (1.0.1)
|
manifique (1.1.0)
|
||||||
faraday (~> 2.9.0)
|
faraday (~> 2.9.0)
|
||||||
faraday-follow_redirects (= 0.3.0)
|
faraday-follow_redirects (= 0.3.0)
|
||||||
nokogiri (~> 1.16.0)
|
nokogiri (~> 1.16.0)
|
||||||
@@ -515,7 +515,7 @@ DEPENDENCIES
|
|||||||
listen (~> 3.2)
|
listen (~> 3.2)
|
||||||
lnurl
|
lnurl
|
||||||
lockbox
|
lockbox
|
||||||
manifique
|
manifique (~> 1.1.0)
|
||||||
net-ldap
|
net-ldap
|
||||||
nostr (~> 0.6.0)
|
nostr (~> 0.6.0)
|
||||||
pagy (~> 6.0, >= 6.0.2)
|
pagy (~> 6.0, >= 6.0.2)
|
||||||
|
|||||||
@@ -9,4 +9,12 @@ class Admin::Settings::RegistrationsController < Admin::SettingsController
|
|||||||
success: "Settings saved"
|
success: "Settings saved"
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def setting_params
|
||||||
|
params.require(:setting).permit([
|
||||||
|
:reserved_usernames, default_services: []
|
||||||
|
])
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -9,11 +9,12 @@ class Admin::SettingsController < Admin::BaseController
|
|||||||
changed_keys = []
|
changed_keys = []
|
||||||
|
|
||||||
setting_params.keys.each do |key|
|
setting_params.keys.each do |key|
|
||||||
next if setting_params[key].nil? ||
|
next if clean_param(key).nil? ||
|
||||||
(Setting.send(key).to_s == setting_params[key].strip)
|
(Setting.send(key).to_s == clean_param(key))
|
||||||
|
|
||||||
changed_keys.push(key)
|
changed_keys.push(key)
|
||||||
setting = Setting.new(var: key)
|
setting = Setting.new(var: key)
|
||||||
setting.value = setting_params[key].strip
|
setting.value = clean_param(key)
|
||||||
unless setting.valid?
|
unless setting.valid?
|
||||||
@errors.merge!(setting.errors)
|
@errors.merge!(setting.errors)
|
||||||
end
|
end
|
||||||
@@ -24,7 +25,7 @@ class Admin::SettingsController < Admin::BaseController
|
|||||||
end
|
end
|
||||||
|
|
||||||
changed_keys.each do |key|
|
changed_keys.each do |key|
|
||||||
Setting.send("#{key}=", setting_params[key].strip)
|
Setting.send("#{key}=", clean_param(key))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -37,4 +38,12 @@ class Admin::SettingsController < Admin::BaseController
|
|||||||
def setting_params
|
def setting_params
|
||||||
params.require(:setting).permit(Setting.editable_keys.map(&:to_sym))
|
params.require(:setting).permit(Setting.editable_keys.map(&:to_sym))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def clean_param(key)
|
||||||
|
if Setting.get_field(key)[:type] == :string
|
||||||
|
setting_params[key].strip
|
||||||
|
else
|
||||||
|
setting_params[key]
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
class LnurlpayController < ApplicationController
|
class LnurlpayController < ApplicationController
|
||||||
before_action :check_service_available
|
before_action :check_service_available
|
||||||
before_action :find_user
|
before_action :find_user
|
||||||
before_action :set_cors_access_control_headers, only: [:invoice]
|
before_action :set_cors_access_control_headers
|
||||||
|
|
||||||
MIN_SATS = 10
|
MIN_SATS = 10
|
||||||
MAX_SATS = 1_000_000
|
MAX_SATS = 1_000_000
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ class Services::ChatController < Services::BaseController
|
|||||||
before_action :require_service_available
|
before_action :require_service_available
|
||||||
|
|
||||||
def show
|
def show
|
||||||
@service_enabled = current_user.service_enabled?(:xmpp)
|
@service_enabled = current_user.service_enabled?(:ejabberd)
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|||||||
@@ -8,8 +8,7 @@ class Services::RemotestorageController < Services::BaseController
|
|||||||
# unless current_user.service_enabled?(:remotestorage)
|
# unless current_user.service_enabled?(:remotestorage)
|
||||||
# redirect_to service_remotestorage_info_path
|
# redirect_to service_remotestorage_info_path
|
||||||
# end
|
# end
|
||||||
@rs_auths = current_user.remote_storage_authorizations
|
# @rs_apps_connected = current_user.remote_storage_authorizations.any?
|
||||||
# TODO sort by app name
|
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|||||||
@@ -3,13 +3,18 @@ class Services::RsAuthsController < Services::BaseController
|
|||||||
before_action :require_feature_enabled
|
before_action :require_feature_enabled
|
||||||
before_action :require_service_available
|
before_action :require_service_available
|
||||||
# before_action :require_service_enabled
|
# before_action :require_service_enabled
|
||||||
before_action :find_rs_auth
|
before_action :find_rs_auth, only: [:destroy, :launch_app]
|
||||||
|
|
||||||
|
def index
|
||||||
|
@rs_auths = current_user.remote_storage_authorizations
|
||||||
|
# TODO sort by app name?
|
||||||
|
end
|
||||||
|
|
||||||
def destroy
|
def destroy
|
||||||
@auth.destroy!
|
@auth.destroy!
|
||||||
|
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html do redirect_to services_storage_url, flash: {
|
format.html do redirect_to apps_services_storage_url, flash: {
|
||||||
success: 'App authorization revoked'
|
success: 'App authorization revoked'
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
class WebfingerController < ApplicationController
|
class WebfingerController < WellKnownController
|
||||||
before_action :allow_cross_origin_requests, only: [:show]
|
before_action :allow_cross_origin_requests, only: [:show]
|
||||||
|
|
||||||
layout false
|
|
||||||
|
|
||||||
def show
|
def show
|
||||||
resource = params[:resource]
|
resource = params[:resource]
|
||||||
|
|
||||||
@@ -76,7 +74,7 @@ class WebfingerController < ApplicationController
|
|||||||
end
|
end
|
||||||
|
|
||||||
def remotestorage_link
|
def remotestorage_link
|
||||||
auth_url = new_rs_oauth_url(@username)
|
auth_url = new_rs_oauth_url(@username, host: Setting.accounts_domain)
|
||||||
storage_url = "#{Setting.rs_storage_url}/#{@username}"
|
storage_url = "#{Setting.rs_storage_url}/#{@username}"
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -91,10 +89,4 @@ class WebfingerController < ApplicationController
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
def allow_cross_origin_requests
|
|
||||||
return unless Rails.env.development?
|
|
||||||
headers['Access-Control-Allow-Origin'] = "*"
|
|
||||||
headers['Access-Control-Allow-Methods'] = "GET"
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,14 +1,23 @@
|
|||||||
class WellKnownController < ApplicationController
|
class WellKnownController < ApplicationController
|
||||||
before_action :require_nostr_enabled, only: [ :nostr ]
|
before_action :require_nostr_enabled, only: [ :nostr ]
|
||||||
|
before_action :allow_cross_origin_requests, only: [ :nostr ]
|
||||||
|
|
||||||
|
layout false
|
||||||
|
|
||||||
def nostr
|
def nostr
|
||||||
http_status :unprocessable_entity and return if params[:name].blank?
|
http_status :unprocessable_entity and return if params[:name].blank?
|
||||||
domain = request.headers["X-Forwarded-Host"].presence || Setting.primary_domain
|
domain = request.headers["X-Forwarded-Host"].presence || Setting.primary_domain
|
||||||
relay_url = Setting.nostr_relay_url
|
relay_url = Setting.nostr_relay_url.presence
|
||||||
|
|
||||||
if params[:name] == "_"
|
if params[:name] == "_"
|
||||||
# pubkey for the primary domain without a username (e.g. kosmos.org)
|
if domain == Setting.primary_domain
|
||||||
res = { names: { "_": Setting.nostr_public_key } }
|
# pubkey for the primary domain without a username (e.g. kosmos.org)
|
||||||
|
res = { names: { "_": Setting.nostr_public_key_primary_domain.presence || Setting.nostr_public_key } }
|
||||||
|
else
|
||||||
|
# pubkey for the akkounts domain without a username (e.g. accounts.kosmos.org)
|
||||||
|
res = { names: { "_": Setting.nostr_public_key } }
|
||||||
|
end
|
||||||
|
|
||||||
res[:relays] = { "_" => [ relay_url ] } if relay_url
|
res[:relays] = { "_" => [ relay_url ] } if relay_url
|
||||||
else
|
else
|
||||||
@user = User.where(cn: params[:name], ou: domain).first
|
@user = User.where(cn: params[:name], ou: domain).first
|
||||||
@@ -30,4 +39,9 @@ class WellKnownController < ApplicationController
|
|||||||
def require_nostr_enabled
|
def require_nostr_enabled
|
||||||
http_status :not_found unless Setting.nostr_enabled?
|
http_status :not_found unless Setting.nostr_enabled?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def allow_cross_origin_requests
|
||||||
|
headers['Access-Control-Allow-Origin'] = "*"
|
||||||
|
headers['Access-Control-Allow-Methods'] = "GET"
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,2 +0,0 @@
|
|||||||
module DashboardHelper
|
|
||||||
end
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
module DonationsHelper
|
|
||||||
end
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
module InvitationsHelper
|
|
||||||
end
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
module LnurlpayHelper
|
|
||||||
end
|
|
||||||
12
app/helpers/services_helper.rb
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
module ServicesHelper
|
||||||
|
|
||||||
|
def service_human_name(key, category = :external)
|
||||||
|
SERVICES[category][key][:name] || key.to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
def service_display_name(key, category = :external)
|
||||||
|
SERVICES[category][key][:display_name] ||
|
||||||
|
service_human_name(key, category)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
module SettingsHelper
|
|
||||||
end
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
module SignupHelper
|
|
||||||
end
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
module UsersHelper
|
|
||||||
end
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
module WalletHelper
|
|
||||||
end
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
module WelcomeHelper
|
|
||||||
end
|
|
||||||
@@ -2,8 +2,8 @@ class XmppExchangeContactsJob < ApplicationJob
|
|||||||
queue_as :default
|
queue_as :default
|
||||||
|
|
||||||
def perform(inviter, invitee)
|
def perform(inviter, invitee)
|
||||||
return unless inviter.service_enabled?(:xmpp) &&
|
return unless inviter.service_enabled?(:ejabberd) &&
|
||||||
invitee.service_enabled?(:xmpp) &&
|
invitee.service_enabled?(:ejabberd) &&
|
||||||
inviter.preferences[:xmpp_exchange_contacts_with_invitees]
|
inviter.preferences[:xmpp_exchange_contacts_with_invitees]
|
||||||
|
|
||||||
ejabberd = EjabberdApiClient.new
|
ejabberd = EjabberdApiClient.new
|
||||||
|
|||||||
24
app/models/concerns/settings/btcpay_settings.rb
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
module Settings
|
||||||
|
module BtcpaySettings
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
included do
|
||||||
|
field :btcpay_api_url, type: :string,
|
||||||
|
default: ENV["BTCPAY_API_URL"].presence
|
||||||
|
|
||||||
|
field :btcpay_enabled, type: :boolean,
|
||||||
|
default: ENV["BTCPAY_API_URL"].present?
|
||||||
|
|
||||||
|
field :btcpay_public_url, type: :string,
|
||||||
|
default: ENV["BTCPAY_PUBLIC_URL"].presence
|
||||||
|
|
||||||
|
field :btcpay_store_id, type: :string,
|
||||||
|
default: ENV["BTCPAY_STORE_ID"].presence
|
||||||
|
|
||||||
|
field :btcpay_auth_token, type: :string,
|
||||||
|
default: ENV["BTCPAY_AUTH_TOKEN"].presence
|
||||||
|
|
||||||
|
field :btcpay_publish_wallet_balances, type: :boolean, default: true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
16
app/models/concerns/settings/discourse_settings.rb
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
module Settings
|
||||||
|
module DiscourseSettings
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
included do
|
||||||
|
field :discourse_public_url, type: :string,
|
||||||
|
default: ENV["DISCOURSE_PUBLIC_URL"].presence
|
||||||
|
|
||||||
|
field :discourse_enabled, type: :boolean,
|
||||||
|
default: ENV["DISCOURSE_PUBLIC_URL"].present?
|
||||||
|
|
||||||
|
field :discourse_connect_secret, type: :string,
|
||||||
|
default: ENV["DISCOURSE_CONNECT_SECRET"].presence
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
13
app/models/concerns/settings/drone_ci_settings.rb
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
module Settings
|
||||||
|
module DroneCiSettings
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
included do
|
||||||
|
field :droneci_public_url, type: :string,
|
||||||
|
default: ENV["DRONECI_PUBLIC_URL"].presence
|
||||||
|
|
||||||
|
field :droneci_enabled, type: :boolean,
|
||||||
|
default: ENV["DRONECI_PUBLIC_URL"].present?
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
19
app/models/concerns/settings/ejabberd_settings.rb
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
module Settings
|
||||||
|
module EjabberdSettings
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
included do
|
||||||
|
field :ejabberd_enabled, type: :boolean,
|
||||||
|
default: ENV["EJABBERD_API_URL"].present?
|
||||||
|
|
||||||
|
field :ejabberd_api_url, type: :string,
|
||||||
|
default: ENV["EJABBERD_API_URL"].presence
|
||||||
|
|
||||||
|
field :ejabberd_admin_url, type: :string,
|
||||||
|
default: ENV["EJABBERD_ADMIN_URL"].presence
|
||||||
|
|
||||||
|
field :ejabberd_buddy_roster, type: :string,
|
||||||
|
default: "Buddies"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
28
app/models/concerns/settings/email_settings.rb
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
module Settings
|
||||||
|
module EmailSettings
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
included do
|
||||||
|
field :email_enabled, type: :boolean,
|
||||||
|
default: ENV["EMAIL_SMTP_HOST"].present?
|
||||||
|
|
||||||
|
# field :email_smtp_host, type: :string,
|
||||||
|
# default: ENV["EMAIL_SMTP_HOST"].presence
|
||||||
|
#
|
||||||
|
# field :email_smtp_port, type: :string,
|
||||||
|
# default: ENV["EMAIL_SMTP_PORT"].presence || 587
|
||||||
|
#
|
||||||
|
# field :email_smtp_enable_starttls, type: :string,
|
||||||
|
# default: ENV["EMAIL_SMTP_PORT"].presence || true
|
||||||
|
#
|
||||||
|
# field :email_auth_method, type: :string,
|
||||||
|
# default: ENV["EMAIL_AUTH_METHOD"].presence || "plain"
|
||||||
|
#
|
||||||
|
# field :email_imap_host, type: :string,
|
||||||
|
# default: ENV["EMAIL_IMAP_HOST"].presence
|
||||||
|
#
|
||||||
|
# field :email_imap_port, type: :string,
|
||||||
|
# default: ENV["EMAIL_IMAP_PORT"].presence || 993
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
34
app/models/concerns/settings/general_settings.rb
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
module Settings
|
||||||
|
module GeneralSettings
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
included do
|
||||||
|
field :primary_domain, type: :string,
|
||||||
|
default: ENV["PRIMARY_DOMAIN"].presence
|
||||||
|
|
||||||
|
field :accounts_domain, type: :string,
|
||||||
|
default: ENV["AKKOUNTS_DOMAIN"].presence
|
||||||
|
|
||||||
|
#
|
||||||
|
# Internal services
|
||||||
|
#
|
||||||
|
|
||||||
|
field :redis_url, type: :string,
|
||||||
|
default: ENV["REDIS_URL"] || "redis://localhost:6379/0"
|
||||||
|
|
||||||
|
field :s3_enabled, type: :boolean,
|
||||||
|
default: ENV["S3_ENABLED"] && ENV["S3_ENABLED"].to_s != "false"
|
||||||
|
|
||||||
|
field :sentry_enabled, type: :boolean, readonly: true,
|
||||||
|
default: ENV["SENTRY_DSN"].present?
|
||||||
|
|
||||||
|
#
|
||||||
|
# Registrations
|
||||||
|
#
|
||||||
|
|
||||||
|
field :reserved_usernames, type: :array, default: %w[
|
||||||
|
account accounts donations mail webmaster support
|
||||||
|
]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
13
app/models/concerns/settings/gitea_settings.rb
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
module Settings
|
||||||
|
module GiteaSettings
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
included do
|
||||||
|
field :gitea_public_url, type: :string,
|
||||||
|
default: ENV["GITEA_PUBLIC_URL"].presence
|
||||||
|
|
||||||
|
field :gitea_enabled, type: :boolean,
|
||||||
|
default: ENV["GITEA_PUBLIC_URL"].present?
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
25
app/models/concerns/settings/lightning_network_settings.rb
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
module Settings
|
||||||
|
module LightningNetworkSettings
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
included do
|
||||||
|
field :lndhub_api_url, type: :string,
|
||||||
|
default: ENV["LNDHUB_API_URL"].presence
|
||||||
|
|
||||||
|
field :lndhub_enabled, type: :boolean,
|
||||||
|
default: ENV["LNDHUB_API_URL"].present?
|
||||||
|
|
||||||
|
field :lndhub_admin_token, type: :string,
|
||||||
|
default: ENV["LNDHUB_ADMIN_TOKEN"].presence
|
||||||
|
|
||||||
|
field :lndhub_admin_enabled, type: :boolean,
|
||||||
|
default: ENV["LNDHUB_ADMIN_UI"] || false
|
||||||
|
|
||||||
|
field :lndhub_public_key, type: :string,
|
||||||
|
default: (ENV["LNDHUB_PUBLIC_KEY"] || "")
|
||||||
|
|
||||||
|
field :lndhub_keysend_enabled, type: :boolean,
|
||||||
|
default: -> { self.lndhub_public_key.present? }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
16
app/models/concerns/settings/mastodon_settings.rb
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
module Settings
|
||||||
|
module MastodonSettings
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
included do
|
||||||
|
field :mastodon_public_url, type: :string,
|
||||||
|
default: ENV["MASTODON_PUBLIC_URL"].presence
|
||||||
|
|
||||||
|
field :mastodon_enabled, type: :boolean,
|
||||||
|
default: ENV["MASTODON_PUBLIC_URL"].present?
|
||||||
|
|
||||||
|
field :mastodon_address_domain, type: :string,
|
||||||
|
default: ENV["MASTODON_ADDRESS_DOMAIN"].presence || self.primary_domain
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
13
app/models/concerns/settings/media_wiki_settings.rb
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
module Settings
|
||||||
|
module MediaWikiSettings
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
included do
|
||||||
|
field :mediawiki_public_url, type: :string,
|
||||||
|
default: ENV["MEDIAWIKI_PUBLIC_URL"].presence
|
||||||
|
|
||||||
|
field :mediawiki_enabled, type: :boolean,
|
||||||
|
default: ENV["MEDIAWIKI_PUBLIC_URL"].present?
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
25
app/models/concerns/settings/nostr_settings.rb
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
module Settings
|
||||||
|
module NostrSettings
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
included do
|
||||||
|
field :nostr_enabled, type: :boolean,
|
||||||
|
default: ENV["NOSTR_PRIVATE_KEY"].present?
|
||||||
|
|
||||||
|
field :nostr_private_key, type: :string,
|
||||||
|
default: ENV["NOSTR_PRIVATE_KEY"].presence
|
||||||
|
|
||||||
|
field :nostr_public_key, type: :string,
|
||||||
|
default: ENV["NOSTR_PUBLIC_KEY"].presence
|
||||||
|
|
||||||
|
field :nostr_public_key_primary_domain, type: :string,
|
||||||
|
default: ENV["NOSTR_PUBLIC_KEY_PRIMARY_DOMAIN"].presence
|
||||||
|
|
||||||
|
field :nostr_relay_url, type: :string,
|
||||||
|
default: ENV["NOSTR_RELAY_URL"].presence
|
||||||
|
|
||||||
|
field :nostr_zaps_relay_limit, type: :integer,
|
||||||
|
default: 12
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
9
app/models/concerns/settings/open_collective_settings.rb
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
module Settings
|
||||||
|
module OpenCollectiveSettings
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
included do
|
||||||
|
field :opencollective_enabled, type: :boolean, default: true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
16
app/models/concerns/settings/remote_storage_settings.rb
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
module Settings
|
||||||
|
module RemoteStorageSettings
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
included do
|
||||||
|
field :remotestorage_enabled, type: :boolean,
|
||||||
|
default: ENV["RS_STORAGE_URL"].present?
|
||||||
|
|
||||||
|
field :rs_storage_url, type: :string,
|
||||||
|
default: ENV["RS_STORAGE_URL"].presence
|
||||||
|
|
||||||
|
field :rs_redis_url, type: :string,
|
||||||
|
default: ENV["RS_REDIS_URL"] || "redis://localhost:6379/1"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
11
app/models/concerns/settings/xmpp_settings.rb
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
module Settings
|
||||||
|
module XmppSettings
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
included do
|
||||||
|
field :xmpp_default_rooms, type: :array, default: []
|
||||||
|
field :xmpp_autojoin_default_rooms, type: :boolean, default: false
|
||||||
|
field :xmpp_notifications_from_address, type: :string, default: primary_domain
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -2,226 +2,30 @@
|
|||||||
class Setting < RailsSettings::Base
|
class Setting < RailsSettings::Base
|
||||||
cache_prefix { "v1" }
|
cache_prefix { "v1" }
|
||||||
|
|
||||||
field :primary_domain, type: :string,
|
Dir[Rails.root.join('app', 'models', 'concerns', 'settings', '*.rb')].each do |file|
|
||||||
default: ENV["PRIMARY_DOMAIN"].presence
|
require file
|
||||||
|
|
||||||
field :accounts_domain, type: :string,
|
|
||||||
default: ENV["AKKOUNTS_DOMAIN"].presence
|
|
||||||
|
|
||||||
#
|
|
||||||
# Internal services
|
|
||||||
#
|
|
||||||
|
|
||||||
field :redis_url, type: :string,
|
|
||||||
default: ENV["REDIS_URL"] || "redis://localhost:6379/0"
|
|
||||||
|
|
||||||
field :s3_enabled, type: :boolean,
|
|
||||||
default: ENV["S3_ENABLED"] && ENV["S3_ENABLED"].to_s != "false"
|
|
||||||
|
|
||||||
#
|
|
||||||
# Registrations
|
|
||||||
#
|
|
||||||
|
|
||||||
field :reserved_usernames, type: :array, default: %w[
|
|
||||||
account accounts donations mail webmaster support
|
|
||||||
]
|
|
||||||
|
|
||||||
#
|
|
||||||
# XMPP
|
|
||||||
#
|
|
||||||
|
|
||||||
field :xmpp_default_rooms, type: :array, default: []
|
|
||||||
field :xmpp_autojoin_default_rooms, type: :boolean, default: false
|
|
||||||
field :xmpp_notifications_from_address, type: :string, default: primary_domain
|
|
||||||
|
|
||||||
#
|
|
||||||
# Sentry
|
|
||||||
#
|
|
||||||
|
|
||||||
field :sentry_enabled, type: :boolean, readonly: true,
|
|
||||||
default: ENV["SENTRY_DSN"].present?
|
|
||||||
|
|
||||||
#
|
|
||||||
# BTCPay Server
|
|
||||||
#
|
|
||||||
|
|
||||||
field :btcpay_api_url, type: :string,
|
|
||||||
default: ENV["BTCPAY_API_URL"].presence
|
|
||||||
|
|
||||||
field :btcpay_enabled, type: :boolean,
|
|
||||||
default: ENV["BTCPAY_API_URL"].present?
|
|
||||||
|
|
||||||
field :btcpay_public_url, type: :string,
|
|
||||||
default: ENV["BTCPAY_PUBLIC_URL"].presence
|
|
||||||
|
|
||||||
field :btcpay_store_id, type: :string,
|
|
||||||
default: ENV["BTCPAY_STORE_ID"].presence
|
|
||||||
|
|
||||||
field :btcpay_auth_token, type: :string,
|
|
||||||
default: ENV["BTCPAY_AUTH_TOKEN"].presence
|
|
||||||
|
|
||||||
field :btcpay_publish_wallet_balances, type: :boolean, default: true
|
|
||||||
|
|
||||||
#
|
|
||||||
# Discourse
|
|
||||||
#
|
|
||||||
|
|
||||||
field :discourse_public_url, type: :string,
|
|
||||||
default: ENV["DISCOURSE_PUBLIC_URL"].presence
|
|
||||||
|
|
||||||
field :discourse_enabled, type: :boolean,
|
|
||||||
default: ENV["DISCOURSE_PUBLIC_URL"].present?
|
|
||||||
|
|
||||||
field :discourse_connect_secret, type: :string,
|
|
||||||
default: ENV["DISCOURSE_CONNECT_SECRET"].presence
|
|
||||||
|
|
||||||
#
|
|
||||||
# Drone CI
|
|
||||||
#
|
|
||||||
|
|
||||||
field :droneci_public_url, type: :string,
|
|
||||||
default: ENV["DRONECI_PUBLIC_URL"].presence
|
|
||||||
|
|
||||||
field :droneci_enabled, type: :boolean,
|
|
||||||
default: ENV["DRONECI_PUBLIC_URL"].present?
|
|
||||||
|
|
||||||
#
|
|
||||||
# ejabberd
|
|
||||||
#
|
|
||||||
|
|
||||||
field :ejabberd_enabled, type: :boolean,
|
|
||||||
default: ENV["EJABBERD_API_URL"].present?
|
|
||||||
|
|
||||||
field :ejabberd_api_url, type: :string,
|
|
||||||
default: ENV["EJABBERD_API_URL"].presence
|
|
||||||
|
|
||||||
field :ejabberd_admin_url, type: :string,
|
|
||||||
default: ENV["EJABBERD_ADMIN_URL"].presence
|
|
||||||
|
|
||||||
field :ejabberd_buddy_roster, type: :string,
|
|
||||||
default: "Buddies"
|
|
||||||
|
|
||||||
#
|
|
||||||
# Gitea
|
|
||||||
#
|
|
||||||
|
|
||||||
field :gitea_public_url, type: :string,
|
|
||||||
default: ENV["GITEA_PUBLIC_URL"].presence
|
|
||||||
|
|
||||||
field :gitea_enabled, type: :boolean,
|
|
||||||
default: ENV["GITEA_PUBLIC_URL"].present?
|
|
||||||
|
|
||||||
#
|
|
||||||
# Lightning Network
|
|
||||||
#
|
|
||||||
|
|
||||||
field :lndhub_api_url, type: :string,
|
|
||||||
default: ENV["LNDHUB_API_URL"].presence
|
|
||||||
|
|
||||||
field :lndhub_enabled, type: :boolean,
|
|
||||||
default: ENV["LNDHUB_API_URL"].present?
|
|
||||||
|
|
||||||
field :lndhub_admin_token, type: :string,
|
|
||||||
default: ENV["LNDHUB_ADMIN_TOKEN"].presence
|
|
||||||
|
|
||||||
field :lndhub_admin_enabled, type: :boolean,
|
|
||||||
default: ENV["LNDHUB_ADMIN_UI"] || false
|
|
||||||
|
|
||||||
field :lndhub_public_key, type: :string,
|
|
||||||
default: (ENV["LNDHUB_PUBLIC_KEY"] || "")
|
|
||||||
|
|
||||||
field :lndhub_keysend_enabled, type: :boolean,
|
|
||||||
default: -> { self.lndhub_public_key.present? }
|
|
||||||
|
|
||||||
#
|
|
||||||
# Mastodon
|
|
||||||
#
|
|
||||||
|
|
||||||
field :mastodon_public_url, type: :string,
|
|
||||||
default: ENV["MASTODON_PUBLIC_URL"].presence
|
|
||||||
|
|
||||||
field :mastodon_enabled, type: :boolean,
|
|
||||||
default: ENV["MASTODON_PUBLIC_URL"].present?
|
|
||||||
|
|
||||||
field :mastodon_address_domain, type: :string,
|
|
||||||
default: ENV["MASTODON_ADDRESS_DOMAIN"].presence || self.primary_domain
|
|
||||||
|
|
||||||
#
|
|
||||||
# MediaWiki
|
|
||||||
#
|
|
||||||
|
|
||||||
field :mediawiki_public_url, type: :string,
|
|
||||||
default: ENV["MEDIAWIKI_PUBLIC_URL"].presence
|
|
||||||
|
|
||||||
field :mediawiki_enabled, type: :boolean,
|
|
||||||
default: ENV["MEDIAWIKI_PUBLIC_URL"].present?
|
|
||||||
|
|
||||||
#
|
|
||||||
# Nostr
|
|
||||||
#
|
|
||||||
|
|
||||||
field :nostr_enabled, type: :boolean,
|
|
||||||
default: ENV["NOSTR_PRIVATE_KEY"].present?
|
|
||||||
|
|
||||||
field :nostr_private_key, type: :string,
|
|
||||||
default: ENV["NOSTR_PRIVATE_KEY"].presence
|
|
||||||
|
|
||||||
field :nostr_public_key, type: :string,
|
|
||||||
default: ENV["NOSTR_PUBLIC_KEY"].presence
|
|
||||||
|
|
||||||
field :nostr_relay_url, type: :string,
|
|
||||||
default: ENV["NOSTR_RELAY_URL"].presence
|
|
||||||
|
|
||||||
field :nostr_zaps_relay_limit, type: :integer,
|
|
||||||
default: 12
|
|
||||||
|
|
||||||
#
|
|
||||||
# OpenCollective
|
|
||||||
#
|
|
||||||
|
|
||||||
field :opencollective_enabled, type: :boolean, default: true
|
|
||||||
|
|
||||||
#
|
|
||||||
# RemoteStorage
|
|
||||||
#
|
|
||||||
|
|
||||||
field :remotestorage_enabled, type: :boolean,
|
|
||||||
default: ENV["RS_STORAGE_URL"].present?
|
|
||||||
|
|
||||||
field :rs_storage_url, type: :string,
|
|
||||||
default: ENV["RS_STORAGE_URL"].presence
|
|
||||||
|
|
||||||
field :rs_redis_url, type: :string,
|
|
||||||
default: ENV["RS_REDIS_URL"] || "redis://localhost:6379/1"
|
|
||||||
|
|
||||||
|
|
||||||
#
|
|
||||||
# E-Mail Service
|
|
||||||
#
|
|
||||||
|
|
||||||
field :email_enabled, type: :boolean,
|
|
||||||
default: ENV["EMAIL_SMTP_HOST"].present?
|
|
||||||
|
|
||||||
# field :email_smtp_host, type: :string,
|
|
||||||
# default: ENV["EMAIL_SMTP_HOST"].presence
|
|
||||||
#
|
|
||||||
# field :email_smtp_port, type: :string,
|
|
||||||
# default: ENV["EMAIL_SMTP_PORT"].presence || 587
|
|
||||||
#
|
|
||||||
# field :email_smtp_enable_starttls, type: :string,
|
|
||||||
# default: ENV["EMAIL_SMTP_PORT"].presence || true
|
|
||||||
#
|
|
||||||
# field :email_auth_method, type: :string,
|
|
||||||
# default: ENV["EMAIL_AUTH_METHOD"].presence || "plain"
|
|
||||||
#
|
|
||||||
# field :email_imap_host, type: :string,
|
|
||||||
# default: ENV["EMAIL_IMAP_HOST"].presence
|
|
||||||
#
|
|
||||||
# field :email_imap_port, type: :string,
|
|
||||||
# default: ENV["EMAIL_IMAP_PORT"].presence || 993
|
|
||||||
|
|
||||||
def self.default_services
|
|
||||||
# TODO Make configurable from respective service settings page
|
|
||||||
%w[ discourse gitea mastodon mediawiki xmpp ]
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
include Settings::GeneralSettings
|
||||||
|
include Settings::BtcpaySettings
|
||||||
|
include Settings::DiscourseSettings
|
||||||
|
include Settings::DroneCiSettings
|
||||||
|
include Settings::EjabberdSettings
|
||||||
|
include Settings::EmailSettings
|
||||||
|
include Settings::GiteaSettings
|
||||||
|
include Settings::LightningNetworkSettings
|
||||||
|
include Settings::MastodonSettings
|
||||||
|
include Settings::MediaWikiSettings
|
||||||
|
include Settings::NostrSettings
|
||||||
|
include Settings::OpenCollectiveSettings
|
||||||
|
include Settings::RemoteStorageSettings
|
||||||
|
include Settings::XmppSettings
|
||||||
|
|
||||||
|
def self.available_services
|
||||||
|
known_services = SERVICES[:external].keys
|
||||||
|
known_services.select {|s| Setting.send "#{s}_enabled?" }
|
||||||
|
end
|
||||||
|
|
||||||
|
field :default_services, type: :array,
|
||||||
|
default: self.available_services
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -180,14 +180,14 @@ class User < ApplicationRecord
|
|||||||
def enable_service(service)
|
def enable_service(service)
|
||||||
current_services = services_enabled
|
current_services = services_enabled
|
||||||
new_services = Array(service).map(&:to_s)
|
new_services = Array(service).map(&:to_s)
|
||||||
services = (current_services + new_services).uniq
|
services = (current_services + new_services).uniq.sort
|
||||||
ldap.replace_attribute(dn, :serviceEnabled, services)
|
ldap.replace_attribute(dn, :serviceEnabled, services)
|
||||||
end
|
end
|
||||||
|
|
||||||
def disable_service(service)
|
def disable_service(service)
|
||||||
current_services = services_enabled
|
current_services = services_enabled
|
||||||
disabled_services = Array(service).map(&:to_s)
|
disabled_services = Array(service).map(&:to_s)
|
||||||
services = (current_services - disabled_services).uniq
|
services = (current_services - disabled_services).uniq.sort
|
||||||
ldap.replace_attribute(dn, :serviceEnabled, services)
|
ldap.replace_attribute(dn, :serviceEnabled, services)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -9,18 +9,36 @@
|
|||||||
<%= render partial: "admin/settings/errors", locals: { errors: @errors } %>
|
<%= render partial: "admin/settings/errors", locals: { errors: @errors } %>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<label class="block">
|
<ul role="list">
|
||||||
<p class="font-bold mb-1">Reserved usernames</p>
|
<%= render FormElements::FieldsetComponent.new(
|
||||||
<p class="text-gray-500">
|
title: "Reserved usernames",
|
||||||
These usernames cannot be registered as accounts:
|
description: "These usernames cannot be registered as accounts."
|
||||||
</p>
|
) do %>
|
||||||
<%= f.text_area :reserved_usernames,
|
<%= f.text_area :reserved_usernames,
|
||||||
value: Setting.reserved_usernames.join("\n"),
|
value: Setting.reserved_usernames.join("\n"),
|
||||||
class: "h-44 mb-2" %>
|
class: "h-44 w-60" %>
|
||||||
<p class="text-sm text-gray-500">
|
<p class="text-sm text-gray-500">
|
||||||
One username per line
|
One username per line
|
||||||
</p>
|
</p>
|
||||||
</label>
|
<% end %>
|
||||||
|
<li>
|
||||||
|
<p class="font-bold mb-1">Default services</p>
|
||||||
|
<p class="text-gray-500">
|
||||||
|
These services are enabled for new users by default after signup.
|
||||||
|
</p>
|
||||||
|
<div class="flex flex-wrap gap-x-6 gap-y-2">
|
||||||
|
<% Setting.available_services.each do |option| %>
|
||||||
|
<div class="md:inline-block">
|
||||||
|
<%= f.check_box :default_services,
|
||||||
|
{ multiple: true, checked: Setting.default_services.include?(option),
|
||||||
|
class: "h-4 w-4 rounded border-gray-300 text-blue-600 focus:ring-blue-600 mr-0.5" },
|
||||||
|
option, nil %>
|
||||||
|
<%= f.label "default_services_#{option.parameterize}", service_human_name(option) %>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
|
|||||||
@@ -19,6 +19,11 @@
|
|||||||
title: "Public key",
|
title: "Public key",
|
||||||
description: "The corresponding public key of the accounts service"
|
description: "The corresponding public key of the accounts service"
|
||||||
) %>
|
) %>
|
||||||
|
<%= render FormElements::FieldsetResettableSettingComponent.new(
|
||||||
|
key: :nostr_public_key_primary_domain,
|
||||||
|
title: "Public key for primary domain (NIP-05)",
|
||||||
|
description: "(optional) A different pubkey to announce for the _@#{Setting.primary_domain} Nostr address"
|
||||||
|
) %>
|
||||||
<%= render FormElements::FieldsetResettableSettingComponent.new(
|
<%= render FormElements::FieldsetResettableSettingComponent.new(
|
||||||
key: :nostr_relay_url,
|
key: :nostr_relay_url,
|
||||||
title: "Relay URL",
|
title: "Relay URL",
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
<h3>RemoteStorage</h3>
|
<h3>RemoteStorage</h3>
|
||||||
<p class="text-red-600 mb-8">Feature currently in development.</p>
|
|
||||||
<ul role="list">
|
<ul role="list">
|
||||||
<%= render FormElements::FieldsetToggleComponent.new(
|
<%= render FormElements::FieldsetToggleComponent.new(
|
||||||
form: f,
|
form: f,
|
||||||
|
|||||||
@@ -43,7 +43,7 @@
|
|||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
We have run two 6-month trials so far, with the next trial period
|
We have run two 6-month trials so far, with the next trial period
|
||||||
starting sometime in Q2 2024. Watch your email for notifications about it!
|
starting sometime soon. Watch your email for notifications about it!
|
||||||
</p>
|
</p>
|
||||||
</section>
|
</section>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|||||||
@@ -41,15 +41,16 @@
|
|||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
<% if Setting.discourse_enabled? %>
|
<% if Setting.remotestorage_enabled? &&
|
||||||
|
Flipper.enabled?(:remotestorage, current_user) %>
|
||||||
<div class="border border-gray-300 rounded-md hover:border-gray-400
|
<div class="border border-gray-300 rounded-md hover:border-gray-400
|
||||||
bg-[length:80%] bg-center bg-no-repeat
|
bg-[length:80%] bg-[center_top_-156px] bg-no-repeat
|
||||||
bg-[url(/img/logos/icon_discourse.svg)]">
|
bg-[url(/img/logos/icon_remotestorage.svg)]">
|
||||||
<%= link_to "#{Setting.discourse_public_url}/session/sso?return_path=/",
|
<%= link_to services_storage_path,
|
||||||
class: "block h-full px-6 py-6 rounded-md" do %>
|
class: "block h-full px-6 py-6 rounded-md" do %>
|
||||||
<h3 class="mb-3.5">Discourse</h3>
|
<h3 class="mb-3.5">Storage</h3>
|
||||||
<p class="text-gray-600">
|
<p class="text-gray-600">
|
||||||
Community forums and support/help site
|
Sync your data between apps and devices
|
||||||
</p>
|
</p>
|
||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
@@ -67,16 +68,15 @@
|
|||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
<% if Setting.remotestorage_enabled? &&
|
<% if Setting.discourse_enabled? %>
|
||||||
Flipper.enabled?(:remotestorage, current_user) %>
|
|
||||||
<div class="border border-gray-300 rounded-md hover:border-gray-400
|
<div class="border border-gray-300 rounded-md hover:border-gray-400
|
||||||
bg-[length:80%] bg-[center_top_-156px] bg-no-repeat
|
bg-[length:80%] bg-center bg-no-repeat
|
||||||
bg-[url(/img/logos/icon_remotestorage.svg)]">
|
bg-[url(/img/logos/icon_discourse.svg)]">
|
||||||
<%= link_to services_storage_path,
|
<%= link_to "#{Setting.discourse_public_url}/session/sso?return_path=/",
|
||||||
class: "block h-full px-6 py-6 rounded-md" do %>
|
class: "block h-full px-6 py-6 rounded-md" do %>
|
||||||
<h3 class="mb-3.5">Storage</h3>
|
<h3 class="mb-3.5">Discourse</h3>
|
||||||
<p class="text-gray-600">
|
<p class="text-gray-600">
|
||||||
Sync your data between apps and devices
|
Community forums and support/help site
|
||||||
</p>
|
</p>
|
||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -2,15 +2,162 @@
|
|||||||
|
|
||||||
<%= render MainSimpleComponent.new do %>
|
<%= render MainSimpleComponent.new do %>
|
||||||
<section>
|
<section>
|
||||||
<h3 class="mb-10">Connected Apps</h3>
|
<p class="mb-6">
|
||||||
<% if @rs_auths.any? %>
|
Store and synchronize your app data across different devices.
|
||||||
<div class="w-full grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-y-10 gap-x-12">
|
</p>
|
||||||
<% @rs_auths.each do |auth| %>
|
</section>
|
||||||
<%= render RsAuthComponent.new(auth: auth) %>
|
|
||||||
<% end %>
|
<%= render partial: "shared/tabnav_remotestorage" %>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h3>Your Storage Address</h3>
|
||||||
|
<p class="mb-6">
|
||||||
|
In order to connect an app to your storage account, give it your address:
|
||||||
|
</p>
|
||||||
|
<p data-controller="clipboard" class="flex items-center gap-1 sm:w-2/5">
|
||||||
|
<img src="/img/logos/icon_remotestorage.svg" class="inline-block h-6 w-6 mr-1">
|
||||||
|
<input type="text" id="user_address" class="grow"
|
||||||
|
value=<%= current_user.address %> disabled="disabled"
|
||||||
|
data-clipboard-target="source" />
|
||||||
|
<button id="copy-user-address" class="btn-md btn-icon btn-outline shrink-0"
|
||||||
|
data-clipboard-target="trigger" data-action="clipboard#copy"
|
||||||
|
title="Copy to clipboard">
|
||||||
|
<span class="content-initial">
|
||||||
|
<%= render partial: "icons/copy", locals: { custom_class: "text-blue-600 h-4 w-4 inline" } %>
|
||||||
|
</span>
|
||||||
|
<span class="content-active hidden">
|
||||||
|
<%= render partial: "icons/check", locals: { custom_class: "text-blue-600 h-4 w-4 inline" } %>
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h3>Compatible Apps</h3>
|
||||||
|
<p>
|
||||||
|
Your Storage account is based on a new open standard called
|
||||||
|
<a href="https://remotestorage.io" target="_blank">
|
||||||
|
<img src="/img/logos/icon_remotestorage.svg" class="h-4 w-4 inline">
|
||||||
|
<strong>remoteStorage</strong>
|
||||||
|
</a>, which is not yet widely supported. Look
|
||||||
|
for the remoteStorage icon, or check the Sync settings in apps.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
If you want your favorite apps to support syncing data with your own
|
||||||
|
Storage account, let the developers know! All relevant information is
|
||||||
|
available on the <a href="https://remotestorage.io"
|
||||||
|
target="_blank" class="ks-text-link">remoteStorage website</a>.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h3>Recommended Apps</h3>
|
||||||
|
<div data-controller="tabs"
|
||||||
|
data-tabs-active-tab-class="-mb-px border-gray-200 border-l border-t border-r rounded-t text-indigo-600 hover:text-indigo-600"
|
||||||
|
data-tabs-inactive-tab-class="text-gray-500 hover:text-gray-700"
|
||||||
|
class="mb-12">
|
||||||
|
<select data-action="tabs#change" data-tabs-target="select"
|
||||||
|
class="block w-full mb-8 sm:hidden">
|
||||||
|
<option>Productivity</option>
|
||||||
|
<option>Bookmarks</option>
|
||||||
|
<option>Reading</option>
|
||||||
|
<option>File sharing</option>
|
||||||
|
<option>Learning</option>
|
||||||
|
</select>
|
||||||
|
<ul class="hidden sm:flex list-reset mb-8 border-gray-200 border-b">
|
||||||
|
<li class="mr-2" data-tabs-target="tab" data-action="click->tabs#change:prevent">
|
||||||
|
<a href="#" class="bg-white inline-block py-2 px-4 font-semibold no-underline">
|
||||||
|
Productivity
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="mr-2" data-tabs-target="tab" data-action="click->tabs#change:prevent">
|
||||||
|
<a href="#" class="bg-white inline-block py-2 px-4 font-semibold no-underline">
|
||||||
|
Bookmarks
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="mr-2" data-tabs-target="tab" data-action="click->tabs#change:prevent">
|
||||||
|
<a href="#" class="bg-white inline-block py-2 px-4 font-semibold no-underline">
|
||||||
|
Reading
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="mr-2" data-tabs-target="tab" data-action="click->tabs#change:prevent">
|
||||||
|
<a href="#" class="bg-white inline-block py-2 px-4 font-semibold no-underline">
|
||||||
|
File sharing
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="mr-2" data-tabs-target="tab" data-action="click->tabs#change:prevent">
|
||||||
|
<a href="#" class="bg-white inline-block py-2 px-4 font-semibold no-underline">
|
||||||
|
Learning
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div class="hidden grid grid-cols-1 gap-6" data-tabs-target="panel">
|
||||||
|
<%= render AppInfoComponent.new(
|
||||||
|
name: "Hyperdraft",
|
||||||
|
description: "Create text notes and (optionally) turn them into a website",
|
||||||
|
icon_path: "/img/app_icons/hyperdraft.png",
|
||||||
|
links: [
|
||||||
|
["Website", "https://hyperdraft.rosano.ca"],
|
||||||
|
]
|
||||||
|
) %>
|
||||||
|
<%= render AppInfoComponent.new(
|
||||||
|
name: "Notes Together",
|
||||||
|
description: "A powerful note-taking app, with support for attaching images and other files",
|
||||||
|
icon_path: "/img/app_icons/notes-together.png",
|
||||||
|
links: [
|
||||||
|
["Web App", "https://notestogether.hominidsoftware.com"],
|
||||||
|
]
|
||||||
|
) %>
|
||||||
|
<%= render AppInfoComponent.new(
|
||||||
|
name: "Papiers",
|
||||||
|
description: "A simple note-taking app",
|
||||||
|
icon_path: "/img/app_icons/papiers.png",
|
||||||
|
links: [
|
||||||
|
["Web App", "https://papiers.gitlab.io"],
|
||||||
|
]
|
||||||
|
) %>
|
||||||
|
</div>
|
||||||
|
<div class="hidden grid grid-cols-1 gap-6" data-tabs-target="panel">
|
||||||
|
<%= render AppInfoComponent.new(
|
||||||
|
name: "Webmarks",
|
||||||
|
description: "Archive your bookmarks in your remote storage",
|
||||||
|
icon_path: "/img/app_icons/webmarks.png",
|
||||||
|
links: [
|
||||||
|
["Web App", "https://webmarks.5apps.com"],
|
||||||
|
]
|
||||||
|
) %>
|
||||||
|
</div>
|
||||||
|
<div class="hidden grid grid-cols-1 gap-6" data-tabs-target="panel">
|
||||||
|
<%= render AppInfoComponent.new(
|
||||||
|
name: "Pétrolette",
|
||||||
|
description: "A news aggregator that syncs with your remote storage",
|
||||||
|
icon_path: "/img/app_icons/petrolette.png",
|
||||||
|
links: [
|
||||||
|
["Web App", "https://petrolette.space"],
|
||||||
|
]
|
||||||
|
) %>
|
||||||
|
</div>
|
||||||
|
<div class="hidden grid grid-cols-1 gap-6" data-tabs-target="panel">
|
||||||
|
<%= render AppInfoComponent.new(
|
||||||
|
name: "Sharesome",
|
||||||
|
description: "Quickly and easily share files from your remote storage",
|
||||||
|
icon_path: "/img/app_icons/sharesome.png",
|
||||||
|
links: [
|
||||||
|
["Web App", "https://sharesome.5apps.com"],
|
||||||
|
]
|
||||||
|
) %>
|
||||||
|
</div>
|
||||||
|
<div class="hidden grid grid-cols-1 gap-6" data-tabs-target="panel">
|
||||||
|
<%= render AppInfoComponent.new(
|
||||||
|
name: "Kommit",
|
||||||
|
description: "Create flashcards and learn them with spaced-repetition",
|
||||||
|
icon_path: "/img/app_icons/kommit.png",
|
||||||
|
links: [
|
||||||
|
["Website", "https://kommit.rosano.ca"],
|
||||||
|
]
|
||||||
|
) %>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<% else %>
|
|
||||||
<p>No apps connected yet.</p>
|
|
||||||
<% end %>
|
|
||||||
</section>
|
</section>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|||||||
33
app/views/services/rs_auths/index.html.erb
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
<%= render HeaderComponent.new(title: "Storage") %>
|
||||||
|
|
||||||
|
<%= render MainSimpleComponent.new do %>
|
||||||
|
<section>
|
||||||
|
<p class="mb-6">
|
||||||
|
Store and synchronize your app data across different devices.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<%= render partial: "shared/tabnav_remotestorage" %>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<% if @rs_auths.any? %>
|
||||||
|
<div class="w-full grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-y-10 gap-x-12 mt-4">
|
||||||
|
<% @rs_auths.each do |auth| %>
|
||||||
|
<%= render RsAuthComponent.new(auth: auth) %>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
<% else %>
|
||||||
|
<div class="text-center">
|
||||||
|
<p class="mt-4 mb-12 inline-flex align-center items-center">
|
||||||
|
<%= image_tag("/img/illustrations/undraw_friends_r511.svg", class: 'h-48') %>
|
||||||
|
</p>
|
||||||
|
<h3>
|
||||||
|
No apps connected
|
||||||
|
</h3>
|
||||||
|
<p class="text-gray-500">
|
||||||
|
When connected, your apps will show up here.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
</section>
|
||||||
|
<% end %>
|
||||||
14
app/views/shared/_tabnav_remotestorage.html.erb
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<section>
|
||||||
|
<div class="border-b border-gray-200">
|
||||||
|
<nav class="-mb-px flex" aria-label="Tabs">
|
||||||
|
<%= render TabnavLinkComponent.new(
|
||||||
|
name: "Info", path: services_storage_path,
|
||||||
|
active: current_page?(services_storage_path)
|
||||||
|
) %>
|
||||||
|
<%= render TabnavLinkComponent.new(
|
||||||
|
name: "Connected Apps", path: apps_services_storage_path,
|
||||||
|
active: current_page?(apps_services_storage_path)
|
||||||
|
) %>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
2
config/initializers/service_details.rb
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
config_path = Rails.root.join('config', 'services.yml')
|
||||||
|
SERVICES = YAML.load_file(config_path).deep_symbolize_keys.with_indifferent_access
|
||||||
@@ -48,7 +48,8 @@ Rails.application.routes.draw do
|
|||||||
end
|
end
|
||||||
|
|
||||||
resource :storage, controller: 'remotestorage', only: [:show] do
|
resource :storage, controller: 'remotestorage', only: [:show] do
|
||||||
resources :rs_auths, only: [:destroy] do
|
get :apps, to: "rs_auths#index"
|
||||||
|
resources :rs_auths, only: [:index, :destroy] do
|
||||||
member do
|
member do
|
||||||
get :revoke, to: 'rs_auths#destroy'
|
get :revoke, to: 'rs_auths#destroy'
|
||||||
get :launch_app
|
get :launch_app
|
||||||
|
|||||||
30
config/services.yml
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
internal:
|
||||||
|
btcpay:
|
||||||
|
name: BTCPay Server
|
||||||
|
postgres:
|
||||||
|
name: PostgreSQL
|
||||||
|
sentry:
|
||||||
|
name: Sentry
|
||||||
|
external:
|
||||||
|
discourse:
|
||||||
|
name: Discourse
|
||||||
|
droneci:
|
||||||
|
name: Drone CI
|
||||||
|
ejabberd:
|
||||||
|
display_name: Chat
|
||||||
|
email:
|
||||||
|
name: E-Mail
|
||||||
|
gitea:
|
||||||
|
name: Gitea
|
||||||
|
lndhub:
|
||||||
|
name: LNDHub
|
||||||
|
display_name: Lightning Network
|
||||||
|
mastodon:
|
||||||
|
name: Mastodon
|
||||||
|
mediawiki:
|
||||||
|
name: MediaWiki
|
||||||
|
nostr:
|
||||||
|
name: Nostr
|
||||||
|
remotestorage:
|
||||||
|
name: remoteStorage
|
||||||
|
display_name: Storage
|
||||||
@@ -11,7 +11,7 @@
|
|||||||
"postcss-preset-env": "^7.8.3",
|
"postcss-preset-env": "^7.8.3",
|
||||||
"tailwindcss": "^3.2.4"
|
"tailwindcss": "^3.2.4"
|
||||||
},
|
},
|
||||||
"version": "0.9.0",
|
"version": "0.10.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build:css:tailwind": "tailwindcss --postcss -i ./app/assets/stylesheets/application.tailwind.css -o ./app/assets/builds/application.css",
|
"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"
|
"build:css": "yarn run build:css:tailwind"
|
||||||
|
|||||||
BIN
public/img/app_icons/hyperdraft.png
Normal file
|
After Width: | Height: | Size: 31 KiB |
BIN
public/img/app_icons/kommit.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
public/img/app_icons/notes-together.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
public/img/app_icons/papiers.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
public/img/app_icons/petrolette.png
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
public/img/app_icons/sharesome.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
BIN
public/img/app_icons/webmarks.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
1
public/img/illustrations/undraw_friends_r511.svg
Normal file
|
After Width: | Height: | Size: 11 KiB |
25
spec/helpers/services_helper_spec.rb
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
describe ServicesHelper do
|
||||||
|
|
||||||
|
describe "#service_human_name" do
|
||||||
|
it "returns the human name when it's configured" do
|
||||||
|
expect(service_human_name("mastodon")).to eq("Mastodon")
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns the key when there is no human name" do
|
||||||
|
expect(service_human_name("ejabberd")).to eq("ejabberd")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#service_display_name" do
|
||||||
|
it "returns the display name when it's configured" do
|
||||||
|
expect(service_display_name("lndhub")).to eq("Lightning Network")
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns the human name when there is no display name" do
|
||||||
|
expect(service_display_name("mastodon")).to eq("Mastodon")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
@@ -44,7 +44,7 @@ RSpec.describe CreateLdapUserJob, type: :job do
|
|||||||
|
|
||||||
it "adds default services for pre-confirmed accounts" do
|
it "adds default services for pre-confirmed accounts" do
|
||||||
allow(ldap_client_mock).to receive(:add) # spy on mock
|
allow(ldap_client_mock).to receive(:add) # spy on mock
|
||||||
allow(Setting).to receive(:default_services).and_return(["xmpp", "discourse"])
|
Setting.default_services = ["ejabberd", "discourse"]
|
||||||
|
|
||||||
perform_enqueued_jobs { job_for_preconfirmed_account }
|
perform_enqueued_jobs { job_for_preconfirmed_account }
|
||||||
|
|
||||||
@@ -56,7 +56,7 @@ RSpec.describe CreateLdapUserJob, type: :job do
|
|||||||
sn: "halfinney",
|
sn: "halfinney",
|
||||||
uid: "halfinney",
|
uid: "halfinney",
|
||||||
mail: "halfinney@example.com",
|
mail: "halfinney@example.com",
|
||||||
serviceEnabled: ["xmpp", "discourse"],
|
serviceEnabled: ["ejabberd", "discourse"],
|
||||||
userPassword: "remember-remember-the-5th-of-november"
|
userPassword: "remember-remember-the-5th-of-november"
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ RSpec.describe XmppExchangeContactsJob, type: :job do
|
|||||||
before do
|
before do
|
||||||
stub_request(:post, "http://xmpp.example.com/api/add_rosteritem")
|
stub_request(:post, "http://xmpp.example.com/api/add_rosteritem")
|
||||||
.to_return(status: 200, body: "", headers: {})
|
.to_return(status: 200, body: "", headers: {})
|
||||||
allow_any_instance_of(User).to receive(:services_enabled).and_return(["xmpp"])
|
allow_any_instance_of(User).to receive(:services_enabled).and_return(["ejabberd"])
|
||||||
end
|
end
|
||||||
|
|
||||||
it "posts add_rosteritem commands to the ejabberd API" do
|
it "posts add_rosteritem commands to the ejabberd API" do
|
||||||
|
|||||||
25
spec/models/setting_spec.rb
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
RSpec.describe Setting, type: :model do
|
||||||
|
|
||||||
|
describe ".available_services" do
|
||||||
|
before do
|
||||||
|
Setting.discourse_enabled = true
|
||||||
|
Setting.ejabberd_enabled = true
|
||||||
|
Setting.email_enabled = false
|
||||||
|
Setting.gitea_enabled = false
|
||||||
|
Setting.lndhub_enabled = true
|
||||||
|
Setting.mastodon_enabled = true
|
||||||
|
Setting.mediawiki_enabled = false
|
||||||
|
Setting.nostr_enabled = false
|
||||||
|
Setting.remotestorage_enabled = true
|
||||||
|
end
|
||||||
|
|
||||||
|
it "contains all enabled services" do
|
||||||
|
expect(Setting.available_services).to eq(%w[
|
||||||
|
discourse ejabberd lndhub mastodon remotestorage
|
||||||
|
])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
@@ -78,9 +78,9 @@ RSpec.describe User, type: :model do
|
|||||||
it "returns the entries from the LDAP service attribute" do
|
it "returns the entries from the LDAP service attribute" do
|
||||||
expect(user).to receive(:ldap_entry).and_return({
|
expect(user).to receive(:ldap_entry).and_return({
|
||||||
uid: user.cn, ou: user.ou, mail: user.email, admin: nil,
|
uid: user.cn, ou: user.ou, mail: user.email, admin: nil,
|
||||||
services_enabled: ["discourse", "email", "gitea", "wiki", "xmpp"]
|
services_enabled: ["discourse", "ejabberd", "email", "gitea", "wiki"]
|
||||||
})
|
})
|
||||||
expect(user.services_enabled).to eq(["discourse", "email", "gitea", "wiki", "xmpp"])
|
expect(user.services_enabled).to eq(["discourse", "ejabberd", "email", "gitea", "wiki"])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -88,7 +88,7 @@ RSpec.describe User, type: :model do
|
|||||||
before do
|
before do
|
||||||
allow(user).to receive(:ldap_entry).and_return({
|
allow(user).to receive(:ldap_entry).and_return({
|
||||||
uid: user.cn, ou: user.ou, mail: user.email, admin: nil,
|
uid: user.cn, ou: user.ou, mail: user.email, admin: nil,
|
||||||
services_enabled: ["gitea", "xmpp"]
|
services_enabled: ["ejabberd", "gitea"]
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -121,9 +121,9 @@ RSpec.describe User, type: :model do
|
|||||||
|
|
||||||
it "adds multiple service to the LDAP entry" do
|
it "adds multiple service to the LDAP entry" do
|
||||||
expect_any_instance_of(LdapService).to receive(:replace_attribute)
|
expect_any_instance_of(LdapService).to receive(:replace_attribute)
|
||||||
.with(dn, :serviceEnabled, ["discourse", "gitea", "wiki", "xmpp"]).and_return(true)
|
.with(dn, :serviceEnabled, ["discourse", "ejabberd", "gitea", "wiki"]).and_return(true)
|
||||||
|
|
||||||
user.enable_service([:wiki, :xmpp])
|
user.enable_service([:ejabberd, :wiki])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -131,7 +131,7 @@ RSpec.describe User, type: :model do
|
|||||||
before do
|
before do
|
||||||
allow(user).to receive(:ldap_entry).and_return({
|
allow(user).to receive(:ldap_entry).and_return({
|
||||||
uid: user.cn, ou: user.ou, mail: user.email, admin: nil,
|
uid: user.cn, ou: user.ou, mail: user.email, admin: nil,
|
||||||
services_enabled: ["discourse", "gitea", "xmpp"]
|
services_enabled: ["discourse", "ejabberd", "gitea"]
|
||||||
})
|
})
|
||||||
allow(user).to receive(:dn).and_return(dn)
|
allow(user).to receive(:dn).and_return(dn)
|
||||||
end
|
end
|
||||||
@@ -140,14 +140,14 @@ RSpec.describe User, type: :model do
|
|||||||
expect_any_instance_of(LdapService).to receive(:replace_attribute)
|
expect_any_instance_of(LdapService).to receive(:replace_attribute)
|
||||||
.with(dn, :serviceEnabled, ["discourse", "gitea"]).and_return(true)
|
.with(dn, :serviceEnabled, ["discourse", "gitea"]).and_return(true)
|
||||||
|
|
||||||
user.disable_service(:xmpp)
|
user.disable_service(:ejabberd)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "removes multiple services from the LDAP entry" do
|
it "removes multiple services from the LDAP entry" do
|
||||||
expect_any_instance_of(LdapService).to receive(:replace_attribute)
|
expect_any_instance_of(LdapService).to receive(:replace_attribute)
|
||||||
.with(dn, :serviceEnabled, ["discourse"]).and_return(true)
|
.with(dn, :serviceEnabled, ["discourse"]).and_return(true)
|
||||||
|
|
||||||
user.disable_service([:xmpp, "gitea"])
|
user.disable_service([:ejabberd, "gitea"])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -178,7 +178,7 @@ RSpec.describe User, type: :model do
|
|||||||
after { clear_enqueued_jobs }
|
after { clear_enqueued_jobs }
|
||||||
|
|
||||||
it "enables default services" do
|
it "enables default services" do
|
||||||
expect(user).to receive(:enable_service).with(%w[ discourse gitea mastodon mediawiki xmpp ])
|
expect(user).to receive(:enable_service).with(Setting.default_services)
|
||||||
user.send :devise_after_confirmation
|
user.send :devise_after_confirmation
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ RSpec.describe "WebFinger", type: :request do
|
|||||||
before do
|
before do
|
||||||
allow_any_instance_of(User).to receive(:ldap_entry).and_return({
|
allow_any_instance_of(User).to receive(:ldap_entry).and_return({
|
||||||
uid: user.cn, ou: user.ou, mail: user.email, admin: nil,
|
uid: user.cn, ou: user.ou, mail: user.email, admin: nil,
|
||||||
services_enabled: ["xmpp"]
|
services_enabled: ["ejabberd"]
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -92,7 +92,13 @@ RSpec.describe "WebFinger", type: :request do
|
|||||||
expect(rs_link["href"]).to eql("#{Setting.rs_storage_url}/tony")
|
expect(rs_link["href"]).to eql("#{Setting.rs_storage_url}/tony")
|
||||||
|
|
||||||
oauth_url = rs_link["properties"]["http://tools.ietf.org/html/rfc6749#section-4.2"]
|
oauth_url = rs_link["properties"]["http://tools.ietf.org/html/rfc6749#section-4.2"]
|
||||||
expect(oauth_url).to eql("http://www.example.com/rs/oauth/tony")
|
expect(oauth_url).to eql("http://accounts.kosmos.org/rs/oauth/tony")
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns CORS headers" do
|
||||||
|
get "/.well-known/nostr.json?name=bobdylan"
|
||||||
|
expect(response.headers['Access-Control-Allow-Origin']).to eq("*")
|
||||||
|
expect(response.headers['Access-Control-Allow-Methods']).to eq('GET')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -100,7 +106,7 @@ RSpec.describe "WebFinger", type: :request do
|
|||||||
before do
|
before do
|
||||||
allow_any_instance_of(User).to receive(:ldap_entry).and_return({
|
allow_any_instance_of(User).to receive(:ldap_entry).and_return({
|
||||||
uid: user.cn, ou: user.ou, mail: user.email, admin: nil,
|
uid: user.cn, ou: user.ou, mail: user.email, admin: nil,
|
||||||
services_enabled: ["xmpp"]
|
services_enabled: ["ejabberd"]
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -46,7 +46,17 @@ RSpec.describe "Well-known URLs", type: :request do
|
|||||||
expect(res["names"]["bobdylan"]).to eq(user.nostr_pubkey)
|
expect(res["names"]["bobdylan"]).to eq(user.nostr_pubkey)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "returns CORS headers" do
|
||||||
|
get "/.well-known/nostr.json?name=bobdylan"
|
||||||
|
expect(response.headers['Access-Control-Allow-Origin']).to eq("*")
|
||||||
|
expect(response.headers['Access-Control-Allow-Methods']).to eq('GET')
|
||||||
|
end
|
||||||
|
|
||||||
context "without relay configured" do
|
context "without relay configured" do
|
||||||
|
before do
|
||||||
|
Setting.nostr_relay_url = ""
|
||||||
|
end
|
||||||
|
|
||||||
it "does not include a recommended relay" do
|
it "does not include a recommended relay" do
|
||||||
get "/.well-known/nostr.json?name=bobdylan"
|
get "/.well-known/nostr.json?name=bobdylan"
|
||||||
res = JSON.parse(response.body)
|
res = JSON.parse(response.body)
|
||||||
@@ -69,10 +79,36 @@ RSpec.describe "Well-known URLs", type: :request do
|
|||||||
end
|
end
|
||||||
|
|
||||||
describe "placeholder username for domain's own pubkey" do
|
describe "placeholder username for domain's own pubkey" do
|
||||||
it "returns the configured nostr pubkey" do
|
describe "for primary domain" do
|
||||||
get "/.well-known/nostr.json?name=_"
|
context "no different pubkey configured for primary domain" do
|
||||||
res = JSON.parse(response.body)
|
it "returns the akkounts nostr pubkey" do
|
||||||
expect(res["names"]["_"]).to eq(Setting.nostr_public_key)
|
get "/.well-known/nostr.json?name=_"
|
||||||
|
res = JSON.parse(response.body)
|
||||||
|
expect(res["names"]["_"]).to eq("bdd76ce2934b2f591f9fad2ebe9da18f20d2921de527494ba00eeaa0a0efadcf")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "different pubkey configured for primary domain" do
|
||||||
|
before do
|
||||||
|
Setting.nostr_public_key_primary_domain = "b3e8f62fbe41217ffc0aa1e178d297339932d8ba4f46d9c7df3b61575e78fecc"
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns the primary domain's nostr pubkey" do
|
||||||
|
get "/.well-known/nostr.json?name=_"
|
||||||
|
res = JSON.parse(response.body)
|
||||||
|
expect(res["names"]["_"]).to eq("b3e8f62fbe41217ffc0aa1e178d297339932d8ba4f46d9c7df3b61575e78fecc")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "for akkounts domain" do
|
||||||
|
it "returns the configured nostr pubkey" do
|
||||||
|
headers = { "X-Forwarded-Host" => "accounts.kosmos.org" }
|
||||||
|
get "/.well-known/nostr.json?name=_"
|
||||||
|
|
||||||
|
res = JSON.parse(response.body)
|
||||||
|
expect(res["names"]["_"]).to eq("bdd76ce2934b2f591f9fad2ebe9da18f20d2921de527494ba00eeaa0a0efadcf")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context "with relay configured" do
|
context "with relay configured" do
|
||||||
|
|||||||
@@ -4,6 +4,10 @@ RSpec.describe NostrManager::PublishZapReceipt, type: :model do
|
|||||||
let(:user) { create :user, ln_account: "123456abcdef" }
|
let(:user) { create :user, ln_account: "123456abcdef" }
|
||||||
let(:zap) { create :zap, user: user }
|
let(:zap) { create :zap, user: user }
|
||||||
|
|
||||||
|
before do
|
||||||
|
Setting.nostr_relay_url = ""
|
||||||
|
end
|
||||||
|
|
||||||
describe "Default/delayed execution" do
|
describe "Default/delayed execution" do
|
||||||
it "publishes zap receipts to all requested relays" do
|
it "publishes zap receipts to all requested relays" do
|
||||||
expect(NostrPublishEventJob).to receive(:perform_later)
|
expect(NostrPublishEventJob).to receive(:perform_later)
|
||||||
|
|||||||