|
@@ -1,4 +1,4 @@
|
|
|
-import base64
|
|
|
+import asyncio
|
|
|
import inspect
|
|
|
import json
|
|
|
import logging
|
|
@@ -7,20 +7,37 @@ import os
|
|
|
import shutil
|
|
|
import sys
|
|
|
import time
|
|
|
-import uuid
|
|
|
-import asyncio
|
|
|
-
|
|
|
from contextlib import asynccontextmanager
|
|
|
from typing import Optional
|
|
|
|
|
|
import aiohttp
|
|
|
import requests
|
|
|
+from fastapi import (
|
|
|
+ Depends,
|
|
|
+ FastAPI,
|
|
|
+ File,
|
|
|
+ Form,
|
|
|
+ HTTPException,
|
|
|
+ Request,
|
|
|
+ UploadFile,
|
|
|
+ status,
|
|
|
+)
|
|
|
+from fastapi.middleware.cors import CORSMiddleware
|
|
|
+from fastapi.responses import JSONResponse
|
|
|
+from fastapi.staticfiles import StaticFiles
|
|
|
+from pydantic import BaseModel
|
|
|
+from sqlalchemy import text
|
|
|
+from starlette.exceptions import HTTPException as StarletteHTTPException
|
|
|
+from starlette.middleware.base import BaseHTTPMiddleware
|
|
|
+from starlette.middleware.sessions import SessionMiddleware
|
|
|
+from starlette.responses import Response, StreamingResponse
|
|
|
|
|
|
+from open_webui.apps.audio.main import app as audio_app
|
|
|
+from open_webui.apps.images.main import app as images_app
|
|
|
from open_webui.apps.ollama.main import (
|
|
|
app as ollama_app,
|
|
|
get_all_models as get_ollama_models,
|
|
|
generate_chat_completion as generate_ollama_chat_completion,
|
|
|
- generate_openai_chat_completion as generate_ollama_openai_chat_completion,
|
|
|
GenerateChatCompletionForm,
|
|
|
)
|
|
|
from open_webui.apps.openai.main import (
|
|
@@ -28,38 +45,24 @@ from open_webui.apps.openai.main import (
|
|
|
generate_chat_completion as generate_openai_chat_completion,
|
|
|
get_all_models as get_openai_models,
|
|
|
)
|
|
|
-
|
|
|
from open_webui.apps.retrieval.main import app as retrieval_app
|
|
|
from open_webui.apps.retrieval.utils import get_rag_context, rag_template
|
|
|
-
|
|
|
from open_webui.apps.socket.main import (
|
|
|
app as socket_app,
|
|
|
periodic_usage_pool_cleanup,
|
|
|
get_event_call,
|
|
|
get_event_emitter,
|
|
|
)
|
|
|
-
|
|
|
+from open_webui.apps.webui.internal.db import Session
|
|
|
from open_webui.apps.webui.main import (
|
|
|
app as webui_app,
|
|
|
generate_function_chat_completion,
|
|
|
get_pipe_models,
|
|
|
)
|
|
|
-from open_webui.apps.webui.internal.db import Session
|
|
|
-
|
|
|
-from open_webui.apps.webui.models.auths import Auths
|
|
|
from open_webui.apps.webui.models.functions import Functions
|
|
|
from open_webui.apps.webui.models.models import Models
|
|
|
from open_webui.apps.webui.models.users import UserModel, Users
|
|
|
-
|
|
|
from open_webui.apps.webui.utils import load_function_module_by_id
|
|
|
-
|
|
|
-from open_webui.apps.audio.main import app as audio_app
|
|
|
-from open_webui.apps.images.main import app as images_app
|
|
|
-
|
|
|
-from authlib.integrations.starlette_client import OAuth
|
|
|
-from authlib.oidc.core import UserInfo
|
|
|
-
|
|
|
-
|
|
|
from open_webui.config import (
|
|
|
CACHE_DIR,
|
|
|
CORS_ALLOW_ORIGIN,
|
|
@@ -67,13 +70,11 @@ from open_webui.config import (
|
|
|
ENABLE_ADMIN_CHAT_ACCESS,
|
|
|
ENABLE_ADMIN_EXPORT,
|
|
|
ENABLE_MODEL_FILTER,
|
|
|
- ENABLE_OAUTH_SIGNUP,
|
|
|
ENABLE_OLLAMA_API,
|
|
|
ENABLE_OPENAI_API,
|
|
|
ENV,
|
|
|
FRONTEND_BUILD_DIR,
|
|
|
MODEL_FILTER_LIST,
|
|
|
- OAUTH_MERGE_ACCOUNTS_BY_EMAIL,
|
|
|
OAUTH_PROVIDERS,
|
|
|
ENABLE_SEARCH_QUERY,
|
|
|
SEARCH_QUERY_GENERATION_PROMPT_TEMPLATE,
|
|
@@ -86,10 +87,9 @@ from open_webui.config import (
|
|
|
WEBUI_AUTH,
|
|
|
WEBUI_NAME,
|
|
|
AppConfig,
|
|
|
- run_migrations,
|
|
|
reset_config,
|
|
|
)
|
|
|
-from open_webui.constants import ERROR_MESSAGES, TASKS, WEBHOOK_MESSAGES
|
|
|
+from open_webui.constants import TASKS
|
|
|
from open_webui.env import (
|
|
|
CHANGELOG,
|
|
|
GLOBAL_LOG_LEVEL,
|
|
@@ -104,34 +104,18 @@ from open_webui.env import (
|
|
|
RESET_CONFIG_ON_START,
|
|
|
OFFLINE_MODE,
|
|
|
)
|
|
|
-from fastapi import (
|
|
|
- Depends,
|
|
|
- FastAPI,
|
|
|
- File,
|
|
|
- Form,
|
|
|
- HTTPException,
|
|
|
- Request,
|
|
|
- UploadFile,
|
|
|
- status,
|
|
|
-)
|
|
|
-from fastapi.middleware.cors import CORSMiddleware
|
|
|
-from fastapi.responses import JSONResponse
|
|
|
-from fastapi.staticfiles import StaticFiles
|
|
|
-from pydantic import BaseModel
|
|
|
-from sqlalchemy import text
|
|
|
-from starlette.exceptions import HTTPException as StarletteHTTPException
|
|
|
-from starlette.middleware.base import BaseHTTPMiddleware
|
|
|
-from starlette.middleware.sessions import SessionMiddleware
|
|
|
-from starlette.responses import RedirectResponse, Response, StreamingResponse
|
|
|
-
|
|
|
-from open_webui.utils.security_headers import SecurityHeadersMiddleware
|
|
|
-
|
|
|
from open_webui.utils.misc import (
|
|
|
add_or_update_system_message,
|
|
|
get_last_user_message,
|
|
|
- parse_duration,
|
|
|
prepend_to_first_user_message_content,
|
|
|
)
|
|
|
+from open_webui.utils.oauth import oauth_manager
|
|
|
+from open_webui.utils.payload import convert_payload_openai_to_ollama
|
|
|
+from open_webui.utils.response import (
|
|
|
+ convert_response_ollama_to_openai,
|
|
|
+ convert_streaming_response_ollama_to_openai,
|
|
|
+)
|
|
|
+from open_webui.utils.security_headers import SecurityHeadersMiddleware
|
|
|
from open_webui.utils.task import (
|
|
|
moa_response_generation_template,
|
|
|
search_query_generation_template,
|
|
@@ -140,27 +124,17 @@ from open_webui.utils.task import (
|
|
|
)
|
|
|
from open_webui.utils.tools import get_tools
|
|
|
from open_webui.utils.utils import (
|
|
|
- create_token,
|
|
|
decode_token,
|
|
|
get_admin_user,
|
|
|
get_current_user,
|
|
|
get_http_authorization_cred,
|
|
|
- get_password_hash,
|
|
|
get_verified_user,
|
|
|
)
|
|
|
-from open_webui.utils.webhook import post_webhook
|
|
|
-
|
|
|
-from open_webui.utils.payload import convert_payload_openai_to_ollama
|
|
|
-from open_webui.utils.response import (
|
|
|
- convert_response_ollama_to_openai,
|
|
|
- convert_streaming_response_ollama_to_openai,
|
|
|
-)
|
|
|
|
|
|
if SAFE_MODE:
|
|
|
print("SAFE MODE ENABLED")
|
|
|
Functions.deactivate_all_functions()
|
|
|
|
|
|
-
|
|
|
logging.basicConfig(stream=sys.stdout, level=GLOBAL_LOG_LEVEL)
|
|
|
log = logging.getLogger(__name__)
|
|
|
log.setLevel(SRC_LOG_LEVELS["MAIN"])
|
|
@@ -217,7 +191,6 @@ app.state.config.MODEL_FILTER_LIST = MODEL_FILTER_LIST
|
|
|
|
|
|
app.state.config.WEBHOOK_URL = WEBHOOK_URL
|
|
|
|
|
|
-
|
|
|
app.state.config.TASK_MODEL = TASK_MODEL
|
|
|
app.state.config.TASK_MODEL_EXTERNAL = TASK_MODEL_EXTERNAL
|
|
|
app.state.config.TITLE_GENERATION_PROMPT_TEMPLATE = TITLE_GENERATION_PROMPT_TEMPLATE
|
|
@@ -232,6 +205,8 @@ app.state.config.TOOLS_FUNCTION_CALLING_PROMPT_TEMPLATE = (
|
|
|
app.state.MODELS = {}
|
|
|
|
|
|
|
|
|
+
|
|
|
+
|
|
|
##################################
|
|
|
#
|
|
|
# ChatCompletion Middleware
|
|
@@ -245,14 +220,14 @@ def get_task_model_id(default_model_id):
|
|
|
# Check if the user has a custom task model and use that model
|
|
|
if app.state.MODELS[task_model_id]["owned_by"] == "ollama":
|
|
|
if (
|
|
|
- app.state.config.TASK_MODEL
|
|
|
- and app.state.config.TASK_MODEL in app.state.MODELS
|
|
|
+ app.state.config.TASK_MODEL
|
|
|
+ and app.state.config.TASK_MODEL in app.state.MODELS
|
|
|
):
|
|
|
task_model_id = app.state.config.TASK_MODEL
|
|
|
else:
|
|
|
if (
|
|
|
- app.state.config.TASK_MODEL_EXTERNAL
|
|
|
- and app.state.config.TASK_MODEL_EXTERNAL in app.state.MODELS
|
|
|
+ app.state.config.TASK_MODEL_EXTERNAL
|
|
|
+ and app.state.config.TASK_MODEL_EXTERNAL in app.state.MODELS
|
|
|
):
|
|
|
task_model_id = app.state.config.TASK_MODEL_EXTERNAL
|
|
|
|
|
@@ -389,7 +364,7 @@ async def get_content_from_response(response) -> Optional[str]:
|
|
|
|
|
|
|
|
|
async def chat_completion_tools_handler(
|
|
|
- body: dict, user: UserModel, extra_params: dict
|
|
|
+ body: dict, user: UserModel, extra_params: dict
|
|
|
) -> tuple[dict, dict]:
|
|
|
# If tool_ids field is present, call the functions
|
|
|
metadata = body.get("metadata", {})
|
|
@@ -690,6 +665,7 @@ class ChatCompletionMiddleware(BaseHTTPMiddleware):
|
|
|
|
|
|
app.add_middleware(ChatCompletionMiddleware)
|
|
|
|
|
|
+
|
|
|
##################################
|
|
|
#
|
|
|
# Pipeline Middleware
|
|
@@ -702,15 +678,15 @@ def get_sorted_filters(model_id):
|
|
|
model
|
|
|
for model in app.state.MODELS.values()
|
|
|
if "pipeline" in model
|
|
|
- and "type" in model["pipeline"]
|
|
|
- and model["pipeline"]["type"] == "filter"
|
|
|
- and (
|
|
|
- model["pipeline"]["pipelines"] == ["*"]
|
|
|
- or any(
|
|
|
- model_id == target_model_id
|
|
|
- for target_model_id in model["pipeline"]["pipelines"]
|
|
|
- )
|
|
|
- )
|
|
|
+ and "type" in model["pipeline"]
|
|
|
+ and model["pipeline"]["type"] == "filter"
|
|
|
+ and (
|
|
|
+ model["pipeline"]["pipelines"] == ["*"]
|
|
|
+ or any(
|
|
|
+ model_id == target_model_id
|
|
|
+ for target_model_id in model["pipeline"]["pipelines"]
|
|
|
+ )
|
|
|
+ )
|
|
|
]
|
|
|
sorted_filters = sorted(filters, key=lambda x: x["pipeline"]["priority"])
|
|
|
return sorted_filters
|
|
@@ -896,8 +872,8 @@ async def update_embedding_function(request: Request, call_next):
|
|
|
@app.middleware("http")
|
|
|
async def inspect_websocket(request: Request, call_next):
|
|
|
if (
|
|
|
- "/ws/socket.io" in request.url.path
|
|
|
- and request.query_params.get("transport") == "websocket"
|
|
|
+ "/ws/socket.io" in request.url.path
|
|
|
+ and request.query_params.get("transport") == "websocket"
|
|
|
):
|
|
|
upgrade = (request.headers.get("Upgrade") or "").lower()
|
|
|
connection = (request.headers.get("Connection") or "").lower().split(",")
|
|
@@ -966,8 +942,8 @@ async def get_all_models():
|
|
|
if custom_model.base_model_id is None:
|
|
|
for model in models:
|
|
|
if (
|
|
|
- custom_model.id == model["id"]
|
|
|
- or custom_model.id == model["id"].split(":")[0]
|
|
|
+ custom_model.id == model["id"]
|
|
|
+ or custom_model.id == model["id"].split(":")[0]
|
|
|
):
|
|
|
model["name"] = custom_model.name
|
|
|
model["info"] = custom_model.model_dump()
|
|
@@ -984,8 +960,8 @@ async def get_all_models():
|
|
|
|
|
|
for model in models:
|
|
|
if (
|
|
|
- custom_model.base_model_id == model["id"]
|
|
|
- or custom_model.base_model_id == model["id"].split(":")[0]
|
|
|
+ custom_model.base_model_id == model["id"]
|
|
|
+ or custom_model.base_model_id == model["id"].split(":")[0]
|
|
|
):
|
|
|
owned_by = model["owned_by"]
|
|
|
if "pipe" in model:
|
|
@@ -1785,7 +1761,7 @@ async def get_pipelines_list(user=Depends(get_admin_user)):
|
|
|
|
|
|
@app.post("/api/pipelines/upload")
|
|
|
async def upload_pipeline(
|
|
|
- urlIdx: int = Form(...), file: UploadFile = File(...), user=Depends(get_admin_user)
|
|
|
+ urlIdx: int = Form(...), file: UploadFile = File(...), user=Depends(get_admin_user)
|
|
|
):
|
|
|
print("upload_pipeline", urlIdx, file.filename)
|
|
|
# Check if the uploaded file is a python file
|
|
@@ -1962,9 +1938,9 @@ async def get_pipelines(urlIdx: Optional[int] = None, user=Depends(get_admin_use
|
|
|
|
|
|
@app.get("/api/pipelines/{pipeline_id}/valves")
|
|
|
async def get_pipeline_valves(
|
|
|
- urlIdx: Optional[int],
|
|
|
- pipeline_id: str,
|
|
|
- user=Depends(get_admin_user),
|
|
|
+ urlIdx: Optional[int],
|
|
|
+ pipeline_id: str,
|
|
|
+ user=Depends(get_admin_user),
|
|
|
):
|
|
|
r = None
|
|
|
try:
|
|
@@ -2000,9 +1976,9 @@ async def get_pipeline_valves(
|
|
|
|
|
|
@app.get("/api/pipelines/{pipeline_id}/valves/spec")
|
|
|
async def get_pipeline_valves_spec(
|
|
|
- urlIdx: Optional[int],
|
|
|
- pipeline_id: str,
|
|
|
- user=Depends(get_admin_user),
|
|
|
+ urlIdx: Optional[int],
|
|
|
+ pipeline_id: str,
|
|
|
+ user=Depends(get_admin_user),
|
|
|
):
|
|
|
r = None
|
|
|
try:
|
|
@@ -2037,10 +2013,10 @@ async def get_pipeline_valves_spec(
|
|
|
|
|
|
@app.post("/api/pipelines/{pipeline_id}/valves/update")
|
|
|
async def update_pipeline_valves(
|
|
|
- urlIdx: Optional[int],
|
|
|
- pipeline_id: str,
|
|
|
- form_data: dict,
|
|
|
- user=Depends(get_admin_user),
|
|
|
+ urlIdx: Optional[int],
|
|
|
+ pipeline_id: str,
|
|
|
+ form_data: dict,
|
|
|
+ user=Depends(get_admin_user),
|
|
|
):
|
|
|
r = None
|
|
|
try:
|
|
@@ -2164,7 +2140,7 @@ class ModelFilterConfigForm(BaseModel):
|
|
|
|
|
|
@app.post("/api/config/model/filter")
|
|
|
async def update_model_filter_config(
|
|
|
- form_data: ModelFilterConfigForm, user=Depends(get_admin_user)
|
|
|
+ form_data: ModelFilterConfigForm, user=Depends(get_admin_user)
|
|
|
):
|
|
|
app.state.config.ENABLE_MODEL_FILTER = form_data.enabled
|
|
|
app.state.config.MODEL_FILTER_LIST = form_data.models
|
|
@@ -2219,7 +2195,7 @@ async def get_app_latest_release_version():
|
|
|
timeout = aiohttp.ClientTimeout(total=1)
|
|
|
async with aiohttp.ClientSession(timeout=timeout, trust_env=True) as session:
|
|
|
async with session.get(
|
|
|
- "https://api.github.com/repos/open-webui/open-webui/releases/latest"
|
|
|
+ "https://api.github.com/repos/open-webui/open-webui/releases/latest"
|
|
|
) as response:
|
|
|
response.raise_for_status()
|
|
|
data = await response.json()
|
|
@@ -2235,20 +2211,6 @@ async def get_app_latest_release_version():
|
|
|
# OAuth Login & Callback
|
|
|
############################
|
|
|
|
|
|
-oauth = OAuth()
|
|
|
-
|
|
|
-for provider_name, provider_config in OAUTH_PROVIDERS.items():
|
|
|
- oauth.register(
|
|
|
- name=provider_name,
|
|
|
- client_id=provider_config["client_id"],
|
|
|
- client_secret=provider_config["client_secret"],
|
|
|
- server_metadata_url=provider_config["server_metadata_url"],
|
|
|
- client_kwargs={
|
|
|
- "scope": provider_config["scope"],
|
|
|
- },
|
|
|
- redirect_uri=provider_config["redirect_uri"],
|
|
|
- )
|
|
|
-
|
|
|
# SessionMiddleware is used by authlib for oauth
|
|
|
if len(OAUTH_PROVIDERS) > 0:
|
|
|
app.add_middleware(
|
|
@@ -2262,16 +2224,7 @@ if len(OAUTH_PROVIDERS) > 0:
|
|
|
|
|
|
@app.get("/oauth/{provider}/login")
|
|
|
async def oauth_login(provider: str, request: Request):
|
|
|
- if provider not in OAUTH_PROVIDERS:
|
|
|
- raise HTTPException(404)
|
|
|
- # If the provider has a custom redirect URL, use that, otherwise automatically generate one
|
|
|
- redirect_uri = OAUTH_PROVIDERS[provider].get("redirect_uri") or request.url_for(
|
|
|
- "oauth_callback", provider=provider
|
|
|
- )
|
|
|
- client = oauth.create_client(provider)
|
|
|
- if client is None:
|
|
|
- raise HTTPException(404)
|
|
|
- return await client.authorize_redirect(request, redirect_uri)
|
|
|
+ return await oauth_manager.handle_login(provider, request)
|
|
|
|
|
|
|
|
|
# OAuth login logic is as follows:
|
|
@@ -2282,118 +2235,7 @@ async def oauth_login(provider: str, request: Request):
|
|
|
# - Email addresses are considered unique, so we fail registration if the email address is already taken
|
|
|
@app.get("/oauth/{provider}/callback")
|
|
|
async def oauth_callback(provider: str, request: Request, response: Response):
|
|
|
- if provider not in OAUTH_PROVIDERS:
|
|
|
- raise HTTPException(404)
|
|
|
- client = oauth.create_client(provider)
|
|
|
- try:
|
|
|
- token = await client.authorize_access_token(request)
|
|
|
- except Exception as e:
|
|
|
- log.warning(f"OAuth callback error: {e}")
|
|
|
- raise HTTPException(400, detail=ERROR_MESSAGES.INVALID_CRED)
|
|
|
- user_data: UserInfo = token["userinfo"]
|
|
|
-
|
|
|
- sub = user_data.get("sub")
|
|
|
- if not sub:
|
|
|
- log.warning(f"OAuth callback failed, sub is missing: {user_data}")
|
|
|
- raise HTTPException(400, detail=ERROR_MESSAGES.INVALID_CRED)
|
|
|
- provider_sub = f"{provider}@{sub}"
|
|
|
- email_claim = webui_app.state.config.OAUTH_EMAIL_CLAIM
|
|
|
- email = user_data.get(email_claim, "").lower()
|
|
|
- # We currently mandate that email addresses are provided
|
|
|
- if not email:
|
|
|
- log.warning(f"OAuth callback failed, email is missing: {user_data}")
|
|
|
- raise HTTPException(400, detail=ERROR_MESSAGES.INVALID_CRED)
|
|
|
-
|
|
|
- # Check if the user exists
|
|
|
- user = Users.get_user_by_oauth_sub(provider_sub)
|
|
|
-
|
|
|
- if not user:
|
|
|
- # If the user does not exist, check if merging is enabled
|
|
|
- if OAUTH_MERGE_ACCOUNTS_BY_EMAIL.value:
|
|
|
- # Check if the user exists by email
|
|
|
- user = Users.get_user_by_email(email)
|
|
|
- if user:
|
|
|
- # Update the user with the new oauth sub
|
|
|
- Users.update_user_oauth_sub_by_id(user.id, provider_sub)
|
|
|
-
|
|
|
- if not user:
|
|
|
- # If the user does not exist, check if signups are enabled
|
|
|
- if ENABLE_OAUTH_SIGNUP.value:
|
|
|
- # Check if an existing user with the same email already exists
|
|
|
- existing_user = Users.get_user_by_email(user_data.get("email", "").lower())
|
|
|
- if existing_user:
|
|
|
- raise HTTPException(400, detail=ERROR_MESSAGES.EMAIL_TAKEN)
|
|
|
-
|
|
|
- picture_claim = webui_app.state.config.OAUTH_PICTURE_CLAIM
|
|
|
- picture_url = user_data.get(picture_claim, "")
|
|
|
- if picture_url:
|
|
|
- # Download the profile image into a base64 string
|
|
|
- try:
|
|
|
- async with aiohttp.ClientSession() as session:
|
|
|
- async with session.get(picture_url) as resp:
|
|
|
- picture = await resp.read()
|
|
|
- base64_encoded_picture = base64.b64encode(picture).decode(
|
|
|
- "utf-8"
|
|
|
- )
|
|
|
- guessed_mime_type = mimetypes.guess_type(picture_url)[0]
|
|
|
- if guessed_mime_type is None:
|
|
|
- # assume JPG, browsers are tolerant enough of image formats
|
|
|
- guessed_mime_type = "image/jpeg"
|
|
|
- picture_url = f"data:{guessed_mime_type};base64,{base64_encoded_picture}"
|
|
|
- except Exception as e:
|
|
|
- log.error(f"Error downloading profile image '{picture_url}': {e}")
|
|
|
- picture_url = ""
|
|
|
- if not picture_url:
|
|
|
- picture_url = "/user.png"
|
|
|
- username_claim = webui_app.state.config.OAUTH_USERNAME_CLAIM
|
|
|
- role = (
|
|
|
- "admin"
|
|
|
- if Users.get_num_users() == 0
|
|
|
- else webui_app.state.config.DEFAULT_USER_ROLE
|
|
|
- )
|
|
|
- user = Auths.insert_new_auth(
|
|
|
- email=email,
|
|
|
- password=get_password_hash(
|
|
|
- str(uuid.uuid4())
|
|
|
- ), # Random password, not used
|
|
|
- name=user_data.get(username_claim, "User"),
|
|
|
- profile_image_url=picture_url,
|
|
|
- role=role,
|
|
|
- oauth_sub=provider_sub,
|
|
|
- )
|
|
|
-
|
|
|
- if webui_app.state.config.WEBHOOK_URL:
|
|
|
- post_webhook(
|
|
|
- webui_app.state.config.WEBHOOK_URL,
|
|
|
- WEBHOOK_MESSAGES.USER_SIGNUP(user.name),
|
|
|
- {
|
|
|
- "action": "signup",
|
|
|
- "message": WEBHOOK_MESSAGES.USER_SIGNUP(user.name),
|
|
|
- "user": user.model_dump_json(exclude_none=True),
|
|
|
- },
|
|
|
- )
|
|
|
- else:
|
|
|
- raise HTTPException(
|
|
|
- status.HTTP_403_FORBIDDEN, detail=ERROR_MESSAGES.ACCESS_PROHIBITED
|
|
|
- )
|
|
|
-
|
|
|
- jwt_token = create_token(
|
|
|
- data={"id": user.id},
|
|
|
- expires_delta=parse_duration(webui_app.state.config.JWT_EXPIRES_IN),
|
|
|
- )
|
|
|
-
|
|
|
- # Set the cookie token
|
|
|
- response.set_cookie(
|
|
|
- key="token",
|
|
|
- value=jwt_token,
|
|
|
- httponly=True, # Ensures the cookie is not accessible via JavaScript
|
|
|
- samesite=WEBUI_SESSION_COOKIE_SAME_SITE,
|
|
|
- secure=WEBUI_SESSION_COOKIE_SECURE,
|
|
|
- )
|
|
|
-
|
|
|
- # Redirect back to the frontend with the JWT token
|
|
|
- redirect_url = f"{request.base_url}auth#token={jwt_token}"
|
|
|
- return RedirectResponse(url=redirect_url)
|
|
|
+ return await oauth_manager.handle_callback(provider, request, response)
|
|
|
|
|
|
|
|
|
@app.get("/manifest.json")
|