forked from open-webui/open-webui
Merge branch 'main' into rag
This commit is contained in:
commit
438942d64e
7 changed files with 207 additions and 64 deletions
|
@ -22,7 +22,6 @@ ARG OLLAMA_API_BASE_URL='/ollama/api'
|
||||||
|
|
||||||
ENV ENV=prod
|
ENV ENV=prod
|
||||||
ENV OLLAMA_API_BASE_URL $OLLAMA_API_BASE_URL
|
ENV OLLAMA_API_BASE_URL $OLLAMA_API_BASE_URL
|
||||||
ENV WEBUI_AUTH ""
|
|
||||||
ENV WEBUI_JWT_SECRET_KEY "SECRET_KEY"
|
ENV WEBUI_JWT_SECRET_KEY "SECRET_KEY"
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
|
@ -8,6 +8,8 @@ app = FastAPI()
|
||||||
|
|
||||||
origins = ["*"]
|
origins = ["*"]
|
||||||
|
|
||||||
|
app.state.ENABLE_SIGNUP = True
|
||||||
|
|
||||||
app.add_middleware(
|
app.add_middleware(
|
||||||
CORSMiddleware,
|
CORSMiddleware,
|
||||||
allow_origins=origins,
|
allow_origins=origins,
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
from fastapi import Response
|
from fastapi import Response, Request
|
||||||
from fastapi import Depends, FastAPI, HTTPException, status
|
from fastapi import Depends, FastAPI, HTTPException, status
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from typing import List, Union
|
from typing import List, Union
|
||||||
|
@ -93,31 +93,62 @@ async def signin(form_data: SigninForm):
|
||||||
|
|
||||||
|
|
||||||
@router.post("/signup", response_model=SigninResponse)
|
@router.post("/signup", response_model=SigninResponse)
|
||||||
async def signup(form_data: SignupForm):
|
async def signup(request: Request, form_data: SignupForm):
|
||||||
if not Users.get_user_by_email(form_data.email.lower()):
|
if request.app.state.ENABLE_SIGNUP:
|
||||||
try:
|
if not Users.get_user_by_email(form_data.email.lower()):
|
||||||
role = "admin" if Users.get_num_users() == 0 else "pending"
|
try:
|
||||||
hashed = get_password_hash(form_data.password)
|
role = "admin" if Users.get_num_users() == 0 else "pending"
|
||||||
user = Auths.insert_new_auth(
|
hashed = get_password_hash(form_data.password)
|
||||||
form_data.email.lower(), hashed, form_data.name, role
|
user = Auths.insert_new_auth(
|
||||||
)
|
form_data.email.lower(), hashed, form_data.name, role
|
||||||
|
)
|
||||||
|
|
||||||
if user:
|
if user:
|
||||||
token = create_token(data={"email": user.email})
|
token = create_token(data={"email": user.email})
|
||||||
# response.set_cookie(key='token', value=token, httponly=True)
|
# response.set_cookie(key='token', value=token, httponly=True)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"token": token,
|
"token": token,
|
||||||
"token_type": "Bearer",
|
"token_type": "Bearer",
|
||||||
"id": user.id,
|
"id": user.id,
|
||||||
"email": user.email,
|
"email": user.email,
|
||||||
"name": user.name,
|
"name": user.name,
|
||||||
"role": user.role,
|
"role": user.role,
|
||||||
"profile_image_url": user.profile_image_url,
|
"profile_image_url": user.profile_image_url,
|
||||||
}
|
}
|
||||||
else:
|
else:
|
||||||
raise HTTPException(500, detail=ERROR_MESSAGES.CREATE_USER_ERROR)
|
raise HTTPException(500, detail=ERROR_MESSAGES.CREATE_USER_ERROR)
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
raise HTTPException(500, detail=ERROR_MESSAGES.DEFAULT(err))
|
raise HTTPException(500, detail=ERROR_MESSAGES.DEFAULT(err))
|
||||||
|
else:
|
||||||
|
raise HTTPException(400, detail=ERROR_MESSAGES.EMAIL_TAKEN)
|
||||||
else:
|
else:
|
||||||
raise HTTPException(400, detail=ERROR_MESSAGES.EMAIL_TAKEN)
|
raise HTTPException(400, detail=ERROR_MESSAGES.ACCESS_PROHIBITED)
|
||||||
|
|
||||||
|
|
||||||
|
############################
|
||||||
|
# ToggleSignUp
|
||||||
|
############################
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/signup/enabled", response_model=bool)
|
||||||
|
async def get_sign_up_status(request: Request, user=Depends(get_current_user)):
|
||||||
|
if user.role == "admin":
|
||||||
|
return request.app.state.ENABLE_SIGNUP
|
||||||
|
else:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_403_FORBIDDEN,
|
||||||
|
detail=ERROR_MESSAGES.ACCESS_PROHIBITED,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/signup/enabled/toggle", response_model=bool)
|
||||||
|
async def toggle_sign_up(request: Request, user=Depends(get_current_user)):
|
||||||
|
if user.role == "admin":
|
||||||
|
request.app.state.ENABLE_SIGNUP = not request.app.state.ENABLE_SIGNUP
|
||||||
|
return request.app.state.ENABLE_SIGNUP
|
||||||
|
else:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_403_FORBIDDEN,
|
||||||
|
detail=ERROR_MESSAGES.ACCESS_PROHIBITED,
|
||||||
|
)
|
||||||
|
|
|
@ -15,6 +15,7 @@ from apps.web.models.auths import Auths
|
||||||
from utils.utils import get_current_user
|
from utils.utils import get_current_user
|
||||||
from constants import ERROR_MESSAGES
|
from constants import ERROR_MESSAGES
|
||||||
|
|
||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
############################
|
############################
|
||||||
|
|
|
@ -119,3 +119,57 @@ export const updateUserPassword = async (token: string, password: string, newPas
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getSignUpEnabledStatus = async (token: string) => {
|
||||||
|
let error = null;
|
||||||
|
|
||||||
|
const res = await fetch(`${WEBUI_API_BASE_URL}/auths/signup/enabled`, {
|
||||||
|
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 toggleSignUpEnabledStatus = async (token: string) => {
|
||||||
|
let error = null;
|
||||||
|
|
||||||
|
const res = await fetch(`${WEBUI_API_BASE_URL}/auths/signup/enabled/toggle`, {
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
|
|
@ -215,42 +215,44 @@
|
||||||
{#if messages.length == 0}
|
{#if messages.length == 0}
|
||||||
<Placeholder models={selectedModels} modelfiles={selectedModelfiles} />
|
<Placeholder models={selectedModels} modelfiles={selectedModelfiles} />
|
||||||
{:else}
|
{:else}
|
||||||
{#each messages as message, messageIdx}
|
{#key chatId}
|
||||||
<div class=" w-full">
|
{#each messages as message, messageIdx}
|
||||||
<div class="flex justify-between px-5 mb-3 max-w-3xl mx-auto rounded-lg group">
|
<div class=" w-full">
|
||||||
{#if message.role === 'user'}
|
<div class="flex justify-between px-5 mb-3 max-w-3xl mx-auto rounded-lg group">
|
||||||
<UserMessage
|
{#if message.role === 'user'}
|
||||||
user={$user}
|
<UserMessage
|
||||||
{message}
|
user={$user}
|
||||||
siblings={message.parentId !== null
|
{message}
|
||||||
? history.messages[message.parentId]?.childrenIds ?? []
|
siblings={message.parentId !== null
|
||||||
: Object.values(history.messages)
|
? history.messages[message.parentId]?.childrenIds ?? []
|
||||||
.filter((message) => message.parentId === null)
|
: Object.values(history.messages)
|
||||||
.map((message) => message.id) ?? []}
|
.filter((message) => message.parentId === null)
|
||||||
{confirmEditMessage}
|
.map((message) => message.id) ?? []}
|
||||||
{showPreviousMessage}
|
{confirmEditMessage}
|
||||||
{showNextMessage}
|
{showPreviousMessage}
|
||||||
{copyToClipboard}
|
{showNextMessage}
|
||||||
/>
|
{copyToClipboard}
|
||||||
{:else}
|
/>
|
||||||
<ResponseMessage
|
{:else}
|
||||||
{message}
|
<ResponseMessage
|
||||||
modelfiles={selectedModelfiles}
|
{message}
|
||||||
siblings={history.messages[message.parentId]?.childrenIds ?? []}
|
modelfiles={selectedModelfiles}
|
||||||
isLastMessage={messageIdx + 1 === messages.length}
|
siblings={history.messages[message.parentId]?.childrenIds ?? []}
|
||||||
{confirmEditResponseMessage}
|
isLastMessage={messageIdx + 1 === messages.length}
|
||||||
{showPreviousMessage}
|
{confirmEditResponseMessage}
|
||||||
{showNextMessage}
|
{showPreviousMessage}
|
||||||
{rateMessage}
|
{showNextMessage}
|
||||||
{copyToClipboard}
|
{rateMessage}
|
||||||
{regenerateResponse}
|
{copyToClipboard}
|
||||||
/>
|
{regenerateResponse}
|
||||||
{/if}
|
/>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
{/each}
|
||||||
{/each}
|
|
||||||
|
|
||||||
{#if bottomPadding}
|
{#if bottomPadding}
|
||||||
<div class=" mb-10" />
|
<div class=" mb-10" />
|
||||||
{/if}
|
{/if}
|
||||||
|
{/key}
|
||||||
{/if}
|
{/if}
|
||||||
|
|
|
@ -7,10 +7,13 @@
|
||||||
import toast from 'svelte-french-toast';
|
import toast from 'svelte-french-toast';
|
||||||
|
|
||||||
import { updateUserRole, getUsers, deleteUserById } from '$lib/apis/users';
|
import { updateUserRole, getUsers, deleteUserById } from '$lib/apis/users';
|
||||||
|
import { getSignUpEnabledStatus, toggleSignUpEnabledStatus } from '$lib/apis/auths';
|
||||||
|
|
||||||
let loaded = false;
|
let loaded = false;
|
||||||
let users = [];
|
let users = [];
|
||||||
|
|
||||||
|
let signUpEnabled = true;
|
||||||
|
|
||||||
const updateRoleHandler = async (id, role) => {
|
const updateRoleHandler = async (id, role) => {
|
||||||
const res = await updateUserRole(localStorage.token, id, role).catch((error) => {
|
const res = await updateUserRole(localStorage.token, id, role).catch((error) => {
|
||||||
toast.error(error);
|
toast.error(error);
|
||||||
|
@ -32,11 +35,17 @@
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const toggleSignUpEnabled = async () => {
|
||||||
|
signUpEnabled = await toggleSignUpEnabledStatus(localStorage.token);
|
||||||
|
};
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
if ($user?.role !== 'admin') {
|
if ($user?.role !== 'admin') {
|
||||||
await goto('/');
|
await goto('/');
|
||||||
} else {
|
} else {
|
||||||
users = await getUsers(localStorage.token);
|
users = await getUsers(localStorage.token);
|
||||||
|
|
||||||
|
signUpEnabled = await getSignUpEnabledStatus(localStorage.token);
|
||||||
}
|
}
|
||||||
loaded = true;
|
loaded = true;
|
||||||
});
|
});
|
||||||
|
@ -49,7 +58,52 @@
|
||||||
<div class="w-full max-w-3xl px-10 md:px-16 min-h-screen flex flex-col">
|
<div class="w-full max-w-3xl px-10 md:px-16 min-h-screen flex flex-col">
|
||||||
<div class="py-10 w-full">
|
<div class="py-10 w-full">
|
||||||
<div class=" flex flex-col justify-center">
|
<div class=" flex flex-col justify-center">
|
||||||
<div class=" text-2xl font-semibold">Users ({users.length})</div>
|
<div class=" flex justify-between items-center">
|
||||||
|
<div class=" text-2xl font-semibold">Users ({users.length})</div>
|
||||||
|
<div>
|
||||||
|
<button
|
||||||
|
class="flex items-center space-x-1 border border-gray-200 dark:border-gray-600 px-3 py-1 rounded-lg"
|
||||||
|
type="button"
|
||||||
|
on:click={() => {
|
||||||
|
toggleSignUpEnabled();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{#if signUpEnabled}
|
||||||
|
<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>
|
||||||
|
|
||||||
|
<div class=" text-xs">
|
||||||
|
New Sign Up <span class=" font-semibold">Enabled</span>
|
||||||
|
</div>
|
||||||
|
{: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>
|
||||||
|
|
||||||
|
<div class=" text-xs">
|
||||||
|
New Sign Up <span class=" font-semibold">Disabled</span>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class=" text-gray-500 text-xs font-medium mt-1">
|
<div class=" text-gray-500 text-xs font-medium mt-1">
|
||||||
Click on the user role cell in the table to change a user's role.
|
Click on the user role cell in the table to change a user's role.
|
||||||
</div>
|
</div>
|
||||||
|
|
Loading…
Reference in a new issue