mirror of
https://github.com/maybe-finance/maybe.git
synced 2025-08-05 05:25:24 +02:00
Add zero-config self hosting on Render (#612)
* v1 of backend implementation for self hosting * Add docs * Add upgrades controller * Add global helpers for self hosting mode * Add self host settings controller * Conditionally show self hosting settings * Environment and config updates * Complete upgrade prompting flow * Update config for forked repo * Move configuration of github provider within class * Add upgrades cron * Update deploy button * Update guides * Fix render deployer * Typo * Enable auto upgrades * Fix cron * Make upgrade modes more clear and consistent * Trigger new available version * Fix logic for displaying upgrade prompts * Finish implementation * Fix regression * Trigger new version * Add i18n translations * trigger new version * reduce caching time for testing * Decrease cache for testing * trigger upgrade * trigger upgrade * Only trigger deploy once * trigger upgrade * If target is commit, always upgrade if any upgrade is available * trigger upgrade * trigger upgrade * Test release * Change back to maybe repo for defaults * Fix lint errors * Clearer naming * Fix relative link * Add abs path * Relative link * Update docs
This commit is contained in:
parent
2bbf120e2f
commit
5aca2ff9b6
53 changed files with 1356 additions and 111 deletions
|
@ -1,5 +1,5 @@
|
|||
class ApplicationController < ActionController::Base
|
||||
include Authentication
|
||||
include Authentication, Invitable, SelfHostable
|
||||
include Pagy::Backend
|
||||
|
||||
before_action :sync_accounts
|
||||
|
@ -11,11 +11,6 @@ class ApplicationController < ActionController::Base
|
|||
|
||||
private
|
||||
|
||||
def hosted_app?
|
||||
ENV["HOSTED"] == "true"
|
||||
end
|
||||
helper_method :hosted_app?
|
||||
|
||||
def sync_accounts
|
||||
return if Current.user.blank?
|
||||
|
||||
|
|
12
app/controllers/concerns/invitable.rb
Normal file
12
app/controllers/concerns/invitable.rb
Normal file
|
@ -0,0 +1,12 @@
|
|||
module Invitable
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
helper_method :invite_code_required?
|
||||
end
|
||||
|
||||
private
|
||||
def invite_code_required?
|
||||
ENV["REQUIRE_INVITE_CODE"] == "true"
|
||||
end
|
||||
end
|
12
app/controllers/concerns/self_hostable.rb
Normal file
12
app/controllers/concerns/self_hostable.rb
Normal file
|
@ -0,0 +1,12 @@
|
|||
module SelfHostable
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
helper_method :self_hosted?
|
||||
end
|
||||
|
||||
private
|
||||
def self_hosted?
|
||||
ENV["SELF_HOSTING_ENABLED"] == "true"
|
||||
end
|
||||
end
|
|
@ -4,7 +4,7 @@ class RegistrationsController < ApplicationController
|
|||
layout "auth"
|
||||
|
||||
before_action :set_user, only: :create
|
||||
before_action :claim_invite_code, only: :create, if: :hosted_app?
|
||||
before_action :claim_invite_code, only: :create, if: :invite_code_required?
|
||||
|
||||
def new
|
||||
@user = User.new
|
||||
|
|
46
app/controllers/settings/self_hosting_controller.rb
Normal file
46
app/controllers/settings/self_hosting_controller.rb
Normal file
|
@ -0,0 +1,46 @@
|
|||
class Settings::SelfHostingController < ApplicationController
|
||||
before_action :verify_self_hosting_enabled
|
||||
|
||||
def edit
|
||||
end
|
||||
|
||||
def update
|
||||
if all_updates_valid?
|
||||
self_hosting_params.keys.each do |key|
|
||||
Setting.send("#{key}=", self_hosting_params[key].strip)
|
||||
end
|
||||
|
||||
redirect_to edit_settings_self_hosting_path, notice: t(".success")
|
||||
else
|
||||
flash.now[:error] = @errors.first.message
|
||||
render :edit, status: :unprocessable_entity
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def all_updates_valid?
|
||||
@errors = ActiveModel::Errors.new(Setting)
|
||||
self_hosting_params.keys.each do |key|
|
||||
setting = Setting.new(var: key)
|
||||
setting.value = self_hosting_params[key].strip
|
||||
|
||||
unless setting.valid?
|
||||
@errors.merge!(setting.errors)
|
||||
end
|
||||
end
|
||||
|
||||
if self_hosting_params[:upgrades_mode] == "auto" && self_hosting_params[:render_deploy_hook].blank?
|
||||
@errors.add(:render_deploy_hook, t("settings.self_hosting.update.render_deploy_hook_error"))
|
||||
end
|
||||
|
||||
@errors.empty?
|
||||
end
|
||||
|
||||
def self_hosting_params
|
||||
params.require(:setting).permit(:render_deploy_hook, :upgrades_mode, :upgrades_target)
|
||||
end
|
||||
|
||||
def verify_self_hosting_enabled
|
||||
head :not_found unless self_hosted?
|
||||
end
|
||||
end
|
56
app/controllers/upgrades_controller.rb
Normal file
56
app/controllers/upgrades_controller.rb
Normal file
|
@ -0,0 +1,56 @@
|
|||
class UpgradesController < ApplicationController
|
||||
before_action :verify_upgrades_enabled
|
||||
|
||||
def acknowledge
|
||||
commit_sha = params[:id]
|
||||
upgrade = Upgrader.find_upgrade(commit_sha)
|
||||
|
||||
if upgrade
|
||||
if upgrade.available?
|
||||
Current.user.acknowledge_upgrade_prompt(upgrade.commit_sha)
|
||||
flash[:notice] = t(".upgrade_dismissed")
|
||||
elsif upgrade.complete?
|
||||
Current.user.acknowledge_upgrade_alert(upgrade.commit_sha)
|
||||
flash[:notice] = t(".upgrade_complete_dismiss")
|
||||
else
|
||||
flash[:alert] = t(".upgrade_not_available")
|
||||
end
|
||||
else
|
||||
flash[:alert] = t(".upgrade_not_found")
|
||||
end
|
||||
|
||||
redirect_back(fallback_location: root_path)
|
||||
end
|
||||
|
||||
def deploy
|
||||
commit_sha = params[:id]
|
||||
upgrade = Upgrader.find_upgrade(commit_sha)
|
||||
|
||||
unless upgrade
|
||||
flash[:alert] = t(".upgrade_not_found")
|
||||
return redirect_back(fallback_location: root_path)
|
||||
end
|
||||
|
||||
prior_acknowledged_upgrade_commit_sha = Current.user.last_prompted_upgrade_commit_sha
|
||||
|
||||
# Optimistically acknowledge the upgrade prompt
|
||||
Current.user.acknowledge_upgrade_prompt(upgrade.commit_sha)
|
||||
|
||||
upgrade_result = Upgrader.upgrade_to(upgrade)
|
||||
|
||||
if upgrade_result[:success]
|
||||
flash[:notice] = upgrade_result[:message]
|
||||
else
|
||||
# If the upgrade fails, revert to the prior acknowledged upgrade
|
||||
Current.user.acknowledge_upgrade_prompt(prior_acknowledged_upgrade_commit_sha)
|
||||
flash[:alert] = upgrade_result[:message]
|
||||
end
|
||||
|
||||
redirect_back(fallback_location: root_path)
|
||||
end
|
||||
|
||||
private
|
||||
def verify_upgrades_enabled
|
||||
head :not_found unless ENV["UPGRADES_ENABLED"] == "true"
|
||||
end
|
||||
end
|
|
@ -37,7 +37,6 @@ module ApplicationHelper
|
|||
render partial: "shared/sidebar_modal", locals: { content: content }
|
||||
end
|
||||
|
||||
|
||||
def sidebar_link_to(name, path, options = {})
|
||||
base_class_names = [ "block", "border", "border-transparent", "rounded-xl", "-ml-2", "p-2", "text-sm", "font-medium", "text-gray-500", "flex", "items-center" ]
|
||||
hover_class_names = [ "hover:bg-white", "hover:border-alpha-black-50", "hover:text-gray-900", "hover:shadow-xs" ]
|
||||
|
|
2
app/helpers/settings/self_hosting_helper.rb
Normal file
2
app/helpers/settings/self_hosting_helper.rb
Normal file
|
@ -0,0 +1,2 @@
|
|||
module Settings::SelfHostingHelper
|
||||
end
|
13
app/helpers/upgrades_helper.rb
Normal file
13
app/helpers/upgrades_helper.rb
Normal file
|
@ -0,0 +1,13 @@
|
|||
module UpgradesHelper
|
||||
def upgrade_notification
|
||||
return nil unless ENV["UPGRADES_ENABLED"] == "true"
|
||||
|
||||
completed_upgrade = Upgrader.completed_upgrade
|
||||
return completed_upgrade if completed_upgrade && Current.user.last_alerted_upgrade_commit_sha != completed_upgrade.commit_sha
|
||||
|
||||
available_upgrade = Upgrader.available_upgrade
|
||||
if available_upgrade && Setting.upgrades_mode == "manual" && Current.user.last_prompted_upgrade_commit_sha != available_upgrade.commit_sha
|
||||
available_upgrade
|
||||
end
|
||||
end
|
||||
end
|
31
app/jobs/auto_upgrade_job.rb
Normal file
31
app/jobs/auto_upgrade_job.rb
Normal file
|
@ -0,0 +1,31 @@
|
|||
class AutoUpgradeJob < ApplicationJob
|
||||
queue_as :default
|
||||
|
||||
def perform(*args)
|
||||
raise_if_disabled
|
||||
|
||||
return Rails.logger.info "Skipping auto-upgrades because app is set to manual upgrades. Please set UPGRADES_MODE=auto to enable auto-upgrades" if Setting.upgrades_mode == "manual"
|
||||
|
||||
Rails.logger.info "Searching for available auto-upgrades..."
|
||||
|
||||
candidate = Upgrader.available_upgrade_by_type(Setting.upgrades_target)
|
||||
|
||||
if candidate
|
||||
if Rails.cache.read("last_auto_upgrade_commit_sha") == candidate.commit_sha
|
||||
Rails.logger.info "Skipping auto upgrade: #{candidate.type} #{candidate.commit_sha} deploy in progress"
|
||||
return
|
||||
end
|
||||
|
||||
Rails.logger.info "Auto upgrading to #{candidate.type} #{candidate.commit_sha}..."
|
||||
Upgrader.upgrade_to(candidate)
|
||||
Rails.cache.write("last_auto_upgrade_commit_sha", candidate.commit_sha, expires_in: 1.day)
|
||||
else
|
||||
Rails.logger.info "No auto upgrade available at this time"
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def raise_if_disabled
|
||||
raise "Upgrades module is disabled. Please set UPGRADES_ENABLED=true to enable upgrade features" unless ENV["UPGRADES_ENABLED"] == "true"
|
||||
end
|
||||
end
|
|
@ -9,5 +9,9 @@ module Providable
|
|||
def exchange_rates_provider
|
||||
Provider::Synth.new
|
||||
end
|
||||
|
||||
def git_repository_provider
|
||||
Provider::Github.new
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
47
app/models/provider/github.rb
Normal file
47
app/models/provider/github.rb
Normal file
|
@ -0,0 +1,47 @@
|
|||
class Provider::Github
|
||||
attr_reader :name, :owner, :branch
|
||||
|
||||
def initialize(config = {})
|
||||
@name = config[:name] || ENV.fetch("GITHUB_REPO_NAME", "maybe")
|
||||
@owner = config[:owner] || ENV.fetch("GITHUB_REPO_OWNER", "maybe-finance")
|
||||
@branch = config[:branch] || ENV.fetch("GITHUB_REPO_BRANCH", "main")
|
||||
end
|
||||
|
||||
def fetch_latest_upgrade_candidates
|
||||
Rails.cache.fetch("latest_github_upgrade_candidates", expires_in: 2.minutes) do
|
||||
Rails.logger.info "Fetching latest GitHub upgrade candidates from #{repo} on branch #{branch}..."
|
||||
begin
|
||||
latest_release = Octokit.releases(repo).first
|
||||
latest_version = latest_release ? Semver.from_release_tag(latest_release.tag_name) : Semver.new(Maybe.version)
|
||||
latest_commit = Octokit.branch(repo, branch)
|
||||
|
||||
release_info = if latest_release
|
||||
{
|
||||
version: latest_version,
|
||||
url: latest_release.html_url,
|
||||
commit_sha: Octokit.commit(repo, latest_release.tag_name).sha
|
||||
}
|
||||
end
|
||||
|
||||
commit_info = {
|
||||
version: latest_version,
|
||||
commit_sha: latest_commit.commit.sha,
|
||||
url: latest_commit.commit.html_url
|
||||
}
|
||||
|
||||
{
|
||||
release: release_info,
|
||||
commit: commit_info
|
||||
}
|
||||
rescue => e
|
||||
Rails.logger.error "Failed to fetch latest GitHub commits: #{e.message}"
|
||||
nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def repo
|
||||
"#{owner}/#{name}"
|
||||
end
|
||||
end
|
19
app/models/setting.rb
Normal file
19
app/models/setting.rb
Normal file
|
@ -0,0 +1,19 @@
|
|||
# Dynamic settings the user can change within the app (helpful for self-hosting)
|
||||
class Setting < RailsSettings::Base
|
||||
cache_prefix { "v1" }
|
||||
|
||||
field :render_deploy_hook,
|
||||
type: :string,
|
||||
default: ENV["RENDER_DEPLOY_HOOK"],
|
||||
validates: { allow_blank: true, format: { with: /\Ahttps:\/\/api\.render\.com\/deploy\/srv-.+\z/ } }
|
||||
|
||||
field :upgrades_mode,
|
||||
type: :string,
|
||||
default: ENV.fetch("UPGRADES_MODE", "manual"),
|
||||
validates: { inclusion: { in: %w[manual auto] } }
|
||||
|
||||
field :upgrades_target,
|
||||
type: :string,
|
||||
default: ENV.fetch("UPGRADES_TARGET", "release"),
|
||||
validates: { inclusion: { in: %w[release commit] } }
|
||||
end
|
57
app/models/upgrader.rb
Normal file
57
app/models/upgrader.rb
Normal file
|
@ -0,0 +1,57 @@
|
|||
class Upgrader
|
||||
include Provided
|
||||
|
||||
class << self
|
||||
attr_writer :config
|
||||
|
||||
def config
|
||||
@config ||= Config.new
|
||||
end
|
||||
|
||||
def upgrade_to(commit_or_upgrade)
|
||||
upgrade = commit_or_upgrade.is_a?(String) ? find_upgrade(commit_or_upgrade) : commit_or_upgrade
|
||||
config.deployer.deploy(upgrade)
|
||||
end
|
||||
|
||||
def find_upgrade(commit)
|
||||
upgrade_candidates.find { |candidate| candidate.commit_sha == commit }
|
||||
end
|
||||
|
||||
def available_upgrade
|
||||
available_upgrades.first
|
||||
end
|
||||
|
||||
# Default to showing releases first, then commits
|
||||
def completed_upgrade
|
||||
completed_upgrades.find { |upgrade| upgrade.type == "release" } || completed_upgrades.first
|
||||
end
|
||||
|
||||
def available_upgrade_by_type(type)
|
||||
if type == "commit"
|
||||
commit_upgrade = available_upgrades.find { |upgrade| upgrade.type == "commit" }
|
||||
commit_upgrade || available_upgrades.first
|
||||
elsif type == "release"
|
||||
available_upgrades.find { |upgrade| upgrade.type == "release" }
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def available_upgrades
|
||||
upgrade_candidates.select(&:available?)
|
||||
end
|
||||
|
||||
def completed_upgrades
|
||||
upgrade_candidates.select(&:complete?)
|
||||
end
|
||||
|
||||
def upgrade_candidates
|
||||
latest_candidates = fetch_latest_upgrade_candidates_from_provider
|
||||
return [] unless latest_candidates
|
||||
|
||||
commit_candidate = Upgrade.new("commit", latest_candidates[:commit])
|
||||
release_candidate = latest_candidates[:release] && Upgrade.new("release", latest_candidates[:release])
|
||||
|
||||
[ release_candidate, commit_candidate ].compact.uniq { |candidate| candidate.commit_sha }
|
||||
end
|
||||
end
|
||||
end
|
17
app/models/upgrader/config.rb
Normal file
17
app/models/upgrader/config.rb
Normal file
|
@ -0,0 +1,17 @@
|
|||
class Upgrader::Config
|
||||
attr_reader :env, :options
|
||||
|
||||
def initialize(options = {}, env: ENV)
|
||||
@env = env
|
||||
@options = options
|
||||
end
|
||||
|
||||
def deployer
|
||||
factory = Upgrader::Deployer
|
||||
factory.for(hosting_platform)
|
||||
end
|
||||
|
||||
def hosting_platform
|
||||
options[:hosting_platform] || env["HOSTING_PLATFORM"]
|
||||
end
|
||||
end
|
12
app/models/upgrader/deployer.rb
Normal file
12
app/models/upgrader/deployer.rb
Normal file
|
@ -0,0 +1,12 @@
|
|||
class Upgrader::Deployer
|
||||
def self.for(platform)
|
||||
case platform
|
||||
when nil, "localhost"
|
||||
Upgrader::Deployer::Null.new
|
||||
when "render"
|
||||
Upgrader::Deployer::Render.new
|
||||
else
|
||||
raise "Unknown platform: #{platform}"
|
||||
end
|
||||
end
|
||||
end
|
8
app/models/upgrader/deployer/null.rb
Normal file
8
app/models/upgrader/deployer/null.rb
Normal file
|
@ -0,0 +1,8 @@
|
|||
class Upgrader::Deployer::Null
|
||||
def deploy(upgrade)
|
||||
{
|
||||
success: true,
|
||||
message: I18n.t("upgrader.deployer.null_deployer.success_message")
|
||||
}
|
||||
end
|
||||
end
|
41
app/models/upgrader/deployer/render.rb
Normal file
41
app/models/upgrader/deployer/render.rb
Normal file
|
@ -0,0 +1,41 @@
|
|||
class Upgrader::Deployer::Render
|
||||
def deploy(upgrade)
|
||||
if Setting.render_deploy_hook.blank?
|
||||
return {
|
||||
success: false,
|
||||
message: I18n.t("upgrader.deployer.render.error_message_not_set"),
|
||||
troubleshooting_url: "/settings/self_hosting/edit"
|
||||
}
|
||||
end
|
||||
|
||||
Rails.logger.info I18n.t("upgrader.deployer.render.deploy_log_info", type: upgrade.type, commit_sha: upgrade.commit_sha)
|
||||
|
||||
begin
|
||||
uri = URI.parse(Setting.render_deploy_hook)
|
||||
uri.query = [ uri.query, "ref=#{upgrade.commit_sha}" ].compact.join("&")
|
||||
response = Faraday.post(uri.to_s)
|
||||
|
||||
unless response.success?
|
||||
Rails.logger.error I18n.t("upgrader.deployer.render.deploy_log_error", type: upgrade.type, commit_sha: upgrade.commit_sha, error_message: response.body)
|
||||
return default_error_response
|
||||
end
|
||||
|
||||
{
|
||||
success: true,
|
||||
message: I18n.t("upgrader.deployer.render.success_message", commit_sha: upgrade.commit_sha.slice(0, 7))
|
||||
}
|
||||
rescue => e
|
||||
Rails.logger.error I18n.t("upgrader.deployer.render.deploy_log_error", type: upgrade.type, commit_sha: upgrade.commit_sha, error_message: e.message)
|
||||
default_error_response
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def default_error_response
|
||||
{
|
||||
success: false,
|
||||
message: I18n.t("upgrader.deployer.render.error_message_failed_deploy"),
|
||||
troubleshooting_url: I18n.t("upgrader.deployer.render.troubleshooting_url")
|
||||
}
|
||||
end
|
||||
end
|
11
app/models/upgrader/provided.rb
Normal file
11
app/models/upgrader/provided.rb
Normal file
|
@ -0,0 +1,11 @@
|
|||
module Upgrader::Provided
|
||||
extend ActiveSupport::Concern
|
||||
include Providable
|
||||
|
||||
class_methods do
|
||||
private
|
||||
def fetch_latest_upgrade_candidates_from_provider
|
||||
git_repository_provider.fetch_latest_upgrade_candidates
|
||||
end
|
||||
end
|
||||
end
|
27
app/models/upgrader/upgrade.rb
Normal file
27
app/models/upgrader/upgrade.rb
Normal file
|
@ -0,0 +1,27 @@
|
|||
class Upgrader::Upgrade
|
||||
attr_reader :type, :commit_sha, :version, :url
|
||||
|
||||
def initialize(type, data)
|
||||
@type = %w[release commit].include?(type) ? type : raise(ArgumentError, "Type must be either 'release' or 'commit'")
|
||||
@commit_sha = data[:commit_sha]
|
||||
@version = normalize_version(data[:version])
|
||||
@url = data[:url]
|
||||
end
|
||||
|
||||
def complete?
|
||||
commit_sha == Maybe.commit_sha
|
||||
end
|
||||
|
||||
def available?
|
||||
version > Maybe.version || (version == Maybe.version && commit_sha != Maybe.commit_sha)
|
||||
end
|
||||
|
||||
def to_s
|
||||
type == "release" ? version.to_release_tag : "#{commit_sha.first(7)} (pre-release)"
|
||||
end
|
||||
|
||||
private
|
||||
def normalize_version(version)
|
||||
version.is_a?(Semver) ? version : Semver.new(version)
|
||||
end
|
||||
end
|
|
@ -10,4 +10,20 @@ class User < ApplicationRecord
|
|||
generates_token_for :password_reset, expires_in: 15.minutes do
|
||||
password_salt&.last(10)
|
||||
end
|
||||
|
||||
def acknowledge_upgrade_prompt(commit_sha)
|
||||
update!(last_prompted_upgrade_commit_sha: commit_sha)
|
||||
end
|
||||
|
||||
def acknowledge_upgrade_alert(commit_sha)
|
||||
update!(last_alerted_upgrade_commit_sha: commit_sha)
|
||||
end
|
||||
|
||||
def has_seen_upgrade_prompt?(upgrade)
|
||||
last_prompted_upgrade_commit_sha == upgrade.commit_sha
|
||||
end
|
||||
|
||||
def has_seen_upgrade_alert?(upgrade)
|
||||
last_alerted_upgrade_commit_sha == upgrade.commit_sha
|
||||
end
|
||||
end
|
||||
|
|
|
@ -40,7 +40,13 @@
|
|||
class="hidden absolute min-w-[200px] z-10 top-10 right-0 bg-white p-1 rounded-sm shadow-xs border border-alpha-black-25 w-fit">
|
||||
<%= link_to edit_settings_path, class: "flex gap-1 items-center hover:bg-gray-50 rounded-md p-2" do %>
|
||||
<%= lucide_icon("pencil-line", class: "w-5 h-5 text-gray-500 shrink-0") %>
|
||||
<span class="text-gray-900 text-sm">Settings</span>
|
||||
<span class="text-gray-900 text-sm">General Settings</span>
|
||||
<% end %>
|
||||
<% if self_hosted? %>
|
||||
<%= link_to edit_settings_self_hosting_path, class: "flex gap-1 items-center hover:bg-gray-50 rounded-md p-2" do %>
|
||||
<%= lucide_icon("pencil-line", class: "w-5 h-5 text-gray-500 shrink-0") %>
|
||||
<span class="text-gray-900 text-sm">Self Host Settings</span>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<%= button_to session_path, method: :delete, class: "w-full text-gray-900 flex gap-1 items-center hover:bg-gray-50 rounded-md p-2" do %>
|
||||
<%= lucide_icon("log-out", class: "w-5 h-5 shrink-0") %>
|
||||
|
@ -86,5 +92,6 @@
|
|||
</div>
|
||||
<%= turbo_frame_tag "modal" %>
|
||||
<%= render "shared/custom_confirm_modal" %>
|
||||
<%= render "shared/upgrade_notification" %>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -1,19 +1,13 @@
|
|||
<%
|
||||
header_title t(".title")
|
||||
%>
|
||||
|
||||
<%= form_with model: @user, url: registration_path do |form| %>
|
||||
<%= auth_messages form %>
|
||||
|
||||
<%= form.email_field :email, autofocus: false, autocomplete: "email", required: "required", placeholder: "you@example.com", label: true %>
|
||||
|
||||
<%= form.password_field :password, autocomplete: "new-password", required: "required", label: true %>
|
||||
|
||||
<%= form.password_field :password_confirmation, autocomplete: "new-password", required: "required", label: true %>
|
||||
|
||||
<% if hosted_app? %>
|
||||
<% if invite_code_required? %>
|
||||
<%= form.password_field :invite_code, required: "required", label: true %>
|
||||
<% end %>
|
||||
|
||||
<%= form.submit %>
|
||||
<% end %>
|
||||
|
|
40
app/views/settings/self_hosting/edit.html.erb
Normal file
40
app/views/settings/self_hosting/edit.html.erb
Normal file
|
@ -0,0 +1,40 @@
|
|||
<div class="space-y-4">
|
||||
<h1 class="text-3xl font-semibold font-display">Edit Self Hosting Settings</h1>
|
||||
<hr>
|
||||
<%= form_with model: Setting.new, url: settings_self_hosting_path, method: :patch, local: true, html: { class: "space-y-4" } do |form| %>
|
||||
<section class="space-y-3">
|
||||
<h2 class="text-2xl font-semibold">Render Deploy Hook</h2>
|
||||
<p class="text-gray-500">You must fill this in so your app can trigger upgrades when Maybe releases upgrades. Learn more about deploy hooks and how they work in the <%= link_to "Render documentation", "https://docs.render.com/docs/deploy-hooks", target: "_blank", rel: "noopener noreferrer", class: "text-blue-500 hover:underline" %>.</p>
|
||||
<%= form.text_field :render_deploy_hook, label: "Render Deploy Hook", placeholder: "https://api.render.com/deploy/srv-xyz...", value: Setting.render_deploy_hook %>
|
||||
</section>
|
||||
<section class="space-y-3">
|
||||
<h2 class="text-2xl font-semibold">Auto Upgrades Setting</h2>
|
||||
<p class="text-gray-500">This setting controls how often your self hosted app will update and what method it uses to do so.</p>
|
||||
<div class="space-y-2">
|
||||
<div class="flex items-center gap-2">
|
||||
<%= form.check_box :upgrades_mode, { checked: Setting.upgrades_mode == "auto", unchecked_value: "manual" }, "auto", "manual" %>
|
||||
<%= form.label :upgrades_mode, "Enable auto upgrades", class: "text-gray-900" %>
|
||||
</div>
|
||||
<% if Setting.upgrades_mode == "auto" %>
|
||||
<div class="flex items-center gap-2">
|
||||
<%= form.radio_button :upgrades_target, "release", checked: Setting.upgrades_target == "release" %>
|
||||
<%= form.label :upgrades_target_release, class: Setting.upgrades_target == "release" ? "text-gray-900" : "text-gray-500" do %>
|
||||
<span class="font-semibold">Latest Release (suggested)</span> - you will automatically be upgraded to the latest stable release of Maybe
|
||||
<% end %>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<%= form.radio_button :upgrades_target, "commit", checked: Setting.upgrades_target == "commit" %>
|
||||
<%= form.label :upgrades_target_commit, class: Setting.upgrades_target == "commit" ? "text-gray-900" : "text-gray-500" do %>
|
||||
<span class="font-semibold">Latest Commit</span> - you will automatically be upgraded any time the Maybe repo is updated
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
</section>
|
||||
<div class="fixed right-5 bottom-5">
|
||||
<button type="submit" class="flex items-center justify-center w-12 h-12 mb-2 bg-black rounded-full shrink-0 grow-0 hover:bg-gray-600">
|
||||
<%= inline_svg_tag("icn-check.svg", class: "text-white fill-current") %>
|
||||
</button>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
19
app/views/shared/_upgrade_notification.html.erb
Normal file
19
app/views/shared/_upgrade_notification.html.erb
Normal file
|
@ -0,0 +1,19 @@
|
|||
<% if upgrade_notification %>
|
||||
<% upgrade = upgrade_notification %>
|
||||
<div class="bg-white space-y-4 text-right fixed bottom-10 right-10 p-5 border border-alpha-black-200 shadow-xs rounded-md z-50 max-w-[350px]">
|
||||
<div>
|
||||
<p><%= link_to upgrade.to_s, upgrade.url, class: "text-sm text-blue-500 underline hover:text-blue-700", target: "_blank" %></p>
|
||||
<% if upgrade.complete? %>
|
||||
<p class="text-gray-900"><%= t(".app_upgraded", version: upgrade.to_s) %></p>
|
||||
<% else %>
|
||||
<p class="text-gray-900"><%= t(".new_version_available") %></p>
|
||||
<% end %>
|
||||
</div>
|
||||
<div class="flex justify-end items-center gap-2">
|
||||
<%= button_to t(".dismiss"), acknowledge_upgrade_path(upgrade.commit_sha), method: :post, class: "#{upgrade.complete? ? 'bg-gray-900 text-white' : 'bg-gray-100 text-gray-900'} text-sm font-bold p-2 rounded-lg" %>
|
||||
<% if upgrade.available? %>
|
||||
<%= button_to t(".upgrade_now"), deploy_upgrade_path(upgrade.commit_sha), method: :post, class: "bg-gray-900 hover:bg-gray-700 text-white font-medium text-sm p-2 rounded-lg" %>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
Loading…
Add table
Add a link
Reference in a new issue