Skip to content

Commit d9f21b9

Browse files
webui: add configurable base path support for subdirectory deployment
- Add configurable base path in svelte.config.js (default: '' for backward compatibility) - Update all hardcoded URLs to use prefix for API calls and navigation - Enables deployment in subdirectories (e.g., /llama, /ai, etc.) while maintaining root deployment compatibility - Affected: fetch calls to /props, /v1, /slots and all goto/href navigation Note: Current implementation assumes client and API share same base path. Future enhancement could separate client base path from API base path for advanced proxy/security configurations where only client UI is in subdirectory.
1 parent ad6bd90 commit d9f21b9

File tree

13 files changed

+33
-17
lines changed

13 files changed

+33
-17
lines changed

tools/server/public/index.html.gz

66 Bytes
Binary file not shown.

tools/server/webui/src/lib/components/app/chat/ChatSidebar/ChatSidebar.svelte

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
<script lang="ts">
2+
import { base } from '$app/paths';
23
import { goto } from '$app/navigation';
34
import { page } from '$app/state';
45
import { ChatSidebarConversationItem } from '$lib/components/app';
@@ -64,13 +65,13 @@
6465
searchQuery = '';
6566
}
6667
67-
await goto(`/chat/${id}`);
68+
await goto(`${base}/chat/${id}`);
6869
}
6970
</script>
7071

7172
<ScrollArea class="h-[100vh]">
7273
<Sidebar.Header class=" top-0 z-10 gap-6 bg-sidebar/50 px-4 pt-4 pb-2 backdrop-blur-lg md:sticky">
73-
<a href="/" onclick={handleMobileSidebarItemClick}>
74+
<a href="{base}/" onclick={handleMobileSidebarItemClick}>
7475
<h1 class="inline-flex items-center gap-1 px-2 text-xl font-semibold">llama.cpp</h1>
7576
</a>
7677

tools/server/webui/src/lib/components/app/chat/ChatSidebar/ChatSidebarActions.svelte

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
<script lang="ts">
2+
import { base } from '$app/paths';
23
import { Search, SquarePen, X } from '@lucide/svelte';
34
import { KeyboardShortcutInfo } from '$lib/components/app';
45
import { Button } from '$lib/components/ui/button';
@@ -51,7 +52,7 @@
5152
{:else}
5253
<Button
5354
class="w-full justify-between hover:[&>kbd]:opacity-100"
54-
href="/?new_chat=true"
55+
href="{base}/?new_chat=true"
5556
onclick={handleMobileSidebarItemClick}
5657
variant="ghost"
5758
>

