mirror of
https://github.com/mudler/LocalAI.git
synced 2025-01-11 23:42:48 +00:00
8cc2d01caa
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>
87 lines
3.9 KiB
HTML
87 lines
3.9 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
{{template "views/partials/head" .}}
|
|
<script defer src="static/tts.js"></script>
|
|
|
|
<body class="bg-gray-900 text-gray-200">
|
|
<div class="flex flex-col min-h-screen">
|
|
|
|
{{template "views/partials/navbar" .}}
|
|
<div class="container mx-auto px-4 flex-grow " x-data="{ component: 'menu' }">
|
|
<div class="mt-12">
|
|
<div class="flex items-center justify-center text-center pb-2">
|
|
<span class="text-3xl font-semibold text-gray-100">
|
|
<i class="fa-solid fa-music"></i> Text to speech/audio
|
|
<a href="https://localai.io/features/text-to-audio/" target="_blank" >
|
|
<i class="fas fa-circle-info pr-2"></i>
|
|
</a>
|
|
</span>
|
|
|
|
</div>
|
|
<div class="text-center font-semibold text-gray-100">
|
|
<div class="flex items-center justify-between">
|
|
|
|
<div x-show="component === 'menu'" id="menu">
|
|
<button @click="component = 'key'" title="Update API key"
|
|
class="m-2 float-right inline-block rounded bg-primary px-6 pb-2.5 mb-3 pt-2.5 text-xs font-medium uppercase leading-normal text-white shadow-primary-3 transition duration-150 ease-in-out hover:bg-primary-accent-300 hover:shadow-primary-2 focus:bg-primary-accent-300 focus:shadow-primary-2 focus:outline-none focus:ring-0 active:bg-primary-600 active:shadow-primary-2 dark:shadow-black/30 dark:hover:shadow-dark-strong dark:focus:shadow-dark-strong dark:active:shadow-dark-strong"
|
|
>Set API Key🔑</button>
|
|
</div>
|
|
<form x-show="component === 'key'" id="key">
|
|
<input
|
|
type="password"
|
|
id="apiKey"
|
|
name="apiKey"
|
|
placeholder="OpenAI API Key"
|
|
x-model.lazy="key"
|
|
/>
|
|
<button @click="component = 'menu'" type="submit" title="Save API key">
|
|
🔒
|
|
</button>
|
|
</form>
|
|
|
|
<select x-data="{ link : '' }" x-model="link" x-init="$watch('link', value => window.location = link)"
|
|
class="bg-gray-800 text-white border border-gray-600 focus:border-blue-500 focus:ring focus:ring-blue-500 focus:ring-opacity-50 rounded-md shadow-sm p-2 appearance-none"
|
|
>
|
|
<!-- Options -->
|
|
<option value="" disabled class="text-gray-400" >Select a model</option>
|
|
{{ $model:=.Model}}
|
|
{{ range .ModelsConfig }}
|
|
{{ if eq .Name $model }}
|
|
<option value="tts/{{.Name}}" selected class="bg-gray-700 text-white">{{.Name}}</option>
|
|
{{ else }}
|
|
<option value="tts/{{.Name}}" class="bg-gray-700 text-white">{{.Name}}</option>
|
|
{{ end }}
|
|
{{ end }}
|
|
</select>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mt-12">
|
|
<input id="tts-model" type="hidden" value="{{.Model}}">
|
|
<form id="tts" action="tts/{{.Model}}" method="get">
|
|
<input
|
|
type="text"
|
|
id="input"
|
|
name="input"
|
|
placeholder="Prompt…"
|
|
autocomplete="off"
|
|
class="p-2 border rounded w-full bg-gray-600 text-white placeholder-gray-300"
|
|
required
|
|
/>
|
|
</form>
|
|
<div class="container max-w-screen-lg mx-auto mt-4 pb-10 flex justify-center">
|
|
<div id="loader" class="my-2 loader" ></div>
|
|
</div>
|
|
<div class="container max-w-screen-lg mx-auto mt-4 pb-10 flex justify-center">
|
|
<div id="result" class="mx-auto"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{{template "views/partials/footer" .}}
|
|
</div>
|
|
</body>
|
|
</html>
|