mirror of
https://github.com/open-webui/open-webui
synced 2025-05-29 09:42:12 +00:00
enh: user chat edit permission
This commit is contained in:
parent
dfa5041b6f
commit
cbadf39d7d
@ -808,10 +808,24 @@ USER_PERMISSIONS_CHAT_DELETION = (
|
|||||||
os.environ.get("USER_PERMISSIONS_CHAT_DELETION", "True").lower() == "true"
|
os.environ.get("USER_PERMISSIONS_CHAT_DELETION", "True").lower() == "true"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
USER_PERMISSIONS_CHAT_EDITING = (
|
||||||
|
os.environ.get("USER_PERMISSIONS_CHAT_EDITING", "True").lower() == "true"
|
||||||
|
)
|
||||||
|
|
||||||
|
USER_PERMISSIONS_CHAT_TEMPORARY = (
|
||||||
|
os.environ.get("USER_PERMISSIONS_CHAT_TEMPORARY", "True").lower() == "true"
|
||||||
|
)
|
||||||
|
|
||||||
USER_PERMISSIONS = PersistentConfig(
|
USER_PERMISSIONS = PersistentConfig(
|
||||||
"USER_PERMISSIONS",
|
"USER_PERMISSIONS",
|
||||||
"ui.user_permissions",
|
"ui.user_permissions",
|
||||||
{"chat": {"deletion": USER_PERMISSIONS_CHAT_DELETION}},
|
{
|
||||||
|
"chat": {
|
||||||
|
"deletion": USER_PERMISSIONS_CHAT_DELETION,
|
||||||
|
"editing": USER_PERMISSIONS_CHAT_EDITING,
|
||||||
|
"temporary": USER_PERMISSIONS_CHAT_TEMPORARY,
|
||||||
|
}
|
||||||
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
ENABLE_MODEL_FILTER = PersistentConfig(
|
ENABLE_MODEL_FILTER = PersistentConfig(
|
||||||
|
@ -68,6 +68,7 @@ from utils.utils import (
|
|||||||
get_http_authorization_cred,
|
get_http_authorization_cred,
|
||||||
get_password_hash,
|
get_password_hash,
|
||||||
create_token,
|
create_token,
|
||||||
|
decode_token,
|
||||||
)
|
)
|
||||||
from utils.task import (
|
from utils.task import (
|
||||||
title_generation_template,
|
title_generation_template,
|
||||||
@ -1957,41 +1958,61 @@ async def update_pipeline_valves(
|
|||||||
|
|
||||||
|
|
||||||
@app.get("/api/config")
|
@app.get("/api/config")
|
||||||
async def get_app_config():
|
async def get_app_config(request: Request):
|
||||||
|
user = None
|
||||||
|
if "token" in request.cookies:
|
||||||
|
token = request.cookies.get("token")
|
||||||
|
data = decode_token(token)
|
||||||
|
if data is not None and "id" in data:
|
||||||
|
user = Users.get_user_by_id(data["id"])
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"status": True,
|
"status": True,
|
||||||
"name": WEBUI_NAME,
|
"name": WEBUI_NAME,
|
||||||
"version": VERSION,
|
"version": VERSION,
|
||||||
"default_locale": str(DEFAULT_LOCALE),
|
"default_locale": str(DEFAULT_LOCALE),
|
||||||
"default_models": webui_app.state.config.DEFAULT_MODELS,
|
|
||||||
"default_prompt_suggestions": webui_app.state.config.DEFAULT_PROMPT_SUGGESTIONS,
|
|
||||||
"features": {
|
|
||||||
"auth": WEBUI_AUTH,
|
|
||||||
"auth_trusted_header": bool(webui_app.state.AUTH_TRUSTED_EMAIL_HEADER),
|
|
||||||
"enable_signup": webui_app.state.config.ENABLE_SIGNUP,
|
|
||||||
"enable_login_form": webui_app.state.config.ENABLE_LOGIN_FORM,
|
|
||||||
"enable_web_search": rag_app.state.config.ENABLE_RAG_WEB_SEARCH,
|
|
||||||
"enable_image_generation": images_app.state.config.ENABLED,
|
|
||||||
"enable_community_sharing": webui_app.state.config.ENABLE_COMMUNITY_SHARING,
|
|
||||||
"enable_message_rating": webui_app.state.config.ENABLE_MESSAGE_RATING,
|
|
||||||
"enable_admin_export": ENABLE_ADMIN_EXPORT,
|
|
||||||
"enable_admin_chat_access": ENABLE_ADMIN_CHAT_ACCESS,
|
|
||||||
},
|
|
||||||
"audio": {
|
|
||||||
"tts": {
|
|
||||||
"engine": audio_app.state.config.TTS_ENGINE,
|
|
||||||
"voice": audio_app.state.config.TTS_VOICE,
|
|
||||||
},
|
|
||||||
"stt": {
|
|
||||||
"engine": audio_app.state.config.STT_ENGINE,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"oauth": {
|
"oauth": {
|
||||||
"providers": {
|
"providers": {
|
||||||
name: config.get("name", name)
|
name: config.get("name", name)
|
||||||
for name, config in OAUTH_PROVIDERS.items()
|
for name, config in OAUTH_PROVIDERS.items()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"features": {
|
||||||
|
"auth": WEBUI_AUTH,
|
||||||
|
"auth_trusted_header": bool(webui_app.state.AUTH_TRUSTED_EMAIL_HEADER),
|
||||||
|
"enable_signup": webui_app.state.config.ENABLE_SIGNUP,
|
||||||
|
"enable_login_form": webui_app.state.config.ENABLE_LOGIN_FORM,
|
||||||
|
**(
|
||||||
|
{
|
||||||
|
"enable_web_search": rag_app.state.config.ENABLE_RAG_WEB_SEARCH,
|
||||||
|
"enable_image_generation": images_app.state.config.ENABLED,
|
||||||
|
"enable_community_sharing": webui_app.state.config.ENABLE_COMMUNITY_SHARING,
|
||||||
|
"enable_message_rating": webui_app.state.config.ENABLE_MESSAGE_RATING,
|
||||||
|
"enable_admin_export": ENABLE_ADMIN_EXPORT,
|
||||||
|
"enable_admin_chat_access": ENABLE_ADMIN_CHAT_ACCESS,
|
||||||
|
}
|
||||||
|
if user is not None
|
||||||
|
else {}
|
||||||
|
),
|
||||||
|
},
|
||||||
|
**(
|
||||||
|
{
|
||||||
|
"default_models": webui_app.state.config.DEFAULT_MODELS,
|
||||||
|
"default_prompt_suggestions": webui_app.state.config.DEFAULT_PROMPT_SUGGESTIONS,
|
||||||
|
"audio": {
|
||||||
|
"tts": {
|
||||||
|
"engine": audio_app.state.config.TTS_ENGINE,
|
||||||
|
"voice": audio_app.state.config.TTS_VOICE,
|
||||||
|
},
|
||||||
|
"stt": {
|
||||||
|
"engine": audio_app.state.config.STT_ENGINE,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"permissions": {**webui_app.state.config.USER_PERMISSIONS},
|
||||||
|
}
|
||||||
|
if user is not None
|
||||||
|
else {}
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -665,6 +665,7 @@ export const getBackendConfig = async () => {
|
|||||||
|
|
||||||
const res = await fetch(`${WEBUI_BASE_URL}/api/config`, {
|
const res = await fetch(`${WEBUI_BASE_URL}/api/config`, {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
|
credentials: 'include',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,9 @@
|
|||||||
let whitelistModels = [''];
|
let whitelistModels = [''];
|
||||||
let permissions = {
|
let permissions = {
|
||||||
chat: {
|
chat: {
|
||||||
deletion: true
|
deletion: true,
|
||||||
|
edit: true,
|
||||||
|
temporary: true
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -92,6 +94,88 @@
|
|||||||
{/if}
|
{/if}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class=" flex w-full justify-between">
|
||||||
|
<div class=" self-center text-xs font-medium">{$i18n.t('Allow Chat Editing')}</div>
|
||||||
|
|
||||||
|
<button
|
||||||
|
class="p-1 px-3 text-xs flex rounded transition"
|
||||||
|
on:click={() => {
|
||||||
|
permissions.chat.editing = !(permissions?.chat?.editing ?? true);
|
||||||
|
}}
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
{#if permissions?.chat?.editing ?? true}
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 16 16"
|
||||||
|
fill="currentColor"
|
||||||
|
class="w-4 h-4"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M11.5 1A3.5 3.5 0 0 0 8 4.5V7H2.5A1.5 1.5 0 0 0 1 8.5v5A1.5 1.5 0 0 0 2.5 15h7a1.5 1.5 0 0 0 1.5-1.5v-5A1.5 1.5 0 0 0 9.5 7V4.5a2 2 0 1 1 4 0v1.75a.75.75 0 0 0 1.5 0V4.5A3.5 3.5 0 0 0 11.5 1Z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
<span class="ml-2 self-center">{$i18n.t('Allow')}</span>
|
||||||
|
{:else}
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 16 16"
|
||||||
|
fill="currentColor"
|
||||||
|
class="w-4 h-4"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
d="M8 1a3.5 3.5 0 0 0-3.5 3.5V7A1.5 1.5 0 0 0 3 8.5v5A1.5 1.5 0 0 0 4.5 15h7a1.5 1.5 0 0 0 1.5-1.5v-5A1.5 1.5 0 0 0 11.5 7V4.5A3.5 3.5 0 0 0 8 1Zm2 6V4.5a2 2 0 1 0-4 0V7h4Z"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
<span class="ml-2 self-center">{$i18n.t("Don't Allow")}</span>
|
||||||
|
{/if}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class=" flex w-full justify-between">
|
||||||
|
<div class=" self-center text-xs font-medium">{$i18n.t('Allow Temporary Chat')}</div>
|
||||||
|
|
||||||
|
<button
|
||||||
|
class="p-1 px-3 text-xs flex rounded transition"
|
||||||
|
on:click={() => {
|
||||||
|
permissions.chat.temporary = !(permissions?.chat?.temporary ?? true);
|
||||||
|
}}
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
{#if permissions?.chat?.temporary ?? true}
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 16 16"
|
||||||
|
fill="currentColor"
|
||||||
|
class="w-4 h-4"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M11.5 1A3.5 3.5 0 0 0 8 4.5V7H2.5A1.5 1.5 0 0 0 1 8.5v5A1.5 1.5 0 0 0 2.5 15h7a1.5 1.5 0 0 0 1.5-1.5v-5A1.5 1.5 0 0 0 9.5 7V4.5a2 2 0 1 1 4 0v1.75a.75.75 0 0 0 1.5 0V4.5A3.5 3.5 0 0 0 11.5 1Z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
<span class="ml-2 self-center">{$i18n.t('Allow')}</span>
|
||||||
|
{:else}
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 16 16"
|
||||||
|
fill="currentColor"
|
||||||
|
class="w-4 h-4"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
d="M8 1a3.5 3.5 0 0 0-3.5 3.5V7A1.5 1.5 0 0 0 3 8.5v5A1.5 1.5 0 0 0 4.5 15h7a1.5 1.5 0 0 0 1.5-1.5v-5A1.5 1.5 0 0 0 11.5 7V4.5A3.5 3.5 0 0 0 8 1Zm2 6V4.5a2 2 0 1 0-4 0V7h4Z"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
<span class="ml-2 self-center">{$i18n.t("Don't Allow")}</span>
|
||||||
|
{/if}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<hr class=" dark:border-gray-850 my-2" />
|
<hr class=" dark:border-gray-850 my-2" />
|
||||||
|
@ -150,7 +150,7 @@
|
|||||||
}`
|
}`
|
||||||
: `border-gray-50 dark:border-gray-850 border-dashed ${
|
: `border-gray-50 dark:border-gray-850 border-dashed ${
|
||||||
$mobile ? 'min-w-full' : 'min-w-80'
|
$mobile ? 'min-w-full' : 'min-w-80'
|
||||||
}`} transition-[width] transform duration-300 p-5 rounded-2xl"
|
}`} transition-all p-5 rounded-2xl"
|
||||||
on:click={() => {
|
on:click={() => {
|
||||||
if (currentMessageId != message.id) {
|
if (currentMessageId != message.id) {
|
||||||
currentMessageId = message.id;
|
currentMessageId = message.id;
|
||||||
|
@ -458,31 +458,33 @@
|
|||||||
|
|
||||||
{#if message.done}
|
{#if message.done}
|
||||||
{#if !readOnly}
|
{#if !readOnly}
|
||||||
<Tooltip content={$i18n.t('Edit')} placement="bottom">
|
{#if $user.role === 'user' ? ($config?.permissions?.chat?.editing ?? true) : true}
|
||||||
<button
|
<Tooltip content={$i18n.t('Edit')} placement="bottom">
|
||||||
class="{isLastMessage
|
<button
|
||||||
? 'visible'
|
class="{isLastMessage
|
||||||
: 'invisible group-hover:visible'} p-1.5 hover:bg-black/5 dark:hover:bg-white/5 rounded-lg dark:hover:text-white hover:text-black transition"
|
? 'visible'
|
||||||
on:click={() => {
|
: 'invisible group-hover:visible'} p-1.5 hover:bg-black/5 dark:hover:bg-white/5 rounded-lg dark:hover:text-white hover:text-black transition"
|
||||||
editMessageHandler();
|
on:click={() => {
|
||||||
}}
|
editMessageHandler();
|
||||||
>
|
}}
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
fill="none"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
stroke-width="2.3"
|
|
||||||
stroke="currentColor"
|
|
||||||
class="w-4 h-4"
|
|
||||||
>
|
>
|
||||||
<path
|
<svg
|
||||||
stroke-linecap="round"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
stroke-linejoin="round"
|
fill="none"
|
||||||
d="M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L6.832 19.82a4.5 4.5 0 01-1.897 1.13l-2.685.8.8-2.685a4.5 4.5 0 011.13-1.897L16.863 4.487zm0 0L19.5 7.125"
|
viewBox="0 0 24 24"
|
||||||
/>
|
stroke-width="2.3"
|
||||||
</svg>
|
stroke="currentColor"
|
||||||
</button>
|
class="w-4 h-4"
|
||||||
</Tooltip>
|
>
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
d="M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L6.832 19.82a4.5 4.5 0 01-1.897 1.13l-2.685.8.8-2.685a4.5 4.5 0 011.13-1.897L16.863 4.487zm0 0L19.5 7.125"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</Tooltip>
|
||||||
|
{/if}
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<Tooltip content={$i18n.t('Copy')} placement="bottom">
|
<Tooltip content={$i18n.t('Copy')} placement="bottom">
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { models, showSettings, settings, user, mobile } from '$lib/stores';
|
import { models, showSettings, settings, user, mobile, config } from '$lib/stores';
|
||||||
import { onMount, tick, getContext } from 'svelte';
|
import { onMount, tick, getContext } from 'svelte';
|
||||||
import { toast } from 'svelte-sonner';
|
import { toast } from 'svelte-sonner';
|
||||||
import Selector from './ModelSelector/Selector.svelte';
|
import Selector from './ModelSelector/Selector.svelte';
|
||||||
@ -46,7 +46,9 @@
|
|||||||
label: model.name,
|
label: model.name,
|
||||||
model: model
|
model: model
|
||||||
}))}
|
}))}
|
||||||
showTemporaryChatControl={true}
|
showTemporaryChatControl={$user.role === 'user'
|
||||||
|
? ($config?.permissions?.chat?.temporary ?? true)
|
||||||
|
: true}
|
||||||
bind:value={selectedModel}
|
bind:value={selectedModel}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -160,6 +160,7 @@
|
|||||||
if (sessionUser) {
|
if (sessionUser) {
|
||||||
// Save Session User to Store
|
// Save Session User to Store
|
||||||
await user.set(sessionUser);
|
await user.set(sessionUser);
|
||||||
|
await config.set(await getBackendConfig());
|
||||||
} else {
|
} else {
|
||||||
// Redirect Invalid Session User to /auth Page
|
// Redirect Invalid Session User to /auth Page
|
||||||
localStorage.removeItem('token');
|
localStorage.removeItem('token');
|
||||||
|
Loading…
Reference in New Issue
Block a user