feat:精简
Some checks failed
Create and publish Docker images with specific build args / build-main-image (linux/amd64, ubuntu-latest) (push) Has been cancelled
Create and publish Docker images with specific build args / build-main-image (linux/arm64, ubuntu-24.04-arm) (push) Has been cancelled
Create and publish Docker images with specific build args / build-cuda-image (linux/amd64, ubuntu-latest) (push) Has been cancelled
Create and publish Docker images with specific build args / build-cuda-image (linux/arm64, ubuntu-24.04-arm) (push) Has been cancelled
Create and publish Docker images with specific build args / build-cuda126-image (linux/amd64, ubuntu-latest) (push) Has been cancelled
Create and publish Docker images with specific build args / build-cuda126-image (linux/arm64, ubuntu-24.04-arm) (push) Has been cancelled
Create and publish Docker images with specific build args / build-ollama-image (linux/amd64, ubuntu-latest) (push) Has been cancelled
Create and publish Docker images with specific build args / build-ollama-image (linux/arm64, ubuntu-24.04-arm) (push) Has been cancelled
Create and publish Docker images with specific build args / build-slim-image (linux/amd64, ubuntu-latest) (push) Has been cancelled
Create and publish Docker images with specific build args / build-slim-image (linux/arm64, ubuntu-24.04-arm) (push) Has been cancelled
Python CI / Format Backend (3.11.x) (push) Has been cancelled
Python CI / Format Backend (3.12.x) (push) Has been cancelled
Frontend Build / Format & Build Frontend (push) Has been cancelled
Frontend Build / Frontend Unit Tests (push) Has been cancelled
Create and publish Docker images with specific build args / merge-main-images (push) Has been cancelled
Create and publish Docker images with specific build args / merge-cuda-images (push) Has been cancelled
Create and publish Docker images with specific build args / merge-cuda126-images (push) Has been cancelled
Create and publish Docker images with specific build args / merge-ollama-images (push) Has been cancelled
Create and publish Docker images with specific build args / merge-slim-images (push) Has been cancelled
Close inactive issues / close-issues (push) Has been cancelled

This commit is contained in:
2026-01-16 18:34:38 +08:00
parent 16263710d9
commit 11fcec9387
137 changed files with 68993 additions and 6435 deletions

View File

