chatwoot/app/controllers/dashboard_controller.rb
Muhsin Keloth 5325e05143
feat: Add platform-wide status banners for outage notifications (#13943)
Adds a platform-wide status banner system to notify all users about
external service outages. Super Admins can create, edit, and manage
banners via the Super Admin console. Banners support markdown for links
and are dismissible by users.


<img width="1099" height="236" alt="image"
src="https://github.com/user-attachments/assets/047a7994-d885-4a8a-b9c4-aeb32f15474a"
/>




## How to test
1. Set `ENABLE_PLATFORM_BANNERS=true` in your environment
2. Go to Super Admin → Platform Banners
3. Create a banner with a message like: `Elevated error rates from Meta
APIs. [Check status](https://metastatus.com)`
4. Select a banner type: `info` (blue), `warning` (amber), or `error`
(red)
5. Visit the dashboard — the banner should appear at the top
6. Click "Dismiss" — the banner hides and stays dismissed across page
reloads
7. Deactivate the banner in Super Admin — it disappears on next page
load

## What changed
- New `PlatformBanner` model with `banner_message`, `banner_type`
(info/warning/error), and `active` flag
- Super Admin CRUD via Administrate (controller, dashboard, routes,
sidebar icon)
- `DashboardController` serves active banners via `globalConfig`
- `StatusBanner.vue` component renders banners with markdown support and
per-banner localStorage dismiss
- Feature gated behind `ENABLE_PLATFORM_BANNERS` env var

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com>
Co-authored-by: iamsivin <iamsivin@gmail.com>
Co-authored-by: Muhsin <12408980+muhsin-k@users.noreply.github.com>
2026-04-29 17:18:38 +04:00

119 lines
3.8 KiB
Ruby

class DashboardController < ActionController::Base
include SwitchLocale
GLOBAL_CONFIG_KEYS = %w[
LOGO
LOGO_DARK
LOGO_THUMBNAIL
INSTALLATION_NAME
WIDGET_BRAND_URL
TERMS_URL
BRAND_URL
BRAND_NAME
PRIVACY_URL
DISPLAY_MANIFEST
CREATE_NEW_ACCOUNT_FROM_DASHBOARD
CHATWOOT_INBOX_TOKEN
API_CHANNEL_NAME
API_CHANNEL_THUMBNAIL
CLOUD_ANALYTICS_TOKEN
DIRECT_UPLOADS_ENABLED
MAXIMUM_FILE_UPLOAD_SIZE
HCAPTCHA_SITE_KEY
LOGOUT_REDIRECT_LINK
DISABLE_USER_PROFILE_UPDATE
DEPLOYMENT_ENV
INSTALLATION_PRICING_PLAN
].freeze
before_action :set_application_pack
before_action :set_global_config
before_action :set_dashboard_scripts
around_action :switch_locale
before_action :ensure_installation_onboarding, only: [:index]
before_action :render_hc_if_custom_domain, only: [:index]
before_action :ensure_html_format
layout 'vueapp'
def index; end
private
def ensure_html_format
render json: { error: 'Please use API routes instead of dashboard routes for JSON requests' }, status: :not_acceptable if request.format.json?
end
def set_global_config
@global_config = GlobalConfig.get(*GLOBAL_CONFIG_KEYS).merge(app_config)
end
def set_dashboard_scripts
@dashboard_scripts = sensitive_path? ? nil : GlobalConfig.get_value('DASHBOARD_SCRIPTS')
end
def ensure_installation_onboarding
redirect_to '/installation/onboarding' if ::Redis::Alfred.get(::Redis::Alfred::CHATWOOT_INSTALLATION_ONBOARDING)
end
def render_hc_if_custom_domain
domain = request.host
return if domain == URI.parse(ENV.fetch('FRONTEND_URL', '')).host
@portal = Portal.find_by(custom_domain: domain)
return unless @portal
@locale = @portal.default_locale
render 'public/api/v1/portals/show', layout: 'portal', portal: @portal and return
end
def app_config
{
APP_VERSION: Chatwoot.config[:version],
VAPID_PUBLIC_KEY: VapidService.public_key,
ENABLE_ACCOUNT_SIGNUP: GlobalConfigService.load('ENABLE_ACCOUNT_SIGNUP', 'false'),
FB_APP_ID: GlobalConfigService.load('FB_APP_ID', ''),
INSTAGRAM_APP_ID: GlobalConfigService.load('INSTAGRAM_APP_ID', ''),
TIKTOK_APP_ID: GlobalConfigService.load('TIKTOK_APP_ID', ''),
FACEBOOK_API_VERSION: GlobalConfigService.load('FACEBOOK_API_VERSION', 'v18.0'),
WHATSAPP_APP_ID: GlobalConfigService.load('WHATSAPP_APP_ID', ''),
WHATSAPP_CONFIGURATION_ID: GlobalConfigService.load('WHATSAPP_CONFIGURATION_ID', ''),
IS_ENTERPRISE: ChatwootApp.enterprise?,
AZURE_APP_ID: GlobalConfigService.load('AZURE_APP_ID', ''),
GIT_SHA: GIT_HASH,
ALLOWED_LOGIN_METHODS: allowed_login_methods,
ACTIVE_PLATFORM_BANNERS: active_platform_banners
}
end
def active_platform_banners
return [] unless ChatwootApp.chatwoot_cloud?
PlatformBanner.active.order(created_at: :desc).as_json(only: %i[id banner_message banner_type updated_at])
end
def allowed_login_methods
methods = ['email']
methods << 'google_oauth' if GlobalConfigService.load('ENABLE_GOOGLE_OAUTH_LOGIN', 'true').to_s != 'false'
methods << 'saml' if ChatwootHub.pricing_plan != 'community' && GlobalConfigService.load('ENABLE_SAML_SSO_LOGIN', 'true').to_s != 'false'
methods
end
def set_application_pack
@application_pack = if request.path.include?('/auth') || request.path.include?('/login')
'v3app'
else
'dashboard'
end
end
def sensitive_path?
# dont load dashboard scripts on sensitive paths like password reset
sensitive_paths = [edit_user_password_path].freeze
# remove app prefix
current_path = request.path.gsub(%r{^/app}, '')
sensitive_paths.include?(current_path)
end
end