diff --git a/private_gpt/launcher.py b/private_gpt/launcher.py index 287a4f67..14273a69 100644 --- a/private_gpt/launcher.py +++ b/private_gpt/launcher.py @@ -11,7 +11,7 @@ import nest_asyncio # type: ignore from fastapi import FastAPI, Request from fastapi.exceptions import RequestValidationError from fastapi.middleware.cors import CORSMiddleware -from fastapi.responses import RedirectResponse +from fastapi.responses import HTMLResponse, RedirectResponse from fastapi.staticfiles import StaticFiles from injector import Injector from llama_index.core.embeddings import MockEmbedding @@ -217,6 +217,23 @@ def create_app(root_injector: Injector) -> FastAPI: root_path = request.scope.get("root_path", "").rstrip("/") return RedirectResponse(url=f"{root_path}{ui_path}/", status_code=307) + connect_host = ( + "127.0.0.1" if settings.server.host == "0.0.0.0" else settings.server.host + ) + server_url = f"http://{connect_host}:{settings.server.port}" + if settings.server.root_path: + server_url += "/" + settings.server.root_path.strip("/") + _index_html = (UI_DIRECTORY / "index.html").read_text() + _index_html = _index_html.replace( + 'const DEFAULT_BASE_URL = window.location.origin === "null" ? "http://127.0.0.1:8080" : window.location.origin;', + f'const DEFAULT_BASE_URL = "{server_url}";', + ) + + @app.get(f"{ui_path}/", include_in_schema=False) + @app.get(f"{ui_path}/index.html", include_in_schema=False) + async def serve_ui_index() -> HTMLResponse: + return HTMLResponse(content=_index_html) + static_files = StaticFiles(directory=UI_DIRECTORY, html=True) async def _ui_app(scope: Any, receive: Any, send: Any) -> None: diff --git a/private_gpt/settings/settings.py b/private_gpt/settings/settings.py index a7517e7b..9a7e354d 100644 --- a/private_gpt/settings/settings.py +++ b/private_gpt/settings/settings.py @@ -313,6 +313,10 @@ class ServerSettings(BaseModel): default="", description="Root path for the FastAPI server", ) + host: str = Field( + default="0.0.0.0", + description="Host of PrivateGPT FastAPI server, defaults to 0.0.0.0", + ) port: int = Field(description="Port of PrivateGPT FastAPI server, defaults to 8080") cors: CorsSettings = Field(description="CORS configuration", default=CorsSettings()) auth: AuthSettings = Field( diff --git a/ui/index.html b/ui/index.html index e3614e04..38544e73 100644 --- a/ui/index.html +++ b/ui/index.html @@ -642,11 +642,10 @@ display: flex; align-items: center; gap: 8px; - min-height: 46px; background: rgba(10, 11, 18, 0.28); border: 1px solid var(--border-soft); border-radius: 14px; - padding: 2px 6px 2px 0; + padding: 0 6px 0 0; overflow: hidden; } @@ -2620,6 +2619,18 @@ display: none; } + .advanced-panel .panel-chevron { + flex-shrink: 0; + width: 16px; + height: 16px; + color: var(--muted); + transition: transform 0.2s ease; + } + + .advanced-panel[open] .panel-chevron { + transform: rotate(180deg); + } + .advanced-panel[open] summary { border-bottom: 1px solid var(--border-soft); } @@ -2884,23 +2895,25 @@ -
-

Authorization

- Configure HTTP Basic auth credentials for PrivateGPT. Stored only in this browser. Debugger headers are always redacted. -
-
-
- - -
-
- -
- - +
+ Authentication (HTTP Basic Auth) +
+

Credentials are Base64-encoded and sent as an Authorization header on every request. Only enable if your PrivateGPT server requires Basic Auth. Stored only in this browser; debugger headers are always redacted.

+
+
+ + +
+
+ +
+ + +
+
-
+
@@ -3116,16 +3129,25 @@ -
-
- - +
+ Authentication (HTTP Basic Auth) +
+

Credentials are Base64-encoded and sent as an Authorization header on every request. Only enable if your PrivateGPT server requires Basic Auth. Stored only in this browser; debugger headers are always redacted.

+
+
+ + +
+
+ +
+ + +
+
+
-
- - -
-
+
@@ -6410,6 +6432,10 @@ const input = $("#privateGptPassword"); input.type = input.type === "password" ? "text" : "password"; }); + $("#toggleOnboardingPasswordButton").addEventListener("click", () => { + const input = $("#onboardingPassword"); + input.type = input.type === "password" ? "text" : "password"; + }); $("#defaultCollection").addEventListener("change", async event => { await switchActiveCollection(event.target.value, { showToast: false }); });