mirror of
https://github.com/open-webui/open-webui.git
synced 2026-09-07 08:27:05 +00:00
fix: flush child form before modal save, guard localStorage parse, explicit dialect check
- InterfaceDefaultsModal submitHandler now requestSubmits the embedded
tab-interface form and awaits a Svelte tick before POSTing. That
flushes submit-scoped writes in the InterfaceSettings component
(updateInterfaceHandler -> saveSettings({ models, imageCompressionSize }))
through the saveAdminSettings callback so they land in adminDefaults
instead of being silently dropped on save.
- +layout.svelte only reads localStorage.settings when the server
didn't return user UI, and wraps the JSON.parse in try/catch with a
{} fallback. A stale or hand-edited localStorage value would
otherwise throw and abort the whole authenticated-load path even
when valid userSettings came back from the backend.
- users.py reset_all_users_interface_settings branches explicitly on
postgresql and sqlite; any other dialect now responds 501 with a
clear message naming the unsupported dialect. The previous
"everything that isn't postgres runs SQLite-specific SQL" assumption
would fail on MySQL/MariaDB with an opaque syntax error.
This commit is contained in:
parent
e7249118da
commit
6cd3c7238b
3 changed files with 51 additions and 6 deletions
|
|
@ -701,6 +701,10 @@ async def reset_all_users_interface_settings(
|
|||
Clears the 'ui' key in all users' settings. Admin only.
|
||||
"""
|
||||
try:
|
||||
# Match dialects explicitly. Falling through to SQLite-specific SQL
|
||||
# (json_set / json_extract) for every non-postgres backend would
|
||||
# silently fail on MySQL / MariaDB / other engines with an opaque
|
||||
# syntax error instead of a clear "unsupported dialect" response.
|
||||
dialect = db.bind.dialect.name
|
||||
|
||||
if dialect == 'postgresql':
|
||||
|
|
@ -723,10 +727,10 @@ async def reset_all_users_interface_settings(
|
|||
)
|
||||
result = await db.execute(stmt)
|
||||
reset_count = result.rowcount
|
||||
else:
|
||||
# SQLite: use native json_set (available since SQLite 3.9+).
|
||||
# rowcount on the UPDATE result reflects the rows actually
|
||||
# changed, so no follow-up SELECT changes() round-trip is needed.
|
||||
elif dialect == 'sqlite':
|
||||
# Native json_set is available since SQLite 3.9+. rowcount on the
|
||||
# UPDATE result reflects the rows actually changed, so no
|
||||
# follow-up SELECT changes() round-trip is needed.
|
||||
result = await db.execute(
|
||||
text(
|
||||
"""
|
||||
|
|
@ -738,6 +742,14 @@ async def reset_all_users_interface_settings(
|
|||
)
|
||||
)
|
||||
reset_count = result.rowcount
|
||||
else:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_501_NOT_IMPLEMENTED,
|
||||
detail=(
|
||||
f"Resetting interface settings is not implemented for the '{dialect}' "
|
||||
'database dialect; supported dialects are postgresql and sqlite.'
|
||||
),
|
||||
)
|
||||
|
||||
await db.commit()
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<script lang="ts">
|
||||
import { toast } from 'svelte-sonner';
|
||||
import { getContext } from 'svelte';
|
||||
import { getContext, tick } from 'svelte';
|
||||
|
||||
import Modal from '$lib/components/common/Modal.svelte';
|
||||
import Spinner from '$lib/components/common/Spinner.svelte';
|
||||
|
|
@ -48,6 +48,19 @@
|
|||
const submitHandler = async () => {
|
||||
saving = true;
|
||||
try {
|
||||
// The embedded InterfaceSettings form has submit-scoped writes
|
||||
// (updateInterfaceHandler -> saveSettings({ models, imageCompressionSize }))
|
||||
// that don't fire on per-control change. Request-submit the child
|
||||
// form first so those fields land in adminDefaults via the
|
||||
// saveAdminSettings callback, then wait one tick for reactivity
|
||||
// to flush before we POST. Without this, admins could save
|
||||
// defaults and silently lose submit-only fields.
|
||||
const innerForm = document.getElementById('tab-interface') as HTMLFormElement | null;
|
||||
if (innerForm && typeof innerForm.requestSubmit === 'function') {
|
||||
innerForm.requestSubmit();
|
||||
}
|
||||
await tick();
|
||||
|
||||
await setInterfaceDefaults(localStorage.token, prepareForBackend(adminDefaults));
|
||||
toast.success($i18n.t('Interface defaults saved successfully'));
|
||||
show = false;
|
||||
|
|
|
|||
|
|
@ -935,7 +935,27 @@
|
|||
// defaults is that THOSE users get them, so always merge
|
||||
// against adminDefaults instead of skipping straight to
|
||||
// localStorage when userSettings is falsy.
|
||||
const localStorageSettings = JSON.parse(localStorage.getItem('settings') ?? '{}');
|
||||
//
|
||||
// Guard the localStorage read: a stale or hand-edited
|
||||
// localStorage.settings value can be invalid JSON, and
|
||||
// letting JSON.parse throw here would abort the whole
|
||||
// authenticated-load path even when we already have valid
|
||||
// userSettings from the backend.
|
||||
let localStorageSettings: any = {};
|
||||
if (!userSettings?.ui) {
|
||||
const raw = localStorage.getItem('settings');
|
||||
if (raw) {
|
||||
try {
|
||||
localStorageSettings = JSON.parse(raw) ?? {};
|
||||
} catch (parseErr) {
|
||||
console.warn(
|
||||
'Ignoring malformed localStorage.settings during init:',
|
||||
parseErr
|
||||
);
|
||||
localStorageSettings = {};
|
||||
}
|
||||
}
|
||||
}
|
||||
const userUI = userSettings?.ui ?? localStorageSettings ?? {};
|
||||
settings.set(deepMerge(adminDefaults ?? {}, userUI));
|
||||
setTextScale($settings?.textScale ?? 1);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue