diff --git a/application/single_app/admin_settings_nav.py b/application/single_app/admin_settings_nav.py index 48026e3ea..742c57fd7 100644 --- a/application/single_app/admin_settings_nav.py +++ b/application/single_app/admin_settings_nav.py @@ -395,18 +395,50 @@ "icon": "bi-database", "tabs": [ { - "id": "data-management", - "label": "Backup, Migrate & Restore", - "icon": "bi-database-check", + # Schedule, storage and encryption are cards nested inside the + # backup card, so they stay with it. + "id": "backup", + "label": "Backup", + "icon": "bi-archive", "sections": [ {"id": "data-management-readiness-section", "label": "Start Here", "icon": "bi-compass"}, {"id": "data-management-backup-section", "label": "Backup", "icon": "bi-archive"}, {"id": "data-management-schedule-section", "label": "Schedule", "icon": "bi-calendar-event"}, {"id": "data-management-storage-section", "label": "Storage", "icon": "bi-hdd"}, {"id": "data-management-encryption-section", "label": "Encryption", "icon": "bi-key"}, + ], + }, + { + "id": "migrate", + "label": "Migrate", + "icon": "bi-arrow-left-right", + "sections": [ {"id": "data-management-migration-section", "label": "Migration", "icon": "bi-arrow-left-right"}, - {"id": "data-management-cosmos-editor-section", "label": "Cosmos Editor", "icon": "bi-database-exclamation"}, + ], + }, + { + "id": "restore", + "label": "Restore", + "icon": "bi-box-seam", + "sections": [ {"id": "data-management-backup-inventory-section", "label": "Backup Inventory & Restore", "icon": "bi-box-seam"}, + ], + }, + { + # A direct database editor. It is a repair tool that belongs + # with the backup and restore tooling it shares a module with. + "id": "cosmos-editor", + "label": "Cosmos Editor", + "icon": "bi-database-exclamation", + "sections": [ + {"id": "data-management-cosmos-editor-section", "label": "Cosmos Editor", "icon": "bi-database-exclamation"}, + ], + }, + { + "id": "jobs", + "label": "Jobs", + "icon": "bi-clock-history", + "sections": [ {"id": "data-management-jobs-section", "label": "Jobs", "icon": "bi-clock-history"}, ], }, diff --git a/application/single_app/config.py b/application/single_app/config.py index 27e50b825..4302f6372 100644 --- a/application/single_app/config.py +++ b/application/single_app/config.py @@ -96,7 +96,7 @@ EXECUTOR_TYPE = 'thread' EXECUTOR_MAX_WORKERS = 30 SESSION_TYPE = 'filesystem' -VERSION = "0.260.015" +VERSION = "0.260.016" IS_DEVELOPMENT = is_development_env_enabled() SESSION_COOKIE_SAMESITE = os.getenv('SESSION_COOKIE_SAMESITE', 'Lax') diff --git a/application/single_app/static/js/admin/admin_sidebar_nav.js b/application/single_app/static/js/admin/admin_sidebar_nav.js index 1bf0e5e53..b452447c9 100644 --- a/application/single_app/static/js/admin/admin_sidebar_nav.js +++ b/application/single_app/static/js/admin/admin_sidebar_nav.js @@ -157,7 +157,19 @@ function initAdminSidebarNav() { } } else { console.log('initAdminSidebarNav - Found existing active tab, preserving current state:', activeTab.getAttribute('data-tab')); + syncAdminGroupSharedRegions(activeTab.getAttribute('data-tab')); } + + // Clicking a tab button directly does not go through showAdminTab, so the + // shared regions are synced from Bootstrap's own event as well. + document.querySelectorAll('button.nav-link[data-bs-target^="#"]').forEach(button => { + button.addEventListener('shown.bs.tab', event => { + const target = event.target.getAttribute('data-bs-target'); + if (target) { + syncAdminGroupSharedRegions(target.slice(1)); + } + }); + }); } function setupAdminGroupToggles() { @@ -297,12 +309,43 @@ const LEGACY_TAB_REDIRECTS = { 'workspaces': 'workspace-types', 'search-extract': 'web-research', 'ai-models': 'model-endpoints', + 'data-management': 'backup', }; function resolveAdminTabId(tabId) { return LEGACY_TAB_REDIRECTS[tabId] || tabId; } +/** + * Some groups share one set of controls across all of their tabs, such as the + * single save button that serves every Backup & Recovery tab. Those controls + * cannot be duplicated into each pane without repeating element ids, and they + * cannot sit in one pane because the other tabs would lose them, so they live + * outside the panes and are revealed only while their group is active. + */ +function syncAdminGroupSharedRegions(tabId) { + const regions = document.querySelectorAll('[data-admin-group-shared]'); + if (!regions.length) { + return; + } + + // Only one of the two navigations is rendered at a time, so resolve the + // owning group from whichever is present. Looking only at the top tab strip + // would leave the region hidden for good in the sidebar layout. + const tabButton = document.querySelector(`.admin-tab-item[data-admin-group] button[data-bs-target="#${tabId}"]`); + let owner = tabButton ? tabButton.closest('[data-admin-group]') : null; + if (!owner) { + const sidebarLink = document.querySelector(`.admin-nav-tab[data-tab="${tabId}"]`); + owner = sidebarLink ? sidebarLink.closest('[data-admin-group]') : null; + } + const activeGroup = owner ? owner.getAttribute('data-admin-group') : null; + + regions.forEach(region => { + const ownerGroup = region.getAttribute('data-admin-group-shared'); + region.hidden = ownerGroup !== activeGroup; + }); +} + function showAdminTab(requestedTabId) { const tabId = resolveAdminTabId(requestedTabId); @@ -333,6 +376,7 @@ function showAdminTab(requestedTabId) { // Update the hash in URL for deep linking window.location.hash = tabId; + syncAdminGroupSharedRegions(tabId); if (typeof window.updateAdminSettingsSaveButtonState === 'function') { window.updateAdminSettingsSaveButtonState(); } diff --git a/application/single_app/templates/admin/_panes/backup.html b/application/single_app/templates/admin/_panes/backup.html new file mode 100644 index 000000000..a00542658 --- /dev/null +++ b/application/single_app/templates/admin/_panes/backup.html @@ -0,0 +1,323 @@ +
Use these checkpoints before running backup, migration, restore, or advanced repair actions.
+Configure dedicated storage, encryption, schedule, and backup scope before queueing jobs.
+ +Connect a destination, choose who moves, run preflight, then execute a recoverable transfer.
+ +Review backup readiness and stage restore decisions from Backup Inventory.
+ +Temporarily raise eligible Cosmos capacity during approved backup or migration windows.
+ +Configure when backups run, where artifacts are stored, and how backup files are encrypted.
+Full backups run on the selected cadence; partial backups run daily only.
+Store backup artifacts in Azure Blob Storage.
+Generate a 256-bit backup encryption key.
+Backups stream deterministic checkpoint batches and commit only verified work. Higher concurrency can increase source Cosmos cost and pressure.
+Source files stream through bounded chunks and durable per-file checkpoints. Peak transfer buffering is bounded by concurrent transfers multiplied by chunk size.
+Queue immediate full or partial backup jobs using the settings above.
+Jobs use Cosmos-backed leases so scaled-out App Service workers do not run the same backup twice.
+Query SimpleChat Cosmos DB containers, inspect one document, and save JSON changes with ETag protection.
+Protect SimpleChat data, move selected workspaces to another environment, and stage restore decisions with guided checks.
-Use these checkpoints before running backup, migration, restore, or advanced repair actions.
-Configure dedicated storage, encryption, schedule, and backup scope before queueing jobs.
- -Connect a destination, choose who moves, run preflight, then execute a recoverable transfer.
- -Review backup readiness and stage restore decisions from Backup Inventory.
- -Temporarily raise eligible Cosmos capacity during approved backup or migration windows.
- -Configure when backups run, where artifacts are stored, and how backup files are encrypted.
-Full backups run on the selected cadence; partial backups run daily only.
-Store backup artifacts in Azure Blob Storage.
-Generate a 256-bit backup encryption key.
-Backups stream deterministic checkpoint batches and commit only verified work. Higher concurrency can increase source Cosmos cost and pressure.
-Source files stream through bounded chunks and durable per-file checkpoints. Peak transfer buffering is bounded by concurrent transfers multiplied by chunk size.
-Queue immediate full or partial backup jobs using the settings above.
-Jobs use Cosmos-backed leases so scaled-out App Service workers do not run the same backup twice.
-Move SimpleChat data through a reviewed, recoverable environment transfer.
-Configure the services this migration will write to. Stored credentials remain redacted.
-Required for every migration.
-Required when AI Search documents are included.
-Required only when source document blobs are included.
-Selections persist while you search and page. “All” always uses the exhaustive server count.
-Search the server catalog.
-Search the server catalog.
-Search the server catalog.
-Choose whether to copy only missing items, catch up changes, or make migrated destination data match the source.
-Preflight runs server-owned access probes and inventory. Any earlier change makes this review stale.
-Review the final server-normalized plan. Submission is guarded against duplicate requests.
-Complete preflight review before confirmation.
-Progress comes from the durable job record. Cancel, Retry, and Resume retain verified checkpoints.
-Query SimpleChat Cosmos DB containers, inspect one document, and save JSON changes with ETag protection.
-Track completed full and partial backups created by Data Management jobs.
-- It permanently deletes backups whose age exceeds the retention period configured in Data Management settings, - and removes their stored artifacts from the backup container. Backups newer than the retention cutoff are left alone. -
-- Seeing “found no expired backups to delete” means every backup is still inside the retention window. That is expected, not an error. -
-| Backup | -Completed | -Contents | -Storage | -Protection | -Warnings | -Actions | -
|---|---|---|---|---|---|---|
| Backup inventory has not loaded yet. | -||||||
| Created | -Operation | -Status | -Progress | -Message | -Actions | -
|---|---|---|---|---|---|
| Job history has not loaded yet. | -|||||
| Created | +Operation | +Status | +Progress | +Message | +Actions | +
|---|---|---|---|---|---|
| Job history has not loaded yet. | +|||||
Move SimpleChat data through a reviewed, recoverable environment transfer.
+Configure the services this migration will write to. Stored credentials remain redacted.
+Required for every migration.
+Required when AI Search documents are included.
+Required only when source document blobs are included.
+Selections persist while you search and page. “All” always uses the exhaustive server count.
+Search the server catalog.
+Search the server catalog.
+Search the server catalog.
+Choose whether to copy only missing items, catch up changes, or make migrated destination data match the source.
+Preflight runs server-owned access probes and inventory. Any earlier change makes this review stale.
+Review the final server-normalized plan. Submission is guarded against duplicate requests.
+Complete preflight review before confirmation.
+Progress comes from the durable job record. Cancel, Retry, and Resume retain verified checkpoints.
+Track completed full and partial backups created by Data Management jobs.
++ It permanently deletes backups whose age exceeds the retention period configured in Data Management settings, + and removes their stored artifacts from the backup container. Backups newer than the retention cutoff are left alone. +
++ Seeing “found no expired backups to delete” means every backup is still inside the retention window. That is expected, not an error. +
+| Backup | +Completed | +Contents | +Storage | +Protection | +Warnings | +Actions | +
|---|---|---|---|---|---|---|
| Backup inventory has not loaded yet. | +||||||
Protect SimpleChat data, move selected workspaces to another environment, and stage restore decisions with guided checks.
+