Add Sandbox/Development environment indicator feature
This commit adds a new setting that displays a visual "SANDBOX" banner when the environment is marked as development or sandbox. Changes: - Add is_sandbox_environment boolean column to SystemSettings model - Add database migration for automatic schema update - Add Environment section in Settings > General with toggle switch - Create sandbox.css with diagonal banner styling (non-interactive) - Inject system_settings into all template contexts - Add sandbox banner to base.html layout - Update changelog with feature description The banner appears as a red diagonal ribbon in the top-left corner and uses pointer-events: none to remain non-interactive while keeping underlying elements clickable. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
d5cbba2f59
commit
0bf1151f01
@ -439,6 +439,10 @@ def settings():
|
||||
# Checkbox: present in form = checked, absent = unchecked.
|
||||
settings.require_daily_dashboard_visit = bool(request.form.get("require_daily_dashboard_visit"))
|
||||
|
||||
# Environment indicator is in the same form (General tab), so process it here.
|
||||
# Checkbox: present in form = checked, absent = unchecked.
|
||||
settings.is_sandbox_environment = bool(request.form.get("is_sandbox_environment"))
|
||||
|
||||
# Autotask integration
|
||||
if "autotask_enabled" in request.form:
|
||||
settings.autotask_enabled = bool(request.form.get("autotask_enabled"))
|
||||
|
||||
@ -109,7 +109,11 @@ def get_user_roles() -> list[str]:
|
||||
|
||||
@main_bp.app_context_processor
|
||||
def _inject_role_context():
|
||||
return {"active_role": get_active_role(), "user_roles": get_user_roles()}
|
||||
return {
|
||||
"active_role": get_active_role(),
|
||||
"user_roles": get_user_roles(),
|
||||
"system_settings": _get_or_create_settings(),
|
||||
}
|
||||
|
||||
|
||||
def roles_required(*roles):
|
||||
|
||||
@ -1034,6 +1034,7 @@ def run_migrations() -> None:
|
||||
migrate_system_settings_daily_jobs_start_date()
|
||||
migrate_system_settings_ui_timezone()
|
||||
migrate_system_settings_autotask_integration()
|
||||
migrate_system_settings_sandbox_environment()
|
||||
migrate_customers_autotask_company_mapping()
|
||||
migrate_mail_messages_columns()
|
||||
migrate_mail_messages_parse_columns()
|
||||
@ -1138,6 +1139,38 @@ def migrate_system_settings_require_daily_dashboard_visit() -> None:
|
||||
print(f"[migrations] Failed to migrate system_settings.require_daily_dashboard_visit: {exc}")
|
||||
|
||||
|
||||
def migrate_system_settings_sandbox_environment() -> None:
|
||||
"""Add is_sandbox_environment column to system_settings if missing.
|
||||
|
||||
When enabled, a visual banner is displayed on all pages to indicate
|
||||
this is not a production environment.
|
||||
"""
|
||||
table = "system_settings"
|
||||
column = "is_sandbox_environment"
|
||||
|
||||
try:
|
||||
engine = db.get_engine()
|
||||
except Exception as exc:
|
||||
print(f"[migrations] Could not get engine for system_settings sandbox environment migration: {exc}")
|
||||
return
|
||||
|
||||
try:
|
||||
if _column_exists(table, column):
|
||||
print("[migrations] system_settings.is_sandbox_environment already exists.")
|
||||
return
|
||||
|
||||
with engine.begin() as conn:
|
||||
conn.execute(
|
||||
text(
|
||||
f'ALTER TABLE "{table}" ADD COLUMN {column} BOOLEAN NOT NULL DEFAULT FALSE'
|
||||
)
|
||||
)
|
||||
|
||||
print("[migrations] migrate_system_settings_sandbox_environment completed.")
|
||||
except Exception as exc:
|
||||
print(f"[migrations] Failed to migrate system_settings.is_sandbox_environment: {exc}")
|
||||
|
||||
|
||||
def migrate_performance_indexes() -> None:
|
||||
"""Add performance indexes for frequently queried foreign key columns.
|
||||
|
||||
|
||||
@ -112,6 +112,11 @@ class SystemSettings(db.Model):
|
||||
# their first page view each day before they can navigate elsewhere.
|
||||
require_daily_dashboard_visit = db.Column(db.Boolean, nullable=False, default=False)
|
||||
|
||||
# Development/Sandbox environment indicator.
|
||||
# When enabled, a visual banner is displayed on all pages to indicate
|
||||
# this is not a production environment.
|
||||
is_sandbox_environment = db.Column(db.Boolean, nullable=False, default=False)
|
||||
|
||||
# Autotask integration settings
|
||||
autotask_enabled = db.Column(db.Boolean, nullable=False, default=False)
|
||||
autotask_environment = db.Column(db.String(32), nullable=True) # sandbox | production
|
||||
|
||||
27
containers/backupchecks/src/static/css/sandbox.css
Normal file
27
containers/backupchecks/src/static/css/sandbox.css
Normal file
@ -0,0 +1,27 @@
|
||||
/* Sandbox environment visual indicator banner
|
||||
* Displays a diagonal "SANDBOX" ribbon in the top-left corner
|
||||
* when the is_sandbox_environment setting is enabled.
|
||||
*/
|
||||
|
||||
.sandbox-banner {
|
||||
position: fixed;
|
||||
top: 30px;
|
||||
left: -60px;
|
||||
width: 250px;
|
||||
background-color: #dc3545;
|
||||
color: white;
|
||||
text-align: center;
|
||||
transform: rotate(-45deg);
|
||||
z-index: 9999;
|
||||
pointer-events: none; /* Banner itself is not clickable, elements behind it are */
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.sandbox-banner-text {
|
||||
display: block;
|
||||
padding: 8px 0;
|
||||
font-weight: bold;
|
||||
font-size: 16px;
|
||||
letter-spacing: 2px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
@ -11,6 +11,7 @@
|
||||
/>
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/layout.css') }}" />
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/status-text.css') }}" />
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/sandbox.css') }}" />
|
||||
<link rel="icon" type="image/x-icon" href="{{ url_for('static', filename='favicon.ico') }}" />
|
||||
{% block head %}{% endblock %}
|
||||
|
||||
@ -52,6 +53,12 @@
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
{% if system_settings and system_settings.is_sandbox_environment %}
|
||||
<div class="sandbox-banner">
|
||||
<span class="sandbox-banner-text">SANDBOX</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<nav class="navbar navbar-expand-lg fixed-top bg-body-tertiary border-bottom">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand" href="{{ url_for('main.dashboard') }}">Backupchecks</a>
|
||||
|
||||
@ -150,6 +150,17 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card mb-3">
|
||||
<div class="card-header">Environment</div>
|
||||
<div class="card-body">
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" id="is_sandbox_environment" name="is_sandbox_environment" {% if settings.is_sandbox_environment %}checked{% endif %} />
|
||||
<label class="form-check-label" for="is_sandbox_environment">Mark this as a Sandbox/Development environment</label>
|
||||
</div>
|
||||
<div class="form-text">When enabled, a visual banner will be displayed on all pages to indicate this is not a production environment.</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-end mt-3">
|
||||
<button type="submit" class="btn btn-primary">Save settings</button>
|
||||
</div>
|
||||
|
||||
@ -4,6 +4,14 @@ This file documents all changes made to this project via Claude Code.
|
||||
|
||||
## [2026-02-06]
|
||||
|
||||
### Added
|
||||
- Added Sandbox/Development environment indicator feature:
|
||||
- New setting in Settings > General > Environment to mark environment as Sandbox/Development
|
||||
- Visual "SANDBOX" banner displayed diagonally in top-left corner when enabled
|
||||
- Banner is non-interactive (pointer-events: none) so elements behind it remain clickable
|
||||
- Setting defaults to OFF for production safety
|
||||
- Database migration added for automatic schema update on deployment
|
||||
|
||||
### Changed
|
||||
- Renamed "Refresh" button to "Search" in Link existing Autotask ticket modal for better clarity (the button performs a search operation)
|
||||
- Added ellipsis-field functionality to "Overall remark" and "Remark" fields in Run Checks modal to prevent long text from hiding action buttons (click to expand/collapse)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user