tools/server/webui/src/lib/components/app/server/ServerErrorSplash.svelte

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
<script lang="ts">
2+
import { base } from '$app/paths';
23
import { AlertTriangle, RefreshCw, Key, CheckCircle, XCircle } from '@lucide/svelte';
34
import { goto } from '$app/navigation';
45
import { Button } from '$lib/components/ui/button';
@@ -64,7 +65,7 @@
6465
updateConfig('apiKey', apiKeyInput.trim());
6566
6667
// Test the API key by making a real request to the server
67-
const response = await fetch('/props', {
68+
const response = await fetch('${base}/props', {
6869
headers: {
6970
'Content-Type': 'application/json',
7071
Authorization: `Bearer ${apiKeyInput.trim()}`
@@ -77,7 +78,7 @@
7778
7879
// Show success state briefly, then navigate to home
7980
setTimeout(() => {
80-
goto('/');
81+
goto(`${base}/`);
8182
}, 1000);
8283
} else {
8384
// API key is invalid - User Story A

tools/server/webui/src/lib/services/chat.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { base } from '$app/paths';
12
import { config } from '$lib/stores/settings.svelte';
23
import { slotsService } from './slots';
34
/**
@@ -164,7 +165,7 @@ export class ChatService {
164165
const currentConfig = config();
165166
const apiKey = currentConfig.apiKey?.toString().trim();
166167

167-
const response = await fetch(`/v1/chat/completions`, {
168+
const response = await fetch(`${base}/v1/chat/completions`, {
168169
method: 'POST',
169170
headers: {
170171
'Content-Type': 'application/json',
@@ -531,7 +532,7 @@ export class ChatService {
531532
const currentConfig = config();
532533
const apiKey = currentConfig.apiKey?.toString().trim();
533534

534-
const response = await fetch(`/props`, {
535+
const response = await fetch(`${base}/props`, {
535536
headers: {
536537
'Content-Type': 'application/json',
537538
...(apiKey ? { Authorization: `Bearer ${apiKey}` } : {})

tools/server/webui/src/lib/services/slots.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { base } from '$app/paths';
12
import { config } from '$lib/stores/settings.svelte';
23

34
/**
@@ -138,7 +139,7 @@ export class SlotsService {
138139
const currentConfig = config();
139140
const apiKey = currentConfig.apiKey?.toString().trim();
140141

141-
const response = await fetch('/slots', {
142+
const response = await fetch(`${base}/slots`, {
142143
headers: {
143144
...(apiKey ? { Authorization: `Bearer ${apiKey}` } : {})
144145
}

tools/server/webui/src/lib/stores/chat.svelte.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { base } from '$app/paths';
12
import { DatabaseStore } from '$lib/stores/database';
23
import { chatService, slotsService } from '$lib/services';
34
import { serverStore } from '$lib/stores/server.svelte';
@@ -100,7 +101,7 @@ class ChatStore {
100101

101102
this.maxContextError = null;
102103

103-
await goto(`/chat/${conversation.id}`);
104+
await goto(`${base}/chat/${conversation.id}`);
104105

105106
return conversation.id;
106107
}
@@ -910,7 +911,7 @@ class ChatStore {
910911
if (this.activeConversation?.id === convId) {
911912
this.activeConversation = null;
912913
this.activeMessages = [];
913-
await goto('/?new_chat=true');
914+
await goto(`${base}/?new_chat=true`);
914915
}
915916
} catch (error) {
916917
console.error('Failed to delete conversation:', error);

tools/server/webui/src/lib/stores/server.svelte.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { base } from '$app/paths';
12
import { ChatService } from '$lib/services/chat';
23
import { config } from '$lib/stores/settings.svelte';
34

@@ -98,7 +99,7 @@ class ServerStore {
9899
const currentConfig = config();
99100
const apiKey = currentConfig.apiKey?.toString().trim();
100101

101-
const response = await fetch('/slots', {
102+
const response = await fetch(`${base}/slots`, {
102103
headers: {
103104
...(apiKey ? { Authorization: `Bearer ${apiKey}` } : {})
104105
}

tools/server/webui/src/lib/utils/api-key-validation.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { base } from '$app/paths';
12
import { error } from '@sveltejs/kit';
23
import { browser } from '$app/environment';
34
import { config } from '$lib/stores/settings.svelte';
@@ -22,7 +23,7 @@ export async function validateApiKey(fetch: typeof globalThis.fetch): Promise<vo
2223
headers.Authorization = `Bearer ${apiKey}`;
2324
}
2425

25-
const response = await fetch('/props', { headers });
26+
const response = await fetch(`${base}/props`, { headers });
2627

2728
if (!response.ok) {
2829
if (response.status === 401 || response.status === 403) {

tools/server/webui/src/routes/+error.svelte

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
<script lang="ts">
2+
import { base } from '$app/paths';
23
import { page } from '$app/stores';
34
import { goto } from '$app/navigation';
45
import { ServerErrorSplash } from '$lib/components/app';
@@ -17,7 +18,7 @@
1718
1819
function handleRetry() {
1920
// Navigate back to home page after successful API key validation
20-
goto('/');
21+
goto(`${base}/`);
2122
}
2223
</script>
2324

@@ -60,7 +61,7 @@
6061
</p>
6162
</div>
6263
<button
63-
onclick={() => goto('/')}
64+
onclick={() => goto(`${base}/`)}
6465
class="rounded-md bg-primary px-4 py-2 text-primary-foreground hover:bg-primary/90"
6566
>
6667
Go Home

0 commit comments

Comments
 (0)