mirror of
https://github.com/maybe-finance/maybe.git
synced 2025-07-22 06:39:39 +02:00
Simplify self host settings controller (#1230)
This commit is contained in:
parent
cb75c537fe
commit
7fabca4679
22 changed files with 252 additions and 399 deletions
|
@ -1,71 +1,43 @@
|
||||||
class Settings::HostingsController < SettingsController
|
class Settings::HostingsController < SettingsController
|
||||||
before_action :verify_hosting_mode
|
before_action :raise_if_not_self_hosted
|
||||||
|
|
||||||
def show
|
def show
|
||||||
|
@synth_usage = Current.family.synth_usage
|
||||||
end
|
end
|
||||||
|
|
||||||
def update
|
def update
|
||||||
if all_updates_valid?
|
if hosting_params[:upgrades_setting].present?
|
||||||
hosting_params.keys.each do |key|
|
mode = hosting_params[:upgrades_setting] == "manual" ? "manual" : "auto"
|
||||||
Setting.send("#{key}=", hosting_params[key].strip)
|
target = hosting_params[:upgrades_setting] == "commit" ? "commit" : "release"
|
||||||
end
|
|
||||||
|
|
||||||
redirect_to settings_hosting_path, notice: t(".success")
|
Setting.upgrades_mode = mode
|
||||||
else
|
Setting.upgrades_target = target
|
||||||
flash.now[:error] = @errors.first.message
|
|
||||||
render :show, status: :unprocessable_entity
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def send_test_email
|
|
||||||
unless Setting.smtp_settings_populated?
|
|
||||||
flash[:alert] = t(".missing_smtp_setting_error")
|
|
||||||
render(:show, status: :unprocessable_entity)
|
|
||||||
return
|
|
||||||
end
|
end
|
||||||
|
|
||||||
begin
|
if hosting_params.key?(:render_deploy_hook)
|
||||||
NotificationMailer.with(user: Current.user).test_email.deliver_now
|
Setting.render_deploy_hook = hosting_params[:render_deploy_hook]
|
||||||
rescue => _e
|
end
|
||||||
flash[:alert] = t(".error")
|
|
||||||
render :show, status: :unprocessable_entity
|
if hosting_params.key?(:require_invite_for_signup)
|
||||||
return
|
Setting.require_invite_for_signup = hosting_params[:require_invite_for_signup]
|
||||||
|
end
|
||||||
|
|
||||||
|
if hosting_params.key?(:synth_api_key)
|
||||||
|
Setting.synth_api_key = hosting_params[:synth_api_key]
|
||||||
end
|
end
|
||||||
|
|
||||||
redirect_to settings_hosting_path, notice: t(".success")
|
redirect_to settings_hosting_path, notice: t(".success")
|
||||||
|
rescue ActiveRecord::RecordInvalid => error
|
||||||
|
flash.now[:alert] = t(".failure")
|
||||||
|
render :show, status: :unprocessable_entity
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
def all_updates_valid?
|
|
||||||
@errors = ActiveModel::Errors.new(Setting)
|
|
||||||
hosting_params.keys.each do |key|
|
|
||||||
setting = Setting.new(var: key)
|
|
||||||
setting.value = hosting_params[key].strip
|
|
||||||
|
|
||||||
unless setting.valid?
|
|
||||||
@errors.merge!(setting.errors)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if hosting_params[:upgrades_mode] == "auto" && hosting_params[:render_deploy_hook].blank?
|
|
||||||
@errors.add(:render_deploy_hook, t("settings.hostings.update.render_deploy_hook_error"))
|
|
||||||
end
|
|
||||||
|
|
||||||
@errors.empty?
|
|
||||||
end
|
|
||||||
|
|
||||||
def hosting_params
|
def hosting_params
|
||||||
permitted_params = params.require(:setting).permit(:render_deploy_hook, :upgrades_mode, :email_sender, :app_domain, :smtp_host, :smtp_port, :smtp_username, :smtp_password, :require_invite_for_signup)
|
params.require(:setting).permit(:render_deploy_hook, :upgrades_setting, :require_invite_for_signup, :synth_api_key)
|
||||||
|
|
||||||
result = {}
|
|
||||||
result[:upgrades_mode] = permitted_params[:upgrades_mode] == "manual" ? "manual" : "auto" if permitted_params.key?(:upgrades_mode)
|
|
||||||
result[:render_deploy_hook] = permitted_params[:render_deploy_hook] if permitted_params.key?(:render_deploy_hook)
|
|
||||||
result[:upgrades_target] = permitted_params[:upgrades_mode] unless permitted_params[:upgrades_mode] == "manual" if permitted_params.key?(:upgrades_mode)
|
|
||||||
result.merge!(permitted_params.slice(:email_sender, :app_domain, :smtp_host, :smtp_port, :smtp_username, :smtp_password, :require_invite_for_signup))
|
|
||||||
result
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def verify_hosting_mode
|
def raise_if_not_self_hosted
|
||||||
head :not_found unless self_hosted?
|
raise "Settings not available on non-self-hosted instance" unless self_hosted?
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,16 +1,3 @@
|
||||||
class ApplicationMailer < ActionMailer::Base
|
class ApplicationMailer < ActionMailer::Base
|
||||||
layout "mailer"
|
layout "mailer"
|
||||||
|
|
||||||
after_action :set_self_host_settings, if: -> { Rails.configuration.app_mode.self_hosted? }
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def set_self_host_settings
|
|
||||||
mail.from = Setting.email_sender
|
|
||||||
mail.delivery_method.settings.merge!({ address: Setting.smtp_host,
|
|
||||||
port: Setting.smtp_port,
|
|
||||||
user_name: Setting.smtp_username,
|
|
||||||
password: Setting.smtp_password,
|
|
||||||
tls: ENV.fetch("SMTP_TLS_ENABLED", "true") == "true" })
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
class NotificationMailer < ApplicationMailer
|
|
||||||
def test_email
|
|
||||||
mail(to: params[:user].email, subject: t(".test_email_subject"), body: t(".test_email_body"))
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -18,13 +18,12 @@ module Providable
|
||||||
Provider::Github.new
|
Provider::Github.new
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def synth_provider
|
||||||
|
api_key = self_hosted? ? Setting.synth_api_key : ENV["SYNTH_API_KEY"]
|
||||||
|
api_key.present? ? Provider::Synth.new(api_key) : nil
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def synth_provider
|
|
||||||
api_key = self_hosted? ? Setting.synth_api_key : ENV["SYNTH_API_KEY"]
|
|
||||||
api_key.present? ? Provider::Synth.new(api_key) : nil
|
|
||||||
end
|
|
||||||
|
|
||||||
def self_hosted?
|
def self_hosted?
|
||||||
Rails.application.config.app_mode.self_hosted?
|
Rails.application.config.app_mode.self_hosted?
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
class Family < ApplicationRecord
|
class Family < ApplicationRecord
|
||||||
|
include Providable
|
||||||
|
|
||||||
has_many :users, dependent: :destroy
|
has_many :users, dependent: :destroy
|
||||||
has_many :tags, dependent: :destroy
|
has_many :tags, dependent: :destroy
|
||||||
has_many :accounts, dependent: :destroy
|
has_many :accounts, dependent: :destroy
|
||||||
|
@ -119,4 +121,8 @@ class Family < ApplicationRecord
|
||||||
def needs_sync?
|
def needs_sync?
|
||||||
last_synced_at.nil? || last_synced_at.to_date < Date.current
|
last_synced_at.nil? || last_synced_at.to_date < Date.current
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def synth_usage
|
||||||
|
self.class.synth_provider&.usage
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -9,6 +9,38 @@ class Provider::Synth
|
||||||
response = client.get("#{base_url}/user")
|
response = client.get("#{base_url}/user")
|
||||||
JSON.parse(response.body).dig("id").present?
|
JSON.parse(response.body).dig("id").present?
|
||||||
end
|
end
|
||||||
|
def usage
|
||||||
|
response = client.get("#{base_url}/user")
|
||||||
|
|
||||||
|
if response.status == 401
|
||||||
|
return UsageResponse.new(
|
||||||
|
success?: false,
|
||||||
|
error: "Unauthorized: Invalid API key",
|
||||||
|
raw_response: response
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
parsed = JSON.parse(response.body)
|
||||||
|
|
||||||
|
remaining = parsed.dig("api_calls_remaining")
|
||||||
|
limit = parsed.dig("api_limit")
|
||||||
|
used = limit - remaining
|
||||||
|
|
||||||
|
UsageResponse.new(
|
||||||
|
used: used,
|
||||||
|
limit: limit,
|
||||||
|
utilization: used.to_f / limit * 100,
|
||||||
|
plan: parsed.dig("plan"),
|
||||||
|
success?: true,
|
||||||
|
raw_response: response
|
||||||
|
)
|
||||||
|
rescue StandardError => error
|
||||||
|
UsageResponse.new(
|
||||||
|
success?: false,
|
||||||
|
error: error,
|
||||||
|
raw_response: error
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
def fetch_security_prices(ticker:, start_date:, end_date:)
|
def fetch_security_prices(ticker:, start_date:, end_date:)
|
||||||
prices = paginate(
|
prices = paginate(
|
||||||
|
@ -96,6 +128,7 @@ class Provider::Synth
|
||||||
ExchangeRateResponse = Struct.new :rate, :success?, :error, :raw_response, keyword_init: true
|
ExchangeRateResponse = Struct.new :rate, :success?, :error, :raw_response, keyword_init: true
|
||||||
SecurityPriceResponse = Struct.new :prices, :success?, :error, :raw_response, keyword_init: true
|
SecurityPriceResponse = Struct.new :prices, :success?, :error, :raw_response, keyword_init: true
|
||||||
ExchangeRatesResponse = Struct.new :rates, :success?, :error, :raw_response, keyword_init: true
|
ExchangeRatesResponse = Struct.new :rates, :success?, :error, :raw_response, keyword_init: true
|
||||||
|
UsageResponse = Struct.new :used, :limit, :utilization, :plan, :success?, :error, :raw_response, keyword_init: true
|
||||||
|
|
||||||
def base_url
|
def base_url
|
||||||
"https://api.synthfinance.com"
|
"https://api.synthfinance.com"
|
||||||
|
|
|
@ -17,21 +17,7 @@ class Setting < RailsSettings::Base
|
||||||
default: ENV.fetch("UPGRADES_TARGET", "release"),
|
default: ENV.fetch("UPGRADES_TARGET", "release"),
|
||||||
validates: { inclusion: { in: %w[release commit] } }
|
validates: { inclusion: { in: %w[release commit] } }
|
||||||
|
|
||||||
field :app_domain, type: :string, default: ENV["APP_DOMAIN"]
|
|
||||||
field :email_sender, type: :string, default: ENV["EMAIL_SENDER"]
|
|
||||||
|
|
||||||
field :synth_api_key, type: :string, default: ENV["SYNTH_API_KEY"]
|
field :synth_api_key, type: :string, default: ENV["SYNTH_API_KEY"]
|
||||||
|
|
||||||
field :require_invite_for_signup, type: :boolean, default: false
|
field :require_invite_for_signup, type: :boolean, default: false
|
||||||
|
|
||||||
scope :smtp_settings do
|
|
||||||
field :smtp_host, type: :string, read_only: true, default: ENV["SMTP_ADDRESS"]
|
|
||||||
field :smtp_port, type: :string, read_only: true, default: ENV["SMTP_PORT"]
|
|
||||||
field :smtp_username, type: :string, read_only: true, default: ENV["SMTP_USERNAME"]
|
|
||||||
field :smtp_password, type: :string, read_only: true, default: ENV["SMTP_PASSWORD"]
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.smtp_settings_populated?
|
|
||||||
Setting.defined_fields.select { |f| f.scope == :smtp_settings }.map(&:read).all?(&:present?)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
34
app/views/settings/hostings/_invite_code_settings.html.erb
Normal file
34
app/views/settings/hostings/_invite_code_settings.html.erb
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
<div class="space-y-4">
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<div class="space-y-1">
|
||||||
|
<p class="text-sm"><%= t(".title") %></p>
|
||||||
|
<p class="text-gray-500 text-sm"><%= t(".description") %></p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<%= styled_form_with model: Setting.new, url: settings_hosting_path, method: :patch, data: { controller: "auto-submit-form", "auto-submit-form-trigger-event-value" => "blur" } do |form| %>
|
||||||
|
<div class="relative inline-block select-none">
|
||||||
|
<%= form.check_box :require_invite_for_signup, class: "sr-only peer", "data-auto-submit-form-target": "auto", "data-autosubmit-trigger-event": "input" %>
|
||||||
|
<%= form.label :require_invite_for_signup, " ".html_safe, class: "maybe-switch" %>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<% if Setting.require_invite_for_signup %>
|
||||||
|
<div class="flex items-center justify-between mb-4">
|
||||||
|
<div>
|
||||||
|
<span class="text-gray-900 text-base font-medium"><%= t(".generated_tokens") %></span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<%= button_to invite_codes_path,
|
||||||
|
method: :post,
|
||||||
|
class: "flex gap-1 bg-gray-50 text-gray-900 text-sm rounded-lg px-3 py-2" do %>
|
||||||
|
<span><%= t(".generate_tokens") %></span>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<%= turbo_frame_tag :invite_codes, src: invite_codes_path %>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
9
app/views/settings/hostings/_provider_settings.html.erb
Normal file
9
app/views/settings/hostings/_provider_settings.html.erb
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
<% if ENV["HOSTING_PLATFORM"] == "render" %>
|
||||||
|
<%= styled_form_with model: Setting.new, url: settings_hosting_path, method: :patch, data: { controller: "auto-submit-form", "auto-submit-form-trigger-event-value" => "blur" } do |form| %>
|
||||||
|
<div class="space-y-4">
|
||||||
|
<h2 class="font-medium mb-1"><%= t(".title") %></h2>
|
||||||
|
<p class="text-gray-500 text-sm mb-4"><%= t(".description") %></p>
|
||||||
|
<%= form.url_field :render_deploy_hook, label: t(".render_deploy_hook_label"), placeholder: t(".render_deploy_hook_placeholder"), value: Setting.render_deploy_hook, data: { "auto-submit-form-target" => "auto" } %>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
<% end %>
|
44
app/views/settings/hostings/_synth_settings.html.erb
Normal file
44
app/views/settings/hostings/_synth_settings.html.erb
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
<div class="space-y-4">
|
||||||
|
<div>
|
||||||
|
<h2 class="font-medium mb-1"><%= t(".title") %></h2>
|
||||||
|
<p class="text-gray-500 text-sm mb-4"><%= t(".description") %></p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<%= styled_form_with model: Setting.new,
|
||||||
|
url: settings_hosting_path,
|
||||||
|
method: :patch,
|
||||||
|
data: {
|
||||||
|
controller: "auto-submit-form",
|
||||||
|
"auto-submit-form-trigger-event-value": "blur"
|
||||||
|
} do |form| %>
|
||||||
|
<%= form.text_field :synth_api_key,
|
||||||
|
label: t(".label"),
|
||||||
|
type: "password",
|
||||||
|
placeholder: t(".placeholder"),
|
||||||
|
value: Setting.synth_api_key,
|
||||||
|
container_class: @synth_usage.present? && !@synth_usage.success? ? "border-red-500" : "",
|
||||||
|
data: { "auto-submit-form-target": "auto" } %>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<% if @synth_usage.present? && @synth_usage.success? %>
|
||||||
|
<div class="space-y-4">
|
||||||
|
<div class="space-y-2">
|
||||||
|
<p class="text-sm text-gray-500">
|
||||||
|
<%= t(".api_calls_used",
|
||||||
|
used: number_with_delimiter(@synth_usage.used),
|
||||||
|
limit: number_with_delimiter(@synth_usage.limit),
|
||||||
|
percentage: number_to_percentage(@synth_usage.utilization, precision: 1)) %>
|
||||||
|
</p>
|
||||||
|
<div class="w-52 h-1.5 bg-gray-100 rounded-2xl">
|
||||||
|
<div class="h-full bg-green-500 rounded-2xl"
|
||||||
|
style="width: <%= [@synth_usage.utilization, 2].max %>%;"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="bg-gray-100 rounded-md px-1.5 py-0.5 w-fit">
|
||||||
|
<p class="text-xs font-medium text-gray-500 uppercase">
|
||||||
|
<%= t(".plan", plan: @synth_usage.plan) %>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
41
app/views/settings/hostings/_upgrade_settings.html.erb
Normal file
41
app/views/settings/hostings/_upgrade_settings.html.erb
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
<% if ENV["HOSTING_PLATFORM"] == "render" %>
|
||||||
|
<div>
|
||||||
|
<h2 class="font-medium mb-1"><%= t(".title") %></h2>
|
||||||
|
<p class="text-gray-500 text-sm mb-4"><%= t(".description") %></p>
|
||||||
|
|
||||||
|
<%= styled_form_with model: Setting.new, url: settings_hosting_path, method: :patch, data: { controller: "auto-submit-form", "auto-submit-form-trigger-event-value" => "blur" } do |form| %>
|
||||||
|
<div class="space-y-4">
|
||||||
|
<div class="flex items-center gap-4">
|
||||||
|
<%= form.radio_button :upgrades_setting, "manual", checked: Setting.upgrades_mode == "manual", data: { "auto-submit-form-target" => "auto", "autosubmit-trigger-event": "input" } %>
|
||||||
|
<%= form.label :upgrades_mode_manual, t(".manual_title"), class: "text-gray-900 text-sm" do %>
|
||||||
|
<span class="font-medium"><%= t(".manual_title") %></span>
|
||||||
|
<br>
|
||||||
|
<span class="text-gray-500">
|
||||||
|
<%= t(".manual_description") %>
|
||||||
|
</span>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center gap-4">
|
||||||
|
<%= form.radio_button :upgrades_setting, "release", checked: Setting.upgrades_mode == "auto" && Setting.upgrades_target == "release", data: { "auto-submit-form-target" => "auto", "autosubmit-trigger-event": "input" } %>
|
||||||
|
<%= form.label :upgrades_mode_release, t(".latest_release_title"), class: "text-gray-900 text-sm" do %>
|
||||||
|
<span class="font-medium"><%= t(".latest_release_title") %></span>
|
||||||
|
<br>
|
||||||
|
<span class="text-gray-500">
|
||||||
|
<%= t(".latest_release_description") %>
|
||||||
|
</span>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center gap-4">
|
||||||
|
<%= form.radio_button :upgrades_setting, "commit", checked: Setting.upgrades_mode == "auto" && Setting.upgrades_target == "commit", data: { "auto-submit-form-target" => "auto", "autosubmit-trigger-event": "input" } %>
|
||||||
|
<%= form.label :upgrades_mode_commit, t(".latest_commit_title"), class: "text-gray-900 text-sm" do %>
|
||||||
|
<span class="font-medium"><%= t(".latest_commit_title") %></span>
|
||||||
|
<br>
|
||||||
|
<span class="text-gray-500">
|
||||||
|
<%= t(".latest_commit_description") %>
|
||||||
|
</span>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
|
@ -3,122 +3,19 @@
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<div class="space-y-4 pb-32">
|
<div class="space-y-4 pb-32">
|
||||||
<h1 class="text-gray-900 text-xl font-medium mb-4"><%= t(".page_title") %></h1>
|
<h1 class="text-gray-900 text-xl font-medium mb-4"><%= t(".title") %></h1>
|
||||||
|
|
||||||
<% if ENV["HOSTING_PLATFORM"] == "render" %>
|
<%= settings_section title: t(".general") do %>
|
||||||
<%= settings_section title: t(".general_settings_title") do %>
|
<div class="space-y-6">
|
||||||
<%= styled_form_with model: Setting.new, url: settings_hosting_path, method: :patch, data: { controller: "auto-submit-form", "auto-submit-form-trigger-event-value" => "blur" } do |form| %>
|
<%= render "settings/hostings/upgrade_settings" %>
|
||||||
<h2 class="font-medium mb-1"><%= t(".upgrades.title") %></h2>
|
<%= render "settings/hostings/provider_settings" %>
|
||||||
<p class="text-gray-500 text-sm mb-4"><%= t(".upgrades.description") %></p>
|
<%= render "settings/hostings/synth_settings" %>
|
||||||
|
|
||||||
<div class="space-y-4">
|
|
||||||
<div class="flex items-center gap-4">
|
|
||||||
<%= form.radio_button :upgrades_mode, "manual", checked: Setting.upgrades_mode == "manual", data: { "auto-submit-form-target" => "auto", "autosubmit-trigger-event": "input" } %>
|
|
||||||
<%= form.label :upgrades_mode_manual, t(".upgrades.manual.title"), class: "text-gray-900 text-sm" do %>
|
|
||||||
<span class="font-medium"><%= t(".upgrades.manual.title") %></span>
|
|
||||||
<br>
|
|
||||||
<span class="text-gray-500">
|
|
||||||
<%= t(".upgrades.manual.description") %>
|
|
||||||
</span>
|
|
||||||
<% end %>
|
|
||||||
</div>
|
|
||||||
<div class="flex items-center gap-4">
|
|
||||||
<%= form.radio_button :upgrades_mode, "release", checked: Setting.upgrades_mode == "auto" && Setting.upgrades_target == "release", data: { "auto-submit-form-target" => "auto", "autosubmit-trigger-event": "input" } %>
|
|
||||||
<%= form.label :upgrades_mode_release, t(".upgrades.latest_release.title"), class: "text-gray-900 text-sm" do %>
|
|
||||||
<span class="font-medium"><%= t(".upgrades.latest_release.title") %></span>
|
|
||||||
<br>
|
|
||||||
<span class="text-gray-500">
|
|
||||||
<%= t(".upgrades.latest_release.description") %>
|
|
||||||
</span>
|
|
||||||
<% end %>
|
|
||||||
</div>
|
|
||||||
<div class="flex items-center gap-4">
|
|
||||||
<%= form.radio_button :upgrades_mode, "commit", checked: Setting.upgrades_mode == "auto" && Setting.upgrades_target == "commit", data: { "auto-submit-form-target" => "auto", "autosubmit-trigger-event": "input" } %>
|
|
||||||
<%= form.label :upgrades_mode_commit, t(".upgrades.latest_commit.title"), class: "text-gray-900 text-sm" do %>
|
|
||||||
<span class="font-medium"><%= t(".upgrades.latest_commit.title") %></span>
|
|
||||||
<br>
|
|
||||||
<span class="text-gray-500">
|
|
||||||
<%= t(".upgrades.latest_commit.description") %>
|
|
||||||
</span>
|
|
||||||
<% end %>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<h2 class="font-medium mb-1"><%= t(".provider_settings.title") %></h2>
|
|
||||||
<p class="text-gray-500 text-sm mb-4"><%= t(".render_deploy_hook_description") %></p>
|
|
||||||
<%= form.url_field :render_deploy_hook, label: t(".render_deploy_hook_label"), placeholder: t(".render_deploy_hook_placeholder"), value: Setting.render_deploy_hook, data: { "auto-submit-form-target" => "auto" } %>
|
|
||||||
</div>
|
|
||||||
<% end %>
|
|
||||||
<% end %>
|
|
||||||
<% end %>
|
|
||||||
|
|
||||||
<%= settings_section title: t(".smtp_settings.title") do %>
|
|
||||||
<%= styled_form_with model: Setting.new, url: settings_hosting_path, method: :patch, data: { controller: "auto-submit-form", "auto-submit-form-trigger-event-value" => "blur" } do |form| %>
|
|
||||||
<p class="text-gray-500 text-sm mb-4"><%= t(".smtp_settings.description") %></p>
|
|
||||||
<div class="space-y-4">
|
|
||||||
<div class="space-y-3">
|
|
||||||
<%= form.text_field :email_sender, label: t(".email_sender"), placeholder: t(".email_sender_placeholder"), value: Setting.email_sender, data: { "auto-submit-form-target" => "auto" } %>
|
|
||||||
<%= form.text_field :app_domain, label: t(".domain"), placeholder: t(".domain_placeholder"), value: Setting.app_domain, data: { "auto-submit-form-target" => "auto" } %>
|
|
||||||
<%= form.text_field :smtp_host, label: t(".smtp_settings.host"), placeholder: t(".smtp_settings.host_placeholder"), value: Setting.smtp_host, data: { "auto-submit-form-target" => "auto" } %>
|
|
||||||
<%= form.number_field :smtp_port, label: t(".smtp_settings.port"), placeholder: t(".smtp_settings.port_placeholder"), value: Setting.smtp_port, data: { "auto-submit-form-target" => "auto" } %>
|
|
||||||
<%= form.text_field :smtp_username, label: t(".smtp_settings.username"), placeholder: t(".smtp_settings.username_placeholder"), value: Setting.smtp_username, data: { "auto-submit-form-target" => "auto" } %>
|
|
||||||
<%= form.password_field :smtp_password, label: t(".smtp_settings.password"), placeholder: t(".smtp_settings.password_placeholder"), value: Setting.smtp_password, data: { "auto-submit-form-target" => "auto" } %>
|
|
||||||
</div>
|
|
||||||
<div class="flex items-center justify-between bg-white border border-alpha-black-100 p-4 rounded-lg">
|
|
||||||
<div class="flex items-center gap-3">
|
|
||||||
<div class="w-10 h-10 rounded-full bg-gray-25 flex items-center justify-center">
|
|
||||||
<%= lucide_icon "mails", class: "w-6 h-6 text-gray-500" %>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<p class="text-gray-900 font-medium text-sm"><%= t(".smtp_settings.send_test_email") %></p>
|
|
||||||
<p class="text-gray-500 text-sm"><%= t(".smtp_settings.send_test_email_description") %></p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<%= link_to t(".smtp_settings.send_test_email_button"), send_test_email_settings_hosting_path, data: { turbo_method: :post }, class: "bg-gray-50 text-gray-900 text-sm font-medium rounded-lg px-3 py-2" %>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<% end %>
|
|
||||||
<% end %>
|
|
||||||
|
|
||||||
<%= settings_section title: t(".invite_settings.title") do %>
|
|
||||||
<div class="space-y-4">
|
|
||||||
<div class="flex items-center justify-between">
|
|
||||||
<div class="space-y-1">
|
|
||||||
<p class="text-sm"><%= t(".invite_settings.require_invite_for_signup") %></p>
|
|
||||||
<p class="text-gray-500 text-sm"><%= t(".invite_settings.invite_code_description") %></p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<%= styled_form_with model: Setting.new, url: settings_hosting_path, method: :patch, data: { controller: "auto-submit-form", "auto-submit-form-trigger-event-value" => "blur" } do |form| %>
|
|
||||||
<div class="relative inline-block select-none">
|
|
||||||
<%= form.check_box :require_invite_for_signup, class: "sr-only peer", "data-auto-submit-form-target": "auto", "data-autosubmit-trigger-event": "input" %>
|
|
||||||
<%= form.label :require_invite_for_signup, " ".html_safe, class: "maybe-switch" %>
|
|
||||||
</div>
|
|
||||||
<% end %>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<% if Setting.require_invite_for_signup %>
|
|
||||||
<div class="flex items-center justify-between mb-4">
|
|
||||||
<div>
|
|
||||||
<span class="text-gray-900 text-base font-medium"><%= t(".invite_settings.generated_tokens") %></span>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<%= button_to invite_codes_path,
|
|
||||||
method: :post,
|
|
||||||
class: "flex gap-1 bg-gray-50 text-gray-900 text-sm rounded-lg px-3 py-2" do %>
|
|
||||||
<span><%= t(".invite_settings.generate_tokens") %></span>
|
|
||||||
<% end %>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<%= turbo_frame_tag :invite_codes, src: invite_codes_path %>
|
|
||||||
</div>
|
|
||||||
<% end %>
|
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
|
<%= settings_section title: t(".invites") do %>
|
||||||
|
<%= render "settings/hostings/invite_code_settings" %>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
<%= settings_nav_footer %>
|
<%= settings_nav_footer %>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -24,8 +24,6 @@ module Maybe
|
||||||
# config.time_zone = "Central Time (US & Canada)"
|
# config.time_zone = "Central Time (US & Canada)"
|
||||||
# config.eager_load_paths << Rails.root.join("extras")
|
# config.eager_load_paths << Rails.root.join("extras")
|
||||||
|
|
||||||
config.action_mailer.default_options = { from: ENV["MAILER_SENDER"] }
|
|
||||||
|
|
||||||
config.active_job.queue_adapter = :good_job
|
config.active_job.queue_adapter = :good_job
|
||||||
|
|
||||||
config.app_mode = (ENV["SELF_HOSTING_ENABLED"] == "true" ? "self_hosted" : "managed").inquiry
|
config.app_mode = (ENV["SELF_HOSTING_ENABLED"] == "true" ? "self_hosted" : "managed").inquiry
|
||||||
|
|
|
@ -40,18 +40,6 @@ Rails.application.configure do
|
||||||
config.action_mailer.raise_delivery_errors = false
|
config.action_mailer.raise_delivery_errors = false
|
||||||
config.action_mailer.delivery_method = :letter_opener
|
config.action_mailer.delivery_method = :letter_opener
|
||||||
|
|
||||||
# Uncomment to send emails in development
|
|
||||||
# config.action_mailer.raise_delivery_errors = true
|
|
||||||
# config.action_mailer.delivery_method = :smtp
|
|
||||||
# config.action_mailer.smtp_settings = {
|
|
||||||
# address: ENV["SMTP_ADDRESS"],
|
|
||||||
# port: ENV["SMTP_PORT"],
|
|
||||||
# user_name: ENV["SMTP_USERNAME"],
|
|
||||||
# password: ENV["SMTP_PASSWORD"],
|
|
||||||
# tls: ENV.fetch("SMTP_TLS_ENABLED", "true") == "true"
|
|
||||||
# }
|
|
||||||
|
|
||||||
|
|
||||||
config.action_mailer.perform_caching = false
|
config.action_mailer.perform_caching = false
|
||||||
|
|
||||||
config.action_mailer.perform_deliveries = true
|
config.action_mailer.perform_deliveries = true
|
||||||
|
|
|
@ -1,7 +0,0 @@
|
||||||
---
|
|
||||||
en:
|
|
||||||
notification_mailer:
|
|
||||||
test_email:
|
|
||||||
test_email_body: Congratulation ! Connection to the SMTP server is now correctly
|
|
||||||
configured.
|
|
||||||
test_email_subject: SMTP settings verified !
|
|
|
@ -1,61 +1,6 @@
|
||||||
---
|
---
|
||||||
en:
|
en:
|
||||||
settings:
|
settings:
|
||||||
hostings:
|
|
||||||
send_test_email:
|
|
||||||
error: 'Configuration error: Test email could not be sent'
|
|
||||||
missing_smtp_setting_error: Ensure that all smtp settings are filled in
|
|
||||||
success: Test email has been sent successfully
|
|
||||||
show:
|
|
||||||
domain: App Domain
|
|
||||||
domain_placeholder: mydomain.com
|
|
||||||
email_sender: Email Sender
|
|
||||||
email_sender_placeholder: user@mydomain.com
|
|
||||||
general_settings_title: General Settings
|
|
||||||
invite_settings:
|
|
||||||
generate_tokens: Generate new code
|
|
||||||
generated_tokens: Generated codes
|
|
||||||
invite_code_description: Every new user that joins your instance if Maybe
|
|
||||||
can only do so via an invite code
|
|
||||||
require_invite_for_signup: Require invite code for new sign ups
|
|
||||||
title: Invite Codes
|
|
||||||
page_title: Self-Hosting
|
|
||||||
provider_settings:
|
|
||||||
title: Provider Settings
|
|
||||||
render_deploy_hook_description: Input the deploy hook URL provided by Render
|
|
||||||
render_deploy_hook_label: Render Deploy Hook URL
|
|
||||||
render_deploy_hook_placeholder: https://api.render.com/deploy/srv-xyz...
|
|
||||||
smtp_settings:
|
|
||||||
description: Configure outgoing mail server settings for notifications and
|
|
||||||
alerts
|
|
||||||
host: SMTP Host
|
|
||||||
host_placeholder: smtp.gmail.com
|
|
||||||
password: Password
|
|
||||||
password_placeholder: "*******"
|
|
||||||
port: Port
|
|
||||||
port_placeholder: 587
|
|
||||||
send_test_email: Send test email
|
|
||||||
send_test_email_button: Send test email
|
|
||||||
send_test_email_description: Verify SMTP settings by sending a test email
|
|
||||||
title: SMTP Email Configuration
|
|
||||||
username: Username
|
|
||||||
username_placeholder: username@gmail.com
|
|
||||||
upgrades:
|
|
||||||
description: Choose how your application receives updates
|
|
||||||
latest_commit:
|
|
||||||
description: Automatically update to the latest commit (unstable)
|
|
||||||
title: Latest Commit
|
|
||||||
latest_release:
|
|
||||||
description: Automatically update to the most recent release (stable)
|
|
||||||
title: Latest Release
|
|
||||||
manual:
|
|
||||||
description: You control when to download and install updates
|
|
||||||
title: Manual
|
|
||||||
title: Auto upgrade
|
|
||||||
update:
|
|
||||||
render_deploy_hook_error: Render deploy hook must be provided to enable auto
|
|
||||||
upgrades
|
|
||||||
success: Settings updated successfully.
|
|
||||||
nav:
|
nav:
|
||||||
accounts_label: Accounts
|
accounts_label: Accounts
|
||||||
categories_label: Categories
|
categories_label: Categories
|
||||||
|
|
39
config/locales/views/settings/hostings/en.yml
Normal file
39
config/locales/views/settings/hostings/en.yml
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
---
|
||||||
|
en:
|
||||||
|
settings:
|
||||||
|
hostings:
|
||||||
|
invite_code_settings:
|
||||||
|
description: Every new user that joins your instance if Maybe can only do
|
||||||
|
so via an invite code
|
||||||
|
generate_tokens: Generate new code
|
||||||
|
generated_tokens: Generated codes
|
||||||
|
title: Require invite code for new sign ups
|
||||||
|
provider_settings:
|
||||||
|
description: Configure settings for your hosting provider
|
||||||
|
render_deploy_hook_label: Render Deploy Hook URL
|
||||||
|
render_deploy_hook_placeholder: https://api.render.com/deploy/srv-xyz...
|
||||||
|
title: Provider Settings
|
||||||
|
show:
|
||||||
|
general: General Settings
|
||||||
|
invites: Invite Codes
|
||||||
|
title: Self-Hosting
|
||||||
|
synth_settings:
|
||||||
|
api_calls_used: "%{used} / %{limit} API calls used (%{percentage})"
|
||||||
|
description: Input the API key provided by Synth
|
||||||
|
label: API Key
|
||||||
|
placeholder: Enter your API key here
|
||||||
|
plan: "%{plan} plan"
|
||||||
|
title: Synth Settings
|
||||||
|
update:
|
||||||
|
failure: Invalid setting value
|
||||||
|
success: Settings updated
|
||||||
|
upgrade_settings:
|
||||||
|
description: Configure how your application receives updates
|
||||||
|
latest_commit_description: Automatically update to the latest commit (unstable)
|
||||||
|
latest_commit_title: Latest Commit
|
||||||
|
latest_release_description: Automatically update to the most recent release
|
||||||
|
(stable)
|
||||||
|
latest_release_title: Latest Release
|
||||||
|
manual_description: You control when to download and install updates
|
||||||
|
manual_title: Manual
|
||||||
|
title: Auto Upgrade
|
|
@ -16,9 +16,7 @@ Rails.application.routes.draw do
|
||||||
namespace :settings do
|
namespace :settings do
|
||||||
resource :profile, only: %i[show update destroy]
|
resource :profile, only: %i[show update destroy]
|
||||||
resource :preferences, only: %i[show update]
|
resource :preferences, only: %i[show update]
|
||||||
resource :hosting, only: %i[show update] do
|
resource :hosting, only: %i[show update]
|
||||||
post :send_test_email, on: :collection
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
resources :tags, except: %i[show destroy] do
|
resources :tags, except: %i[show destroy] do
|
||||||
|
|
|
@ -6,11 +6,13 @@ class Settings::HostingsControllerTest < ActionDispatch::IntegrationTest
|
||||||
end
|
end
|
||||||
|
|
||||||
test "cannot edit when self hosting is disabled" do
|
test "cannot edit when self hosting is disabled" do
|
||||||
get settings_hosting_url
|
assert_raises(RuntimeError, "Settings not available on non-self-hosted instance") do
|
||||||
assert :not_found
|
get settings_hosting_url
|
||||||
|
end
|
||||||
|
|
||||||
patch settings_hosting_url, params: { setting: { render_deploy_hook: "https://example.com" } }
|
assert_raises(RuntimeError, "Settings not available on non-self-hosted instance") do
|
||||||
assert :not_found
|
patch settings_hosting_url, params: { setting: { render_deploy_hook: "https://example.com" } }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
test "should get edit when self hosting is enabled" do
|
test "should get edit when self hosting is enabled" do
|
||||||
|
@ -31,63 +33,16 @@ class Settings::HostingsControllerTest < ActionDispatch::IntegrationTest
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
test "cannot set auto upgrades mode without a deploy hook" do
|
|
||||||
with_self_hosting do
|
|
||||||
patch settings_hosting_url, params: { setting: { upgrades_mode: "auto" } }
|
|
||||||
assert_response :unprocessable_entity
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
test "can choose auto upgrades mode with a deploy hook" do
|
test "can choose auto upgrades mode with a deploy hook" do
|
||||||
with_self_hosting do
|
with_self_hosting do
|
||||||
NEW_RENDER_DEPLOY_HOOK = "https://api.render.com/deploy/srv-abc123"
|
NEW_RENDER_DEPLOY_HOOK = "https://api.render.com/deploy/srv-abc123"
|
||||||
assert_nil Setting.render_deploy_hook
|
assert_nil Setting.render_deploy_hook
|
||||||
|
|
||||||
patch settings_hosting_url, params: { setting: { render_deploy_hook: NEW_RENDER_DEPLOY_HOOK, upgrades_mode: "release" } }
|
patch settings_hosting_url, params: { setting: { render_deploy_hook: NEW_RENDER_DEPLOY_HOOK, upgrades_setting: "release" } }
|
||||||
|
|
||||||
assert_equal "auto", Setting.upgrades_mode
|
assert_equal "auto", Setting.upgrades_mode
|
||||||
assert_equal "release", Setting.upgrades_target
|
assert_equal "release", Setting.upgrades_target
|
||||||
assert_equal NEW_RENDER_DEPLOY_HOOK, Setting.render_deploy_hook
|
assert_equal NEW_RENDER_DEPLOY_HOOK, Setting.render_deploy_hook
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
test " #send_test_email if smtp settings are populated try to send an email and redirect with notice" do
|
|
||||||
with_self_hosting do
|
|
||||||
Setting.stubs(:smtp_settings_populated?).returns(true)
|
|
||||||
|
|
||||||
test_email_mock = mock
|
|
||||||
test_email_mock.expects(:deliver_now)
|
|
||||||
|
|
||||||
mailer_mock = mock
|
|
||||||
mailer_mock.expects(:test_email).returns(test_email_mock)
|
|
||||||
|
|
||||||
NotificationMailer.expects(:with).with(user: users(:family_admin)).returns(mailer_mock)
|
|
||||||
|
|
||||||
post send_test_email_settings_hosting_path
|
|
||||||
assert_response :found
|
|
||||||
assert controller.flash[:notice].present?
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
test "#send_test_email with one blank smtp setting" do
|
|
||||||
with_self_hosting do
|
|
||||||
Setting.stubs(:smtp_settings_populated?).returns(false)
|
|
||||||
NotificationMailer.expects(:with).never
|
|
||||||
|
|
||||||
post send_test_email_settings_hosting_path
|
|
||||||
assert_response :unprocessable_entity
|
|
||||||
assert controller.flash[:alert].present?
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
test "#send_test_email when sending the email raise an error" do
|
|
||||||
with_self_hosting do
|
|
||||||
Setting.stubs(:smtp_settings_populated?).returns(true)
|
|
||||||
NotificationMailer.stubs(:with).raises(StandardError)
|
|
||||||
|
|
||||||
post send_test_email_settings_hosting_path
|
|
||||||
assert_response :unprocessable_entity
|
|
||||||
assert controller.flash[:alert].present?
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,43 +0,0 @@
|
||||||
require "test_helper"
|
|
||||||
|
|
||||||
class ApplicationMailerTest < ActionMailer::TestCase
|
|
||||||
setup do
|
|
||||||
class TestMailer < ApplicationMailer
|
|
||||||
def test_email
|
|
||||||
mail(to: "testto@email.com", from: "testfrom@email.com", subject: "Test email subject", body: "Test email body")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
test "should use self host settings when self host enabled" do
|
|
||||||
with_self_hosting do
|
|
||||||
smtp_host = "smtp.example.com"
|
|
||||||
smtp_port = 466
|
|
||||||
smtp_username = "user@example.com"
|
|
||||||
smtp_password = "password"
|
|
||||||
email_sender = "notification@example.com"
|
|
||||||
|
|
||||||
smtp_settings_from_settings = { address: smtp_host,
|
|
||||||
port: smtp_port,
|
|
||||||
user_name: smtp_username,
|
|
||||||
password: smtp_password }
|
|
||||||
|
|
||||||
Setting.stubs(:smtp_host).returns(smtp_host)
|
|
||||||
Setting.stubs(:smtp_port).returns(smtp_port)
|
|
||||||
Setting.stubs(:smtp_username).returns(smtp_username)
|
|
||||||
Setting.stubs(:smtp_password).returns(smtp_password)
|
|
||||||
Setting.stubs(:email_sender).returns(email_sender)
|
|
||||||
|
|
||||||
TestMailer.test_email.deliver_now
|
|
||||||
assert_emails 1
|
|
||||||
assert_equal smtp_settings_from_settings, ActionMailer::Base.deliveries.first.delivery_method.settings.slice(:address, :port, :user_name, :password)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
test "should use regular env settings when self host disabled" do
|
|
||||||
TestMailer.test_email.deliver_now
|
|
||||||
|
|
||||||
assert_emails 1
|
|
||||||
assert_nil ActionMailer::Base.deliveries.first.delivery_method.settings[:address]
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,23 +0,0 @@
|
||||||
require "test_helper"
|
|
||||||
|
|
||||||
class AccountTest < ActiveSupport::TestCase
|
|
||||||
test "#send_test_email return true if all smtp settings are populated" do
|
|
||||||
Setting.smtp_host = "smtp.example.com"
|
|
||||||
Setting.smtp_port = 466
|
|
||||||
Setting.smtp_username = "user@example.com"
|
|
||||||
Setting.smtp_password = "notification@example.com"
|
|
||||||
Setting.email_sender = "password"
|
|
||||||
|
|
||||||
assert Setting.smtp_settings_populated?
|
|
||||||
end
|
|
||||||
|
|
||||||
test "#send_test_email return false if one smtp settings is not populated" do
|
|
||||||
Setting.smtp_host = ""
|
|
||||||
Setting.smtp_port = 466
|
|
||||||
Setting.smtp_username = "user@example.com"
|
|
||||||
Setting.smtp_password = "notification@example.com"
|
|
||||||
Setting.email_sender = "password"
|
|
||||||
|
|
||||||
assert_not Setting.smtp_settings_populated?
|
|
||||||
end
|
|
||||||
end
|
|
Loading…
Add table
Add a link
Reference in a new issue