mirror of
https://github.com/open-webui/open-webui
synced 2025-06-26 18:26:48 +00:00
Merge branch 'dev' of https://github.com/open-webui/open-webui into feat/web-search-toggle
This commit is contained in:
@@ -43,6 +43,8 @@ from config import (
|
||||
DEVICE_TYPE,
|
||||
AUDIO_OPENAI_API_BASE_URL,
|
||||
AUDIO_OPENAI_API_KEY,
|
||||
AUDIO_OPENAI_API_MODEL,
|
||||
AUDIO_OPENAI_API_VOICE,
|
||||
)
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
@@ -60,6 +62,8 @@ app.add_middleware(
|
||||
|
||||
app.state.OPENAI_API_BASE_URL = AUDIO_OPENAI_API_BASE_URL
|
||||
app.state.OPENAI_API_KEY = AUDIO_OPENAI_API_KEY
|
||||
app.state.OPENAI_API_MODEL = AUDIO_OPENAI_API_MODEL
|
||||
app.state.OPENAI_API_VOICE = AUDIO_OPENAI_API_VOICE
|
||||
|
||||
# setting device type for whisper model
|
||||
whisper_device_type = DEVICE_TYPE if DEVICE_TYPE and DEVICE_TYPE == "cuda" else "cpu"
|
||||
@@ -72,6 +76,8 @@ SPEECH_CACHE_DIR.mkdir(parents=True, exist_ok=True)
|
||||
class OpenAIConfigUpdateForm(BaseModel):
|
||||
url: str
|
||||
key: str
|
||||
model: str
|
||||
speaker: str
|
||||
|
||||
|
||||
@app.get("/config")
|
||||
@@ -79,6 +85,8 @@ async def get_openai_config(user=Depends(get_admin_user)):
|
||||
return {
|
||||
"OPENAI_API_BASE_URL": app.state.OPENAI_API_BASE_URL,
|
||||
"OPENAI_API_KEY": app.state.OPENAI_API_KEY,
|
||||
"OPENAI_API_MODEL": app.state.OPENAI_API_MODEL,
|
||||
"OPENAI_API_VOICE": app.state.OPENAI_API_VOICE,
|
||||
}
|
||||
|
||||
|
||||
@@ -91,11 +99,15 @@ async def update_openai_config(
|
||||
|
||||
app.state.OPENAI_API_BASE_URL = form_data.url
|
||||
app.state.OPENAI_API_KEY = form_data.key
|
||||
app.state.OPENAI_API_MODEL = form_data.model
|
||||
app.state.OPENAI_API_VOICE = form_data.speaker
|
||||
|
||||
return {
|
||||
"status": True,
|
||||
"OPENAI_API_BASE_URL": app.state.OPENAI_API_BASE_URL,
|
||||
"OPENAI_API_KEY": app.state.OPENAI_API_KEY,
|
||||
"OPENAI_API_MODEL": app.state.OPENAI_API_MODEL,
|
||||
"OPENAI_API_VOICE": app.state.OPENAI_API_VOICE,
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ import uuid
|
||||
import aiohttp
|
||||
import asyncio
|
||||
import logging
|
||||
import time
|
||||
from urllib.parse import urlparse
|
||||
from typing import Optional, List, Union
|
||||
|
||||
@@ -1031,6 +1032,75 @@ async def generate_openai_chat_completion(
|
||||
)
|
||||
|
||||
|
||||
@app.get("/v1/models")
|
||||
@app.get("/v1/models/{url_idx}")
|
||||
async def get_openai_models(
|
||||
url_idx: Optional[int] = None,
|
||||
user=Depends(get_verified_user),
|
||||
):
|
||||
if url_idx == None:
|
||||
models = await get_all_models()
|
||||
|
||||
if app.state.ENABLE_MODEL_FILTER:
|
||||
if user.role == "user":
|
||||
models["models"] = list(
|
||||
filter(
|
||||
lambda model: model["name"] in app.state.MODEL_FILTER_LIST,
|
||||
models["models"],
|
||||
)
|
||||
)
|
||||
|
||||
return {
|
||||
"data": [
|
||||
{
|
||||
"id": model["model"],
|
||||
"object": "model",
|
||||
"created": int(time.time()),
|
||||
"owned_by": "openai",
|
||||
}
|
||||
for model in models["models"]
|
||||
],
|
||||
"object": "list",
|
||||
}
|
||||
|
||||
else:
|
||||
url = app.state.OLLAMA_BASE_URLS[url_idx]
|
||||
try:
|
||||
r = requests.request(method="GET", url=f"{url}/api/tags")
|
||||
r.raise_for_status()
|
||||
|
||||
models = r.json()
|
||||
|
||||
return {
|
||||
"data": [
|
||||
{
|
||||
"id": model["model"],
|
||||
"object": "model",
|
||||
"created": int(time.time()),
|
||||
"owned_by": "openai",
|
||||
}
|
||||
for model in models["models"]
|
||||
],
|
||||
"object": "list",
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
log.exception(e)
|
||||
error_detail = "Open WebUI: Server Connection Error"
|
||||
if r is not None:
|
||||
try:
|
||||
res = r.json()
|
||||
if "error" in res:
|
||||
error_detail = f"Ollama: {res['error']}"
|
||||
except:
|
||||
error_detail = f"Ollama: {e}"
|
||||
|
||||
raise HTTPException(
|
||||
status_code=r.status_code if r else 500,
|
||||
detail=error_detail,
|
||||
)
|
||||
|
||||
|
||||
class UrlForm(BaseModel):
|
||||
url: str
|
||||
|
||||
|
||||
@@ -93,6 +93,7 @@ from config import (
|
||||
CHUNK_OVERLAP,
|
||||
RAG_TEMPLATE,
|
||||
ENABLE_RAG_LOCAL_WEB_FETCH,
|
||||
YOUTUBE_LOADER_LANGUAGE,
|
||||
RAG_WEB_SEARCH_CONCURRENT_REQUESTS,
|
||||
)
|
||||
|
||||
@@ -126,6 +127,10 @@ app.state.OPENAI_API_KEY = RAG_OPENAI_API_KEY
|
||||
app.state.PDF_EXTRACT_IMAGES = PDF_EXTRACT_IMAGES
|
||||
|
||||
|
||||
app.state.YOUTUBE_LOADER_LANGUAGE = YOUTUBE_LOADER_LANGUAGE
|
||||
app.state.YOUTUBE_LOADER_TRANSLATION = None
|
||||
|
||||
|
||||
def update_embedding_model(
|
||||
embedding_model: str,
|
||||
update_model: bool = False,
|
||||
@@ -320,6 +325,10 @@ async def get_rag_config(user=Depends(get_admin_user)):
|
||||
"chunk_overlap": app.state.CHUNK_OVERLAP,
|
||||
},
|
||||
"web_loader_ssl_verification": app.state.ENABLE_RAG_WEB_LOADER_SSL_VERIFICATION,
|
||||
"youtube": {
|
||||
"language": app.state.YOUTUBE_LOADER_LANGUAGE,
|
||||
"translation": app.state.YOUTUBE_LOADER_TRANSLATION,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -328,10 +337,16 @@ class ChunkParamUpdateForm(BaseModel):
|
||||
chunk_overlap: int
|
||||
|
||||
|
||||
class YoutubeLoaderConfig(BaseModel):
|
||||
language: List[str]
|
||||
translation: Optional[str] = None
|
||||
|
||||
|
||||
class ConfigUpdateForm(BaseModel):
|
||||
pdf_extract_images: Optional[bool] = None
|
||||
chunk: Optional[ChunkParamUpdateForm] = None
|
||||
web_loader_ssl_verification: Optional[bool] = None
|
||||
youtube: Optional[YoutubeLoaderConfig] = None
|
||||
|
||||
|
||||
@app.post("/config/update")
|
||||
@@ -358,6 +373,18 @@ async def update_rag_config(form_data: ConfigUpdateForm, user=Depends(get_admin_
|
||||
else app.state.ENABLE_RAG_WEB_LOADER_SSL_VERIFICATION
|
||||
)
|
||||
|
||||
app.state.YOUTUBE_LOADER_LANGUAGE = (
|
||||
form_data.youtube.language
|
||||
if form_data.youtube != None
|
||||
else app.state.YOUTUBE_LOADER_LANGUAGE
|
||||
)
|
||||
|
||||
app.state.YOUTUBE_LOADER_TRANSLATION = (
|
||||
form_data.youtube.translation
|
||||
if form_data.youtube != None
|
||||
else app.state.YOUTUBE_LOADER_TRANSLATION
|
||||
)
|
||||
|
||||
return {
|
||||
"status": True,
|
||||
"pdf_extract_images": app.state.PDF_EXTRACT_IMAGES,
|
||||
@@ -366,6 +393,10 @@ async def update_rag_config(form_data: ConfigUpdateForm, user=Depends(get_admin_
|
||||
"chunk_overlap": app.state.CHUNK_OVERLAP,
|
||||
},
|
||||
"web_loader_ssl_verification": app.state.ENABLE_RAG_WEB_LOADER_SSL_VERIFICATION,
|
||||
"youtube": {
|
||||
"language": app.state.YOUTUBE_LOADER_LANGUAGE,
|
||||
"translation": app.state.YOUTUBE_LOADER_TRANSLATION,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -492,7 +523,12 @@ def query_collection_handler(
|
||||
@app.post("/youtube")
|
||||
def store_youtube_video(form_data: UrlForm, user=Depends(get_current_user)):
|
||||
try:
|
||||
loader = YoutubeLoader.from_youtube_url(form_data.url, add_video_info=False)
|
||||
loader = YoutubeLoader.from_youtube_url(
|
||||
form_data.url,
|
||||
add_video_info=True,
|
||||
language=app.state.YOUTUBE_LOADER_LANGUAGE,
|
||||
translation=app.state.YOUTUBE_LOADER_TRANSLATION,
|
||||
)
|
||||
data = loader.load()
|
||||
|
||||
collection_name = form_data.collection_name
|
||||
@@ -676,7 +712,7 @@ def store_docs_in_vector_db(docs, collection_name, overwrite: bool = False) -> b
|
||||
|
||||
for batch in create_batches(
|
||||
api=CHROMA_CLIENT,
|
||||
ids=[str(uuid.uuid1()) for _ in texts],
|
||||
ids=[str(uuid.uuid4()) for _ in texts],
|
||||
metadatas=metadatas,
|
||||
embeddings=embeddings,
|
||||
documents=texts,
|
||||
|
||||
@@ -33,7 +33,7 @@ from utils.utils import (
|
||||
from utils.misc import parse_duration, validate_email_format
|
||||
from utils.webhook import post_webhook
|
||||
from constants import ERROR_MESSAGES, WEBHOOK_MESSAGES
|
||||
from config import WEBUI_AUTH_TRUSTED_EMAIL_HEADER
|
||||
from config import WEBUI_AUTH, WEBUI_AUTH_TRUSTED_EMAIL_HEADER
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@@ -118,6 +118,22 @@ async def signin(request: Request, form_data: SigninForm):
|
||||
),
|
||||
)
|
||||
user = Auths.authenticate_user_by_trusted_header(trusted_email)
|
||||
elif WEBUI_AUTH == False:
|
||||
admin_email = "admin@localhost"
|
||||
admin_password = "admin"
|
||||
|
||||
if Users.get_user_by_email(admin_email.lower()):
|
||||
user = Auths.authenticate_user(admin_email.lower(), admin_password)
|
||||
else:
|
||||
if Users.get_num_users() != 0:
|
||||
raise HTTPException(400, detail=ERROR_MESSAGES.EXISTING_USERS)
|
||||
|
||||
await signup(
|
||||
request,
|
||||
SignupForm(email=admin_email, password=admin_password, name="User"),
|
||||
)
|
||||
|
||||
user = Auths.authenticate_user(admin_email.lower(), admin_password)
|
||||
else:
|
||||
user = Auths.authenticate_user(form_data.email.lower(), form_data.password)
|
||||
|
||||
@@ -147,7 +163,7 @@ async def signin(request: Request, form_data: SigninForm):
|
||||
|
||||
@router.post("/signup", response_model=SigninResponse)
|
||||
async def signup(request: Request, form_data: SignupForm):
|
||||
if not request.app.state.ENABLE_SIGNUP:
|
||||
if not request.app.state.ENABLE_SIGNUP and WEBUI_AUTH:
|
||||
raise HTTPException(
|
||||
status.HTTP_403_FORBIDDEN, detail=ERROR_MESSAGES.ACCESS_PROHIBITED
|
||||
)
|
||||
|
||||
@@ -76,8 +76,11 @@ WEBUI_NAME = os.environ.get("WEBUI_NAME", "Open WebUI")
|
||||
if WEBUI_NAME != "Open WebUI":
|
||||
WEBUI_NAME += " (Open WebUI)"
|
||||
|
||||
WEBUI_URL = os.environ.get("WEBUI_URL", "http://localhost:3000")
|
||||
|
||||
WEBUI_FAVICON_URL = "https://openwebui.com/favicon.png"
|
||||
|
||||
|
||||
####################################
|
||||
# ENV (dev,test,prod)
|
||||
####################################
|
||||
@@ -151,6 +154,23 @@ for version in soup.find_all("h2"):
|
||||
|
||||
CHANGELOG = changelog_json
|
||||
|
||||
|
||||
####################################
|
||||
# WEBUI_VERSION
|
||||
####################################
|
||||
|
||||
WEBUI_VERSION = os.environ.get("WEBUI_VERSION", "v1.0.0-alpha.100")
|
||||
|
||||
####################################
|
||||
# WEBUI_AUTH (Required for security)
|
||||
####################################
|
||||
|
||||
WEBUI_AUTH = os.environ.get("WEBUI_AUTH", "True").lower() == "true"
|
||||
WEBUI_AUTH_TRUSTED_EMAIL_HEADER = os.environ.get(
|
||||
"WEBUI_AUTH_TRUSTED_EMAIL_HEADER", None
|
||||
)
|
||||
|
||||
|
||||
####################################
|
||||
# DATA/FRONTEND BUILD DIR
|
||||
####################################
|
||||
@@ -343,7 +363,11 @@ OPENAI_API_BASE_URL = "https://api.openai.com/v1"
|
||||
# WEBUI
|
||||
####################################
|
||||
|
||||
ENABLE_SIGNUP = os.environ.get("ENABLE_SIGNUP", "True").lower() == "true"
|
||||
ENABLE_SIGNUP = (
|
||||
False
|
||||
if WEBUI_AUTH == False
|
||||
else os.environ.get("ENABLE_SIGNUP", "True").lower() == "true"
|
||||
)
|
||||
DEFAULT_MODELS = os.environ.get("DEFAULT_MODELS", None)
|
||||
|
||||
|
||||
@@ -400,21 +424,6 @@ WEBHOOK_URL = os.environ.get("WEBHOOK_URL", "")
|
||||
|
||||
ENABLE_ADMIN_EXPORT = os.environ.get("ENABLE_ADMIN_EXPORT", "True").lower() == "true"
|
||||
|
||||
####################################
|
||||
# WEBUI_VERSION
|
||||
####################################
|
||||
|
||||
WEBUI_VERSION = os.environ.get("WEBUI_VERSION", "v1.0.0-alpha.100")
|
||||
|
||||
####################################
|
||||
# WEBUI_AUTH (Required for security)
|
||||
####################################
|
||||
|
||||
WEBUI_AUTH = True
|
||||
WEBUI_AUTH_TRUSTED_EMAIL_HEADER = os.environ.get(
|
||||
"WEBUI_AUTH_TRUSTED_EMAIL_HEADER", None
|
||||
)
|
||||
|
||||
####################################
|
||||
# WEBUI_SECRET_KEY
|
||||
####################################
|
||||
@@ -490,13 +499,6 @@ RAG_RERANKING_MODEL_TRUST_REMOTE_CODE = (
|
||||
os.environ.get("RAG_RERANKING_MODEL_TRUST_REMOTE_CODE", "").lower() == "true"
|
||||
)
|
||||
|
||||
# device type embedding models - "cpu" (default), "cuda" (nvidia gpu required) or "mps" (apple silicon) - choosing this right can lead to better performance
|
||||
USE_CUDA = os.environ.get("USE_CUDA_DOCKER", "false")
|
||||
|
||||
if USE_CUDA.lower() == "true":
|
||||
DEVICE_TYPE = "cuda"
|
||||
else:
|
||||
DEVICE_TYPE = "cpu"
|
||||
|
||||
if CHROMA_HTTP_HOST != "":
|
||||
CHROMA_CLIENT = chromadb.HttpClient(
|
||||
@@ -516,6 +518,16 @@ else:
|
||||
database=CHROMA_DATABASE,
|
||||
)
|
||||
|
||||
|
||||
# device type embedding models - "cpu" (default), "cuda" (nvidia gpu required) or "mps" (apple silicon) - choosing this right can lead to better performance
|
||||
USE_CUDA = os.environ.get("USE_CUDA_DOCKER", "false")
|
||||
|
||||
if USE_CUDA.lower() == "true":
|
||||
DEVICE_TYPE = "cuda"
|
||||
else:
|
||||
DEVICE_TYPE = "cpu"
|
||||
|
||||
|
||||
CHUNK_SIZE = int(os.environ.get("CHUNK_SIZE", "1500"))
|
||||
CHUNK_OVERLAP = int(os.environ.get("CHUNK_OVERLAP", "100"))
|
||||
|
||||
@@ -542,6 +554,8 @@ ENABLE_RAG_LOCAL_WEB_FETCH = (
|
||||
os.getenv("ENABLE_RAG_LOCAL_WEB_FETCH", "False").lower() == "true"
|
||||
)
|
||||
|
||||
YOUTUBE_LOADER_LANGUAGE = os.getenv("YOUTUBE_LOADER_LANGUAGE", "en").split(",")
|
||||
|
||||
SEARXNG_QUERY_URL = os.getenv("SEARXNG_QUERY_URL", "")
|
||||
GOOGLE_PSE_API_KEY = os.getenv("GOOGLE_PSE_API_KEY", "")
|
||||
GOOGLE_PSE_ENGINE_ID = os.getenv("GOOGLE_PSE_ENGINE_ID", "")
|
||||
@@ -595,6 +609,8 @@ IMAGE_GENERATION_MODEL = os.getenv("IMAGE_GENERATION_MODEL", "")
|
||||
|
||||
AUDIO_OPENAI_API_BASE_URL = os.getenv("AUDIO_OPENAI_API_BASE_URL", OPENAI_API_BASE_URL)
|
||||
AUDIO_OPENAI_API_KEY = os.getenv("AUDIO_OPENAI_API_KEY", OPENAI_API_KEY)
|
||||
AUDIO_OPENAI_API_MODEL = os.getenv("AUDIO_OPENAI_API_MODEL", "tts-1")
|
||||
AUDIO_OPENAI_API_VOICE = os.getenv("AUDIO_OPENAI_API_VOICE", "alloy")
|
||||
|
||||
####################################
|
||||
# LiteLLM
|
||||
|
||||
@@ -42,6 +42,9 @@ class ERROR_MESSAGES(str, Enum):
|
||||
"The password provided is incorrect. Please check for typos and try again."
|
||||
)
|
||||
INVALID_TRUSTED_HEADER = "Your provider has not provided a trusted header. Please contact your administrator for assistance."
|
||||
|
||||
EXISTING_USERS = "You can't turn off authentication because there are existing users. If you want to disable WEBUI_AUTH, make sure your web interface doesn't have any existing users and is a fresh installation."
|
||||
|
||||
UNAUTHORIZED = "401 Unauthorized"
|
||||
ACCESS_PROHIBITED = "You do not have permission to access this resource. Please contact your administrator for assistance."
|
||||
ACTION_PROHIBITED = (
|
||||
|
||||
@@ -15,7 +15,7 @@ from fastapi.middleware.wsgi import WSGIMiddleware
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from starlette.exceptions import HTTPException as StarletteHTTPException
|
||||
from starlette.middleware.base import BaseHTTPMiddleware
|
||||
from starlette.responses import StreamingResponse
|
||||
from starlette.responses import StreamingResponse, Response
|
||||
|
||||
from apps.ollama.main import app as ollama_app
|
||||
from apps.openai.main import app as openai_app
|
||||
@@ -43,6 +43,8 @@ from apps.rag.utils import rag_messages
|
||||
from config import (
|
||||
CONFIG_DATA,
|
||||
WEBUI_NAME,
|
||||
WEBUI_URL,
|
||||
WEBUI_AUTH,
|
||||
ENV,
|
||||
VERSION,
|
||||
CHANGELOG,
|
||||
@@ -239,6 +241,7 @@ async def get_app_config():
|
||||
"status": True,
|
||||
"name": WEBUI_NAME,
|
||||
"version": VERSION,
|
||||
"auth": WEBUI_AUTH,
|
||||
"default_locale": default_locale,
|
||||
"images": images_app.state.ENABLED,
|
||||
"default_models": webui_app.state.DEFAULT_MODELS,
|
||||
@@ -350,6 +353,21 @@ async def get_manifest_json():
|
||||
}
|
||||
|
||||
|
||||
@app.get("/opensearch.xml")
|
||||
async def get_opensearch_xml():
|
||||
xml_content = rf"""
|
||||
<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/" xmlns:moz="http://www.mozilla.org/2006/browser/search/">
|
||||
<ShortName>{WEBUI_NAME}</ShortName>
|
||||
<Description>Search {WEBUI_NAME}</Description>
|
||||
<InputEncoding>UTF-8</InputEncoding>
|
||||
<Image width="16" height="16" type="image/x-icon">{WEBUI_URL}/favicon.png</Image>
|
||||
<Url type="text/html" method="get" template="{WEBUI_URL}/?q={"{searchTerms}"}"/>
|
||||
<moz:SearchForm>{WEBUI_URL}</moz:SearchForm>
|
||||
</OpenSearchDescription>
|
||||
"""
|
||||
return Response(content=xml_content, media_type="application/xml")
|
||||
|
||||
|
||||
app.mount("/static", StaticFiles(directory=STATIC_DIR), name="static")
|
||||
app.mount("/cache", StaticFiles(directory=CACHE_DIR), name="cache")
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@ Flask-Cors==4.0.0
|
||||
python-socketio==5.11.2
|
||||
python-jose==3.3.0
|
||||
passlib[bcrypt]==1.7.4
|
||||
uuid==1.30
|
||||
|
||||
requests==2.31.0
|
||||
aiohttp==3.9.5
|
||||
@@ -19,7 +18,6 @@ psycopg2-binary==2.9.9
|
||||
PyMySQL==1.1.0
|
||||
bcrypt==4.1.2
|
||||
|
||||
litellm==1.35.28
|
||||
litellm[proxy]==1.35.28
|
||||
|
||||
boto3==1.34.95
|
||||
@@ -54,9 +52,9 @@ rank-bm25==0.2.2
|
||||
|
||||
faster-whisper==1.0.1
|
||||
|
||||
PyJWT==2.8.0
|
||||
PyJWT[crypto]==2.8.0
|
||||
|
||||
black==24.4.2
|
||||
langfuse==2.27.3
|
||||
youtube-transcript-api
|
||||
youtube-transcript-api==0.6.2
|
||||
pytube
|
||||
@@ -8,7 +8,7 @@ KEY_FILE=.webui_secret_key
|
||||
PORT="${PORT:-8080}"
|
||||
HOST="${HOST:-0.0.0.0}"
|
||||
if test "$WEBUI_SECRET_KEY $WEBUI_JWT_SECRET_KEY" = " "; then
|
||||
echo "No WEBUI_SECRET_KEY provided"
|
||||
echo "Loading WEBUI_SECRET_KEY from file, not provided as an environment variable."
|
||||
|
||||
if ! [ -e "$KEY_FILE" ]; then
|
||||
echo "Generating WEBUI_SECRET_KEY"
|
||||
|
||||
@@ -13,7 +13,7 @@ SET "WEBUI_JWT_SECRET_KEY=%WEBUI_JWT_SECRET_KEY%"
|
||||
|
||||
:: Check if WEBUI_SECRET_KEY and WEBUI_JWT_SECRET_KEY are not set
|
||||
IF "%WEBUI_SECRET_KEY%%WEBUI_JWT_SECRET_KEY%" == " " (
|
||||
echo No WEBUI_SECRET_KEY provided
|
||||
echo Loading WEBUI_SECRET_KEY from file, not provided as an environment variable.
|
||||
|
||||
IF NOT EXIST "%KEY_FILE%" (
|
||||
echo Generating WEBUI_SECRET_KEY
|
||||
|
||||
@@ -38,9 +38,10 @@ def calculate_sha256_string(string):
|
||||
|
||||
|
||||
def validate_email_format(email: str) -> bool:
|
||||
if not re.match(r"[^@]+@[^@]+\.[^@]+", email):
|
||||
return False
|
||||
return True
|
||||
if email.endswith("@localhost"):
|
||||
return True
|
||||
|
||||
return bool(re.match(r"[^@]+@[^@]+\.[^@]+", email))
|
||||
|
||||
|
||||
def sanitize_filename(file_name):
|
||||
|
||||
Reference in New Issue
Block a user