env.py 7.6 KB

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