diff --git a/application/single_app/admin_settings_nav.py b/application/single_app/admin_settings_nav.py index 48026e3ea..84229254c 100644 --- a/application/single_app/admin_settings_nav.py +++ b/application/single_app/admin_settings_nav.py @@ -66,6 +66,8 @@ {"id": "chat-file-uploads-section", "label": "Chat File Uploads", "icon": "bi-paperclip"}, {"id": "conversation-contents-drawer-section", "label": "Conversation Contents Drawer", "icon": "bi-list-nested"}, {"id": "workspace-scope-lock-section", "label": "Workspace Scope Lock", "icon": "bi-lock"}, + {"id": "conversation-history-section", "label": "Conversation History", "icon": "bi-clock-history"}, + {"id": "default-system-prompt-section", "label": "Default System Prompt", "icon": "bi-chat-square-quote"}, ], }, { @@ -184,6 +186,7 @@ {"id": "file-download-settings-section", "label": "File Downloads", "icon": "bi-download"}, {"id": "file-sharing-section", "label": "File Sharing", "icon": "bi-share"}, {"id": "shared-conversation-file-approvals-section", "label": "Shared Conversation File Approvals", "icon": "bi-check2-square"}, + {"id": "file-size-limit-section", "label": "Maximum File Size", "icon": "bi-file-earmark-arrow-up"}, ], }, { @@ -284,6 +287,8 @@ "icon": "bi-person-check", "sections": [ {"id": "permissions-section", "label": "Permissions", "icon": "bi-person-check"}, + {"id": "app-role-requirements-section", "label": "App Role Requirements", "icon": "bi-person-badge"}, + {"id": "access-denied-message-section", "label": "Access Denied Message", "icon": "bi-shield-x"}, ], }, { @@ -303,14 +308,14 @@ ], }, { - # Interim home for the mixed System Settings card. Four of its - # eight fields are idle-timeout, which is the plurality; the - # rest are split out to their real homes in a later change. + # Idle timeout is the only thing left here. The rest of the old + # System Settings card was split out to the tabs that own each + # setting, without renaming a single field. "id": "session", "label": "Session", "icon": "bi-hourglass-split", "sections": [ - {"id": "system-settings-section", "label": "System Settings", "icon": "bi-sliders"}, + {"id": "idle-timeout-section", "label": "Idle Session Timeout", "icon": "bi-hourglass-split"}, ], }, { @@ -395,18 +400,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..f2d9bfa54 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.017" 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_access_roles_roster.js b/application/single_app/static/js/admin/admin_access_roles_roster.js new file mode 100644 index 000000000..3b7993b16 --- /dev/null +++ b/application/single_app/static/js/admin/admin_access_roles_roster.js @@ -0,0 +1,148 @@ +// admin_access_roles_roster.js +// +// Access & Roles shows every "require an Entra app role" switch in one place. +// +// The switches themselves stay on the tabs that own them, because that is where +// they make sense in context. Duplicating the real inputs here would submit +// each setting twice, so this builds a roster of mirrors instead: each row +// carries no name attribute and simply drives the canonical input. +// +// The roster is built from the page rather than from a hand-written list, so a +// new role requirement anywhere in Admin Settings appears here on its own and +// this list cannot fall out of step with reality. +// +// Roster links carry data-admin-link, which admin_card_links.js already handles +// through a delegated listener, so no wiring is needed here. + +const ROLE_INPUT_SELECTOR = 'input[type="checkbox"][name^="require_member_of_"]'; +const LIST_ID = 'app-role-requirements-list'; +const EMPTY_ID = 'app-role-requirements-empty'; +const ROSTER_CARD_ID = 'app-role-requirements-section'; + +/** + * Read the visible label for a control, falling back to its field name. + * @param {HTMLInputElement} input Canonical role checkbox. + * @returns {string} Human readable label. + */ +function labelFor(input) { + const explicit = input.id ? document.querySelector(`label[for="${input.id}"]`) : null; + if (explicit && explicit.textContent.trim()) { + return explicit.textContent.trim(); + } + + const wrapping = input.closest('label'); + if (wrapping && wrapping.textContent.trim()) { + return wrapping.textContent.trim(); + } + + return input.name; +} + +/** + * Find the card a control belongs to, so the roster can link back to it. + * @param {HTMLInputElement} input Canonical role checkbox. + * @returns {HTMLElement|null} The owning card, when it has an id. + */ +function owningCard(input) { + let card = input.closest('.card[id]'); + while (card && card.id === ROSTER_CARD_ID) { + card = card.parentElement ? card.parentElement.closest('.card[id]') : null; + } + return card; +} + +/** + * Read the heading of a card, used as the "where does this live" hint. + * @param {HTMLElement} card Owning card. + * @returns {string} Card title, or an empty string when it has none. + */ +function cardTitle(card) { + const heading = card ? card.querySelector('h5, h4, h6, .card-title') : null; + return heading ? heading.textContent.trim() : ''; +} + +/** + * Build one roster row: a mirror switch, its label, and a link to the setting. + * @param {HTMLInputElement} input Canonical role checkbox. + * @returns {HTMLElement} The row element. + */ +function buildRow(input) { + const row = document.createElement('div'); + row.className = 'd-flex flex-wrap align-items-center gap-2'; + row.setAttribute('data-role-requirement-row', input.name); + + const wrapper = document.createElement('div'); + wrapper.className = 'form-check form-switch mb-0 flex-grow-1'; + + // No name attribute: only the canonical input is submitted with the form. + const mirror = document.createElement('input'); + mirror.type = 'checkbox'; + mirror.className = 'form-check-input'; + mirror.id = `${input.name}-roster-mirror`; + mirror.checked = input.checked; + mirror.disabled = input.disabled; + mirror.setAttribute('data-role-mirror-for', input.id || input.name); + mirror.setAttribute('data-ignore-settings-change', 'true'); + + const label = document.createElement('label'); + label.className = 'form-check-label ms-2'; + label.setAttribute('for', mirror.id); + label.textContent = labelFor(input); + + wrapper.append(mirror, label); + row.appendChild(wrapper); + + const card = owningCard(input); + if (card) { + const title = cardTitle(card); + const link = document.createElement('a'); + link.href = `#${card.id}`; + link.className = 'small text-nowrap'; + link.setAttribute('data-admin-link', card.id); + link.textContent = title ? `In ${title}` : 'Go to setting'; + row.appendChild(link); + } + + // Two-way: the mirror drives the real input, and the real input keeps the + // mirror honest when it is changed on its own tab. + mirror.addEventListener('change', () => { + if (input.checked === mirror.checked) { + return; + } + input.checked = mirror.checked; + input.dispatchEvent(new Event('change', { bubbles: true })); + }); + + input.addEventListener('change', () => { + mirror.checked = input.checked; + mirror.disabled = input.disabled; + }); + + return row; +} + +/** + * Populate the Access & Roles roster from the role switches on the page. + */ +export function initAdminAccessRolesRoster() { + const list = document.getElementById(LIST_ID); + if (!list) { + return; + } + + const empty = document.getElementById(EMPTY_ID); + const inputs = Array.from(document.querySelectorAll(ROLE_INPUT_SELECTOR)) + .filter(input => !list.contains(input)); + + list.replaceChildren(); + inputs + .map(input => ({ input, label: labelFor(input) })) + .sort((a, b) => a.label.localeCompare(b.label)) + .forEach(({ input }) => list.appendChild(buildRow(input))); + + if (empty) { + empty.classList.toggle('d-none', inputs.length > 0); + } +} + +document.addEventListener('DOMContentLoaded', initAdminAccessRolesRoster); 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/access-roles.html b/application/single_app/templates/admin/_panes/access-roles.html index 3e93d9ff7..e87ef059d 100644 --- a/application/single_app/templates/admin/_panes/access-roles.html +++ b/application/single_app/templates/admin/_panes/access-roles.html @@ -43,7 +43,37 @@

+
+
+ App Role Requirements +
+

+ Every setting that can require an Entra app role, gathered here so the + full access policy can be read in one place. Each switch is a mirror of + the setting on its own tab, so changing it here changes it there. +

+ {# Rows are built from the page itself by + admin_access_roles_roster.js, so a new role requirement appears here + automatically and this list can never fall out of step. #} +
+

+ No app role requirements are available on this page. +

+
- +
+
+ Access Denied Message +
+

+ Shown to a user whose account does not carry a required role. +

+
+ + Shown to signed-in users who lack the required roles. Use Enter for line breaks. + +
+
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 @@ +
+
+
+
+

Start Here

+

Use these checkpoints before running backup, migration, restore, or advanced repair actions.

+
+ +
+
+
+
+
Back up
+

Configure dedicated storage, encryption, schedule, and backup scope before queueing jobs.

+ +
+
+
+
+
Migrate
+

Connect a destination, choose who moves, run preflight, then execute a recoverable transfer.

+ +
+
+
+
+
Restore
+

Review backup readiness and stage restore decisions from Backup Inventory.

+ +
+
+
+
+
RU Boost
+

Temporarily raise eligible Cosmos capacity during approved backup or migration windows.

+ +
+
+
+
+ +
+
+
+ +
+
+

Backup

+

Configure when backups run, where artifacts are stored, and how backup files are encrypted.

+
+
+ +
+
+
+
Schedule
+

Full backups run on the selected cadence; partial backups run daily only.

+
+
+
+ + +
+
+
+ + +
+
+ + +
Default is 03:00 UTC.
+
+
+ +
+ + + +
+ +
Automatic cleanup keeps the newest successful full backup as a safety baseline.
+
+
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ +
+
+
+ +
+
+
+ + +
Core application records required for meaningful restore and migration.
+
+
+
+
+ + +
Search index schemas and retrievable indexed documents.
+
+
+
+
+ + +
Original source files used by Enhanced Citations.
+
+
+
+
+
+
+
+ +
+
+
+
Storage
+

Store backup artifacts in Azure Blob Storage.

+
+ +
+
+ + Use a dedicated backup storage account. Data Management will reject storage that matches the Enhanced Citations connection string or Blob endpoint. +
+
+
+ + +
+
+ + +
+
+ + +
+
+
+
+ + +
No connection string saved yet.
+
+
+ + +
+
+
+ +
+
+
+
Encryption
+

Generate a 256-bit backup encryption key.

+
+ +
+
+ + +
+
+
Key storage
+
Not configured
+
Key reference
+
Not configured
+
+
+ +
+
Key Vault is strongly recommended
+
Generated backup encryption keys are stored in the Data Management settings document when Key Vault is not enabled.
+ Open Key Vault settings +
+
+
+ +
+
+
+
Cosmos Backup Performance
+

Backups stream deterministic checkpoint batches and commit only verified work. Higher concurrency can increase source Cosmos cost and pressure.

+
+ +
+ +
+
+
Source Blob Backup Performance
+

Source files stream through bounded chunks and durable per-file checkpoints. Peak transfer buffering is bounded by concurrent transfers multiplied by chunk size.

+
+
+
+ + +
+
+ + +
+
+ + +
+
+
Defaults bound application transfer buffering to approximately 32 MiB, excluding Azure SDK overhead. Throttling temporarily reduces active transfers.
+
+
+
+ + +
+
+ + +
+
+ + +
+
+
+ + +
+
+
+ + +
+
+
The backup records the current source capacity, raises only eligible targets up to 10,000 RU/s, and restores the original setting after completion, cancellation, failure, or recovery. This can increase Cosmos charges and requires source ARM throughput permission.
+
+
+
+ +
+
+
+
Backup Operations
+

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.

+
+
+ + +
diff --git a/application/single_app/templates/admin/_panes/chat-experience.html b/application/single_app/templates/admin/_panes/chat-experience.html index be9826832..9073b87ba 100644 --- a/application/single_app/templates/admin/_panes/chat-experience.html +++ b/application/single_app/templates/admin/_panes/chat-experience.html @@ -97,4 +97,34 @@
+
+
+ Conversation History +
+

+ How many previous messages are carried into each new request. +

+
+ + +
+
+ +
+
+ Default System Prompt +
+

+ The system prompt applied to conversations that do not set their own. +

+
+ + +
+
+ \ No newline at end of file diff --git a/application/single_app/templates/admin/_panes/cosmos-editor.html b/application/single_app/templates/admin/_panes/cosmos-editor.html new file mode 100644 index 000000000..3639ba706 --- /dev/null +++ b/application/single_app/templates/admin/_panes/cosmos-editor.html @@ -0,0 +1,49 @@ +
+
+
+
+

Cosmos DB JSON Editor

+

Query SimpleChat Cosmos DB containers, inspect one document, and save JSON changes with ETag protection.

+
+ +
+ +
+ The Cosmos DB JSON editor is locked. Acknowledge the danger prompt before querying or editing data. +
+
+
+
+ + +
Choose a known SimpleChat Cosmos DB container.
+
+
+ + +
Max 100 per request.
+
+
+ + +
Empty query returns only the first 100 documents. Custom SELECT queries can page beyond 100 with Next Page.
+
+
+
+ + No query has run yet. +
+
+ Query results and the JSON editor open in a modal so the Data Management page stays compact. +
+
+
+
diff --git a/application/single_app/templates/admin/_panes/data-management.html b/application/single_app/templates/admin/_panes/data-management.html deleted file mode 100644 index d99c04990..000000000 --- a/application/single_app/templates/admin/_panes/data-management.html +++ /dev/null @@ -1,1621 +0,0 @@ -
-
-
-

Backup, Migrate & Restore

-

Protect SimpleChat data, move selected workspaces to another environment, and stage restore decisions with guided checks.

-
- -
- - - -
- -
-
-
-

Start Here

-

Use these checkpoints before running backup, migration, restore, or advanced repair actions.

-
- -
-
-
-
-
Back up
-

Configure dedicated storage, encryption, schedule, and backup scope before queueing jobs.

- -
-
-
-
-
Migrate
-

Connect a destination, choose who moves, run preflight, then execute a recoverable transfer.

- -
-
-
-
-
Restore
-

Review backup readiness and stage restore decisions from Backup Inventory.

- -
-
-
-
-
RU Boost
-

Temporarily raise eligible Cosmos capacity during approved backup or migration windows.

- -
-
-
-
- -
-
-
- -
-
-

Backup

-

Configure when backups run, where artifacts are stored, and how backup files are encrypted.

-
-
- -
-
-
-
Schedule
-

Full backups run on the selected cadence; partial backups run daily only.

-
-
-
- - -
-
-
- - -
-
- - -
Default is 03:00 UTC.
-
-
- -
- - - -
- -
Automatic cleanup keeps the newest successful full backup as a safety baseline.
-
-
-
-
-
- - -
-
-
-
- - -
-
-
-
- -
-
-
- -
-
-
- - -
Core application records required for meaningful restore and migration.
-
-
-
-
- - -
Search index schemas and retrievable indexed documents.
-
-
-
-
- - -
Original source files used by Enhanced Citations.
-
-
-
-
-
-
-
- -
-
-
-
Storage
-

Store backup artifacts in Azure Blob Storage.

-
- -
-
- - Use a dedicated backup storage account. Data Management will reject storage that matches the Enhanced Citations connection string or Blob endpoint. -
-
-
- - -
-
- - -
-
- - -
-
-
-
- - -
No connection string saved yet.
-
-
- - -
-
-
- -
-
-
-
Encryption
-

Generate a 256-bit backup encryption key.

-
- -
-
- - -
-
-
Key storage
-
Not configured
-
Key reference
-
Not configured
-
-
- -
-
Key Vault is strongly recommended
-
Generated backup encryption keys are stored in the Data Management settings document when Key Vault is not enabled.
- Open Key Vault settings -
-
-
- -
-
-
-
Cosmos Backup Performance
-

Backups stream deterministic checkpoint batches and commit only verified work. Higher concurrency can increase source Cosmos cost and pressure.

-
- -
- -
-
-
Source Blob Backup Performance
-

Source files stream through bounded chunks and durable per-file checkpoints. Peak transfer buffering is bounded by concurrent transfers multiplied by chunk size.

-
-
-
- - -
-
- - -
-
- - -
-
-
Defaults bound application transfer buffering to approximately 32 MiB, excluding Azure SDK overhead. Throttling temporarily reduces active transfers.
-
-
-
- - -
-
- - -
-
- - -
-
-
- - -
-
-
- - -
-
-
The backup records the current source capacity, raises only eligible targets up to 10,000 RU/s, and restores the original setting after completion, cancellation, failure, or recovery. This can increase Cosmos charges and requires source ARM throughput permission.
-
-
-
- -
-
-
-
Backup Operations
-

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.

-
-
- - -
-
-
-

- Migration -

-

Move SimpleChat data through a reviewed, recoverable environment transfer.

-
-
- - Not reviewed -
-
- -
- - - - - -
-
-
-
Connect the destination
-

Configure the services this migration will write to. Stored credentials remain redacted.

-
- Destination database: SimpleChat -
- -
-
-
-
-
Target Cosmos Database
-

Required for every migration.

-
- -
-
- Managed identity requires Cosmos DB Data Contributor and target network access. -
-
-
- - -
-
- - -
-
- - -
Fixed app contract.
-
-
-
- - -
-
- -
-
-
-
Target Search
-

Required when AI Search documents are included.

-
- -
-
-
- - -
-
- - -
-
- - -
-
-
- -
-
-
-
Target Enhanced Citation Storage
-

Required only when source document blobs are included.

-
- -
-
-
- - -
-
- - -
-
- - -
-
-
-
-
- - -
-
-
-
Choose who and what moves
-

Selections persist while you search and page. “All” always uses the exhaustive server count.

-
-
0 principal scopes selected
-
- -
- - - -
- -
- -
- Migration mode -
- - - -
-
- -
-
-
-
-
Available users
-

Search the server catalog.

-
- -
-
-
- - Page 1 - -
-
- -
- -
- -
- Loading exhaustive count… - Every current user record will be resolved by the server when the job starts. -
-
- -
- - -
-
- -
- -
- Migration mode -
- - - -
-
-
-
-
-
-
Available groups
-

Search the server catalog.

-
- -
-
-
- - Page 1 - -
-
- -
-
- -
- Loading exhaustive count… - Every current group record will be resolved by the server when the job starts. -
-
-
- - -
-
- -
- -
- Migration mode -
- - - -
-
-
-
-
-
-
Available public workspaces
-

Search the server catalog.

-
- -
-
-
- - Page 1 - -
-
- -
-
- -
- Loading exhaustive count… - Every current public workspace record will be resolved by the server when the job starts. -
-
-
- - -
-
-
- - -
-
-
-
Choose what happens at the destination
-

Choose whether to copy only missing items, catch up changes, or make migrated destination data match the source.

-
-
- -
- Destination behavior -
- - - - - - -
-

- Copies source items that are absent from the destination. Existing destination data is never updated or deleted. -

-
-
- - -
Leave blank to let SimpleChat choose the latest compatible completed migration as the starting point for this catch-up run.
-
-
-
- -
- Data surfaces -
-
-
- - -
-
- - -
SimpleChat pauses its own target indexing. Freeze other writers before review.
-
-
-
-
- - -
Requires Enhanced Citation storage at both source and destination.
-
-
-
-
- -
- Performance and resume -

Migration uses durable resource checkpoints and retains the same migration ID after Retry or Resume.

-
-
- - -
-
- - -
-
- - -
-
-
- - - -
-
-
- - -
-
- - -
-
- - -
-
- -
-
-
RU Boost validates Azure management-plane throughput permissions separately from Cosmos data-copy access. Eligible capacity is raised only up to 10,000 RU/s during execution and restored after completion or failure.
-
-
-
-
- - -
-
-
-
Prove the plan is ready
-

Preflight runs server-owned access probes and inventory. Any earlier change makes this review stale.

-
-
- - -
-
-
- -
- Review has not run. - Run preflight to verify target access, counts, collisions, locks, and capacity policy. -
-
-
-
- -
- - -
-
-
-
Confirm execution
-

Review the final server-normalized plan. Submission is guarded against duplicate requests.

-
-
-
-

Complete preflight review before confirmation.

-
- -
- - -
- -
- - -
-
-
-
Operate the migration
-

Progress comes from the durable job record. Cancel, Retry, and Resume retain verified checkpoints.

-
-
- - -
-
-
- -
- No migration is attached to this workflow yet. - After execution, this stage follows the queued job and exposes its recovery actions. -
-
-
-
-
-
-
-
- -
-
- - -
- Step 1 of 6 -
-
-
- -
-
-
-

Cosmos DB JSON Editor

-

Query SimpleChat Cosmos DB containers, inspect one document, and save JSON changes with ETag protection.

-
- -
- -
- The Cosmos DB JSON editor is locked. Acknowledge the danger prompt before querying or editing data. -
-
-
-
- - -
Choose a known SimpleChat Cosmos DB container.
-
-
- - -
Max 100 per request.
-
-
- - -
Empty query returns only the first 100 documents. Custom SELECT queries can page beyond 100 with Next Page.
-
-
-
- - No query has run yet. -
-
- Query results and the JSON editor open in a modal so the Data Management page stays compact. -
-
-
- -
-
-
-

Backup Inventory

-

Track completed full and partial backups created by Data Management jobs.

-
-
- - - -
-
-
-
-
What does Run Retention Cleanup do?
-

- 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. -

-
    -
  • Only backups in a finished state are eligible; running or queued jobs are skipped.
  • -
  • When Keep latest full backup is enabled, the most recent successful full backup is protected even if it is past the cutoff.
  • -
  • Each run deletes at most 25 backups, so very large cleanups may need several runs.
  • -
  • Cleanup also runs automatically on the configured schedule; this button just runs it now.
  • -
-

- Seeing “found no expired backups to delete” means every backup is still inside the retention window. That is expected, not an error. -

-
-
-
-
- -
-
- -
-
- -
-
-
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
-
- - - - - - - - - - - - - - - - - -
BackupCompletedContentsStorageProtectionWarningsActions
Backup inventory has not loaded yet.
-
- -
- - - -
-
-

Job History

- -
-
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
-
- - - - - - - - - - - - - - - - -
CreatedOperationStatusProgressMessageActions
Job history has not loaded yet.
-
- -
- - - - - - - - - - - - - - - - - - - - -
diff --git a/application/single_app/templates/admin/_panes/files-sharing.html b/application/single_app/templates/admin/_panes/files-sharing.html index a66a04b76..a856abdff 100644 --- a/application/single_app/templates/admin/_panes/files-sharing.html +++ b/application/single_app/templates/admin/_panes/files-sharing.html @@ -220,4 +220,18 @@
+
+
+ Maximum File Size +
+

+ The largest file a user may upload into a workspace. +

+
+ + +
+
+ diff --git a/application/single_app/templates/admin/_panes/jobs.html b/application/single_app/templates/admin/_panes/jobs.html new file mode 100644 index 000000000..98c939eed --- /dev/null +++ b/application/single_app/templates/admin/_panes/jobs.html @@ -0,0 +1,89 @@ +
+
+
+

Job History

+ +
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+ + + + + + + + + + + + + + + + +
CreatedOperationStatusProgressMessageActions
Job history has not loaded yet.
+
+ +
+
diff --git a/application/single_app/templates/admin/_panes/migrate.html b/application/single_app/templates/admin/_panes/migrate.html new file mode 100644 index 000000000..ad6290a15 --- /dev/null +++ b/application/single_app/templates/admin/_panes/migrate.html @@ -0,0 +1,578 @@ +
+
+
+
+

+ Migration +

+

Move SimpleChat data through a reviewed, recoverable environment transfer.

+
+
+ + Not reviewed +
+
+ +
+ + + + + +
+
+
+
Connect the destination
+

Configure the services this migration will write to. Stored credentials remain redacted.

+
+ Destination database: SimpleChat +
+ +
+
+
+
+
Target Cosmos Database
+

Required for every migration.

+
+ +
+
+ Managed identity requires Cosmos DB Data Contributor and target network access. +
+
+
+ + +
+
+ + +
+
+ + +
Fixed app contract.
+
+
+
+ + +
+
+ +
+
+
+
Target Search
+

Required when AI Search documents are included.

+
+ +
+
+
+ + +
+
+ + +
+
+ + +
+
+
+ +
+
+
+
Target Enhanced Citation Storage
+

Required only when source document blobs are included.

+
+ +
+
+
+ + +
+
+ + +
+
+ + +
+
+
+
+
+ + +
+
+
+
Choose who and what moves
+

Selections persist while you search and page. “All” always uses the exhaustive server count.

+
+
0 principal scopes selected
+
+ +
+ + + +
+ +
+ +
+ Migration mode +
+ + + +
+
+ +
+
+
+
+
Available users
+

Search the server catalog.

+
+ +
+
+
+ + Page 1 + +
+
+ +
+ +
+ +
+ Loading exhaustive count… + Every current user record will be resolved by the server when the job starts. +
+
+ +
+ + +
+
+ +
+ +
+ Migration mode +
+ + + +
+
+
+
+
+
+
Available groups
+

Search the server catalog.

+
+ +
+
+
+ + Page 1 + +
+
+ +
+
+ +
+ Loading exhaustive count… + Every current group record will be resolved by the server when the job starts. +
+
+
+ + +
+
+ +
+ +
+ Migration mode +
+ + + +
+
+
+
+
+
+
Available public workspaces
+

Search the server catalog.

+
+ +
+
+
+ + Page 1 + +
+
+ +
+
+ +
+ Loading exhaustive count… + Every current public workspace record will be resolved by the server when the job starts. +
+
+
+ + +
+
+
+ + +
+
+
+
Choose what happens at the destination
+

Choose whether to copy only missing items, catch up changes, or make migrated destination data match the source.

+
+
+ +
+ Destination behavior +
+ + + + + + +
+

+ Copies source items that are absent from the destination. Existing destination data is never updated or deleted. +

+
+
+ + +
Leave blank to let SimpleChat choose the latest compatible completed migration as the starting point for this catch-up run.
+
+
+
+ +
+ Data surfaces +
+
+
+ + +
+
+ + +
SimpleChat pauses its own target indexing. Freeze other writers before review.
+
+
+
+
+ + +
Requires Enhanced Citation storage at both source and destination.
+
+
+
+
+ +
+ Performance and resume +

Migration uses durable resource checkpoints and retains the same migration ID after Retry or Resume.

+
+
+ + +
+
+ + +
+
+ + +
+
+
+ + + +
+
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
+
RU Boost validates Azure management-plane throughput permissions separately from Cosmos data-copy access. Eligible capacity is raised only up to 10,000 RU/s during execution and restored after completion or failure.
+
+
+
+
+ + +
+
+
+
Prove the plan is ready
+

Preflight runs server-owned access probes and inventory. Any earlier change makes this review stale.

+
+
+ + +
+
+
+ +
+ Review has not run. + Run preflight to verify target access, counts, collisions, locks, and capacity policy. +
+
+
+
+ +
+ + +
+
+
+
Confirm execution
+

Review the final server-normalized plan. Submission is guarded against duplicate requests.

+
+
+
+

Complete preflight review before confirmation.

+
+ +
+ + +
+ +
+ + +
+
+
+
Operate the migration
+

Progress comes from the durable job record. Cancel, Retry, and Resume retain verified checkpoints.

+
+
+ + +
+
+
+ +
+ No migration is attached to this workflow yet. + After execution, this stage follows the queued job and exposes its recovery actions. +
+
+
+
+
+
+
+
+ +
+
+ + +
+ Step 1 of 6 +
+
+
+
diff --git a/application/single_app/templates/admin/_panes/restore.html b/application/single_app/templates/admin/_panes/restore.html new file mode 100644 index 000000000..6b8e1ff5a --- /dev/null +++ b/application/single_app/templates/admin/_panes/restore.html @@ -0,0 +1,147 @@ +
+
+
+
+

Backup Inventory

+

Track completed full and partial backups created by Data Management jobs.

+
+
+ + + +
+
+
+
+
What does Run Retention Cleanup do?
+

+ 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. +

+
    +
  • Only backups in a finished state are eligible; running or queued jobs are skipped.
  • +
  • When Keep latest full backup is enabled, the most recent successful full backup is protected even if it is past the cutoff.
  • +
  • Each run deletes at most 25 backups, so very large cleanups may need several runs.
  • +
  • Cleanup also runs automatically on the configured schedule; this button just runs it now.
  • +
+

+ Seeing “found no expired backups to delete” means every backup is still inside the retention window. That is expected, not an error. +

+
+
+
+
+ +
+
+ +
+
+ +
+
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+ + + + + + + + + + + + + + + + + +
BackupCompletedContentsStorageProtectionWarningsActions
Backup inventory has not loaded yet.
+
+ +
+
diff --git a/application/single_app/templates/admin/_panes/session.html b/application/single_app/templates/admin/_panes/session.html index cb8fe9abe..d5bc9c36f 100644 --- a/application/single_app/templates/admin/_panes/session.html +++ b/application/single_app/templates/admin/_panes/session.html @@ -1,24 +1,11 @@
-
+
- System Settings + Idle Session Timeout

- System-level settings that control application behavior, including file size limits, conversation history, - and default prompts. + Warn inactive users and sign them out after a period of inactivity.

-
- - -
-
- - -
Custom text shown at the top of the idle warning dialog.
-
- - -
-
- - Shown to signed-in users who lack the required roles. Use Enter for line breaks. - -
-
\ No newline at end of file + diff --git a/application/single_app/templates/admin_settings.html b/application/single_app/templates/admin_settings.html index a34e450cf..f2daba2bc 100644 --- a/application/single_app/templates/admin_settings.html +++ b/application/single_app/templates/admin_settings.html @@ -983,6 +983,27 @@

12. Enhanced Citations and Image Generation

{# Governance status is rendered outside the panes so a message stays visible whichever governance tab is active. #} + {# Shared by every Backup & Recovery tab: one save button, one status + line and one operational warning serve all five tabs, so they sit + outside the panes and are shown only while that group is active. #} + +
{% include "admin/_panes/secrets.html" %} {% include "admin/_panes/access-roles.html" %} @@ -1016,7 +1037,11 @@

12. Enhanced Citations and Image Generation

{% include "admin/_panes/control-center-config.html" %} - {% include "admin/_panes/data-management.html" %} + {% include "admin/_panes/backup.html" %} + {% include "admin/_panes/migrate.html" %} + {% include "admin/_panes/restore.html" %} + {% include "admin/_panes/cosmos-editor.html" %} + {% include "admin/_panes/jobs.html" %} {% include "admin/_panes/redis-caching.html" %} {% include "admin/_panes/cosmos.html" %} @@ -1173,6 +1198,433 @@
Recommended setup
+ + + + + + + + + + + + + + + + + + + + + + +