@@ -71,6 +71,9 @@ from open_webui.utils.auth import (
get_http_authorization_cred,
send_verify_email,
verify_email_by_code,
apply_branding,
send_signup_email_code,
verify_signup_email_code,
)
from open_webui.utils.webhook import post_webhook
from open_webui.utils.access_control import get_permissions, has_permission
@@ -107,7 +110,6 @@ signin_rate_limiter = RateLimiter(
class SessionUserResponse(Token, UserProfileImageResponse):
expires_at: Optional[int] = None
permissions: Optional[dict] = None
credit: Decimal
class SessionUserInfoResponse(SessionUserResponse, UserStatus):
@@ -173,7 +175,6 @@ async def get_session_user(
"status_message": user.status_message,
"status_expires_at": user.status_expires_at,
"permissions": user_permissions,
"credit": credit.credit,
}
@@ -513,7 +514,6 @@ async def ldap_auth(request: Request, response: Response, form_data: LdapForm):
"role": user.role,
"profile_image_url": user.profile_image_url,
"permissions": user_permissions,
"credit": credit.credit,
}
else:
raise HTTPException(400, detail=ERROR_MESSAGES.INVALID_CRED)
@@ -654,12 +654,39 @@ async def signin(request: Request, response: Response, form_data: SigninForm):
"role": user.role,
"profile_image_url": user.profile_image_url,
"permissions": user_permissions,
"credit": credit.credit,
}
else:
raise HTTPException(400, detail=ERROR_MESSAGES.INVALID_CRED)
############################
# Send Email Code
############################
class SendEmailCodeForm(BaseModel):
email: str
@router.post("/send_email_code")
async def send_email_code(request: Request, form_data: SendEmailCodeForm):
if not validate_email_format(form_data.email.lower()):
raise HTTPException(
status.HTTP_400_BAD_REQUEST, detail=ERROR_MESSAGES.INVALID_EMAIL_FORMAT
)
# check if email already registered
if Users.get_user_by_email(form_data.email.lower()):
raise HTTPException(400, detail=ERROR_MESSAGES.EMAIL_TAKEN)
try:
send_signup_email_code(form_data.email.lower())
return {"success": True, "message": "Verification code sent"}
except Exception as e:
log.error(f"Failed to send email code: {e}")
raise HTTPException(500, detail="Failed to send verification code")
############################
# SignUp
############################
@@ -706,6 +733,13 @@ async def signup(request: Request, response: Response, form_data: SignupForm):
if Users.get_user_by_email(form_data.email.lower()):
raise HTTPException(400, detail=ERROR_MESSAGES.EMAIL_TAKEN)
# verify email code for non-admin signup
if has_users and form_data.email_code:
if not verify_signup_email_code(form_data.email.lower(), form_data.email_code):
raise HTTPException(400, detail="Invalid or expired verification code")
elif has_users and not form_data.email_code:
raise HTTPException(400, detail="Verification code is required")
try:
try:
validate_password(form_data.password)
@@ -795,7 +829,6 @@ async def signup(request: Request, response: Response, form_data: SignupForm):
"role": user.role,
"profile_image_url": user.profile_image_url,
"permissions": user_permissions,
"credit": credit.credit,
}
else:
raise HTTPException(500, detail=ERROR_MESSAGES.CREATE_USER_ERROR)
@@ -1250,6 +1283,154 @@ async def update_ldap_config(
return {"ENABLE_LDAP": request.app.state.config.ENABLE_LDAP}
############################
# Branding Config
############################
@router.get("/admin/config/branding")
async def get_branding_config(request: Request, user=Depends(get_admin_user)):
def get_value(config_obj):
if hasattr(config_obj, "value"):
return config_obj.value
return config_obj
return {
"ORGANIZATION_NAME": get_value(request.app.state.config.BRANDING_ORGANIZATION_NAME),
"CUSTOM_NAME": get_value(request.app.state.config.BRANDING_CUSTOM_NAME),
"FAVICON_ICO": get_value(request.app.state.config.BRANDING_FAVICON_ICO),
"FAVICON_PNG": get_value(request.app.state.config.BRANDING_FAVICON_PNG),
"FAVICON_DARK_PNG": get_value(request.app.state.config.BRANDING_FAVICON_DARK_PNG),
"FAVICON_SVG": get_value(request.app.state.config.BRANDING_FAVICON_SVG),
"AUTH_LOGO_URL": get_value(request.app.state.config.BRANDING_AUTH_LOGO_URL),
"SIDEBAR_LOGO_URL": get_value(request.app.state.config.BRANDING_SIDEBAR_LOGO_URL),
"AUTH_LOGO_LINK_URL": get_value(request.app.state.config.BRANDING_AUTH_LOGO_LINK_URL),
}
class BrandingConfig(BaseModel):
ORGANIZATION_NAME: Optional[str] = ""
CUSTOM_NAME: Optional[str] = ""
FAVICON_ICO: Optional[str] = ""
FAVICON_PNG: Optional[str] = ""
FAVICON_DARK_PNG: Optional[str] = ""
FAVICON_SVG: Optional[str] = ""
AUTH_LOGO_URL: Optional[str] = ""
SIDEBAR_LOGO_URL: Optional[str] = ""
AUTH_LOGO_LINK_URL: Optional[str] = ""
@router.post("/admin/config/branding")
async def update_branding_config(
request: Request, form_data: BrandingConfig, user=Depends(get_admin_user)
):
# Helper function to update config value
# Must access _state directly to get PersistentConfig object (not the value)
def update_config(key: str, new_value):
config_obj = request.app.state.config._state.get(key)
if config_obj and hasattr(config_obj, "value") and hasattr(config_obj, "save"):
config_obj.value = new_value
config_obj.save()
log.info(f"Branding: Saved config {key} = {new_value}")
else:
log.warning(f"Branding: config key '{key}' not found or not PersistentConfig")
# Update config values
update_config("BRANDING_ORGANIZATION_NAME", form_data.ORGANIZATION_NAME)
update_config("BRANDING_CUSTOM_NAME", form_data.CUSTOM_NAME)
update_config("BRANDING_FAVICON_ICO", form_data.FAVICON_ICO)
update_config("BRANDING_FAVICON_PNG", form_data.FAVICON_PNG)
update_config("BRANDING_FAVICON_DARK_PNG", form_data.FAVICON_DARK_PNG)
update_config("BRANDING_FAVICON_SVG", form_data.FAVICON_SVG)
update_config("BRANDING_AUTH_LOGO_URL", form_data.AUTH_LOGO_URL)
update_config("BRANDING_SIDEBAR_LOGO_URL", form_data.SIDEBAR_LOGO_URL)
update_config("BRANDING_AUTH_LOGO_LINK_URL", form_data.AUTH_LOGO_LINK_URL)
# Apply branding changes immediately
apply_branding(request.app)
# Helper to get value
def get_value(config_obj):
if hasattr(config_obj, "value"):
return config_obj.value
return config_obj
return {
"ORGANIZATION_NAME": get_value(request.app.state.config.BRANDING_ORGANIZATION_NAME),
"CUSTOM_NAME": get_value(request.app.state.config.BRANDING_CUSTOM_NAME),
"FAVICON_ICO": get_value(request.app.state.config.BRANDING_FAVICON_ICO),
"FAVICON_PNG": get_value(request.app.state.config.BRANDING_FAVICON_PNG),
"FAVICON_DARK_PNG": get_value(request.app.state.config.BRANDING_FAVICON_DARK_PNG),
"FAVICON_SVG": get_value(request.app.state.config.BRANDING_FAVICON_SVG),
"AUTH_LOGO_URL": get_value(request.app.state.config.BRANDING_AUTH_LOGO_URL),
"SIDEBAR_LOGO_URL": get_value(request.app.state.config.BRANDING_SIDEBAR_LOGO_URL),
"AUTH_LOGO_LINK_URL": get_value(request.app.state.config.BRANDING_AUTH_LOGO_LINK_URL),
}
############################
# Email Templates
############################
@router.get("/admin/config/email-templates")
async def get_email_templates(request: Request, user=Depends(get_admin_user)):
from open_webui.config import (
DEFAULT_EMAIL_VERIFY_TEMPLATE,
DEFAULT_EMAIL_CODE_TEMPLATE,
)
def get_value(config_obj):
if hasattr(config_obj, "value"):
return config_obj.value
return config_obj
return {
"EMAIL_VERIFY_TEMPLATE": get_value(
request.app.state.config.EMAIL_VERIFY_TEMPLATE
),
"EMAIL_CODE_TEMPLATE": get_value(
request.app.state.config.EMAIL_CODE_TEMPLATE
),
"DEFAULT_EMAIL_VERIFY_TEMPLATE": DEFAULT_EMAIL_VERIFY_TEMPLATE,
"DEFAULT_EMAIL_CODE_TEMPLATE": DEFAULT_EMAIL_CODE_TEMPLATE,
}
class EmailTemplatesConfig(BaseModel):
EMAIL_VERIFY_TEMPLATE: Optional[str] = ""
EMAIL_CODE_TEMPLATE: Optional[str] = ""
@router.post("/admin/config/email-templates")
async def update_email_templates(
request: Request, form_data: EmailTemplatesConfig, user=Depends(get_admin_user)
):
def update_config(key: str, new_value):
config_obj = request.app.state.config._state.get(key)
if config_obj and hasattr(config_obj, "value") and hasattr(config_obj, "save"):
config_obj.value = new_value
config_obj.save()
log.info(f"Email Templates: Saved config {key}")
update_config("EMAIL_VERIFY_TEMPLATE", form_data.EMAIL_VERIFY_TEMPLATE)
update_config("EMAIL_CODE_TEMPLATE", form_data.EMAIL_CODE_TEMPLATE)
def get_value(config_obj):
if hasattr(config_obj, "value"):
return config_obj.value
return config_obj
return {
"EMAIL_VERIFY_TEMPLATE": get_value(
request.app.state.config.EMAIL_VERIFY_TEMPLATE
),
"EMAIL_CODE_TEMPLATE": get_value(
request.app.state.config.EMAIL_CODE_TEMPLATE
),
}
############################
# API Key
############################