mirror of
https://github.com/mudler/LocalAI.git
synced 2025-02-11 13:15:20 +00:00
Makes the web app honour the `X-Forwarded-Prefix` HTTP request header that may be sent by a reverse-proxy in order to inform the app that its public routes contain a path prefix.
For instance this allows to serve the webapp via a reverse-proxy/ingress controller under a path prefix/sub path such as e.g. `/localai/` while still being able to use the regular LocalAI routes/paths without prefix when directly connecting to the LocalAI server.
Changes:
* Add new `StripPathPrefix` middleware to strip the path prefix (provided with the `X-Forwarded-Prefix` HTTP request header) from the request path prior to matching the HTTP route.
* Add a `BaseURL` utility function to build the base URL, honouring the `X-Forwarded-Prefix` HTTP request header.
* Generate the derived base URL into the HTML (`head.html` template) as `<base/>` tag.
* Make all webapp-internal URLs (within HTML+JS) relative in order to make the browser resolve them against the `<base/>` URL specified within each HTML page's header.
* Make font URLs within the CSS files relative to the CSS file.
* Generate redirect location URLs using the new `BaseURL` function.
* Use the new `BaseURL` function to generate absolute URLs within gallery JSON responses.
Closes #3095
TL;DR:
The header-based approach allows to move the path prefix configuration concern completely to the reverse-proxy/ingress as opposed to having to align the path prefix configuration between LocalAI, the reverse-proxy and potentially other internal LocalAI clients.
The gofiber swagger handler already supports path prefixes this way, see e2d9e9916d/swagger.go (L79)
Signed-off-by: Max Goltzsche <max.goltzsche@gmail.com>
90 lines
2.0 KiB
Go
90 lines
2.0 KiB
Go
package elements
|
|
|
|
import (
|
|
"github.com/chasefleming/elem-go"
|
|
"github.com/chasefleming/elem-go/attrs"
|
|
"github.com/microcosm-cc/bluemonday"
|
|
)
|
|
|
|
func DoneProgress(galleryID, text string, showDelete bool) string {
|
|
return elem.Div(
|
|
attrs.Props{
|
|
"id": "action-div-" + dropBadChars(galleryID),
|
|
},
|
|
elem.H3(
|
|
attrs.Props{
|
|
"role": "status",
|
|
"id": "pblabel",
|
|
"tabindex": "-1",
|
|
"autofocus": "",
|
|
},
|
|
elem.Text(bluemonday.StrictPolicy().Sanitize(text)),
|
|
),
|
|
elem.If(showDelete, deleteButton(galleryID), reInstallButton(galleryID)),
|
|
).Render()
|
|
}
|
|
|
|
func ErrorProgress(err, galleryName string) string {
|
|
return elem.Div(
|
|
attrs.Props{},
|
|
elem.H3(
|
|
attrs.Props{
|
|
"role": "status",
|
|
"id": "pblabel",
|
|
"tabindex": "-1",
|
|
"autofocus": "",
|
|
},
|
|
elem.Text("Error "+bluemonday.StrictPolicy().Sanitize(err)),
|
|
),
|
|
installButton(galleryName),
|
|
).Render()
|
|
}
|
|
|
|
func ProgressBar(progress string) string {
|
|
return elem.Div(attrs.Props{
|
|
"class": "progress",
|
|
"role": "progressbar",
|
|
"aria-valuemin": "0",
|
|
"aria-valuemax": "100",
|
|
"aria-valuenow": "0",
|
|
"aria-labelledby": "pblabel",
|
|
},
|
|
elem.Div(attrs.Props{
|
|
"id": "pb",
|
|
"class": "progress-bar",
|
|
"style": "width:" + progress + "%",
|
|
}),
|
|
).Render()
|
|
}
|
|
|
|
func StartProgressBar(uid, progress, text string) string {
|
|
if progress == "" {
|
|
progress = "0"
|
|
}
|
|
return elem.Div(
|
|
attrs.Props{
|
|
"hx-trigger": "done",
|
|
"hx-get": "browse/job/" + uid,
|
|
"hx-swap": "outerHTML",
|
|
"hx-target": "this",
|
|
},
|
|
elem.H3(
|
|
attrs.Props{
|
|
"role": "status",
|
|
"id": "pblabel",
|
|
"tabindex": "-1",
|
|
"autofocus": "",
|
|
},
|
|
elem.Text(bluemonday.StrictPolicy().Sanitize(text)), //Perhaps overly defensive
|
|
elem.Div(attrs.Props{
|
|
"hx-get": "browse/job/progress/" + uid,
|
|
"hx-trigger": "every 600ms",
|
|
"hx-target": "this",
|
|
"hx-swap": "innerHTML",
|
|
},
|
|
elem.Raw(ProgressBar(progress)),
|
|
),
|
|
),
|
|
).Render()
|
|
}
|