env.py 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. import importlib.metadata
  2. import json
  3. import logging
  4. import os
  5. import pkgutil
  6. import sys
  7. from pathlib import Path
  8. import markdown
  9. from bs4 import BeautifulSoup
  10. from constants import ERROR_MESSAGES
  11. ####################################
  12. # Load .env file
  13. ####################################
  14. BACKEND_DIR = Path(__file__).parent # the path containing this file
  15. BASE_DIR = BACKEND_DIR.parent # the path containing the backend/
  16. print(BASE_DIR)
  17. try:
  18. from dotenv import find_dotenv, load_dotenv
  19. load_dotenv(find_dotenv(str(BASE_DIR / ".env")))
  20. except ImportError:
  21. print("dotenv not installed, skipping...")
  22. ####################################
  23. # LOGGING
  24. ####################################
  25. log_levels = ["CRITICAL", "ERROR", "WARNING", "INFO", "DEBUG"]
  26. GLOBAL_LOG_LEVEL = os.environ.get("GLOBAL_LOG_LEVEL", "").upper()
  27. if GLOBAL_LOG_LEVEL in log_levels:
  28. logging.basicConfig(stream=sys.stdout, level=GLOBAL_LOG_LEVEL, force=True)
  29. else:
  30. GLOBAL_LOG_LEVEL = "INFO"
  31. log = logging.getLogger(__name__)
  32. log.info(f"GLOBAL_LOG_LEVEL: {GLOBAL_LOG_LEVEL}")
  33. log_sources = [
  34. "AUDIO",
  35. "COMFYUI",
  36. "CONFIG",
  37. "DB",
  38. "IMAGES",
  39. "MAIN",
  40. "MODELS",
  41. "OLLAMA",
  42. "OPENAI",
  43. "RAG",
  44. "WEBHOOK",
  45. ]
  46. SRC_LOG_LEVELS = {}
  47. for source in log_sources:
  48. log_env_var = source + "_LOG_LEVEL"
  49. SRC_LOG_LEVELS[source] = os.environ.get(log_env_var, "").upper()
  50. if SRC_LOG_LEVELS[source] not in log_levels:
  51. SRC_LOG_LEVELS[source] = GLOBAL_LOG_LEVEL
  52. log.info(f"{log_env_var}: {SRC_LOG_LEVELS[source]}")
  53. log.setLevel(SRC_LOG_LEVELS["CONFIG"])
  54. WEBUI_NAME = os.environ.get("WEBUI_NAME", "Open WebUI")
  55. if WEBUI_NAME != "Open WebUI":
  56. WEBUI_NAME += " (Open WebUI)"
  57. WEBUI_URL = os.environ.get("WEBUI_URL", "http://localhost:3000")
  58. WEBUI_FAVICON_URL = "https://openwebui.com/favicon.png"
  59. ####################################
  60. # ENV (dev,test,prod)
  61. ####################################
  62. ENV = os.environ.get("ENV", "dev")
  63. try:
  64. PACKAGE_DATA = json.loads((BASE_DIR / "package.json").read_text())
  65. except Exception:
  66. try:
  67. PACKAGE_DATA = {"version": importlib.metadata.version("open-webui")}
  68. except importlib.metadata.PackageNotFoundError:
  69. PACKAGE_DATA = {"version": "0.0.0"}
  70. VERSION = PACKAGE_DATA["version"]
  71. # Function to parse each section
  72. def parse_section(section):
  73. items = []
  74. for li in section.find_all("li"):
  75. # Extract raw HTML string
  76. raw_html = str(li)
  77. # Extract text without HTML tags
  78. text = li.get_text(separator=" ", strip=True)
  79. # Split into title and content
  80. parts = text.split(": ", 1)
  81. title = parts[0].strip() if len(parts) > 1 else ""
  82. content = parts[1].strip() if len(parts) > 1 else text
  83. items.append({"title": title, "content": content, "raw": raw_html})
  84. return items
  85. try:
  86. changelog_path = BASE_DIR / "CHANGELOG.md"
  87. with open(str(changelog_path.absolute()), "r", encoding="utf8") as file:
  88. changelog_content = file.read()
  89. except Exception:
  90. changelog_content = (pkgutil.get_data("open_webui", "CHANGELOG.md") or b"").decode()
  91. # Convert markdown content to HTML
  92. html_content = markdown.markdown(changelog_content)
  93. # Parse the HTML content
  94. soup = BeautifulSoup(html_content, "html.parser")
  95. # Initialize JSON structure
  96. changelog_json = {}
  97. # Iterate over each version
  98. for version in soup.find_all("h2"):
  99. version_number = version.get_text().strip().split(" - ")[0][1:-1] # Remove brackets
  100. date = version.get_text().strip().split(" - ")[1]
  101. version_data = {"date": date}
  102. # Find the next sibling that is a h3 tag (section title)
  103. current = version.find_next_sibling()
  104. while current and current.name != "h2":
  105. if current.name == "h3":
  106. section_title = current.get_text().lower() # e.g., "added", "fixed"
  107. section_items = parse_section(current.find_next_sibling("ul"))
  108. version_data[section_title] = section_items
  109. # Move to the next element
  110. current = current.find_next_sibling()
  111. changelog_json[version_number] = version_data
  112. CHANGELOG = changelog_json
  113. ####################################
  114. # SAFE_MODE
  115. ####################################
  116. SAFE_MODE = os.environ.get("SAFE_MODE", "false").lower() == "true"
  117. ####################################
  118. # WEBUI_BUILD_HASH
  119. ####################################
  120. WEBUI_BUILD_HASH = os.environ.get("WEBUI_BUILD_HASH", "dev-build")
  121. ####################################
  122. # DATA/FRONTEND BUILD DIR
  123. ####################################
  124. DATA_DIR = Path(os.getenv("DATA_DIR", BACKEND_DIR / "data")).resolve()
  125. FRONTEND_BUILD_DIR = Path(os.getenv("FRONTEND_BUILD_DIR", BASE_DIR / "build")).resolve()
  126. RESET_CONFIG_ON_START = (
  127. os.environ.get("RESET_CONFIG_ON_START", "False").lower() == "true"
  128. )
  129. if RESET_CONFIG_ON_START:
  130. try:
  131. os.remove(f"{DATA_DIR}/config.json")
  132. with open(f"{DATA_DIR}/config.json", "w") as f:
  133. f.write("{}")
  134. except Exception:
  135. pass
  136. try:
  137. CONFIG_DATA = json.loads((DATA_DIR / "config.json").read_text())
  138. except Exception:
  139. CONFIG_DATA = {}
  140. ####################################
  141. # Database
  142. ####################################
  143. # Check if the file exists
  144. if os.path.exists(f"{DATA_DIR}/ollama.db"):
  145. # Rename the file
  146. os.rename(f"{DATA_DIR}/ollama.db", f"{DATA_DIR}/webui.db")
  147. log.info("Database migrated from Ollama-WebUI successfully.")
  148. else:
  149. pass
  150. DATABASE_URL = os.environ.get("DATABASE_URL", f"sqlite:///{DATA_DIR}/webui.db")
  151. # Replace the postgres:// with postgresql://
  152. if "postgres://" in DATABASE_URL:
  153. DATABASE_URL = DATABASE_URL.replace("postgres://", "postgresql://")
  154. ####################################
  155. # WEBUI_AUTH (Required for security)
  156. ####################################
  157. WEBUI_AUTH = os.environ.get("WEBUI_AUTH", "True").lower() == "true"
  158. WEBUI_AUTH_TRUSTED_EMAIL_HEADER = os.environ.get(
  159. "WEBUI_AUTH_TRUSTED_EMAIL_HEADER", None
  160. )
  161. WEBUI_AUTH_TRUSTED_NAME_HEADER = os.environ.get("WEBUI_AUTH_TRUSTED_NAME_HEADER", None)
  162. ####################################
  163. # WEBUI_SECRET_KEY
  164. ####################################
  165. WEBUI_SECRET_KEY = os.environ.get(
  166. "WEBUI_SECRET_KEY",
  167. os.environ.get(
  168. "WEBUI_JWT_SECRET_KEY", "t0p-s3cr3t"
  169. ), # DEPRECATED: remove at next major version
  170. )
  171. WEBUI_SESSION_COOKIE_SAME_SITE = os.environ.get(
  172. "WEBUI_SESSION_COOKIE_SAME_SITE",
  173. os.environ.get("WEBUI_SESSION_COOKIE_SAME_SITE", "lax"),
  174. )
  175. WEBUI_SESSION_COOKIE_SECURE = os.environ.get(
  176. "WEBUI_SESSION_COOKIE_SECURE",
  177. os.environ.get("WEBUI_SESSION_COOKIE_SECURE", "false").lower() == "true",
  178. )
  179. if WEBUI_AUTH and WEBUI_SECRET_KEY == "":
  180. raise ValueError(ERROR_MESSAGES.ENV_VAR_NOT_FOUND)