fix: ui auth header (#2242)

* fix: default ui path

(cherry picked from commit ceaa84249b50e17221d6d6720fe7af3f0d064272)

* style: use accordion instead plain block

(cherry picked from commit 103c0c3313a89553197d76c23d1b4ced29eb8e3b)
This commit is contained in:
Javier Martinez
2026-06-03 06:53:07 +02:00
committed by GitHub
parent 86ceea5de8
commit 3dbadc0b32
3 changed files with 74 additions and 27 deletions

View File

@@ -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:

View File

@@ -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(

View File

@@ -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 @@
<label for="privateGptBaseUrl">PrivateGPT base URL</label>
<input id="privateGptBaseUrl" autocomplete="off" spellcheck="false" />
</div>
<div class="field">
<h3>Authorization</h3>
<small>Configure HTTP Basic auth credentials for PrivateGPT. Stored only in this browser. Debugger headers are always redacted.</small>
</div>
<div class="auth-grid">
<div class="field">
<label for="privateGptUsername">Username</label>
<input id="privateGptUsername" autocomplete="username" spellcheck="false" placeholder="Optional" />
</div>
<div class="field">
<label for="privateGptPassword">Password</label>
<div class="password-field">
<input id="privateGptPassword" type="password" autocomplete="current-password" spellcheck="false" placeholder="Optional" />
<button class="icon-button" id="togglePasswordButton" type="button" aria-label="Show password"><span data-icon="eye"></span></button>
<details class="advanced-panel">
<summary><span>Authentication <span class="subtle" style="font-weight:400;font-size:12px;">(HTTP Basic Auth)</span></span><span data-icon="chevron-down" class="panel-chevron"></span></summary>
<div class="advanced-panel-body">
<p class="subtle" style="margin:0;font-size:12px;">Credentials are Base64-encoded and sent as an <code>Authorization</code> header on every request. Only enable if your PrivateGPT server requires Basic Auth. Stored only in this browser; debugger headers are always redacted.</p>
<div class="auth-grid">
<div class="field">
<label for="privateGptUsername">Username</label>
<input id="privateGptUsername" autocomplete="username" spellcheck="false" placeholder="Optional" />
</div>
<div class="field">
<label for="privateGptPassword">Password</label>
<div class="password-field">
<input id="privateGptPassword" type="password" autocomplete="current-password" spellcheck="false" placeholder="Optional" />
<button class="icon-button" id="togglePasswordButton" type="button" aria-label="Show password"><span data-icon="eye"></span></button>
</div>
</div>
</div>
</div>
</div>
</details>
</div>
</div>
@@ -3116,16 +3129,25 @@
<label for="onboardingBaseUrl">PrivateGPT base URL</label>
<input id="onboardingBaseUrl" autocomplete="off" spellcheck="false" placeholder="Current host" />
</div>
<div class="auth-grid">
<div class="field">
<label for="onboardingUsername">Username</label>
<input id="onboardingUsername" autocomplete="username" spellcheck="false" placeholder="Optional" />
<details class="advanced-panel">
<summary><span>Authentication <span class="subtle" style="font-weight:400;font-size:12px;">(HTTP Basic Auth)</span></span><span data-icon="chevron-down" class="panel-chevron"></span></summary>
<div class="advanced-panel-body">
<p class="subtle" style="margin:0;font-size:12px;">Credentials are Base64-encoded and sent as an <code>Authorization</code> header on every request. Only enable if your PrivateGPT server requires Basic Auth. Stored only in this browser; debugger headers are always redacted.</p>
<div class="auth-grid">
<div class="field">
<label for="onboardingUsername">Username</label>
<input id="onboardingUsername" autocomplete="username" spellcheck="false" placeholder="Optional" />
</div>
<div class="field">
<label for="onboardingPassword">Password</label>
<div class="password-field">
<input id="onboardingPassword" type="password" autocomplete="current-password" spellcheck="false" placeholder="Optional" />
<button class="icon-button" id="toggleOnboardingPasswordButton" type="button" aria-label="Show password"><span data-icon="eye"></span></button>
</div>
</div>
</div>
</div>
<div class="field">
<label for="onboardingPassword">Password</label>
<input id="onboardingPassword" type="password" autocomplete="current-password" spellcheck="false" placeholder="Optional" />
</div>
</div>
</details>
<div class="field">
<label for="onboardingCollection">Collection ID</label>
<input id="onboardingCollection" autocomplete="off" spellcheck="false" placeholder="pgpt_collection" />
@@ -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 });
});