Merge branch 'master' into feature/donations_btcpay
This commit is contained in:
commit
e3b96d5cff
46
README.md
46
README.md
@ -14,8 +14,10 @@ so:
|
|||||||
|
|
||||||
1. Make sure [Docker Compose is installed][1] and Docker is running (included in
|
1. Make sure [Docker Compose is installed][1] and Docker is running (included in
|
||||||
Docker Desktop)
|
Docker Desktop)
|
||||||
3. Run `docker compose up` and wait until 389ds announces its successful start
|
3. Run `docker compose up --build` and wait until all services have started
|
||||||
in the log output
|
(389ds might take an extra minute to be ready). This will take a while when
|
||||||
|
running for the first time, so you might want to do something else in the
|
||||||
|
meantime.
|
||||||
4. `docker-compose exec ldap dsconf localhost backend create --suffix="dc=kosmos,dc=org" --be-name="dev"`
|
4. `docker-compose exec ldap dsconf localhost backend create --suffix="dc=kosmos,dc=org" --be-name="dev"`
|
||||||
5. `docker compose run web rails ldap:setup`
|
5. `docker compose run web rails ldap:setup`
|
||||||
6. `docker compose run web rails db:setup`
|
6. `docker compose run web rails db:setup`
|
||||||
@ -28,38 +30,44 @@ have the password "user is user".
|
|||||||
|
|
||||||
### Rails app
|
### Rails app
|
||||||
|
|
||||||
|
_Note: when using Docker Compose, prefix the following commands with `docker-compose
|
||||||
|
run web`._
|
||||||
|
|
||||||
Installing dependencies:
|
Installing dependencies:
|
||||||
|
|
||||||
bundle install
|
bundle install
|
||||||
yarn install
|
yarn install
|
||||||
|
|
||||||
Setting up local database (SQLite):
|
Migrating the local database (after schema changes):
|
||||||
|
|
||||||
bundle exec rails db:create
|
|
||||||
bundle exec rails db:migrate
|
bundle exec rails db:migrate
|
||||||
|
|
||||||
Running the dev server and auto-building CSS files on change:
|
Running the dev server, and auto-building CSS files on change _(automatic with Docker Compose)_:
|
||||||
|
|
||||||
bin/dev
|
bin/dev
|
||||||
|
|
||||||
Running the background workers (requires Redis):
|
Running the background workers (requires Redis) _(automatic with Docker Compose)_:
|
||||||
|
|
||||||
bundle exec sidekiq -C config/sidekiq.yml
|
bundle exec sidekiq -C config/sidekiq.yml
|
||||||
|
|
||||||
Running all specs:
|
Running the test suite:
|
||||||
|
|
||||||
bundle exec rspec
|
bundle exec rspec
|
||||||
|
|
||||||
### Docker (Compose)
|
Running the test suite with Docker Compose requires overriding the Rails
|
||||||
|
environment:
|
||||||
|
|
||||||
There is a working Docker Compose config file, which define a number of services including
|
docker-compose run -e "RAILS_ENV=test" web rspec
|
||||||
an app server for Rails as well as a local 389ds (LDAP) server.
|
|
||||||
|
|
||||||
For Rails developers, you probably just want to start the LDAP server: `docker-compose up ldap`,
|
### Docker Compose
|
||||||
listening on port 389 on your machine.
|
|
||||||
|
|
||||||
You can pick and choose your services adding them by name (listed in `docker-compose.yml`) at
|
Services/containers are configured in `docker-compose.yml`.
|
||||||
the end of the docker compose command. eg. `docker compose up ldap redis`
|
|
||||||
|
You can run services selectively, for example if you want to run the Rails app
|
||||||
|
and test suite on the host machine. Just add the service names of the
|
||||||
|
containers you want to run to the `up` command, like so:
|
||||||
|
|
||||||
|
docker-compose up ldap redis
|
||||||
|
|
||||||
#### LDAP server
|
#### LDAP server
|
||||||
|
|
||||||
@ -76,13 +84,15 @@ Now you can seed the back-end with data using this Rails task:
|
|||||||
The setup task will first delete any existing entries in the directory tree
|
The setup task will first delete any existing entries in the directory tree
|
||||||
("dc=kosmos,dc=org"), and then create our development entries.
|
("dc=kosmos,dc=org"), and then create our development entries.
|
||||||
|
|
||||||
Note that all 389ds data is stored in `tmp/389ds`. So if you want to start over
|
Note that all 389ds data is stored in the `389ds-data` volume. So if you want
|
||||||
with a fresh installation, delete both that directory as well as the container.
|
to start over with a fresh installation, delete both that volume as well as the
|
||||||
|
container.
|
||||||
|
|
||||||
#### Minio / RS
|
#### Minio / remoteStorage
|
||||||
|
|
||||||
If you want to run remoteStorage accounts locally, you will have to create the
|
If you want to run remoteStorage accounts locally, you will have to create the
|
||||||
respective bucket first:
|
respective bucket first. With the `minio` container running (run by default
|
||||||
|
when using Docker Compose), follow these steps:
|
||||||
|
|
||||||
* `docker compose up web redis minio liquor-cabinet`
|
* `docker compose up web redis minio liquor-cabinet`
|
||||||
* Head to http://localhost:9001 and log in with user `minioadmin`, password
|
* Head to http://localhost:9001 and log in with user `minioadmin`, password
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
class Admin::Settings::RegistrationsController < Admin::SettingsController
|
class Admin::Settings::RegistrationsController < Admin::SettingsController
|
||||||
def index
|
def show
|
||||||
end
|
end
|
||||||
|
|
||||||
def create
|
def update
|
||||||
update_settings
|
update_settings
|
||||||
|
|
||||||
redirect_to admin_settings_registrations_path, flash: {
|
redirect_to admin_settings_registrations_path, flash: {
|
||||||
|
@ -1,19 +1,32 @@
|
|||||||
class Admin::Settings::ServicesController < Admin::SettingsController
|
class Admin::Settings::ServicesController < Admin::SettingsController
|
||||||
def index
|
before_action :set_service, only: [:show, :update]
|
||||||
@service = params[:s]
|
|
||||||
|
|
||||||
if @service.blank?
|
def index
|
||||||
redirect_to admin_settings_services_path(params: { s: "btcpay" })
|
redirect_to admin_settings_service_path("btcpay")
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def create
|
def show
|
||||||
service = params.require(:service)
|
end
|
||||||
|
|
||||||
|
def update
|
||||||
update_settings
|
update_settings
|
||||||
|
|
||||||
redirect_to admin_settings_services_path(params: { s: service }), flash: {
|
redirect_to admin_settings_service_path(@service), flash: {
|
||||||
success: "Settings saved"
|
success: "Settings saved"
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def set_subsection
|
||||||
|
@subsection = "services"
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_service
|
||||||
|
@service = params[:service]
|
||||||
|
|
||||||
|
if @service.blank?
|
||||||
|
redirect_to admin_settings_services_path and return
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
@ -20,7 +20,7 @@ class Admin::SettingsController < Admin::BaseController
|
|||||||
end
|
end
|
||||||
|
|
||||||
if @errors.any?
|
if @errors.any?
|
||||||
render :index and return
|
render :show and return
|
||||||
end
|
end
|
||||||
|
|
||||||
changed_keys.each do |key|
|
changed_keys.each do |key|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<%= render HeaderComponent.new(title: "Settings") %>
|
<%= render HeaderComponent.new(title: "Settings") %>
|
||||||
|
|
||||||
<%= render MainWithSidenavComponent.new(sidenav_partial: 'shared/admin_sidenav_settings') do %>
|
<%= render MainWithSidenavComponent.new(sidenav_partial: 'shared/admin_sidenav_settings') do %>
|
||||||
<%= form_for(Setting.new, url: admin_settings_registrations_path) do |f| %>
|
<%= form_for(Setting.new, url: admin_settings_registrations_path, method: :put) do |f| %>
|
||||||
<section>
|
<section>
|
||||||
<h3>Registrations</h3>
|
<h3>Registrations</h3>
|
||||||
|
|
@ -1,9 +1,7 @@
|
|||||||
<%= render HeaderComponent.new(title: "Settings") %>
|
<%= render HeaderComponent.new(title: "Settings") %>
|
||||||
|
|
||||||
<%= render MainWithSidenavComponent.new(sidenav_partial: 'shared/admin_sidenav_settings') do %>
|
<%= render MainWithSidenavComponent.new(sidenav_partial: 'shared/admin_sidenav_settings') do %>
|
||||||
<%= form_for(Setting.new, url: admin_settings_services_path) do |f| %>
|
<%= form_for(Setting.new, url: admin_settings_service_path(@service), method: :put) do |f| %>
|
||||||
<%= hidden_field_tag :service, @service %>
|
|
||||||
|
|
||||||
<% if @errors && @errors.any? %>
|
<% if @errors && @errors.any? %>
|
||||||
<section>
|
<section>
|
||||||
<%= render partial: "admin/settings/errors", locals: { errors: @errors } %>
|
<%= render partial: "admin/settings/errors", locals: { errors: @errors } %>
|
@ -4,9 +4,9 @@
|
|||||||
) %>
|
) %>
|
||||||
<%= render SidenavLinkComponent.new(
|
<%= render SidenavLinkComponent.new(
|
||||||
name: "Services", path: admin_settings_services_path, icon: "grid",
|
name: "Services", path: admin_settings_services_path, icon: "grid",
|
||||||
active: current_page?(admin_settings_services_path)
|
active: controller_name == "services"
|
||||||
) %>
|
) %>
|
||||||
<% if current_page?(admin_settings_services_path) %>
|
<% if controller_name == "services" %>
|
||||||
<%= render partial: "shared/admin_sidenav_settings_services" %>
|
<%= render partial: "shared/admin_sidenav_settings_services" %>
|
||||||
<% end %>
|
<% end %>
|
||||||
<%= render SidenavLinkComponent.new(
|
<%= render SidenavLinkComponent.new(
|
||||||
|
@ -1,77 +1,77 @@
|
|||||||
<%= render SidenavLinkComponent.new(
|
<%= render SidenavLinkComponent.new(
|
||||||
level: 2,
|
level: 2,
|
||||||
name: "BTCPay",
|
name: "BTCPay",
|
||||||
path: admin_settings_services_path(params: { s: "btcpay" }),
|
path: admin_settings_service_path("btcpay"),
|
||||||
text_icon: Setting.btcpay_enabled? ? "◉" : "○",
|
text_icon: Setting.btcpay_enabled? ? "◉" : "○",
|
||||||
active: current_page?(admin_settings_services_path(params: { s: "btcpay" })),
|
active: current_page?(admin_settings_service_path("btcpay")),
|
||||||
) %>
|
) %>
|
||||||
<%= render SidenavLinkComponent.new(
|
<%= render SidenavLinkComponent.new(
|
||||||
level: 2,
|
level: 2,
|
||||||
name: "Discourse",
|
name: "Discourse",
|
||||||
path: admin_settings_services_path(params: { s: "discourse" }),
|
path: admin_settings_service_path("discourse"),
|
||||||
text_icon: Setting.discourse_enabled? ? "◉" : "○",
|
text_icon: Setting.discourse_enabled? ? "◉" : "○",
|
||||||
active: current_page?(admin_settings_services_path(params: { s: "discourse" })),
|
active: current_page?(admin_settings_service_path("discourse")),
|
||||||
) %>
|
) %>
|
||||||
<%= render SidenavLinkComponent.new(
|
<%= render SidenavLinkComponent.new(
|
||||||
level: 2,
|
level: 2,
|
||||||
name: "Drone CI",
|
name: "Drone CI",
|
||||||
path: admin_settings_services_path(params: { s: "droneci" }),
|
path: admin_settings_service_path("droneci"),
|
||||||
text_icon: Setting.droneci_enabled? ? "◉" : "○",
|
text_icon: Setting.droneci_enabled? ? "◉" : "○",
|
||||||
active: current_page?(admin_settings_services_path(params: { s: "droneci" })),
|
active: current_page?(admin_settings_service_path("droneci")),
|
||||||
) %>
|
) %>
|
||||||
<%= render SidenavLinkComponent.new(
|
<%= render SidenavLinkComponent.new(
|
||||||
level: 2,
|
level: 2,
|
||||||
name: "E-Mail",
|
name: "E-Mail",
|
||||||
path: admin_settings_services_path(params: { s: "email" }),
|
path: admin_settings_service_path("email"),
|
||||||
text_icon: Setting.email_enabled? ? "◉" : "○",
|
text_icon: Setting.email_enabled? ? "◉" : "○",
|
||||||
active: current_page?(admin_settings_services_path(params: { s: "email" })),
|
active: current_page?(admin_settings_services_path(params: { s: "email" })),
|
||||||
) %>
|
) %>
|
||||||
<%= render SidenavLinkComponent.new(
|
<%= render SidenavLinkComponent.new(
|
||||||
level: 2,
|
level: 2,
|
||||||
name: "ejabberd",
|
name: "ejabberd",
|
||||||
path: admin_settings_services_path(params: { s: "ejabberd" }),
|
path: admin_settings_service_path("ejabberd"),
|
||||||
text_icon: Setting.ejabberd_enabled? ? "◉" : "○",
|
text_icon: Setting.ejabberd_enabled? ? "◉" : "○",
|
||||||
active: current_page?(admin_settings_services_path(params: { s: "ejabberd" })),
|
active: current_page?(admin_settings_service_path("ejabberd")),
|
||||||
) %>
|
) %>
|
||||||
<%= render SidenavLinkComponent.new(
|
<%= render SidenavLinkComponent.new(
|
||||||
level: 2,
|
level: 2,
|
||||||
name: "Gitea",
|
name: "Gitea",
|
||||||
path: admin_settings_services_path(params: { s: "gitea" }),
|
path: admin_settings_service_path("gitea"),
|
||||||
text_icon: Setting.gitea_enabled? ? "◉" : "○",
|
text_icon: Setting.gitea_enabled? ? "◉" : "○",
|
||||||
active: current_page?(admin_settings_services_path(params: { s: "gitea" })),
|
active: current_page?(admin_settings_service_path("gitea")),
|
||||||
) %>
|
) %>
|
||||||
<%= render SidenavLinkComponent.new(
|
<%= render SidenavLinkComponent.new(
|
||||||
level: 2,
|
level: 2,
|
||||||
name: "LNDHub",
|
name: "LNDHub",
|
||||||
path: admin_settings_services_path(params: { s: "lndhub" }),
|
path: admin_settings_service_path("lndhub"),
|
||||||
text_icon: Setting.lndhub_enabled? ? "◉" : "○",
|
text_icon: Setting.lndhub_enabled? ? "◉" : "○",
|
||||||
active: current_page?(admin_settings_services_path(params: { s: "lndhub" })),
|
active: current_page?(admin_settings_service_path("lndhub")),
|
||||||
) %>
|
) %>
|
||||||
<%= render SidenavLinkComponent.new(
|
<%= render SidenavLinkComponent.new(
|
||||||
level: 2,
|
level: 2,
|
||||||
name: "Mastodon",
|
name: "Mastodon",
|
||||||
path: admin_settings_services_path(params: { s: "mastodon" }),
|
path: admin_settings_service_path("mastodon"),
|
||||||
text_icon: Setting.mastodon_enabled? ? "◉" : "○",
|
text_icon: Setting.mastodon_enabled? ? "◉" : "○",
|
||||||
active: current_page?(admin_settings_services_path(params: { s: "mastodon" })),
|
active: current_page?(admin_settings_service_path("mastodon")),
|
||||||
) %>
|
) %>
|
||||||
<%= render SidenavLinkComponent.new(
|
<%= render SidenavLinkComponent.new(
|
||||||
level: 2,
|
level: 2,
|
||||||
name: "MediaWiki",
|
name: "MediaWiki",
|
||||||
path: admin_settings_services_path(params: { s: "mediawiki" }),
|
path: admin_settings_service_path("mediawiki"),
|
||||||
text_icon: Setting.mediawiki_enabled? ? "◉" : "○",
|
text_icon: Setting.mediawiki_enabled? ? "◉" : "○",
|
||||||
active: current_page?(admin_settings_services_path(params: { s: "mediawiki" })),
|
active: current_page?(admin_settings_service_path("mediawiki")),
|
||||||
) %>
|
) %>
|
||||||
<%= render SidenavLinkComponent.new(
|
<%= render SidenavLinkComponent.new(
|
||||||
level: 2,
|
level: 2,
|
||||||
name: "Nostr",
|
name: "Nostr",
|
||||||
path: admin_settings_services_path(params: { s: "nostr" }),
|
path: admin_settings_service_path("nostr"),
|
||||||
text_icon: Setting.nostr_enabled? ? "◉" : "○",
|
text_icon: Setting.nostr_enabled? ? "◉" : "○",
|
||||||
active: current_page?(admin_settings_services_path(params: { s: "nostr" })),
|
active: current_page?(admin_settings_service_path("nostr")),
|
||||||
) %>
|
) %>
|
||||||
<%= render SidenavLinkComponent.new(
|
<%= render SidenavLinkComponent.new(
|
||||||
level: 2,
|
level: 2,
|
||||||
name: "RemoteStorage",
|
name: "RemoteStorage",
|
||||||
path: admin_settings_services_path(params: { s: "remotestorage" }),
|
path: admin_settings_service_path("remotestorage"),
|
||||||
text_icon: Setting.remotestorage_enabled? ? "◉" : "○",
|
text_icon: Setting.remotestorage_enabled? ? "◉" : "○",
|
||||||
active: current_page?(admin_settings_services_path(params: { s: "remotestorage" })),
|
active: current_page?(admin_settings_service_path("remotestorage")),
|
||||||
) %>
|
) %>
|
||||||
|
@ -52,10 +52,9 @@ Rails.application.configure do
|
|||||||
|
|
||||||
config.active_job.queue_adapter = :test
|
config.active_job.queue_adapter = :test
|
||||||
|
|
||||||
if ENV["S3_ENABLED"]
|
if ENV["S3_ENABLED"] && ENV["S3_ENABLED"].to_s != "false"
|
||||||
config.active_storage.service = :s3
|
config.active_storage.service = :s3
|
||||||
else
|
else
|
||||||
# Store attachments on the local disk (in ./tmp)
|
config.active_storage.service = :local
|
||||||
config.active_storage.service = :test
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -97,8 +97,8 @@ Rails.application.routes.draw do
|
|||||||
end
|
end
|
||||||
|
|
||||||
namespace :settings do
|
namespace :settings do
|
||||||
resources 'registrations', only: ['index', 'create']
|
resource 'registrations', only: ['show', 'update']
|
||||||
resources 'services', only: ['index', 'create']
|
resources 'services', param: 'service', only: ['index', 'show', 'update']
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -60,6 +60,7 @@ ActiveRecord::Schema[7.1].define(version: 2024_02_16_124640) do
|
|||||||
t.string "payment_method"
|
t.string "payment_method"
|
||||||
t.string "btcpay_invoice_id"
|
t.string "btcpay_invoice_id"
|
||||||
t.string "payment_status"
|
t.string "payment_status"
|
||||||
|
t.index ["payment_status"], name: "index_donations_on_payment_status"
|
||||||
t.index ["user_id"], name: "index_donations_on_user_id"
|
t.index ["user_id"], name: "index_donations_on_user_id"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -23,35 +23,35 @@ RSpec.describe 'Admin/global settings', type: :feature do
|
|||||||
scenario "Opening service settings shows page for first service" do
|
scenario "Opening service settings shows page for first service" do
|
||||||
visit admin_settings_services_path
|
visit admin_settings_services_path
|
||||||
|
|
||||||
expect(current_url).to eq(admin_settings_services_url(params: { s: "btcpay" }))
|
expect(current_url).to eq(admin_settings_service_url("btcpay"))
|
||||||
end
|
end
|
||||||
|
|
||||||
scenario "View service settings" do
|
scenario "View service settings" do
|
||||||
visit admin_settings_services_path(params: { s: "ejabberd" })
|
visit admin_settings_service_path("ejabberd")
|
||||||
|
|
||||||
expect(page).to have_content("Enable ejabberd integration")
|
expect(page).to have_content("Enable ejabberd integration")
|
||||||
expect(page).to have_field("API URL", with: "http://xmpp.example.com/api")
|
expect(page).to have_field("API URL", with: "http://xmpp.example.com/api")
|
||||||
end
|
end
|
||||||
|
|
||||||
scenario "Disable a service integration" do
|
scenario "Disable a service integration" do
|
||||||
visit admin_settings_services_path(params: { s: "ejabberd" })
|
visit admin_settings_service_path("ejabberd")
|
||||||
expect(page).to have_checked_field("setting[ejabberd_enabled]")
|
expect(page).to have_checked_field("setting[ejabberd_enabled]")
|
||||||
|
|
||||||
uncheck "setting[ejabberd_enabled]"
|
uncheck "setting[ejabberd_enabled]"
|
||||||
click_button "Save"
|
click_button "Save"
|
||||||
|
|
||||||
expect(current_url).to eq(admin_settings_services_url(params: { s: "ejabberd" }))
|
expect(current_url).to eq(admin_settings_service_url("ejabberd"))
|
||||||
expect(page).to_not have_checked_field("setting[ejabberd_enabled]")
|
expect(page).to_not have_checked_field("setting[ejabberd_enabled]")
|
||||||
expect(page).to_not have_field("API URL", disabled: true)
|
expect(page).to_not have_field("API URL", disabled: true)
|
||||||
end
|
end
|
||||||
|
|
||||||
scenario "Resettable fields" do
|
scenario "Resettable fields" do
|
||||||
visit admin_settings_services_path(params: { s: "ejabberd" })
|
visit admin_settings_service_path("ejabberd")
|
||||||
expect(page).to have_field("API URL", with: "http://xmpp.example.com/api")
|
expect(page).to have_field("API URL", with: "http://xmpp.example.com/api")
|
||||||
expect(page).to_not have_css('input#setting_ejabberd_api_url+button')
|
expect(page).to_not have_css('input#setting_ejabberd_api_url+button')
|
||||||
|
|
||||||
Setting.ejabberd_api_url = "http://example.com/foo"
|
Setting.ejabberd_api_url = "http://example.com/foo"
|
||||||
visit admin_settings_services_path(params: { s: "ejabberd" })
|
visit admin_settings_service_path("ejabberd")
|
||||||
expect(page).to have_field("API URL", with: "http://example.com/foo")
|
expect(page).to have_field("API URL", with: "http://example.com/foo")
|
||||||
expect(page).to have_css('input#setting_ejabberd_api_url+button')
|
expect(page).to have_css('input#setting_ejabberd_api_url+button')
|
||||||
end
|
end
|
||||||
|
@ -15,7 +15,7 @@ RSpec.describe "WebFinger", type: :request do
|
|||||||
res = JSON.parse(response.body)
|
res = JSON.parse(response.body)
|
||||||
rs_link = res["links"].find {|l| l["rel"] == "http://tools.ietf.org/id/draft-dejong-remotestorage"}
|
rs_link = res["links"].find {|l| l["rel"] == "http://tools.ietf.org/id/draft-dejong-remotestorage"}
|
||||||
|
|
||||||
expect(rs_link["href"]).to eql("https://storage.kosmos.org/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://www.example.com/rs/oauth/tony")
|
||||||
|
Loading…
x
Reference in New Issue
Block a user