mirror of
https://github.com/we-promise/sure.git
synced 2026-05-24 13:04:56 +00:00
* refactor: rename beta features gate to preview features Renames the opt-in gate introduced in PR #1829 from "beta" to "preview". Same shape (per-user JSONB toggle, `before_action` concern, marker pill) just retitled so the surface speaks the language Sure uses elsewhere ("preview" reads as in-progress, "beta" had baggage with provider maturity copy and external testing programs). Renames: - BetaGateable -> PreviewGateable - require_beta_features! -> require_preview_features! - beta_features_enabled? -> preview_features_enabled? - preferences["beta_features_enabled"] -> preferences["preview_features_enabled"] - DS::Pill default label "Beta" -> "Preview" - Settings -> Preferences toggle copy "beta features" -> "preview features" - config/locales/views/beta/ -> config/locales/views/preview/ - docs/llm-guides/gating-a-beta-feature.md -> gating-a-preview-feature.md Includes a data migration that copies any existing `beta_features_enabled` JSONB key into `preview_features_enabled` so early opt-ins survive the rename, then removes the old key. The migration is fully reversible. Provider maturity copy ("maturity.beta = Beta" under Settings -> Bank sync) is intentionally untouched - that's a separate concept describing a provider's integration stability, not Sure's feature gate. * review: apply CodeRabbit findings on PR #1837 - Settings::PreferencesController#update now routes the `preview_features_enabled` input through strong params and casts via ActiveModel::Type::Boolean instead of reading raw params and string- comparing to "1". Matches Sure's controller convention for permitted params and avoids stringly-typed boolean handling. - Rename migration now wraps the destination JSONB key write in COALESCE so a row that somehow ends up with both keys keeps the destination value instead of having it overwritten by the source. Up and down paths get the same defensive shape. * 📝 CodeRabbit Chat: Implement requested code changes * 📝 CodeRabbit Chat: Implement requested code changes * fix: restore all missing translation keys; rename beta→preview label * fix: restore all missing sections (appearances, debugs, llm_usages, providers, etc.); rename beta→preview * fix: restore missing keys (member_removal_failed, confirm_delete, etc.); add preview section * fix(i18n/ca): use 'està en vista prèvia' instead of 'és una vista prèvia' * fix(i18n/ca): use 'en desenvolupament'; drop article in preview title * fix(i18n/es): use 'en desarrollo' instead of 'en progreso' * fix(i18n/ca): use 'funcions experimentals' instead of 'vista prèvia' * fix(i18n/es): use 'funciones experimentales' instead of 'vista previa' * fix(i18n/ca): use 'funcions experimentals' in preferences.show.preview * fix(i18n/es): use 'funciones experimentales' in preferences.show.preview * fix(i18n/ca): use 'Experimental' pill label instead of 'Vista prèvia' * fix(i18n/es): use 'Experimental' pill label instead of 'Vista previa' --------- Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
107 lines
2.8 KiB
Ruby
107 lines
2.8 KiB
Ruby
class ApplicationController < ActionController::Base
|
|
include RestoreLayoutPreferences, Onboardable, Localize, AutoSync, Authentication, Invitable,
|
|
SelfHostable, StoreLocation, Impersonatable, Breadcrumbable,
|
|
FeatureGuardable, Notifiable, SafePagination, AccountAuthorizable,
|
|
PreviewGateable
|
|
include Pundit::Authorization
|
|
|
|
include Pagy::Backend
|
|
|
|
# Pundit uses current_user by default, but this app uses Current.user
|
|
def pundit_user
|
|
Current.user
|
|
end
|
|
|
|
before_action :detect_os
|
|
before_action :set_default_chat
|
|
before_action :set_active_storage_url_options
|
|
|
|
helper_method :demo_config, :demo_host_match?, :show_demo_warning?
|
|
|
|
private
|
|
def accept_pending_invitation_for(user)
|
|
return false if user.blank?
|
|
|
|
token = session[:pending_invitation_token]
|
|
return false if token.blank?
|
|
|
|
invitation = Invitation.pending.find_by(token: token.to_s)
|
|
return false unless invitation
|
|
return false unless invitation.accept_for(user)
|
|
|
|
session.delete(:pending_invitation_token)
|
|
true
|
|
end
|
|
|
|
def store_pending_invitation_if_valid
|
|
token = params[:invitation].to_s.presence
|
|
return if token.blank?
|
|
|
|
invitation = Invitation.pending.find_by(token: token)
|
|
session[:pending_invitation_token] = token if invitation
|
|
end
|
|
|
|
def require_admin!
|
|
return if Current.user&.admin?
|
|
|
|
respond_to do |format|
|
|
format.html { redirect_to accounts_path, alert: t("shared.require_admin") }
|
|
format.turbo_stream { head :forbidden }
|
|
format.json { head :forbidden }
|
|
format.any { head :forbidden }
|
|
end
|
|
end
|
|
|
|
def detect_os
|
|
user_agent = request.user_agent
|
|
@os = case user_agent
|
|
when /Windows/i then "windows"
|
|
when /Macintosh/i then "mac"
|
|
when /Linux/i then "linux"
|
|
when /Android/i then "android"
|
|
when /iPhone|iPad/i then "ios"
|
|
else ""
|
|
end
|
|
end
|
|
|
|
# By default, we show the user the last chat they interacted with
|
|
def set_default_chat
|
|
@last_viewed_chat = Current.user&.last_viewed_chat
|
|
@chat = @last_viewed_chat
|
|
end
|
|
|
|
def set_active_storage_url_options
|
|
ActiveStorage::Current.url_options = {
|
|
protocol: request.protocol,
|
|
host: request.host,
|
|
port: request.optional_port
|
|
}
|
|
end
|
|
|
|
def demo_config
|
|
Rails.application.config_for(:demo)
|
|
rescue RuntimeError, Errno::ENOENT, Psych::SyntaxError
|
|
nil
|
|
end
|
|
|
|
def demo_host_match?(demo = demo_config)
|
|
return false unless demo.is_a?(Hash) && demo["hosts"].present?
|
|
|
|
demo["hosts"].include?(request.host)
|
|
end
|
|
|
|
def show_demo_warning?
|
|
demo_host_match?
|
|
end
|
|
|
|
def accessible_accounts
|
|
Current.accessible_accounts
|
|
end
|
|
helper_method :accessible_accounts
|
|
|
|
def finance_accounts
|
|
Current.finance_accounts
|
|
end
|
|
helper_method :finance_accounts
|
|
end
|