mirror of
https://github.com/open-webui/open-webui
synced 2024-11-16 13:40:55 +00:00
feat: import/export config
This commit is contained in:
parent
aec7cd572c
commit
6bbb755997
@ -3,9 +3,37 @@ from fastapi import APIRouter, Depends, Request
|
|||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
from utils.utils import get_admin_user, get_verified_user
|
from utils.utils import get_admin_user, get_verified_user
|
||||||
|
|
||||||
|
|
||||||
|
from config import get_config, save_config
|
||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
|
|
||||||
|
############################
|
||||||
|
# ImportConfig
|
||||||
|
############################
|
||||||
|
|
||||||
|
|
||||||
|
class ImportConfigForm(BaseModel):
|
||||||
|
config: dict
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/import", response_model=dict)
|
||||||
|
async def import_config(form_data: ImportConfigForm, user=Depends(get_admin_user)):
|
||||||
|
save_config(form_data.config)
|
||||||
|
return get_config()
|
||||||
|
|
||||||
|
|
||||||
|
############################
|
||||||
|
# ExportConfig
|
||||||
|
############################
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/export", response_model=dict)
|
||||||
|
async def export_config(user=Depends(get_admin_user)):
|
||||||
|
return get_config()
|
||||||
|
|
||||||
|
|
||||||
class SetDefaultModelsForm(BaseModel):
|
class SetDefaultModelsForm(BaseModel):
|
||||||
models: str
|
models: str
|
||||||
|
|
||||||
|
@ -1,6 +1,63 @@
|
|||||||
import { WEBUI_API_BASE_URL } from '$lib/constants';
|
import { WEBUI_API_BASE_URL } from '$lib/constants';
|
||||||
import type { Banner } from '$lib/types';
|
import type { Banner } from '$lib/types';
|
||||||
|
|
||||||
|
export const importConfig = async (token: string, config) => {
|
||||||
|
let error = null;
|
||||||
|
|
||||||
|
const res = await fetch(`${WEBUI_API_BASE_URL}/configs/import`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
Authorization: `Bearer ${token}`
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
config: config
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.then(async (res) => {
|
||||||
|
if (!res.ok) throw await res.json();
|
||||||
|
return res.json();
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.log(err);
|
||||||
|
error = err.detail;
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const exportConfig = async (token: string) => {
|
||||||
|
let error = null;
|
||||||
|
|
||||||
|
const res = await fetch(`${WEBUI_API_BASE_URL}/configs/export`, {
|
||||||
|
method: 'GET',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
Authorization: `Bearer ${token}`
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(async (res) => {
|
||||||
|
if (!res.ok) throw await res.json();
|
||||||
|
return res.json();
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.log(err);
|
||||||
|
error = err.detail;
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
};
|
||||||
|
|
||||||
export const setDefaultModels = async (token: string, models: string) => {
|
export const setDefaultModels = async (token: string, models: string) => {
|
||||||
let error = null;
|
let error = null;
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
import { config, user } from '$lib/stores';
|
import { config, user } from '$lib/stores';
|
||||||
import { toast } from 'svelte-sonner';
|
import { toast } from 'svelte-sonner';
|
||||||
import { getAllUserChats } from '$lib/apis/chats';
|
import { getAllUserChats } from '$lib/apis/chats';
|
||||||
|
import { exportConfig, importConfig } from '$lib/apis/configs';
|
||||||
|
|
||||||
const i18n = getContext('i18n');
|
const i18n = getContext('i18n');
|
||||||
|
|
||||||
@ -34,6 +35,92 @@
|
|||||||
<div>
|
<div>
|
||||||
<div class=" mb-2 text-sm font-medium">{$i18n.t('Database')}</div>
|
<div class=" mb-2 text-sm font-medium">{$i18n.t('Database')}</div>
|
||||||
|
|
||||||
|
<input
|
||||||
|
id="config-json-input"
|
||||||
|
hidden
|
||||||
|
type="file"
|
||||||
|
accept=".json"
|
||||||
|
on:change={(e) => {
|
||||||
|
const file = e.target.files[0];
|
||||||
|
const reader = new FileReader();
|
||||||
|
|
||||||
|
reader.onload = async (e) => {
|
||||||
|
const res = await importConfig(localStorage.token, JSON.parse(e.target.result)).catch(
|
||||||
|
(error) => {
|
||||||
|
toast.error(error);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (res) {
|
||||||
|
toast.success('Config imported successfully');
|
||||||
|
}
|
||||||
|
e.target.value = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
reader.readAsText(file);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class=" flex rounded-md py-2 px-3 w-full hover:bg-gray-200 dark:hover:bg-gray-800 transition"
|
||||||
|
on:click={async () => {
|
||||||
|
document.getElementById('config-json-input').click();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div class=" self-center mr-3">
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 16 16"
|
||||||
|
fill="currentColor"
|
||||||
|
class="w-4 h-4"
|
||||||
|
>
|
||||||
|
<path d="M2 3a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1v1a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V3Z" />
|
||||||
|
<path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
d="M13 6H3v6a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V6ZM8.75 7.75a.75.75 0 0 0-1.5 0v2.69L6.03 9.22a.75.75 0 0 0-1.06 1.06l2.5 2.5a.75.75 0 0 0 1.06 0l2.5-2.5a.75.75 0 1 0-1.06-1.06l-1.22 1.22V7.75Z"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div class=" self-center text-sm font-medium">
|
||||||
|
{$i18n.t('Import Config from JSON File')}
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class=" flex rounded-md py-2 px-3 w-full hover:bg-gray-200 dark:hover:bg-gray-800 transition"
|
||||||
|
on:click={async () => {
|
||||||
|
const config = await exportConfig(localStorage.token);
|
||||||
|
const blob = new Blob([JSON.stringify(config)], {
|
||||||
|
type: 'application/json'
|
||||||
|
});
|
||||||
|
saveAs(blob, `config-${Date.now()}.json`);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div class=" self-center mr-3">
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 16 16"
|
||||||
|
fill="currentColor"
|
||||||
|
class="w-4 h-4"
|
||||||
|
>
|
||||||
|
<path d="M2 3a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1v1a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V3Z" />
|
||||||
|
<path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
d="M13 6H3v6a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V6ZM8.75 7.75a.75.75 0 0 0-1.5 0v2.69L6.03 9.22a.75.75 0 0 0-1.06 1.06l2.5 2.5a.75.75 0 0 0 1.06 0l2.5-2.5a.75.75 0 1 0-1.06-1.06l-1.22 1.22V7.75Z"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div class=" self-center text-sm font-medium">
|
||||||
|
{$i18n.t('Export Config to JSON File')}
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<hr class=" dark:border-gray-850 my-1" />
|
||||||
|
|
||||||
{#if $config?.features.enable_admin_export ?? true}
|
{#if $config?.features.enable_admin_export ?? true}
|
||||||
<div class=" flex w-full justify-between">
|
<div class=" flex w-full justify-between">
|
||||||
<!-- <div class=" self-center text-xs font-medium">{$i18n.t('Allow Chat Deletion')}</div> -->
|
<!-- <div class=" self-center text-xs font-medium">{$i18n.t('Allow Chat Deletion')}</div> -->
|
||||||
@ -97,40 +184,34 @@
|
|||||||
|
|
||||||
<hr class=" dark:border-gray-850 my-1" />
|
<hr class=" dark:border-gray-850 my-1" />
|
||||||
|
|
||||||
<div class=" flex w-full justify-between">
|
<button
|
||||||
<!-- <div class=" self-center text-xs font-medium">{$i18n.t('Allow Chat Deletion')}</div> -->
|
type="button"
|
||||||
|
class=" flex rounded-md py-2 px-3 w-full hover:bg-gray-200 dark:hover:bg-gray-800 transition"
|
||||||
<button
|
on:click={() => {
|
||||||
class=" flex rounded-md py-1.5 px-3 w-full hover:bg-gray-200 dark:hover:bg-gray-800 transition"
|
downloadLiteLLMConfig(localStorage.token).catch((error) => {
|
||||||
type="button"
|
toast.error(error);
|
||||||
on:click={() => {
|
});
|
||||||
downloadLiteLLMConfig(localStorage.token).catch((error) => {
|
}}
|
||||||
toast.error(error);
|
>
|
||||||
});
|
<div class=" self-center mr-3">
|
||||||
}}
|
<svg
|
||||||
>
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
<div class=" self-center mr-3">
|
viewBox="0 0 16 16"
|
||||||
<svg
|
fill="currentColor"
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
class="w-4 h-4"
|
||||||
viewBox="0 0 24 24"
|
>
|
||||||
fill="currentColor"
|
<path d="M2 3a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1v1a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V3Z" />
|
||||||
class="size-4"
|
<path
|
||||||
>
|
fill-rule="evenodd"
|
||||||
<path
|
d="M13 6H3v6a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V6ZM8.75 7.75a.75.75 0 0 0-1.5 0v2.69L6.03 9.22a.75.75 0 0 0-1.06 1.06l2.5 2.5a.75.75 0 0 0 1.06 0l2.5-2.5a.75.75 0 1 0-1.06-1.06l-1.22 1.22V7.75Z"
|
||||||
fill-rule="evenodd"
|
clip-rule="evenodd"
|
||||||
d="M5.625 1.5H9a3.75 3.75 0 0 1 3.75 3.75v1.875c0 1.036.84 1.875 1.875 1.875H16.5a3.75 3.75 0 0 1 3.75 3.75v7.875c0 1.035-.84 1.875-1.875 1.875H5.625a1.875 1.875 0 0 1-1.875-1.875V3.375c0-1.036.84-1.875 1.875-1.875Zm5.845 17.03a.75.75 0 0 0 1.06 0l3-3a.75.75 0 1 0-1.06-1.06l-1.72 1.72V12a.75.75 0 0 0-1.5 0v4.19l-1.72-1.72a.75.75 0 0 0-1.06 1.06l3 3Z"
|
/>
|
||||||
clip-rule="evenodd"
|
</svg>
|
||||||
/>
|
</div>
|
||||||
<path
|
<div class=" self-center text-sm font-medium">
|
||||||
d="M14.25 5.25a5.23 5.23 0 0 0-1.279-3.434 9.768 9.768 0 0 1 6.963 6.963A5.23 5.23 0 0 0 16.5 7.5h-1.875a.375.375 0 0 1-.375-.375V5.25Z"
|
{$i18n.t('Export LiteLLM config.yaml')}
|
||||||
/>
|
</div>
|
||||||
</svg>
|
</button>
|
||||||
</div>
|
|
||||||
<div class=" self-center text-sm font-medium">
|
|
||||||
{$i18n.t('Export LiteLLM config.yaml')}
|
|
||||||
</div>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user