main.py 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. from bs4 import BeautifulSoup
  2. import json
  3. import markdown
  4. import time
  5. from fastapi import FastAPI, Request
  6. from fastapi.staticfiles import StaticFiles
  7. from fastapi import HTTPException
  8. from fastapi.middleware.wsgi import WSGIMiddleware
  9. from fastapi.middleware.cors import CORSMiddleware
  10. from starlette.exceptions import HTTPException as StarletteHTTPException
  11. from apps.ollama.main import app as ollama_app
  12. from apps.openai.main import app as openai_app
  13. from apps.audio.main import app as audio_app
  14. from apps.images.main import app as images_app
  15. from apps.rag.main import app as rag_app
  16. from apps.web.main import app as webui_app
  17. from config import ENV, VERSION, FRONTEND_BUILD_DIR
  18. class SPAStaticFiles(StaticFiles):
  19. async def get_response(self, path: str, scope):
  20. try:
  21. return await super().get_response(path, scope)
  22. except (HTTPException, StarletteHTTPException) as ex:
  23. if ex.status_code == 404:
  24. return await super().get_response("index.html", scope)
  25. else:
  26. raise ex
  27. app = FastAPI(docs_url="/docs" if ENV == "dev" else None, redoc_url=None)
  28. origins = ["*"]
  29. app.add_middleware(
  30. CORSMiddleware,
  31. allow_origins=origins,
  32. allow_credentials=True,
  33. allow_methods=["*"],
  34. allow_headers=["*"],
  35. )
  36. @app.middleware("http")
  37. async def check_url(request: Request, call_next):
  38. start_time = int(time.time())
  39. response = await call_next(request)
  40. process_time = int(time.time()) - start_time
  41. response.headers["X-Process-Time"] = str(process_time)
  42. return response
  43. app.mount("/api/v1", webui_app)
  44. app.mount("/ollama/api", ollama_app)
  45. app.mount("/openai/api", openai_app)
  46. app.mount("/images/api/v1", images_app)
  47. app.mount("/audio/api/v1", audio_app)
  48. app.mount("/rag/api/v1", rag_app)
  49. @app.get("/api/config")
  50. async def get_app_config():
  51. return {
  52. "status": True,
  53. "version": VERSION,
  54. "images": images_app.state.ENABLED,
  55. "default_models": webui_app.state.DEFAULT_MODELS,
  56. "default_prompt_suggestions": webui_app.state.DEFAULT_PROMPT_SUGGESTIONS,
  57. }
  58. # Function to parse each section
  59. def parse_section(section):
  60. items = []
  61. for li in section.find_all("li"):
  62. # Extract raw HTML string
  63. raw_html = str(li)
  64. # Extract text without HTML tags
  65. text = li.get_text(separator=" ", strip=True)
  66. # Split into title and content
  67. parts = text.split(": ", 1)
  68. title = parts[0].strip() if len(parts) > 1 else ""
  69. content = parts[1].strip() if len(parts) > 1 else text
  70. items.append({"title": title, "content": content, "raw": raw_html})
  71. return items
  72. @app.get("/api/changelog")
  73. async def get_app_changelog():
  74. try:
  75. with open("../CHANGELOG.md", "r") as file:
  76. changelog_content = file.read()
  77. # Convert markdown content to HTML
  78. html_content = markdown.markdown(changelog_content)
  79. # Parse the HTML content
  80. soup = BeautifulSoup(html_content, "html.parser")
  81. print(soup)
  82. # Initialize JSON structure
  83. changelog_json = {}
  84. # Iterate over each version
  85. for version in soup.find_all("h2"):
  86. version_number = (
  87. version.get_text().strip().split(" - ")[0][1:-1]
  88. ) # Remove brackets
  89. date = version.get_text().strip().split(" - ")[1]
  90. version_data = {"date": date}
  91. # Find the next sibling that is a h3 tag (section title)
  92. current = version.find_next_sibling()
  93. print(current)
  94. while current and current.name != "h2":
  95. if current.name == "h3":
  96. section_title = current.get_text().lower() # e.g., "added", "fixed"
  97. section_items = parse_section(current.find_next_sibling("ul"))
  98. version_data[section_title] = section_items
  99. # Move to the next element
  100. current = current.find_next_sibling()
  101. changelog_json[version_number] = version_data
  102. # print(changelog_json)
  103. # Return content as JSON string
  104. return changelog_json
  105. except FileNotFoundError:
  106. return {"error": "readme.md not found"}
  107. except Exception as e:
  108. return {"error": f"An error occurred: {e}"}
  109. app.mount(
  110. "/",
  111. SPAStaticFiles(directory=FRONTEND_BUILD_DIR, html=True),
  112. name="spa-static-files",
  113. )